diff options
-rw-r--r-- | src/include/usr/ucd/ucd_reasoncodes.H | 3 | ||||
-rwxr-xr-x | src/usr/i2c/i2c.C | 8 | ||||
-rw-r--r-- | src/usr/isteps/ucd/updateUcdFlash.C | 447 |
3 files changed, 448 insertions, 10 deletions
diff --git a/src/include/usr/ucd/ucd_reasoncodes.H b/src/include/usr/ucd/ucd_reasoncodes.H index 4e7cc9841..9462da3bd 100644 --- a/src/include/usr/ucd/ucd_reasoncodes.H +++ b/src/include/usr/ucd/ucd_reasoncodes.H @@ -34,6 +34,7 @@ namespace UCD_RC { MOD_UCD_INIT = 0x01, MOD_UPDATE_ALL_UCD_FLASH_IMAGES = 0x02, + MOD_UPDATE_UCD_FLASH_IMAGE = 0x03, }; enum UcdReasonCode @@ -45,6 +46,8 @@ namespace UCD_RC UCD_TOC_ENTRY_TOO_SMALL = UCD_COMP_ID | 0x05, UCD_UNSUPPORTED_DEVICE_ID = UCD_COMP_ID | 0x06, UCD_EOF = UCD_COMP_ID | 0x07, + UCD_UNSUPPORTED_OPERATION_REQUEST = UCD_COMP_ID | 0x08, + UCD_INVALID_COMMANDLINE = UCD_COMP_ID | 0x09, }; }; // namespace UCD diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C index 7f696c4e6..88233dadb 100755 --- a/src/usr/i2c/i2c.C +++ b/src/usr/i2c/i2c.C @@ -1452,7 +1452,6 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, || (i_args.subop == DeviceFW::I2C_SMBUS_WORD))) { // Note: The SMBUS spec calls a 2 byte value a "word" - TRACUCOMP(g_trac_i2c, INFO_MRK "I2C SMBUS Write %s: Command code = 0x%02X, " "Use PEC = %d.", @@ -1542,9 +1541,9 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, { TRACUCOMP(g_trac_i2c, INFO_MRK "I2C SMBUS Block Write: Command code = 0x%02X, " - "Use PEC = %d.", + "Use PEC = %d. io_buflen = %d", i_args.smbus.commandCode, - i_args.smbus.usePec); + i_args.smbus.usePec, io_buflen); // If requested length is for < 1 byte or > 255 bytes for a block // write transaction, throw an error. @@ -1622,7 +1621,6 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, || (i_args.subop == DeviceFW::I2C_SMBUS_WORD))) { // Note: The SMBUS spec calls a 2 byte value a "word" - TRACUCOMP(g_trac_i2c, INFO_MRK "I2C SMBUS Read %s: Command code = 0x%02X, " "Use PEC = %d", @@ -2671,7 +2669,7 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target, break; } - for( bytesWritten = 0x0; bytesWritten < io_buflen; bytesWritten++ ) + for( bytesWritten = 0x0; bytesWritten < io_buflen; ++bytesWritten ) { // Wait for FIFO space to be available for the write err = i2cWaitForFifoSpace( i_target, diff --git a/src/usr/isteps/ucd/updateUcdFlash.C b/src/usr/isteps/ucd/updateUcdFlash.C index d0e746c18..037a731e9 100644 --- a/src/usr/isteps/ucd/updateUcdFlash.C +++ b/src/usr/isteps/ucd/updateUcdFlash.C @@ -39,9 +39,12 @@ #include <errl/errlentry.H> #include <errl/errlmanager.H> #include <errl/hberrltypes.H> +#include <errl/errludstring.H> #include <trace/interface.H> #include <string.h> +#include <sys/time.h> +#include <stdlib.h> #include <hbotcompid.H> #include <util/utilmem.H> #include <util/utilstream.H> @@ -57,6 +60,10 @@ namespace UCD // UCD Series trace_desc_t* g_trac_ucd = nullptr; TRAC_INIT(&g_trac_ucd, UCD_COMP_NAME, 2*KILOBYTE); +// Switch to turn on and off debug traces: +#define TRACUCOMP(args...) TRACDCOMP(args) + + class Ucd { private: @@ -371,6 +378,106 @@ public: return err; } // end of initialize() + /** + * @brief smbus_op_t struct to hold on SMBUS operation + */ + struct smbus_op_t + { + uint64_t addr; + uint64_t cmd; + uint64_t pec_byte; + std::vector<uint8_t> vData; + + /** + * @brief smbus_op_t constructor + */ + smbus_op_t() + : addr(0), + cmd(0), + pec_byte(0) + { + } + + }; + + /** + * @brief Processes a command line from a UCD update file for + * WriteByte, WriteWord, and BlockWrite operations + * + * @param[in] i_str Pointer to the start of the 2nd field of the command + * NOTE: This parameter is not a const - it is updated + * @param[out] o_op Output structure with the derived info + * + * @note There is an expected layout for the 2nd, 3rd, and 4th fields for + * these operations. Any detected deviation will result in an assert + * + * @return void + */ + void convertStringToOp(char * i_str, smbus_op_t & o_op) + { + // PEC byte is calculated on all message bytes, including + // addresses and Read/Write bit. + // SMBus Fields are Request,Address,Command,Data with PEC byte + // For reads, the last field is what is expected back from the device + assert(i_str != nullptr, "convertStringToOp: i_str is nullptr"); + + // Second Field is Address + auto pDelimiter = strchr(i_str, ',' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + assert(false,"convertStringToOp: pDelimiter for 2nd field is nullptr"); + } + o_op.addr = strtoul(i_str, nullptr, 16); + + // Move Past Second Field + i_str = pDelimiter+1; + + // Third Field is Command + pDelimiter = strchr(i_str, ',' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + assert(false,"convertStringToOp: pDelimiter for 3rd field is nullptr"); + } + o_op.cmd = strtoul(i_str, nullptr, 16 ); + + // Move Past Third Field + i_str = pDelimiter+1; + + // Fourth field is Data + PEC byte + // - should already have '\0' at it from above + o_op.vData.clear(); + char char_byte[] = "\0\0\0"; + + // Skip the expected "0x" at the start of the data stream + i_str += 2; + + while (*i_str != '\0') + { + char_byte[0] = *(i_str++); // Get 1st nibble then increment + char_byte[1] = *(i_str++); // Get 2nd nibble then increment + o_op.vData.push_back(strtoul(char_byte, nullptr, 16)); + } + + // Last byte is pec byte + o_op.pec_byte = o_op.vData.back(); + o_op.vData.pop_back(); + + // Ignore checking pec byte for now as SMBUS Device Op calculates it + + // Setup data + TRACDCOMP(g_trac_ucd,"convertStringToOp: data size=%d, byte0=0x%.2X", + o_op.vData.size(), o_op.vData[0]); + + return; + } /** * @brief Updates a UCD target's flash image @@ -387,19 +494,340 @@ public: errlHndl_t updateUcdFlash(const void* i_pFlashImage, size_t i_size) { - errlHndl_t pError = nullptr; + errlHndl_t err = nullptr; - // Stub for future additional support TRACFCOMP(g_trac_ucd, ENTER_MRK"updateUcdFlash: ucd_tgt=0x%.08X, " "i2cInfo: e%d/p%d/da=0x%X. i_pFlashImage=%p, i_size=0x%X", TARGETING::get_huid(iv_pUcd), iv_i2cInfo.engine, iv_i2cInfo.port, iv_i2cInfo.devAddr, i_pFlashImage, i_size); - TRACFBIN(g_trac_ucd,"updateUcdFlash: Start of i_pFlashImage", - i_pFlashImage, 64); + TRACDBIN(g_trac_ucd,"updateUcdFlash: Start of i_pFlashImage", + i_pFlashImage, 128); + + size_t char_count = 0; + size_t op_count = 1; + char * tmp_str = reinterpret_cast<char*>( + const_cast<void*>(i_pFlashImage)); - return pError; + while (char_count < i_size) + { + size_t size=0; + size_t expSize = 0; + + // Each op is one line in the processed .csv file + auto pDelimiter = strchr(tmp_str, '\n' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + TRACFCOMP(g_trac_ucd,"updateUcdFlash: op=%d: " + "found nullptr: tmp_str=%p, pDelimiter=%p", + op_count, tmp_str, pDelimiter); + /*@ + * @errortype + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @reasoncode UCD_RC::UCD_INVALID_COMMANDLINE + * @moduleid UCD_RC::MOD_UPDATE_UCD_FLASH_IMAGE + * @userdata1 HUID of UCD Target + * @userdata2 The order of this operation + * @devdesc A 'new line' character was not found while + * processing this UCD flash image's command line + * @custdesc Unexpected IPL firmware data format error + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + UCD_RC::MOD_UPDATE_UCD_FLASH_IMAGE, + UCD_RC::UCD_INVALID_COMMANDLINE, + TARGETING::get_huid(iv_pUcd), + op_count, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + + break; + } + + size_t op_str_length = strlen(tmp_str); + char_count += op_str_length+1; + + TRACUCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s " + "(len=%d, count=%d, total=%d)", + op_count, tmp_str, op_str_length, char_count, i_size); + char * next_op = tmp_str + op_str_length + 1; + + // Process First Field - the Operation Type (or "Request") + pDelimiter = strchr(tmp_str, ',' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + assert(false,"updateUcdFlash: pDelimiter for 1st field is nullptr"); + } + // Save start for if-check and tracing purposes + char * op_type = tmp_str; + + // Move past first field + tmp_str = pDelimiter+1; + + // Majority of ops need this structure + smbus_op_t op{}; + + // Look for expected Operation Types + if (strcmp(op_type,"WriteByte") == 0) + { + // Convert the other fields to address, command, and data + convertStringToOp(tmp_str, op); + + // Data size == 1 is checked by SMBUS Device Operation + TRACUCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s: " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, data=0x%.2X, " + "pec=0x%.2X", op_count, op_type, op.addr, op.cmd, + op.vData.size(), op.vData[0], op.pec_byte); + + size = op.vData.size(); + expSize = size; + + err = deviceOp(DeviceFW::WRITE, + iv_pI2cMaster, + reinterpret_cast<void*>(op.vData.data()), + size, + DEVICE_I2C_SMBUS_BYTE(iv_i2cInfo.engine, + iv_i2cInfo.port, + iv_i2cInfo.devAddr, + op.cmd, + iv_i2cInfo.i2cMuxBusSelector, + &iv_i2cInfo.i2cMuxPath)); + + if (err) + { + TRACFCOMP(g_trac_ucd,ERR_MRK"updateUcdFlash: op=%d: %s: " + "DEVICE_I2C_SMBUS_BYTE Failed: err plid=0x%.8X. " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, data=0x%.2X, " + "pec=0x%.2X", op_count, op_type, err->plid(), + op.addr, op.cmd, op.vData.size(), op.vData[0], + op.pec_byte); + break; + } + assert(size==expSize, "updateUcdFlash: DEVICE_I2C_SMBUS_BYTE size mismatch: returned %d, but expected %d", + size, expSize); + } + else if (strcmp(op_type,"WriteWord") == 0) + { + // Convert the other fields to address, command, and data + convertStringToOp(tmp_str, op); + + // Data size == 2 is checked by SMBUS Device Operation + + TRACUCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s: " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, " + "data=0x%.2X 0x%.2X, pec=0x%.2X", + op_count, op_type, op.addr, op.cmd, op.vData.size(), + op.vData[0], op.vData[1], op.pec_byte); + + size = op.vData.size(); + expSize = size; + + err = deviceOp(DeviceFW::WRITE, + iv_pI2cMaster, + reinterpret_cast<void*>(op.vData.data()), + size, + DEVICE_I2C_SMBUS_WORD(iv_i2cInfo.engine, + iv_i2cInfo.port, + iv_i2cInfo.devAddr, + op.cmd, + iv_i2cInfo.i2cMuxBusSelector, + &iv_i2cInfo.i2cMuxPath)); + + if (err) + { + TRACFCOMP(g_trac_ucd,ERR_MRK"updateUcdFlash: op=%d: %s: " + "DEVICE_I2C_SMBUS_WORD Failed: err plid=0x%.8X. " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, data=0x%.2X " + "0x%.2X, pec=0x%.2X", op_count, op_type, err->plid(), + op.addr, op.cmd, op.vData.size(), op.vData[0], + op.vData[1], op.pec_byte); + break; + } + assert(size==expSize, "updateUcdFlash: DEVICE_I2C_SMBUS_WORD size mismatch: returned %d, but expected %d", + size, expSize); + } + else if (strcmp(op_type,"BlockWrite") == 0) + { + // Convert the other fields to address, command, and data + convertStringToOp(tmp_str, op); + + // Flash image file for BlockWrite has length as the first data + // element, but that element should not get passed into the + // I2C Device Driver calls as it already calculates the proper + // length based on what is being passed in. Also, this element + // needs to be removed so that the I2C DD can calculate the PEC + // byte correctly. + op.vData.erase(op.vData.begin()); + + TRACUCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, data0=0x%.2X, " + "pec=0x%X", op_count, op_type, op.addr, op.cmd, + op.vData.size(), op.vData[0], op.pec_byte); + + size = op.vData.size(); + expSize = size; + + err = deviceOp(DeviceFW::WRITE, + iv_pI2cMaster, + reinterpret_cast<void*>(op.vData.data()), + size, + DEVICE_I2C_SMBUS_BLOCK(iv_i2cInfo.engine, + iv_i2cInfo.port, + iv_i2cInfo.devAddr, + op.cmd, + iv_i2cInfo.i2cMuxBusSelector, + &iv_i2cInfo.i2cMuxPath)); + + if (err) + { + TRACFCOMP(g_trac_ucd,ERR_MRK"updateUcdFlash: op=%d: %s: " + "DEVICE_I2C_SMBUS_BLOCK Failed. err plid=0x%.8X. " + "addr=0x%.2X, cmd=0x%.2X data_size=%d, data0=0x%.2X, " + "pec=0x%X", op_count, op_type, err->plid(), op.addr, + op.cmd, op.vData.size(), op.vData[0], op.pec_byte); + break; + } + assert(size==expSize, "updateUcdFlash: DEVICE_I2C_SMBUS_BLOCK size mismatch: returned %d, but expected %d", + size, expSize); + } + else if (strcmp(op_type,"SendByte") == 0) + { + // Second Field is Address + pDelimiter = strchr(tmp_str, ',' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + assert(false,"updateUcdFlash: SendByte: pDelimiter for 2nd field is nullptr"); + } + uint64_t addr = strtoul(tmp_str, nullptr, 16); + + // Move Past Second Field + tmp_str = pDelimiter+1; + + // Third Field is Command and ends with a nullptr already + uint8_t cmd = strtoul(tmp_str, nullptr, 16 ); + + // There is no additional data, which is why convertStringToOp() + // is not being used + TRACUCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s: " + "addr=0x%.2X, cmd=0x%.2X", + op_count, op_type, addr, cmd); + + // For this operation, the 'cmd' is the actual data sent, but + // we use the following API without the cmd field + size = sizeof(uint8_t); + expSize = size; + + err = deviceOp(DeviceFW::WRITE, + iv_pI2cMaster, + &cmd, + size, + DEVICE_I2C_SMBUS_SEND_OR_RECV(iv_i2cInfo.engine, + iv_i2cInfo.port, + iv_i2cInfo.devAddr, + iv_i2cInfo.i2cMuxBusSelector, + &iv_i2cInfo.i2cMuxPath)); + + if (err) + { + TRACFCOMP(g_trac_ucd,ERR_MRK"updateUcdFlash: op=%d: %s: " + "DEVICE_I2C_SMBUS_SEND_OR_RECV Failed. err plid=" + "0x%.8X. addr=0x%.2X, cmd=0x%.2X", + op_count, op_type, err->plid(), op.addr, op.cmd); + break; + } + assert(size==expSize, "updateUcdFlash: DEVICE_I2C_SMBUS_SEND_OR_RECV size mismatch: returned %d, but expected %d", + size, expSize); + } + else if (strcmp(op_type,"Pause") == 0) + { + // 2nd field is decimal value of ms to wait + pDelimiter = strchr(tmp_str, ',' ); + if (pDelimiter != nullptr) + { + *pDelimiter = '\0'; + } + else + { + assert(false,"updateUcdFlash: Pause: pDelimiter for 2nd field is nullptr"); + } + + // NOTE: Hostboot's implementation of strtoul only supports + // base 16 so have to do this in a clunky way + uint64_t sleep_ms = 0; + + while (*tmp_str != '\0') + { + // Another numeral found so shift previous result + // by multiplying by 10 and then add in the current + // value, where *tmp_str will be in ASCII so you need + // to subtract ASCII '0' which is 0x30 + sleep_ms = (10 * sleep_ms) + (*(tmp_str++) - 0x30); + } + + // Important to trace this pause so users know why there is a + // gap in the trace output + TRACFCOMP(g_trac_ucd,"updateUcdFlash: op=%d: %s: " + "Sleep for %d ms", op_count, op_type, sleep_ms); + nanosleep(0, sleep_ms * NS_PER_MSEC); + } + else + { + TRACFCOMP(g_trac_ucd,ERR_MRK"updateUcdFlash: op=%d: " + "Unrecognized Requested Operation: %s", + op_count, op_type); + + /*@ + * @errortype + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @reasoncode UCD_RC::UCD_UNSUPPORTED_OPERATION_REQUEST + * @moduleid UCD_RC::MOD_UPDATE_UCD_FLASH_IMAGE + * @userdata1 HUID of UCD Target + * @userdata2 The order of the operation + * @devdesc The UCD flash image commandline is not supported + * @custdesc Unexpected IPL firmware data format error + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + UCD_RC::MOD_UPDATE_UCD_FLASH_IMAGE, + UCD_RC::UCD_UNSUPPORTED_OPERATION_REQUEST, + TARGETING::get_huid(iv_pUcd), + op_count, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + + ERRORLOG::ErrlUserDetailsString(op_type).addToLog(err); + break; + } + + // Set up tmp_str for next op + ++op_count; + tmp_str = next_op; + + } // end of while loop + + if (err) + { + err->collectTrace(UCD_COMP_NAME); + } + + + TRACFCOMP(g_trac_ucd,"updateUcdFlash: End of op loop: " + "op_count=%d, char_count=%d, total=%d): %s", + op_count, char_count, i_size, + err ? "ERROR" : "No Error") + + return err; } // end of updateUcdFlash() @@ -800,6 +1228,14 @@ errlHndl_t updateAllUcdFlashImages( nextUcd=true; break; } + else + { + TRACFCOMP(g_trac_ucd,INFO_MRK + "updateAllUcdFlashImages: Device has different MFR revision" + " (0x%04X) than the incoming UCD sub-flash image " + "version (0x%04X), so perform flash update", + mfrRevision, tocEntry.mfrRevision); + } // Turns out doing the check via UtilMem is not that easy, // so for feeding the image to the updater, use manual @@ -886,6 +1322,7 @@ errlHndl_t updateAllUcdFlashImages( break; } + } // End loop through all power sequencers } while(0); |