From a5b5103756c8428077258306a9be85205c836a94 Mon Sep 17 00:00:00 2001 From: Zane Shelley Date: Fri, 3 Nov 2017 15:52:10 -0500 Subject: FIRDATA: fix error handling for SCOMs via SBE FIFO Change-Id: I31f13780de325ecc66b733b0571c9801b615d851 CQ: SW406544 Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/49072 Tested-by: FSP CI Jenkins Reviewed-by: William A. Bryan Reviewed-by: Prachi Gupta Reviewed-by: William G. Hoffa Reviewed-by: Martha Broyles --- src/occ_gpe0/firdata/firData.c | 2 + src/occ_gpe0/firdata/sbe_fifo.c | 324 +++++++++++++++++++++------------------- src/occ_gpe0/firdata/sbe_fifo.h | 45 +++--- 3 files changed, 189 insertions(+), 182 deletions(-) (limited to 'src/occ_gpe0/firdata') diff --git a/src/occ_gpe0/firdata/firData.c b/src/occ_gpe0/firdata/firData.c index 78468ce..7ef2d7c 100644 --- a/src/occ_gpe0/firdata/firData.c +++ b/src/occ_gpe0/firdata/firData.c @@ -775,8 +775,10 @@ bool FirData_addTrgtToPnor( FirData_t * io_fd, SCOM_Trgt_t i_sTrgt, *o_noAttn = false; /* Must be false if there are no global regs. */ +#ifdef DEBUG_PRD_CHKSTOP_ANALYSIS TRAC_IMP( "FIRDATA: t=%d p=%d u=%d FSI=0x%08x", i_sTrgt.type, i_sTrgt.procPos, i_sTrgt.procUnitPos, (uint32_t)i_sTrgt.fsiBaseAddr); +#endif do { diff --git a/src/occ_gpe0/firdata/sbe_fifo.c b/src/occ_gpe0/firdata/sbe_fifo.c index aac8b15..dafc93b 100644 --- a/src/occ_gpe0/firdata/sbe_fifo.c +++ b/src/occ_gpe0/firdata/sbe_fifo.c @@ -25,8 +25,25 @@ #include "sbe_fifo.h" #include #include +#include extern void busy_wait(uint32_t t_microseconds); + +enum +{ + RC_RESP_DATA_OVERFLOW = 1001, + RC_RESP_MIN_SIZE_INVALID = 1002, + RC_RESP_DISTANCE_INVALID = 1003, + RC_RESP_MAGIC_WORD_INVALID = 1004, + RC_RESP_UNEXPECTED_CMD = 1005, + RC_RESP_UNEXPECTED_DATA_SIZE = 1006, + + RC_RESP_SCOM_ERROR = 1010, + + RC_FIFO_TIMEOUT_UP = 1021, + RC_FIFO_TIMEOUT_DN = 1022, +}; + /** @brief Waits for FIFO to be ready to be written to * @param i_target The SCOM target. * @return Non-SUCCESS if the SCOM fails. SUCCESS otherwise. @@ -58,7 +75,7 @@ uint32_t waitUpFifoReady(SCOM_Trgt_t* i_target) //Check for timeout if(l_elapsed_time_ns >= MAX_UP_FIFO_TIMEOUT_NS) { - l_rc = FAIL; + l_rc = RC_FIFO_TIMEOUT_UP; TRAC_ERR("waitUpFifoReady: timeout occurred while waiting for" " FIFO to clear"); break; @@ -104,7 +121,7 @@ uint32_t waitDnFifoReady(SCOM_Trgt_t* i_target, uint32_t* o_status) { TRAC_ERR("waitDnFifoReady: timeout waiting for downstream FIFO" " to be empty."); - l_rc = FAIL; + l_rc = RC_FIFO_TIMEOUT_DN; break; } @@ -182,181 +199,184 @@ uint32_t writeRequest(SCOM_Trgt_t* i_target, uint32_t* i_fifoRequest) return l_rc; } +// For error path debug. +void printBuffer( uint32_t* i_readBuffer, uint32_t i_wordsReceived ) +{ + // OCC traces only support max 4 arguments and trace buffers very limited. + // So try to print as many entries as possible on one line as possible. + uint32_t i = 0; + for ( i = 0; i < i_wordsReceived; i+=4 ) + { + uint32_t j = i_wordsReceived - i; + if ( 4 <= j ) + { + TRAC_ERR( " %08x %08x %08x %08x", i_readBuffer[i], + i_readBuffer[i+1], i_readBuffer[i+2], i_readBuffer[i+3] ); + } + else if ( 3 == j ) + { + TRAC_ERR( " %08x %08x %08x", i_readBuffer[i], i_readBuffer[i+1], + i_readBuffer[i+2] ); + } + else if ( 2 == j ) + { + TRAC_ERR( " %08x %08x", i_readBuffer[i], i_readBuffer[i+1] ); + } + else if ( 1 == j ) + { + TRAC_ERR( " %08x", i_readBuffer[i] ); + } + } +} + /** @brief Reads and processes the FIFO response - * @param i_target The SCOM target. - * @param i_fifoRequest the original FIFO request. - * @param o_fifoResponse the FIFO response. - * @param i_responseSize the expected size of the response. - * @return Non-SUCCESS if the SCOM fails. SUCCESS otherwise. + * @param i_target The SCOM target. + * @param i_fifoReqCmd The FIFO request command. + * @param o_responseData The returned response data (getSCOMs only). + * @return Non-SUCCESS on error, otherwise SUCCESS. */ -uint32_t readResponse(SCOM_Trgt_t* i_target, - uint32_t* i_fifoRequest, - uint32_t* o_fifoResponse, - uint32_t i_responseSize) +uint32_t readResponse( SCOM_Trgt_t* i_target, FifoCmd_t* i_fifoReqCmd, + uint64_t* o_responseData ) { uint32_t l_rc = SUCCESS; - uint32_t l_readBuffer[READ_BUFFER_SIZE]; - - // EOT is expected before the response buffer is full. Room for - // the PCBPIB status or FFDC is included, but is only returned - // if there is an error. The last received word has the distance - // to the status, which is placed at the end of the returned data - // in order to reflect errors during transfer. - - uint32_t* l_received = o_fifoResponse; // advance as words are received - uint32_t l_maxWords = i_responseSize / sizeof(uint32_t); - uint32_t l_wordsReceived = 0; // Used to validata the "distance" to status - bool l_eotReceived = FALSE; - uint32_t l_lastWord = 0; // Last word received. Final read is the "distance" - // in words to the status header. - bool l_overRun = FALSE; + + memset( o_responseData, 0, sizeof(uint64_t) ); // Just in case. + + uint32_t readBuffer[READ_BUFFER_SIZE]; + uint32_t wordsReceived = 0; do { - // Wait for data to be ready to receive (download) or if the EOT - // has been sent. If not EOT, then data ready to receive. + // Wait to read data or EOT from the FIFO. uint32_t l_status = 0; - l_rc = waitDnFifoReady(i_target, &l_status); - if(l_rc != SUCCESS) + l_rc = waitDnFifoReady( i_target, &l_status ); + if ( SUCCESS != l_rc ) { return l_rc; } - if(l_status & DNFIFO_STATUS_DEQUEUED_EOT_FLAG) + // Check for EOT. + if ( l_status & DNFIFO_STATUS_DEQUEUED_EOT_FLAG ) { - l_eotReceived = TRUE; - // Ignore EOT dummy word - if(l_wordsReceived >= (sizeof(struct statusHeader) / sizeof(uint32_t))) - { - if(l_overRun == FALSE) - { - l_received--; - l_wordsReceived--; - l_lastWord = o_fifoResponse[l_wordsReceived-1]; - } - else - { - l_lastWord = l_readBuffer[l_wordsReceived-2]; - } - } - break; - } + // There should be a word at the end of the buffer containing a EOT + // dummy word, which can be ignored. + if ( 0 < wordsReceived ) wordsReceived--; - // When error occurs, SBE will write more than l_maxWords - // we have to keep reading 1 word at a time until we get EOT - // or more than READ_BUFFER_SIZE. Save what we read in the buffer - if(l_wordsReceived >= l_maxWords) - { - l_overRun = TRUE; + break; // Nothing more to read. } - // Read next word - l_rc = getfsi(i_target, SBE_FIFO_DNFIFO_DATA_OUT, &l_lastWord); - if(l_rc != SUCCESS) + // Ensure there is enough room in the buffer to read the next word. + if ( READ_BUFFER_SIZE <= wordsReceived ) { - return l_rc; + TRAC_ERR( "readResponse: data overflow without EOT. " + "wordsReceived=%u", wordsReceived ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_DATA_OVERFLOW; } - l_readBuffer[l_wordsReceived] = l_lastWord; - - if(l_overRun == FALSE) + // Read the next word. + l_rc = getfsi( i_target, SBE_FIFO_DNFIFO_DATA_OUT, + &readBuffer[wordsReceived] ); + if ( SUCCESS != l_rc ) { - *l_received = l_lastWord; // Copy to returned output buffer - l_received++; // Advance to the next position - } - l_wordsReceived++; - - if(l_wordsReceived > READ_BUFFER_SIZE) - { - TRAC_ERR("readResponse: data overflow without EOT"); - l_rc = FAIL; return l_rc; } - }while(TRUE); + // Increment the number of words received. + wordsReceived++; - // At this point, l_wordsReceived of words received. - // l_received points to 1 word past last word received. - // l_lastWord has last word received, which is "distance" to status - // EOT is expected before running out of response buffer - if(!l_eotReceived) + } while ( TRUE ); + + // The only path that allows us to get here is if we successfully received + // the EOT. Notify the SBE that it has been received. + l_rc = putfsi( i_target, SBE_FIFO_DNFIFO_ACK_EOT, FSB_UPFIFO_SIG_EOT ); + if ( SUCCESS != l_rc ) { - l_rc = FAIL; - TRAC_ERR("readResponse: no EOT cmd = 0x%08x size = %d", - i_fifoRequest[1], i_responseSize); return l_rc; } - // Notify SBE that EOT has been received - uint32_t l_eotSig = FSB_UPFIFO_SIG_EOT; - l_rc = putfsi(i_target, SBE_FIFO_DNFIFO_ACK_EOT, l_eotSig); - if(l_rc != SUCCESS) + // At a minimum, the response should have returned enough data to contain + // the status header and a word that contains the distance from the end of + // the response to the beginning of the status header. + uint32_t l_minWords = (sizeof(FifoRespStatus_t) / sizeof(uint32_t)) + 1; + if ( wordsReceived < l_minWords ) { - return l_rc; + TRAC_ERR( "readResponse: minimum response size is invalid. " + "wordsReceived=%u", wordsReceived ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_MIN_SIZE_INVALID; } - // Determine if transmission is successful. - // Last word received has the distance to status in words including itself. - // l_wordsReceived has number of words received. - // Need to have received at least status header and distance word. - if((l_lastWord < (sizeof(struct statusHeader)/sizeof(uint32_t) + 1)) || - (l_wordsReceived < (sizeof(struct statusHeader)/sizeof(uint32_t) + 1)) || - (l_lastWord > l_wordsReceived)) + // The distance between the end of the response to the beginning of the + // status header is stored in the last word in the buffer. + uint32_t distance = readBuffer[wordsReceived-1]; + if ( distance < l_minWords || wordsReceived < distance ) { - TRAC_ERR("readResponse: invalid status distance. Cmd = 0x%08x distance" - " = %d allocated response size = %d received word size = %d", - i_fifoRequest[1], l_lastWord, i_responseSize, l_wordsReceived); - l_rc = FAIL; - return l_rc; + TRAC_ERR( "readResponse: invalid response distance. wordsReceived=%u " + "distance=%u", wordsReceived, distance ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_DISTANCE_INVALID; } - // Check status for success. - // l_received points one word past last word received. - // l_lastWord has number of words to status header including self. - uint32_t* l_statusTmp = (l_overRun == FALSE) ? (l_received - l_lastWord) : - &l_readBuffer[l_wordsReceived - 1]; - struct statusHeader* l_statusHeader = (struct statusHeader*)l_statusTmp; - if((FIFO_STATUS_MAGIC != l_statusHeader->magic) || - (SBE_PRI_OPERATION_SUCCESSFUL != l_statusHeader->primaryStatus) || - (SBE_SEC_OPERATION_SUCCESSFUL != l_statusHeader->secondaryStatus)) + // Check for a successful response. + uint32_t numDataWords = wordsReceived - distance; + FifoRespStatus_t* status = (FifoRespStatus_t*)(&readBuffer[numDataWords]); + + if ( FIFO_STATUS_MAGIC != status->magic ) { - TRAC_ERR("readResponse: failing downstream status cmd = 0x%08x magic = " - "0x%08x primary status = 0x%08x secondary status = 0x%08x", - i_fifoRequest[1], - l_statusHeader->magic, - l_statusHeader->primaryStatus, - l_statusHeader->secondaryStatus); - - l_rc = FAIL; + TRAC_ERR( "readResponse: invalid magic word. magic=0x%04x", + status->magic ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_MAGIC_WORD_INVALID; } - return l_rc; -} + // Verify that this response was for the command that was sent. + if ( i_fifoReqCmd->u != status->command.u ) + { + TRAC_ERR( "readResponse: unexpected response command. cmd=0x%08x", + status->command.u ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_UNEXPECTED_CMD; + } -/** @brief Performs a FIFO operation (read or write) - * @param i_target The SCOM target. - * @param i_fifoRequest the FIFO request data structure - * @param i_fifoResponse the response from SBE - * @param i_responseSize the size of the response - * @return Non-SUCCESS if the operation fails. SUCCESS otherwise. - */ -uint32_t performFifoChipOp(SCOM_Trgt_t* i_target, - uint32_t* i_fifoRequest, - uint32_t* i_fifoResponse, - uint32_t i_responseSize) -{ - uint32_t l_rc = SUCCESS; + // For getSCOMs only, get the response data (if it exists) regardless if + // there was a SCOM error. + uint32_t expDataWords = 0; + if ( SBE_FIFO_CMD_GET_SCOM == status->command.s.type ) + { + expDataWords = sizeof(uint64_t) / sizeof(uint32_t); + if ( expDataWords == numDataWords ) + { + memcpy( o_responseData, &readBuffer[0], sizeof(uint64_t) ); + } + } - l_rc = writeRequest(i_target, i_fifoRequest); - if(l_rc != SUCCESS) + // Check for SCOM errors. + if ( (SBE_PRI_OPERATION_SUCCESSFUL != status->primaryStatus ) || + (SBE_SEC_OPERATION_SUCCESSFUL != status->secondaryStatus) ) { - return l_rc; + // NOTE: If there was some sort of SCOM error, there should be an FFDC + // section after the status. At this time, we have no use for the + // data so it will be ignored for now. + + TRAC_ERR( "readResponse: unexpected response status. cmd=0x%08x " + "primaryStatus=0x%08x secondaryStatus=0x%08x", + i_fifoReqCmd->u, status->primaryStatus, + status->secondaryStatus ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_SCOM_ERROR; } - l_rc = readResponse(i_target, - i_fifoRequest, - i_fifoResponse, - i_responseSize); + // The command was successful. Ensure the response data was at least a + // valid size. + if ( expDataWords != numDataWords ) + { + TRAC_ERR( "readResponse: unexpected response data size. cmd=0x%08x " + "wordsReceived=%u distance=%u", i_fifoReqCmd->u, + wordsReceived, distance ); + printBuffer( readBuffer, wordsReceived ); + return RC_RESP_UNEXPECTED_DATA_SIZE; + } return l_rc; } @@ -372,19 +392,19 @@ int32_t putFifoScom(SCOM_Trgt_t* i_target, uint64_t i_addr, uint64_t i_data) uint32_t l_rc = SUCCESS; struct fifoPutScomRequest l_fifoRequest; - struct fifoPutScomResponse l_fifoResponse; - l_fifoRequest.wordCnt = PUT_SCOM_REQUEST_WORD_CNT; l_fifoRequest.reserved = 0; - l_fifoRequest.commandClass = SBE_FIFO_CLASS_SCOM_ACCESS; - l_fifoRequest.command = SBE_FIFO_CMD_PUT_SCOM; + l_fifoRequest.command.s.class = SBE_FIFO_CLASS_SCOM_ACCESS; + l_fifoRequest.command.s.type = SBE_FIFO_CMD_PUT_SCOM; l_fifoRequest.address = i_addr; l_fifoRequest.data = i_data; - l_rc = performFifoChipOp(i_target, - (uint32_t*)&l_fifoRequest, - (uint32_t*)&l_fifoResponse, - sizeof(struct fifoPutScomResponse)); + l_rc = writeRequest( i_target, (uint32_t*)&l_fifoRequest ); + if ( SUCCESS == l_rc ) + { + uint64_t unused = 0; + l_rc = readResponse( i_target, &l_fifoRequest.command, &unused ); + } if ( l_rc != SUCCESS ) { @@ -407,18 +427,17 @@ int32_t getFifoScom(SCOM_Trgt_t* i_target, uint64_t i_addr, uint64_t* o_data) uint32_t l_rc = SUCCESS; struct fifoGetScomRequest l_fifoRequest; - struct fifoGetScomResponse l_fifoResponse; - l_fifoRequest.wordCnt = GET_SCOM_REQUEST_WORD_CNT; l_fifoRequest.reserved = 0; - l_fifoRequest.commandClass = SBE_FIFO_CLASS_SCOM_ACCESS; - l_fifoRequest.command = SBE_FIFO_CMD_GET_SCOM; + l_fifoRequest.command.s.class = SBE_FIFO_CLASS_SCOM_ACCESS; + l_fifoRequest.command.s.type = SBE_FIFO_CMD_GET_SCOM; l_fifoRequest.address = i_addr; - l_rc = performFifoChipOp(i_target, - (uint32_t*)&l_fifoRequest, - (uint32_t*)&l_fifoResponse, - sizeof(struct fifoGetScomResponse)); + l_rc = writeRequest( i_target, (uint32_t*)&l_fifoRequest ); + if ( SUCCESS == l_rc ) + { + l_rc = readResponse( i_target, &l_fifoRequest.command, o_data ); + } if ( l_rc != SUCCESS ) { @@ -427,8 +446,5 @@ int32_t getFifoScom(SCOM_Trgt_t* i_target, uint64_t i_addr, uint64_t* o_data) putfsi( i_target, 0x2450, l_data ); } - //Always return data even if there is an error - *o_data = l_fifoResponse.data; - return l_rc; } diff --git a/src/occ_gpe0/firdata/sbe_fifo.h b/src/occ_gpe0/firdata/sbe_fifo.h index 193039a..83d293a 100644 --- a/src/occ_gpe0/firdata/sbe_fifo.h +++ b/src/occ_gpe0/firdata/sbe_fifo.h @@ -54,55 +54,44 @@ #define SBE_PRI_OPERATION_SUCCESSFUL 0x00 #define SBE_SEC_OPERATION_SUCCESSFUL 0x00 -struct statusHeader +typedef union +{ + struct + { + uint8_t class; + uint8_t type; + } PACKED s; + + uint16_t u; + +} FifoCmd_t; + +typedef struct { uint16_t magic; //set to 0xC0DE - uint8_t commandClass; - uint8_t command; + FifoCmd_t command; uint16_t primaryStatus; uint16_t secondaryStatus; -} PACKED; -struct ffdc_struct -{ - const void* ptr; - uint32_t size; -}; +} PACKED FifoRespStatus_t; struct fifoPutScomRequest { uint32_t wordCnt; uint16_t reserved; - uint8_t commandClass; - uint8_t command; + FifoCmd_t command; uint64_t address; uint64_t data; } PACKED; -struct fifoPutScomResponse -{ - struct statusHeader status; - struct ffdc_struct ffdc; //ffdc data - uint32_t status_distance; //distance to status -} PACKED; - struct fifoGetScomRequest { uint32_t wordCnt; uint16_t reserved; - uint8_t commandClass; - uint8_t command; + FifoCmd_t command; uint64_t address; } PACKED; -struct fifoGetScomResponse -{ - uint64_t data; //Data (0..31) + (32..63) - struct statusHeader status; - struct ffdc_struct ffdc; //ffdc data - uint32_t status_distance; // distance to status -} PACKED; - /** @brief Performs a write SCOM operation using SBE FIFO * @param i_target The SCOM target. * @param i_addr 64-bit SCOM address. -- cgit v1.2.1