diff --git a/src/usr/isteps/nvdimm/bpm_update.C b/src/usr/isteps/nvdimm/bpm_update.C index 404fcbeccb0..25bf29234e3 100644 --- a/src/usr/isteps/nvdimm/bpm_update.C +++ b/src/usr/isteps/nvdimm/bpm_update.C @@ -33,6 +33,7 @@ #include #include #include +#include namespace NVDIMM { @@ -40,29 +41,57 @@ namespace BPM { trace_desc_t* g_trac_bpm = nullptr; -TRAC_INIT(&g_trac_bpm, BPM_COMP_NAME, 2*KILOBYTE); +TRAC_INIT(&g_trac_bpm, BPM_COMP_NAME, 4*KILOBYTE); // For debug traces #define TRACUCOMP(args...) //#define TRACUCOMP(args...) TRACFCOMP(args) // See bpm_update.H for more info on these constants. -const size_t MAX_PAYLOAD_SIZE = 26; -const size_t MAX_PAYLOAD_DATA_SIZE = 16; +const size_t MAX_PAYLOAD_SIZE = 26; +const size_t MAX_PAYLOAD_DATA_SIZE = 16; const size_t MAX_PAYLOAD_OTHER_DATA_SIZE = 10; -const uint8_t PAYLOAD_HEADER_SIZE = 4; -const uint8_t SYNC_BYTE = 0x80; +const uint8_t PAYLOAD_HEADER_SIZE = 4; +const uint8_t SYNC_BYTE = 0x80; // These constants are kept out of the header file since they aren't relevant // outside of this file. const uint16_t BPM_ADDRESS_ZERO = 0; +const uint16_t BPM_CONFIG_START_ADDRESS = 0x1800; // In order to disable write protection on the BPM to perform updates a sequence // of characters must be written. The hex represenation of those characters are // defined by this constant. The sequence is SMOD const uint8_t BPM_PASSWORD[] = {0x53, 0x4D, 0x4F, 0x44}; const size_t BPM_PASSWORD_LENGTH = 4; -const size_t NUM_MAGIC_REGISTERS = 2; + +// These are the production magic values for the BPM that should be written in +// BPM_MAGIC_REG1 and BPM_MAGIC_REG2 respectively. +const uint8_t PRODUCTION_MAGIC_VALUES[NUM_MAGIC_REGISTERS] = {0x55, 0xAA}; + +// These are the segment codes used to dump out a particular config data segment +// on the BPM. +const uint16_t DEFAULT_REG_PAGE = 0x905E; +const uint16_t SEGMENT_A_CODE = 0x9A5E; +const uint16_t SEGMENT_B_CODE = 0x9B5E; +const uint16_t SEGMENT_C_CODE = 0x9C5E; +const uint16_t SEGMENT_D_CODE = 0x9D5E; + +// Starting addresses relative to address 0x1800. +// Segments appear in reverse order on BPM. +// Each segment is SEGMENT_SIZE long. +const size_t SEGMENT_D_START_ADDR = 0x000; +const size_t SEGMENT_C_START_ADDR = 0x080; +const size_t SEGMENT_B_START_ADDR = 0x100; +const size_t SEGMENT_A_START_ADDR = 0x180; + +const std::map segmentMap +{ + {SEGMENT_A_CODE, SEGMENT_A_START_ADDR}, + {SEGMENT_B_CODE, SEGMENT_B_START_ADDR}, + {SEGMENT_C_CODE, SEGMENT_C_START_ADDR}, + {SEGMENT_D_CODE, SEGMENT_D_START_ADDR}, +}; /** * @brief A helper function used in assert statements to verify the correct @@ -157,6 +186,41 @@ bool isBslCommand(const uint8_t i_command) return result; } +/** + * @brief Helper function to extract the Segement ID from the segment code. + * + * @param[in] i_segmentCode The Segment code to pull the segment ID from + * + * @return uint8_t The Segment ID (A, B, C, D) as a hex value. + * For example 0xA, 0xB, etc. + */ +uint8_t getSegmentIdentifier(uint16_t i_segmentCode) +{ + uint8_t segmentId = (i_segmentCode >> 8) & 0xF; + return segmentId; +} + +/** + * @brief Helper function to sleep for longer durations in 5 second increments. + * + * @param[in] i_sleepInSeconds How many seconds to sleep. + */ +void longSleep(uint8_t const i_sleepInSeconds) +{ + int iterations = i_sleepInSeconds / 5; + do + { + // Send progress code. + INITSERVICE::sendProgressCode(); + + // Sleep for 5 seconds + nanosleep(5, 0); + + --iterations; + } while (iterations > 0); +} + + // ============================================================================= // BpmFirmwareLidImage Class Functions // ============================================================================= @@ -211,15 +275,90 @@ void const * BpmFirmwareLidImage::getFirstBlock() const return block; } +// ============================================================================= +// BpmConfigLidImage Class Functions +// ============================================================================= + +BpmConfigLidImage::BpmConfigLidImage(void * const i_lidImageAddr, + size_t i_size) + : iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size) +{ + assert(i_lidImageAddr != nullptr, + "BPM::BpmConfigLidImage(): Provided LID image must not be nullptr"); +} + +uint16_t BpmConfigLidImage::getVersion() const +{ + uint16_t version = INVALID_VERSION; + + if (iv_lidImageSize >= sizeof(config_image_header_t)) + { + const config_image_header_t * header = + reinterpret_cast(iv_lidImage); + + version = TWO_UINT8_TO_UINT16(header->iv_versionMajor, + header->iv_versionMinor); + } + + return version; +} + +uint16_t BpmConfigLidImage::getNumberOfFragments() const +{ + uint16_t numberOfFragments = 0; + + if (iv_lidImageSize >= sizeof(config_image_header_t)) + { + const config_image_header_t * header = + reinterpret_cast(iv_lidImage); + + numberOfFragments = header->iv_numberOfFragments; + } + + return numberOfFragments; +} + +void const * BpmConfigLidImage::getFirstFragment() const +{ + void * fragment = nullptr; + + if (getNumberOfFragments() > 0) + { + fragment = reinterpret_cast(iv_lidImage) + + sizeof(config_image_header_t); + } + + return fragment; +} + // ============================================================================= // Bpm Class Functions // ============================================================================= Bpm::Bpm(const TARGETING::TargetHandle_t i_nvdimm) - : iv_nvdimm(i_nvdimm), iv_bslVersion(0) + : iv_nvdimm(i_nvdimm), + iv_bslVersion(0), + iv_firmwareStartAddress(0), + iv_attemptAnotherUpdate(false), + iv_segmentDMerged(false), + iv_segmentBMerged(false) { assert((i_nvdimm != nullptr) && (isNVDIMM(i_nvdimm)), - "An nvdimm target must be given."); + "BPM::Bpm(): An nvdimm target must be given."); + + memset(&iv_segmentD, 0, SEGMENT_SIZE); + memset(&iv_segmentB, 0, SEGMENT_SIZE); + +} + +bool Bpm::attemptAnotherUpdate() +{ + return iv_attemptAnotherUpdate; +} + +const TARGETING::TargetHandle_t Bpm::getNvdimm() +{ + return iv_nvdimm; } errlHndl_t Bpm::readBslVersion() @@ -228,103 +367,77 @@ errlHndl_t Bpm::readBslVersion() errlHndl_t errl = nullptr; do { - // Command to get the version is a BSL command, so it has to be sent as - // a payload. - payload_t payload; - errl = setupPayload(payload, BSL_TX_BSL_VERSION, BPM_ADDRESS_ZERO); + // Enter Update mode + errl = enterUpdateMode(); if (errl != nullptr) { break; } - // Issue the BSL command - errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE); + // Verify in Update mode + errl = inUpdateMode(); if (errl != nullptr) { break; } - // Get the result from the BPM. - // First clear the error status register - errl = nvdimmWriteReg(iv_nvdimm, - BPM_REG_ERR_STATUS, - 0x00); + // Enter Bootstrap Loader (BSL) mode to perform firmware update + errl = enterBootstrapLoaderMode(); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " - "Failed to clear error status register"); break; } - // Set the payload length - uint8_t data = payload.size(); - errl = nvdimmWriteReg(iv_nvdimm, - BPM_PAYLOAD_LENGTH, - data); + // Unlock the device. This is a BSL command so we must already be in + // BSL mode to execute it. + errl = unlockDevice(); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " - "Failed to set payload length"); break; } - // Setup the command status register - command_status_register_t commandStatus; - commandStatus.bits.Bsp_Cmd_In_Progress = 1; - commandStatus.bits.Operator_Type = READ; - errl = nvdimmWriteReg(iv_nvdimm, - BPM_CMD_STATUS, - commandStatus.value); + // Command to get the version is a BSL command, so it has to be sent as + // a payload. + payload_t payload; + errl = setupPayload(payload, BSL_TX_BSL_VERSION, BPM_ADDRESS_ZERO); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " - "Failed to setup command status register"); break; } - // Setup command type. - errl = nvdimmWriteReg(iv_nvdimm, - BPM_REG_CMD, - BPM_PASSTHROUGH); + // Issue the BSL command + errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " - "Failed to setup command type. " - "Read BSL version command was not sent to BPM."); break; } - errl = waitForCommandStatusBitReset(commandStatus); + // Get the result from the BPM. + errl = getResponse(&iv_bslVersion, sizeof(uint8_t)); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " + "Failed to determine BSL Version."); + break; + } - // Read out the version payload. - payload_t versionPayload; + TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): BSL Version is 0x%X", + iv_bslVersion); - const size_t VERSION_PAYLOAD_SIZE = 7; - const size_t VERSION_PAYLOAD_VERSION_INDEX = 4; - for (size_t i = 0; i < VERSION_PAYLOAD_SIZE; ++i) + // Reset the device. This will exit BSL mode. + errlHndl_t exitErrl = resetDevice(); + if (exitErrl != nullptr) { - uint8_t data = 0; - errl = nvdimmReadReg(iv_nvdimm, - (BPM_REG_PAYLOAD_START + (i * sizeof(uint8_t))), - data); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): " - "Failed to read version payload"); - break; - } - - versionPayload.push_back(data); + break; } - if (errl != nullptr) + + // Exit update mode + exitErrl = exitUpdateMode(); + if (exitErrl != nullptr) { break; } - iv_bslVersion = versionPayload[VERSION_PAYLOAD_VERSION_INDEX]; - - TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): BSL Version is 0x%X", - iv_bslVersion); } while(0); @@ -496,7 +609,8 @@ errlHndl_t Bpm::issueCommand(const uint8_t i_command, return errl; } -errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) +errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_fwImage, + BpmConfigLidImage i_configImage) { TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::runUpdate(): " "Running BPM Update for NVDIMM 0x%.8X", @@ -516,7 +630,7 @@ errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) break; } - if (i_image.getVersion() == bpmFwVersion) + if (i_fwImage.getVersion() == bpmFwVersion) { TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): " "Firmware version on the BPM matches the version in the " @@ -526,10 +640,42 @@ errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) //break; } + // Depending on the BSL version a CRC check may be necessary + errl = readBslVersion(); + if (errl != nullptr) + { + break; + } + + // If the BSL version read from the BPM isn't a supported version then + // don't perform the updates as the update flow may have changed between + // BSL versions. + if (iv_bslVersion != BSL_VERSION_1_4) + { + TRACFCOMP(g_trac_bpm, "Bpm::runUpdate(): " + "Unsupported BSL Version 0x%.2X detected on BPM. " + "Skipping Update."); + + break; + } + TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): " "Firmware version on the BPM 0x%.4X, " - "Firmware version of image 0x%.4X. Running Updates", - bpmFwVersion, i_image.getVersion()); + "Firmware version of image 0x%.4X. Running Update", + bpmFwVersion, i_fwImage.getVersion()); + + // Before the update begins, we must do some preprocessing prior to the + // config part of the update. Segment D and B need to be dumped from the + // BPM into buffers and then the config data from the image needs to be + // inserted into them. This must happen before enterUpdateMode() as both + // tamper with the BPM_MAGIC_REGs. Additionally, to dump segment data, + // it is required to have working firmware which will not be the case + // during update. + errl = preprocessSegments(i_configImage); + if (errl != nullptr) + { + break; + } // Enter Update mode errl = enterUpdateMode(); @@ -560,48 +706,30 @@ errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) break; } - // Depending on the BSL version a CRC check may be necessary - // @TODO RTC 212446: Add CRC check to update procedure - errl = readBslVersion(); + // Run Firmware Update + errl = updateFirmware(i_fwImage); if (errl != nullptr) { break; } - // Run Firmware Update - errl = updateFirmware(i_image); + // Perform the configuration data segment updates. + // As of BSL 1.4 this is done via the BSL interface instead of SCAP + // registers. + errl = updateConfig(); if (errl != nullptr) { break; } - - - // @TODO RTC 212446 Config Update - // Perform the configuration data segment updates. - // This is done via SCAP Registers and not the BSL interface. - -// // Reset the device. This will exit BSL mode. -// errl = resetDevice(); -// if (errl != nullptr) -// { -// break; -// } -// -// // Enter BSL mode to reset the device -// errl = enterBootstrapLoaderMode(); -// if (errl != nullptr) -// { -// break; -// } -// -// // Unlock the device. This is a BSL command so we must already be in -// // BSL mode to execute it. -// errl = unlockDevice(); -// if (errl != nullptr) -// { -// break; -// } + errl = checkFirmwareCrc(); + if (errl != nullptr) + { + // @TODO RTC 212447: Add support for multiple update attempts. + TRACFCOMP(g_trac_bpm, "Bpm:: runUpdate(): " + "Final CRC check failed. Attempting update again..."); + break; + } } while(0); @@ -637,7 +765,7 @@ errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) TRACFCOMP(g_trac_bpm, "Bpm::runUpdate(): " "Reset command sent to NVDIMM controller, sleep for 15 seconds"); - nanosleep(15,0); + longSleep(15); uint16_t bpmFwVersion = INVALID_VERSION; exitErrl = getFwVersion(bpmFwVersion); @@ -649,8 +777,7 @@ errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image) delete exitErrl; } - // @TODO RTC 212446: Depending on BSL version, need to do CRC check. - if (i_image.getVersion() == bpmFwVersion) + if (i_fwImage.getVersion() == bpmFwVersion) { TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): " "Firmware version on the BPM matches the version in the " @@ -708,101 +835,22 @@ errlHndl_t Bpm::enterUpdateMode() do { - // The following write sequence to the I2C_REG_PROTECT register removes - // write protection from registers 0x40-0x7F on page 4. - for ( size_t i = 0; i < BPM_PASSWORD_LENGTH; ++i) - { - errl = nvdimmWriteReg(iv_nvdimm, - I2C_REG_PROTECT, - BPM_PASSWORD[i]); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): " - "Failed to write the unlock sequence to " - "I2C_REG_PROTECT"); - break; - } - } - if (errl != nullptr) - { - break; - } - - // Make sure protection was removed - uint8_t data = 0; - errl = nvdimmReadReg(iv_nvdimm, - I2C_REG_PROTECT, - data); + // Disable write protection on the BPM. Otherwise, we can't write the + // magic values that enable the nvdimm-bpm interface. + errl = disableWriteProtection(); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): " - "Failed to verify that write protection was removed"); - break; - } - - if (!(data & SYNC_BYTE)) - { - // @TODO RTC 212447 Error - TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::enterUpdateMode(): " - "Failed to disable write protection. I2C_REG_PROTECT"); break; } // Write the magic values to enable nvdimm-bpm interface const uint8_t magic_values[NUM_MAGIC_REGISTERS] = {0xB0, 0xDA}; - const uint16_t magic_registers[NUM_MAGIC_REGISTERS] = - {BPM_MAGIC_REG1, BPM_MAGIC_REG2}; - - for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) - { - errl = nvdimmWriteReg(iv_nvdimm, - magic_registers[i], - magic_values[i]); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): " - "Failed to write magic numbers that enable " - "update mode"); - break; - } - } - if (errl != nullptr) - { - break; - } - - // Verify the magic values were written - uint8_t magic_data[NUM_MAGIC_REGISTERS] = {}; - for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) - { - errl = nvdimmReadReg(iv_nvdimm, - magic_registers[i], - magic_data[i]); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): " - "Failed to read back values in magic registers to " - "verfiy that update mode was enabled"); - break; - } - } + errl = writeToMagicRegisters(magic_values); if (errl != nullptr) { - break; - } - - // If either of the magic values stored in magic_data don't match the - // corresponding expected values in magic_values then an error occurred. - if ( (magic_data[0] != magic_values[0]) - || (magic_data[1] != magic_values[1])) - { - // @TODO RTC 212447 Error - TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::enterUpdateMode(): " - "Magic values read from BPM didn't match expected values " - "BPM_MAGIC_REG1 Expected 0x%.2X Actual 0x%.2X " - "BPM_MAGIC_REG2 Expected 0x%.2X Actual 0x%.2X", - magic_values[0], magic_data[0], - magic_values[1], magic_data[1]); + TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): " + "Failed to write magic numbers that enable " + "update mode"); break; } @@ -833,60 +881,12 @@ errlHndl_t Bpm::exitUpdateMode() } // Write back the production magic values - const uint8_t magic_values[NUM_MAGIC_REGISTERS] = {0x55, 0xAA}; - const uint16_t magic_registers[NUM_MAGIC_REGISTERS] = - {BPM_MAGIC_REG1, BPM_MAGIC_REG2}; - - for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) - { - errl = nvdimmWriteReg(iv_nvdimm, - magic_registers[i], - magic_values[i]); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): " - "Failed to write the production magic values to " - "disable update mode."); - break; - } - } + errl = writeToMagicRegisters(PRODUCTION_MAGIC_VALUES); if (errl != nullptr) { - break; - } - - // Verify the magic values were written - uint8_t magic_data[NUM_MAGIC_REGISTERS] = {}; - for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) - { - errl = nvdimmReadReg(iv_nvdimm, - magic_registers[i], - magic_data[i]); - if (errl != nullptr) - { - TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): " - "Failed to read back magic values to verify that " - "update mode was disabled."); - break; - } - } - if (errl != nullptr) - { - break; - } - - // If either of the magic values stored in magic_data don't match the - // corresponding expected values in magic_values then an error occurred. - if ( (magic_data[0] != magic_values[0]) - || (magic_data[1] != magic_values[1])) - { - // @TODO RTC 212447 Error - TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::exitUpdateMode(): " - "Magic values read from BPM didn't match expected values " - "BPM_MAGIC_REG1 Expected 0x%.2X Actual 0x%.2X " - "BPM_MAGIC_REG2 Expected 0x%.2X Actual 0x%.2X", - magic_values[0], magic_data[0], - magic_values[1], magic_data[1]); + TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): " + "Failed to write the production magic values to " + "disable update mode."); break; } @@ -900,7 +900,14 @@ errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image) TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::updateFirmware()"); errlHndl_t errl = nullptr; + // There are two potential start addresses for the firmware section. + // They are: const uint16_t MAIN_PROGRAM_ADDRESS = 0x8000; + const uint16_t MAIN_PROGRAM_ADDRESS_ALT = 0xA000; + + bool mainAddressEncountered = false; + + // Get the number of blocks in the image const uint16_t NUMBER_OF_BLOCKS = i_image.getNumberOfBlocks(); char const * data = @@ -913,10 +920,21 @@ errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image) for(size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) { // This is done once at the main program address. - if (block->iv_addressOffset == MAIN_PROGRAM_ADDRESS) + if ( ((block->iv_addressOffset == MAIN_PROGRAM_ADDRESS) + || (block->iv_addressOffset == MAIN_PROGRAM_ADDRESS_ALT)) + && !mainAddressEncountered) { + // Only execute this once. + mainAddressEncountered = true; + + // Save the firmware start address for later. This will be needed + // for the final CRC check when the update is completed. + iv_firmwareStartAddress = block->iv_addressOffset; + payload_t payload; - errl = setupPayload(payload, BSL_MASS_ERASE, MAIN_PROGRAM_ADDRESS); + errl = setupPayload(payload, + BSL_MASS_ERASE, + iv_firmwareStartAddress); if (errl != nullptr) { break; @@ -930,7 +948,7 @@ errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image) TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): " "Performing BSL_MASS_ERASE, sleep for 5 seconds."); - nanosleep(5,0); + longSleep(5); } // Construct the payload for this block in the image @@ -948,18 +966,65 @@ errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image) break; } - // Sleep for 0.01 second - nanosleep(0, 0.1 * NS_PER_MSEC); + // Sleep for 0.001 second + nanosleep(0, 1 * NS_PER_MSEC); // Move to the next block // iv_blocksize doesn't include the sizeof itself. So, add another byte // for it. data += block->iv_blockSize + sizeof(uint8_t); - block = reinterpret_cast(data); } - return nullptr; + return errl; +} + +errlHndl_t Bpm::updateConfig() +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::updateConfig()"); + errlHndl_t errl = nullptr; + + do { + + // Erase Segment D on the BPM via the BSL interface. + errl = eraseSegment(SEGMENT_D_CODE); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): " + "Failed to erase Segment D."); + break; + } + + // Write the updated Segment D buffer to the BPM via the BSL interface. + errl = writeSegment(iv_segmentD, SEGMENT_D_CODE); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): " + "Failed to write Segment D."); + break; + } + + // Erase Segment B on the BPM via the BSL interface. + errl = eraseSegment(SEGMENT_B_CODE); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): " + "Failed to erase Segment B."); + break; + } + + // Write the updated Segment B buffer to the BPM via the BSL interface. + errl = writeSegment(iv_segmentB, SEGMENT_B_CODE); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): " + "Failed to write Segment B."); + break; + } + + } while(0); + + return errl; } errlHndl_t Bpm::enterBootstrapLoaderMode() @@ -1000,7 +1065,16 @@ errlHndl_t Bpm::enterBootstrapLoaderMode() inBslMode = true; TRACFCOMP(g_trac_bpm, "Bpm::enterBootstrapLoaderMode(): " "BSL Mode entered, sleep for 5 seconds."); - nanosleep(5,0); + longSleep(5); + break; + } + + // Sleep for 0.001 second. + nanosleep(0, 1 * NS_PER_MSEC); + + errl = issueCommand(BPM_LOCAL, BCL_ENTER_BSL_MODE, WRITE); + if (errl != nullptr) + { break; } @@ -1008,15 +1082,9 @@ errlHndl_t Bpm::enterBootstrapLoaderMode() "Unable to enter BSL Mode, retries remaining %d. " "Sleep for 2 seconds before trying again.", (retry - 1)); - nanosleep(2,0); - - errl = issueCommand(BPM_LOCAL, BCL_ENTER_BSL_MODE, WRITE); - --retry; - // Sleep for 0.01 second before next attempt. - nanosleep(0, 0.1 * NS_PER_MSEC); } if (!inBslMode) @@ -1262,93 +1330,1014 @@ errlHndl_t Bpm::resetDevice() TRACFCOMP(g_trac_bpm, "Bpm::resetDevice(): " "Resetting BPM for NVDIMM 0x%.8X, sleep for 10 seconds.", TARGETING::get_huid(iv_nvdimm)); - nanosleep(10,0); + longSleep(10); } while(0); return errl; } -errlHndl_t Bpm::waitForCommandStatusBitReset( - command_status_register_t i_commandStatus) +errlHndl_t Bpm::readViaScapRegister(uint8_t const i_reg, uint8_t & io_data) { + TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::readViaScapRegister()"); errlHndl_t errl = nullptr; do { - // Wait until the COMMAND_IN_PROGRESS bit is reset + + // Wait for the SCAP_STATUS Busy bit to be zero. + errl = waitForBusyBit(); + if (errl != nullptr) + { + break; + } + + // Write to SCAP register which register we're attempting to access on + // the BPM + errl = nvdimmWriteReg(iv_nvdimm, + SCAP_REG, + i_reg); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::readViaScapRegister(): " + "Failed to set SCAP_REG to register 0x%.2X", + i_reg); + break; + } + + // Wait for the SCAP_STATUS Busy bit to be zero. + errl = waitForBusyBit(); + if (errl != nullptr) + { + break; + } + + // Read out the data from the requested register errl = nvdimmReadReg(iv_nvdimm, - BPM_CMD_STATUS, - i_commandStatus.value); + SCAP_DATA, + io_data); if (errl != nullptr) { + TRACFCOMP(g_trac_bpm, "BPM::readViaScapRegister(): " + "Failed to read data from SCAP_DATA for register 0x%.2X.", + i_reg); break; } - int retry = 10; + } while(0); - while (i_commandStatus.bits.Bsp_Cmd_In_Progress) + return errl; +} + +errlHndl_t Bpm::writeViaScapRegister(uint8_t const i_reg, uint8_t const i_data) +{ + TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeViaScapRegister()"); + errlHndl_t errl = nullptr; + + do { + + // Wait for the SCAP_STATUS Busy bit to be zero. + errl = waitForBusyBit(); + if (errl != nullptr) { - nanosleep(0, 1 * NS_PER_MSEC); - errl = nvdimmReadReg(iv_nvdimm, - BPM_CMD_STATUS, - i_commandStatus.value); + break; + } + + // Write to SCAP register which register we're attempting to access on + // the BPM + errl = nvdimmWriteReg(iv_nvdimm, + SCAP_REG, + i_reg); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::writeViaScapRegister(): " + "Failed to set SCAP_REG to register 0x%.2X", + i_reg); + break; + } + + // Wait for the SCAP_STATUS Busy bit to be zero. + errl = waitForBusyBit(); + if (errl != nullptr) + { + break; + } + + // Write the data to the register we're attempting to access on the BPM. + errl = nvdimmWriteReg(iv_nvdimm, + SCAP_DATA, + i_data); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "BPM::writeViaScapRegister(): " + "Failed to write data 0x%.2X to SCAP_DATA for " + "register 0x%.2X.", + i_data, + i_reg); + break; + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::disableWriteProtection() +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::disableWriteProtection()"); + errlHndl_t errl = nullptr; + + do { + + // The following write sequence to the I2C_REG_PROTECT register + // indirectly removes write protection from registers 0x40-0x7F on + // page 4. + for ( size_t i = 0; i < BPM_PASSWORD_LENGTH; ++i) + { + errl = nvdimmWriteReg(iv_nvdimm, + I2C_REG_PROTECT, + BPM_PASSWORD[i]); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): " - "Failed to read BPM_CMD_STATUS register"); + TRACFCOMP(g_trac_bpm, "Bpm::disableWriteProtection(): " + "Failed to write the unlock sequence to " + "I2C_REG_PROTECT"); break; } + } + if (errl != nullptr) + { + break; + } - if (--retry <= 0) + // Make sure protection was removed + uint8_t data = 0; + errl = nvdimmReadReg(iv_nvdimm, + I2C_REG_PROTECT, + data); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::disableWriteProtection(): " + "Failed to verify that write protection was removed"); + break; + } + const uint8_t WRITE_PROTECT_DISABLED = 0x80; + if (!(data & WRITE_PROTECT_DISABLED)) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::disableWriteProtection(): " + "Failed to disable write protection. I2C_REG_PROTECT"); + //@TODO RTC 212447 Error + break; + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::writeToMagicRegisters( + uint8_t const (&i_magicValues)[NUM_MAGIC_REGISTERS]) +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeToMagicRegisters()"); + errlHndl_t errl = nullptr; + + do { + const uint16_t magic_registers[NUM_MAGIC_REGISTERS] = + {BPM_MAGIC_REG1, BPM_MAGIC_REG2}; + + for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) + { + errl = nvdimmWriteReg(iv_nvdimm, + magic_registers[i], + i_magicValues[i]); + if (errl != nullptr) { - //@TODO RTC 212447: Error - TRACFCOMP(g_trac_bpm, ERR_MRK - "BPM::waitForCommandStatusBitReset(): " - "BSP_CMD_IN_PROGRESS bit has not reset in allotted " - "number of retries. Cancel update procedure"); + TRACFCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): " + "Failed to write the magic values to the magic " + "registers"); break; } - } if (errl != nullptr) { break; } - // Check for error - if (i_commandStatus.bits.Error_Flag) + // Verify the magic values were written + uint8_t magic_data[NUM_MAGIC_REGISTERS] = {0}; + for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i) { - uint8_t error = 0; errl = nvdimmReadReg(iv_nvdimm, - BPM_REG_ERR_STATUS, - error); + magic_registers[i], + magic_data[i]); if (errl != nullptr) { - TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): " - "Failed to read BPM_REG_ERR_STATUS"); + TRACFCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): " + "Failed to read back magic values to verify that " + "they were written."); break; } + } + if (errl != nullptr) + { + break; + } + // If either of the magic values stored in magic_data don't match the + // corresponding expected values in magic_values then an error occurred. + if ( (magic_data[0] != i_magicValues[0]) + || (magic_data[1] != i_magicValues[1])) + { // @TODO RTC 212447 Error - TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::getBslVersion(): " - "BPM_CMD_STATUS Error Flag is set"); + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::writeToMagicRegisters(): " + "Magic values read from BPM didn't match expected values " + "BPM_MAGIC_REG1 Expected 0x%.2X Actual 0x%.2X " + "BPM_MAGIC_REG2 Expected 0x%.2X Actual 0x%.2X", + i_magicValues[0], magic_data[0], + i_magicValues[1], magic_data[1]); break; - } + TRACUCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): " + "Magic values successfully written to BPM " + "BPM_MAGIC_REG1 0x%.2X " + "BPM_MAGIC_REG2 0x%.2X ", + magic_data[0], + magic_data[1]); + } while(0); return errl; } -uint16_t Bpm::crc16_calc(const void* i_ptr, int i_size) +errlHndl_t Bpm::dumpSegment(uint16_t const i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]) { - uint16_t crc = 0xFFFF; - const uint8_t* data = reinterpret_cast(i_ptr); + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::dumpSegment(): Segment %X", + getSegmentIdentifier(i_segmentCode)); + errlHndl_t errl = nullptr; + + memset(&o_buffer, 0, SEGMENT_SIZE); + + const uint8_t SPECIAL_CONTROL_COMMAND1 = 0x3E; + const uint8_t SPECIAL_CONTROL_COMMAND2 = 0x3F; + + bool isSegmentPageOpen = false, magicValuesChanged = false; + + do { + // We cannot be in BSL mode when dumping the config segments. Verify we + // aren't in BSL mode by checking SCAP_STATUS + scap_status_register_t status; + errl = nvdimmReadReg(iv_nvdimm, + SCAP_STATUS, + status.full); + if (errl != nullptr) + { + break; + } + + if (status.bit.Bpm_Bsl_Mode) + { + //@TODO RTC 212447 Error + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::dumpSegment(): " + "Couldn't dump Segment %X. BSL Mode is enabled.", + getSegmentIdentifier(i_segmentCode)); + break; + } + + // First the NVDIMM MAGIC registers BPM_MAGIC_REG1 and BPM_MAGIC_REG2 + // must be programmed to 0xBA and 0xAB respectively. + const uint8_t magic_values[NUM_MAGIC_REGISTERS] = {0xBA, 0xAB}; + errl = writeToMagicRegisters(magic_values); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Failed to write magic numbers that enable " + "reading of segment data."); + break; + } + + // Magic values were changed. Need to write back production values. + magicValuesChanged = true; + + // Next, switch to the desired BPM segment by writing the segment code + // to the BPM's Special Control Command registers. + // + // Since the SCAP_DATA register can only hold 1 byte at a time we must + // do this in two steps. + // According to SMART, the segment code must be written in the following + // form to those registers: + // Register 0x3E gets LO(i_segmentCode) byte + // Register 0x3F gets HI(i_segmentCode) byte + // Example: 0x9D5E is the segment code for Segment D. It must be written + // as follows + // 0x3E, 0x5E + // 0x3F, 0x9D + const uint8_t loSegCode = i_segmentCode & 0xFF; + const uint8_t hiSegCode = (i_segmentCode >> 8) & 0xFF; + + TRACUCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND1 and " + "0x%.2X to SPECIAL_CONTROL_COMMAND2", + loSegCode, + hiSegCode); + + // Write the LO segment code first. + errl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND1, loSegCode); + if (errl != nullptr) + { + break; + } + + // Write the HI segment code next. + errl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND2, hiSegCode); + if (errl != nullptr) + { + break; + } + + // Request to open segment page is sent. + // Wait 2 seconds for the operation to complete. + nanosleep(2,0); + isSegmentPageOpen = true; + + TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Dumping Segment %X to buffer.", + getSegmentIdentifier(i_segmentCode)); + + // Dump the segment data + for (uint8_t reg = 0; reg < SEGMENT_SIZE; ++reg) + { + errl = readViaScapRegister(reg, o_buffer[reg]); + if (errl != nullptr) + { + break; + } + } + if (errl != nullptr) + { + break; + } - assert(*data == SYNC_BYTE, - "The first byte of data pointed to by i_ptr must be the SYNC_BYTE " - "in order to calculate the correct CRC16"); + + } while(0); + + do { + errlHndl_t closeSegmentErrl = nullptr; + if (isSegmentPageOpen) + { + // Close the segment by writing the DEFAULT_REG_PAGE code to the + // BPM's SPECIAL_CONTROL_COMMAND registers. + // This must also be done as described above + const uint8_t lo = DEFAULT_REG_PAGE & 0xFF; + const uint8_t hi = (DEFAULT_REG_PAGE >> 8) & 0xFF; + + TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Closing Segment %X's page.", + getSegmentIdentifier(i_segmentCode)); + TRACUCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND1 and " + "0x%.2X to SPECIAL_CONTROL_COMMAND2", + lo, + hi); + + // Write the LO segment code first. + closeSegmentErrl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND1, + lo); + if (closeSegmentErrl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::dumpSegment(): " + "Failed to write DEFAULT_REG_PAGE low byte!! " + "NVDIMM will be stuck on this segment's page!!"); + // @TODO RTC 212447 Do something with the error. + break; + } + + // Write the HI segment code next. + closeSegmentErrl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND2, + hi); + if (closeSegmentErrl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::dumpSegment(): " + "Failed to write DEFAULT_REG_PAGE high byte!! " + "NVDIMM will be stuck on this segment's page!!"); + // @TODO RTC 212447 Do something with the error. + break; + } + + // Sleep for 2 seconds to allow time for segment page to close. + nanosleep(2, 0); + } + } while(0); + + if (magicValuesChanged) + { + // Write back the production magic values. + errlHndl_t magicErrl = writeToMagicRegisters(PRODUCTION_MAGIC_VALUES); + if (magicErrl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): " + "Failed to write production magic numbers."); + // @TODO RTC 212447 Do something with the error. + } + } + + return errl; +} + +errlHndl_t Bpm::mergeSegment(BpmConfigLidImage const i_configImage, + uint16_t const i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]) +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::mergeSegment(): Segment %X", + getSegmentIdentifier(i_segmentCode)); + errlHndl_t errl = nullptr; + + size_t segmentStartOffset = 0; + auto it = segmentMap.find(i_segmentCode); + if (it != segmentMap.end()) + { + segmentStartOffset = it->second; + } + else + { + //@TODO RTC 212447 Error + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::mergeSegment(): " + "Couldn't find start offset for Segment %X", + getSegmentIdentifier(i_segmentCode)); + } + + TRACFCOMP(g_trac_bpm, "Bpm::mergeSegment(): " + "Segment %X Start offset: 0x%X", + getSegmentIdentifier(i_segmentCode), + segmentStartOffset); + + memset(&o_buffer, 0, SEGMENT_SIZE); + + do { + + // Dump the segment into a buffer. + errl = dumpSegment(i_segmentCode, o_buffer); + if (errl != nullptr) + { + break; + } + + const size_t NUMBER_OF_FRAGMENTS = i_configImage.getNumberOfFragments(); + char const * data = reinterpret_cast( + i_configImage.getFirstFragment()); + + config_image_fragment_t const * fragment = + reinterpret_cast(data); + + TRACUCOMP(g_trac_bpm, "mergeSegment(): " + "NUMBER_OF_FRAGMENTS = 0x%.4X", NUMBER_OF_FRAGMENTS); + + for(size_t i = 0; i < NUMBER_OF_FRAGMENTS; ++i) + { + // The fragment offsets are given as offsets within the + // configuration segment data. So, if the fragment offset is less + // than the starting offset of this segment then the fragment is not + // relevant to this segment. + if (fragment->iv_offset < segmentStartOffset) + { + // This fragment is not for the segment we are dealing with. + TRACUCOMP(g_trac_bpm, "mergeSegment(): " + "Fragment with offset 0x%.4X not related to " + "Segment %X, skipping", + fragment->iv_offset, + getSegmentIdentifier(i_segmentCode)); + + // Move to the next fragment + data += sizeof(config_image_fragment_t) + + fragment->iv_fragmentSize; + fragment = + reinterpret_cast(data); + continue; + } + // Each segment is 128 bytes in size. So, if the offset given for + // the fragment is greater than the upper boundry then no more + // fragments exist for this segment. + if (fragment->iv_offset >= segmentStartOffset + SEGMENT_SIZE) + { + // This fragment and all other fragments afterward are not for + // this segment. + TRACUCOMP(g_trac_bpm, "mergeSegment(): " + "Fragment with offset 0x%.4X greater than/equal to " + "Segment %X ending offset, skipping", + fragment->iv_offset, + getSegmentIdentifier(i_segmentCode)); + break; + } + + // The fragment offset may be out of bounds for the buffer so + // scale it down to be within the buffer size. + size_t offset = fragment->iv_offset % SEGMENT_SIZE; + + // Overwrite the BPM segment data at the offset specified by the + // fragment. + memcpy(&o_buffer[offset], + &(fragment->iv_data), + fragment->iv_fragmentSize); + + // Move to the next fragment + data += sizeof(config_image_fragment_t) + fragment->iv_fragmentSize; + fragment = reinterpret_cast(data); + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::eraseSegment(uint16_t i_segmentCode) +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::eraseSegment(): Segment %X", + getSegmentIdentifier(i_segmentCode)); + errlHndl_t errl = nullptr; + + do { + + payload_t payload; + errl = setupPayload(payload, BSL_ERASE_SEGMENT, i_segmentCode); + if (errl != nullptr) + { + break; + } + + errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE); + if (errl != nullptr) + { + break; + } + + // Wait 1 second for the operation to complete. + TRACFCOMP(g_trac_bpm, "Bpm::eraseSegment(): " + "Erasing Segment %X. " + "Waiting 1 second for operation to complete.", + getSegmentIdentifier(i_segmentCode)); + nanosleep(1,0); + + } while(0); + + return errl; +} + +errlHndl_t Bpm::writeSegment(uint8_t const (&i_buffer)[SEGMENT_SIZE], + uint16_t const i_segmentCode) +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeSegment(): Segment %X", + getSegmentIdentifier(i_segmentCode)); + errlHndl_t errl = nullptr; + + do { + + auto it = segmentMap.find(i_segmentCode); + size_t segmentStartOffset = 0; + if (it != segmentMap.end()) + { + segmentStartOffset = it->second; + } + + // To update the given segment, we have to send over the data as + // payloads. Since the max size of a payload's data is 16 bytes, there + // will be 8 payloads sent to update a given segment because each + // segment is 128 bytes. + for (size_t offset = 0; + offset < SEGMENT_SIZE; + offset += MAX_PAYLOAD_DATA_SIZE) + { + // Construct a payload for the data at this offset up to the + // MAX_PAYLOAD_DATA_SIZE. + payload_t payload; + // Each segment is 128 bytes and the segment start addresses + // are their relative position to BPM_CONFIG_START_ADDRESS. To + // arrive at the correct address offset for this data we must + // calculate the addressOffset in the following way. + uint16_t addressOffset = BPM_CONFIG_START_ADDRESS + + segmentStartOffset + + offset; + errl = setupPayload(payload, + BSL_RX_DATA_BLOCK, + addressOffset, + &i_buffer[offset], + MAX_PAYLOAD_DATA_SIZE); + + if (errl != nullptr) + { + break; + } + + // Send the payload data over as a pass-through command + errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE); + if (errl != nullptr) + { + break; + } + + // Sleep for 0.001 second. + nanosleep(0, 1 * NS_PER_MSEC); + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::preprocessSegments(BpmConfigLidImage const i_configImage) +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::preprocessSegments()"); + errlHndl_t errl = nullptr; + + do { + + if (iv_attemptAnotherUpdate && iv_segmentDMerged && iv_segmentBMerged) + { + // The segment data has already been merged with the flash image + // data. Doing it again has the potential to fail depending on where + // the last update attempt failed. + TRACFCOMP(g_trac_bpm, "Bpm::preprocessSegments(): " + "Segment data was merged in a previous update attempt, " + "skipping preprocessing and using existing data."); + break; + } + + // Disable write protection on the BPM. Otherwise, we can't write the + // magic values that will enable segment preprocessing. + errl = disableWriteProtection(); + if (errl != nullptr) + { + break; + } + + // Merge the fragments for D with the data from the BPM. + if (!iv_segmentDMerged) + { + errl = mergeSegment(i_configImage, SEGMENT_D_CODE, iv_segmentD); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::preprocessSegments(): " + "Failed to merge Segment D."); + break; + } + iv_segmentDMerged = true; + } + else + { + TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::preprocessSegments(): " + "Segment %X has been merged already. Skipping merge...", + getSegmentIdentifier(SEGMENT_D_CODE)); + } + + // Merge the fragments for B with the data from the BPM. + if (!iv_segmentBMerged) + { + errl = mergeSegment(i_configImage, SEGMENT_B_CODE, iv_segmentB); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::preprocessSegments(): " + "Failed to merge Segment B."); + break; + } + iv_segmentBMerged = true; + } + else + { + TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::preprocessSegments(): " + "Segment %X has been merged already. Skipping merge...", + getSegmentIdentifier(SEGMENT_B_CODE)); + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::getResponse(uint8_t * const o_responseData, + uint8_t const i_responseSize) +{ + TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::getResponse()"); + + errlHndl_t errl = nullptr; + memset(o_responseData, 0xFF, i_responseSize); + + do { + + // Get the result from the BPM. + // First clear the error status register + errl = nvdimmWriteReg(iv_nvdimm, + BPM_REG_ERR_STATUS, + 0x00); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): " + "Failed to clear error status register"); + break; + } + + // Set the payload length + // The 4 header bytes plus 2 CRC bytes make up the other data size in + // the response payload. + const uint8_t RESPONSE_PAYLOAD_OTHER_DATA_SIZE = 6; + uint8_t responsePayloadSize = RESPONSE_PAYLOAD_OTHER_DATA_SIZE + + i_responseSize; + + errl = nvdimmWriteReg(iv_nvdimm, + BPM_PAYLOAD_LENGTH, + responsePayloadSize); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): " + "Failed to set payload length"); + break; + } + + // Setup the command status register + command_status_register_t commandStatus; + commandStatus.bits.Bsp_Cmd_In_Progress = 1; + commandStatus.bits.Operator_Type = READ; + errl = nvdimmWriteReg(iv_nvdimm, + BPM_CMD_STATUS, + commandStatus.value); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): " + "Failed to setup command status register"); + break; + } + + // Setup command type. + errl = nvdimmWriteReg(iv_nvdimm, + BPM_REG_CMD, + BPM_PASSTHROUGH); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): " + "Failed to setup command type."); + break; + } + + errl = waitForCommandStatusBitReset(commandStatus); + if (errl != nullptr) + { + break; + } + + // Read out the response payload. + payload_t responsePayload; + + for (size_t i = 0; i < responsePayloadSize; ++i) + { + uint8_t data = 0; + errl = nvdimmReadReg(iv_nvdimm, + (BPM_REG_PAYLOAD_START + (i * sizeof(uint8_t))), + data); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): " + "Failed to read response payload"); + break; + } + + responsePayload.push_back(data); + } + if (errl != nullptr) + { + break; + } + + // Verify the data from the response was good. + uint8_t* responseIterator = responsePayload.data(); + uint16_t responseCrc = *(reinterpret_cast + (&responseIterator[PAYLOAD_HEADER_SIZE + i_responseSize])); + // The BPM is going to give the response CRC in LE. So convert it to BE. + responseCrc = le16toh(responseCrc); + uint16_t expectedCrc = crc16_calc(responseIterator, + PAYLOAD_HEADER_SIZE + i_responseSize); + if (responseCrc != expectedCrc) + { + // @TODO RTC 212447: Error, invalid data read from BPM. + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::getResponse(): " + "Response CRC verification failed. " + "Received invalid data from BPM."); + break; + } + + // Write the data to the output buffer + for (size_t i = 0; i < i_responseSize; ++i) + { + // Only copy the response data from the payload to the output buffer + o_responseData[i] = responsePayload[i + PAYLOAD_HEADER_SIZE]; + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::waitForCommandStatusBitReset( + command_status_register_t i_commandStatus) +{ + errlHndl_t errl = nullptr; + + do { + // Wait until the COMMAND_IN_PROGRESS bit is reset + errl = nvdimmReadReg(iv_nvdimm, + BPM_CMD_STATUS, + i_commandStatus.value); + if (errl != nullptr) + { + break; + } + + int retry = 10; + + while (i_commandStatus.bits.Bsp_Cmd_In_Progress) + { + nanosleep(0, 1 * NS_PER_MSEC); + errl = nvdimmReadReg(iv_nvdimm, + BPM_CMD_STATUS, + i_commandStatus.value); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): " + "Failed to read BPM_CMD_STATUS register"); + break; + } + + if (--retry <= 0) + { + //@TODO RTC 212447: Error + TRACFCOMP(g_trac_bpm, ERR_MRK + "BPM::waitForCommandStatusBitReset(): " + "BSP_CMD_IN_PROGRESS bit has not reset in allotted " + "number of retries. Cancel update procedure"); + break; + } + + } + if (errl != nullptr) + { + break; + } + + // Check for error + if (i_commandStatus.bits.Error_Flag) + { + uint8_t error = 0; + errl = nvdimmReadReg(iv_nvdimm, + BPM_REG_ERR_STATUS, + error); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): " + "Failed to read BPM_REG_ERR_STATUS"); + break; + } + + // @TODO RTC 212447 Error + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::getBslVersion(): " + "BPM_CMD_STATUS Error Flag is set"); + break; + + } + + } while(0); + + return errl; +} + +errlHndl_t Bpm::waitForBusyBit() +{ + errlHndl_t errl = nullptr; + int retry = 10; + scap_status_register_t status; + + while (retry > 0) + { + + errl = nvdimmReadReg(iv_nvdimm, + SCAP_STATUS, + status.full); + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, "Bpm::waitForBusyBit(): " + "Failed to read from SCAP_STATUS to determine " + "state of Busy bit."); + break; + } + + if (!status.bit.Busy) + { + // SCAP Register is no longer busy. Stop retries. + break; + } + + if (retry <= 0) + { + //@TODO RTC 212447 Error + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::waitForBusyBit(): " + "SCAP_STATUS Busy bit failed to reset to 0 " + "in 10 retries."); + break; + } + + --retry; + nanosleep(0, 2 * NS_PER_MSEC); + } + + return errl; +} + +errlHndl_t Bpm::checkFirmwareCrc() +{ + TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::checkFirmwareCrc()"); + errlHndl_t errl = nullptr; + + // The COMMAND_CRC_CHECK would return a 3 byte response in the following + // format: + // + // ======================================================================== + // [Status Code] [Computed_CRC_Lo] [Computed_CRC_Hi] + // ======================================================================== + // BSL_LOCKED 0x00 0x00 + // PARAMETER_ERROR 0x00 0x00 + // MAIN_FW_NOT_SUPPORT_CRC_CHECK 0x00 0x00 + // MEMORY_WRITE_CHECK_FAILED CRC_Low CRC_Hi + // WRITE_FORBIDDEN CRC_Low CRC_Hi + // VERIFY_MISMATCH CRC_Low CRC_Hi + // SUCCESSFUL_OPERATION CRC_Low CRC_Hi + // + // For status codes BSL_LOCKED, PARAMETER_ERROR, and + // MAIN_FW_NOT_SUPPORT_CRC_CHECK the response CRC values are considered + // as DONT CARE. + // + // For the remainder of the status codes the CRC values are the + // computed CRC of the image. + // + // For SUCCESSFUL_OPERATION, the RESET_VECTOR was written. + // See bpm_update.H for more info on the status codes + const uint8_t CRC_CHECK_RESPONSE_SIZE = 3; + uint8_t responseData[CRC_CHECK_RESPONSE_SIZE] = {0}; + + do { + + TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): " + "Performing final CRC check."); + payload_t crcPayload; + errl = setupPayload(crcPayload, + BSL_CRC_CHECK, + iv_firmwareStartAddress); + if (errl != nullptr) + { + break; + } + + errl = issueCommand(BPM_PASSTHROUGH, crcPayload, WRITE); + if (errl != nullptr) + { + break; + } + + // Wait 10 seconds for the CRC check to complete. + TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): " + "Allow CRC check to complete on BPM by waiting 10 seconds."); + longSleep(10); + + errl = getResponse(responseData, CRC_CHECK_RESPONSE_SIZE); + if (errl != nullptr) + { + break; + } + + TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): " + "CRC check status = 0x%.X, CRC_Low = 0x%.X, CRC_Hi = 0x%.X", + responseData[0], + responseData[1], + responseData[2]); + + if (responseData[0] != SUCCESSFUL_OPERATION) + { + // @TODO RTC 212447 Error + break; + } + + } while(0); + + if (errl != nullptr) + { + TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::checkFirmwareCrc(): " + "Error occurred during BPM Firmware CRC check. " + "Firmware image will not load on BPM and update must be " + "attempted again."); + } + + return errl; +} + +uint16_t Bpm::crc16_calc(const void* i_ptr, int i_size) +{ + uint16_t crc = 0xFFFF; + const uint8_t* data = reinterpret_cast(i_ptr); while (--i_size >= 0) { diff --git a/src/usr/isteps/nvdimm/bpm_update.H b/src/usr/isteps/nvdimm/bpm_update.H index a1ed79c057f..f602968fe02 100644 --- a/src/usr/isteps/nvdimm/bpm_update.H +++ b/src/usr/isteps/nvdimm/bpm_update.H @@ -69,6 +69,41 @@ enum COMMAND : uint8_t BPM_LOCAL = 0xFF, }; +// These are the various response codes returned by the BPM after the +// BSL_CRC_CHECK command is sent at the end of the update procedure. +enum COMMAND_BSL_CRC_CHECK_RESPONSE_CODES : uint16_t +{ + // The updated firmware is set up with all necessary loading parameters to + // load and execute upon reset. + SUCCESSFUL_OPERATION = 0x00, + + // Error setting up the necessary loading parameters for the updated + // firmware image. + MEMORY_WRITE_CHECK_FAILED = 0x01, + + // The command was attempted without unlocking the BSL with the password. + BSL_LOCKED = 0x04, + + // Error setting up the necessary loading parameters for the updated + // firmware image. + WRITE_FORBIDDEN = 0x06, + + // The checksum validation of the updated firmware image failed. The + // calculated checksum doesn't match the checksum data provided @FF7A in the + // firmware image file. + VERIFY_MISMATCH = 0x09, + + // The firmware image start address given for the command is wrong. + PARAMETER_ERROR = 0x0A, + + // Firmware image file used for the update doesn't hae the checksum data + // defined @FF7A + MAIN_FW_NOT_SUPPORT_CRC_CHECK = 0x0B, +}; + +// BSL versions that this code supports. +const uint8_t BSL_VERSION_1_4 = 0x14; + // The operator types for the BPM_CMD_STATUS register enum COMMAND_STATUS_REGISTER_OP_TYPES : uint8_t { @@ -98,6 +133,24 @@ struct firmware_image_block typedef firmware_image_block firmware_image_block_t; +// Used to overlay onto the LID image +struct config_image_fragment +{ + // The fragment size is the size of iv_data. + uint8_t iv_fragmentSize; + + // The offset where the first byte in iv_data should begin overwritting the + // BPM config data in the BPM configuration segment dump buffer. + uint16_t iv_offset; + + // A variable sized array of config segment data. + char iv_data[0]; + +} PACKED; + +typedef config_image_fragment config_image_fragment_t; + + /* Max payload size is 26 bytes * 4 bytes: header * 1 byte: sync byte @@ -125,6 +178,15 @@ extern const uint8_t PAYLOAD_HEADER_SIZE; // automatically sends the sync byte ahead of the payload. extern const uint8_t SYNC_BYTE; +// Maximum size of any segment in the config data section +constexpr size_t SEGMENT_SIZE = 128; + +// Maximum size of the config data section. +constexpr size_t ALL_SEGMENTS_SIZE = 512; + +// Number of magic registers for the BPM +constexpr size_t NUM_MAGIC_REGISTERS = 2; + typedef std::vector payload_t; @@ -223,8 +285,90 @@ private: size_t iv_lidImageSize; }; + +class BpmConfigLidImage +{ +public: + + /** + * @brief Constructor that sets access to LID information + * + * @param[in] i_lidImageAddr virtual address where LID was loaded + * @param[in] i_size size of the loaded LID + */ + BpmConfigLidImage(void * const i_lidImageAddr, size_t i_size); + + /** + * @brief Returns the version of the config binary as a uint16_t. There isn't + * a way to check the version of the config data on the BPM but the + * config binary still has the version of the flash image it + * originally came from. + * + * @return uint16_t version of the firmware image as MMmm. + * MM = major version, mm = minor. + */ + uint16_t getVersion() const; + + /** + * @brief Returns the number of fragments in the LID image. + * + */ + uint16_t getNumberOfFragments() const; + + /** + * @brief Returns a pointer to the first fragment in LID image. + */ + void const * getFirstFragment() const; + + /* The binary will be organized in the following way: + * Byte 1: Major version number (MM) + * Byte 2: Minor version number (mm) + * Byte 3: N number of fragments in the file (NN) + * Byte 4-EOF: Fragments of the form: + * FRAGMENT_SIZE Byte 1: X number of bytes in fragment data + * section. (XX) + * INDEX_OFFSET Byte 2-3: Each BPM's config section is unique + * to itself. So, during the update + * the contents of a BPM's config data + * will be dumped into a buffer. + * These two bytes will be used as an + * offset into that buffer from which + * overwritting will take place. + * (IN DX) + * DATA_BYTES Byte 4-X: Fragment data bytes to be written + * at the INDEX_OFFSET in the dumped + * config data buffer. (DD) + * + * Example file output: + * 01 05 01 04 01 28 6a 14 31 80 + * MM mm NN XX IN DX DD DD DD DD + */ + typedef struct config_image_header + { + uint8_t iv_versionMajor; + uint8_t iv_versionMinor; + uint16_t iv_numberOfFragments; + } config_image_header_t; + +private: + + // Pointer to the LID image allocated outside of the class + void * const iv_lidImage; + + // The size of the LID image. + size_t iv_lidImageSize; +}; + class Bpm { + /* + * The Bpm can either be in Bootstrap Loader (BSL) mode or not. Many of + * member functions utilize BSL mode for the update procedure and must + * therefore be in BSL mode to succeed. Other functions perform operations + * that will not work in BSL mode since that mode is strictly for updating + * the device and turns of some functionality while in that mode. The "mode" + * the BPM must be in is given in the function brief description. + */ public: @@ -241,7 +385,24 @@ public: * @return errlHndl_t nullptr on success. Otherwise, pointer to an * errlEntry. */ - errlHndl_t runUpdate(BpmFirmwareLidImage i_image); + errlHndl_t runUpdate(BpmFirmwareLidImage i_fwImage, + BpmConfigLidImage i_configImage); + + /** + * @brief At most, one full update retry should occur in some + * circumstances. If one of those occurances happens then the + * member iv_attemptAnotherUpdate will be set to true. Otherwise, it + * will remain false. + * + * @return bool true if another update should be attempted. + * Otherwise, false. + */ + bool attemptAnotherUpdate(); + + /** + * @brief returns the nvdimm that is associated with this BPM. + */ + const TARGETING::TargetHandle_t getNvdimm(); private: @@ -250,6 +411,21 @@ private: // The Bootstrap Loader version of the BPM uint8_t iv_bslVersion; + uint16_t iv_firmwareStartAddress; + + // Keeps track of if the update should be attempted again. + bool iv_attemptAnotherUpdate; + + // Buffers for the segment data in case another update attempt is needed. + // If the first update fails there won't be any running firmware on the + // device which is required to dump the segment data. + uint8_t iv_segmentD[SEGMENT_SIZE]; + uint8_t iv_segmentB[SEGMENT_SIZE]; + + // Keeps track if the segments have been merged with the flash image data + // yet. + bool iv_segmentDMerged; + bool iv_segmentBMerged; /** * @brief Gets the BSL version from the BPM and sets the iv_bslVersion @@ -288,11 +464,11 @@ private: uint8_t i_opType); /** - * @brief This function issues a BCL command to the BPM by setting up a + * @brief This function issues a BSP command to the BPM by setting up a * payload containing only that command and then calling the * issueCommand function that accepts a payload as an argument. * - * NOTE: Since the BCL command is not a BSL command, it doesn't need + * NOTE: Since the BSP command is not a BSL command, it doesn't need * to be formatted as a BSL payload but it still must be written to * the BPM_REG_PAYLOAD_START register. * @@ -344,6 +520,13 @@ private: */ errlHndl_t updateFirmware(BpmFirmwareLidImage i_image); + /** + * @brief Executes the config portion of the BPM update. + * + * @return errlHndl_t nullptr on success. Otherwise, an Error. + */ + errlHndl_t updateConfig(); + /** * @brief Commands the BPM to enter BSL mode to allow for BSL commands to be * executed. @@ -408,6 +591,129 @@ private: */ errlHndl_t resetDevice(); + /** + * @brief Write to the BPM register via the SCAP registers + * + * @param[in] i_reg The BPM register to write to. + * + * @param[in] i_data The data to write to the given register. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeViaScapRegister(uint8_t i_reg, uint8_t i_data); + + /** + * @brief Reads the BPM register via the SCAP registers + * + * @param[in] i_reg The BPM register to read from. + * + * @param[in/out] io_data The data that was in the given register. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t readViaScapRegister(uint8_t i_reg, uint8_t & io_data); + + /** + * @brief Disables write protection on the BPM by sending the password + * sequence to I2C_REG_PROTECT + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t disableWriteProtection(); + + /** + * @brief Many operations performed on the BPM require the magic registers + * to have specific values written in them. This function acts as a + * helper to facilitate that process. + * + * NOTE: Write protection on the BPM must be disabled, otherwise + * this function will fail. + * + * @param[in] i_magicValues The pair of magic values to be written to + * BPM_MAGIC_REG1 and BPM_MAGIC_REG2 + * respectively. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeToMagicRegisters( + uint8_t const (&i_magicValues)[NUM_MAGIC_REGISTERS]); + + /** + * @brief Dumps the given segment data from the BPM. CANNOT be in BSL mode. + * + * @param[in] i_segmentCode The segment code that corresponds to the + * segment to dump from the BPM. + * + * @param[out] o_buffer A pointer to the buffer to fill with segment + * data. Must be SEGMENT_SIZE in size. + * + * @return errlHndl_t nullptr on success. Otherwise, an error + * + */ + errlHndl_t dumpSegment(uint16_t i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]); + + /** + * @brief Merges the segment data dumped from the BPM with the segment data + * fragments present in the BpmConfigLidImage that correspond to the + * given segment code. + * + * @param[in] i_configImage The image that holds the fragments of + * segment data. + * + * @param[in] i_segmentCode The segment code that corresponds to the + * segment to dump from the BPM. + * + * @param[out] o_buffer The merged segment data for the BPM. + * Must be SEGMENT_SIZE in length. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t mergeSegment(BpmConfigLidImage i_configImage, + uint16_t i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]); + + /** + * @brief Commands the BPM to erase the segment data on the BPM using the + * given segment code to tell it which to erase. + * The BPM must be in BSL mode for this function to work. + * + * @param[in] i_segmentCode The segment from the config data section to + * erase. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t eraseSegment(uint16_t i_segmentCode); + + /** + * @brief Writes the segment data from the buffer to the BPM using the + * given segment code to determine which segment the data belongs + * to. The BPM must be in BSL mode for this function to work. + * + * @param[in] i_buffer The segment data to write to the BPM. + * + * @param[in] i_segmentCode The segment from the config data section the + * data belongs to. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeSegment(uint8_t const (&i_buffer)[SEGMENT_SIZE], + uint16_t i_segmentCode); + + /** + * @brief Dumps segment D and B data from the BPM and merges it with the + * data from the config image to create the unique updated segments + * for this BPM. The BPM CANNOT be in BSL mode for this function to + * work because the data is dumped using SCAP registers. There must + * also be working firmware on the device otherwise this will fail. + * + * @param[in] i_configImage The config image that has the fragments to + * merge into the BPM's existing segment data. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t preprocessSegments(BpmConfigLidImage i_configImage); + /** * @brief A helper function used to wait for the command status bit to reset * after a command is executed. @@ -421,6 +727,41 @@ private: errlHndl_t waitForCommandStatusBitReset( command_status_register_t i_commandStatus); + /** + * @brief Helper function for the SCAP register functions that will poll + * the busy bit in SCAP_STATUS until it is zero. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t waitForBusyBit(); + + /** + * @brief Starting with BSL version 1.4 it is necessary to check the CRC of + * the firmware image once it has been written to the BPM. If this + * is not done or fails to succeed then the firmware image will not + * be loaded and executed by the BPM. If the CRC check fails then + * the update must be attempted again. + * Must be in BSL mode. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t checkFirmwareCrc(); + + /** + * @brief After a command is sent to the BPM to request info from it this + * function processes the response and returns it to the caller. + * Must be in BSL mode. + * + * @param[in] o_responseData The buffer to be filled with the + * response data from the BPM. + * + * @param[in] i_responseSize The size of the buffer to be filled. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t getResponse(uint8_t * o_responseData, + uint8_t i_responseSize); + /** * @brief Calculates the CRC16 bytes for the BSL payload. This CRC differs * from the NVDIMM CRC calculation in that the initial value is @@ -442,6 +783,36 @@ private: }; +typedef std::vector bpmList_t; + +/** + * @brief Runs the firmware and config updates on the list of BPMs given. + * + * @param[in] i_16gb_BPMs The list of BPMs sitting on 16gb NVDIMMs that + * potentially need to be updated. + * + * @param[in] i_32gb_BPMs The list of BPMs sitting on 32gb NVDIMMs that + * potentially need to be updated. + * + * @param[in] i_16gb_fwImage The firmware image associated with BPMs sitting + * on 16gb NVDIMMs. + * + * @param[in] i_32gb_fwImage The firmware image associated with BPMs sitting + * on 32gb NVDIMMs. + * + * @param[in] i_16gb_configImage The configuration data associated with BPMs + * sitting on 16gb NVDIMMs. + * + * @param[in] i_32gb_configImage The configuration data associated with BPMs + * sitting on 32gb NVDIMMs. + * + */ +void runBpmUpdates(bpmList_t * const i_16gb_BPMs, + bpmList_t * const i_32gb_BPMs, + BpmFirmwareLidImage * const i_16gb_fwImage, + BpmFirmwareLidImage * const i_32gb_fwImage, + BpmConfigLidImage * const i_16gb_configImage, + BpmConfigLidImage * const i_32gb_configImage); }; // end of BPM namespace }; // end of NVDIMM namespace diff --git a/src/usr/isteps/nvdimm/nvdimm.H b/src/usr/isteps/nvdimm/nvdimm.H index 947cfdc996d..8304486f620 100644 --- a/src/usr/isteps/nvdimm/nvdimm.H +++ b/src/usr/isteps/nvdimm/nvdimm.H @@ -277,6 +277,9 @@ enum i2cReg : uint16_t TYPED_BLOCK_DATA_OFFSET = 0x3E0, BPM_MAGIC_REG1 = 0x430, BPM_MAGIC_REG2 = 0x431, + SCAP_STATUS = 0x432, + SCAP_REG = 0x434, + SCAP_DATA = 0x435, I2C_REG_PROTECT = 0x43D, BPM_REG_CMD = 0x440, BPM_CMD_STATUS = 0x441, @@ -389,6 +392,36 @@ struct nvdimmKeyData_t uint8_t ak[ENC_KEY_SIZE]; // Access Key (AK) }; +struct scap_status_bits +{ + uint8_t Reserved1 : 1; // Bit 7 + uint8_t Bpm_Bsl_Mode : 1; // Bit 6 + uint8_t Reserved2 : 1; // Bit 5 + uint8_t Present : 1; // Bit 4 + uint8_t Delay : 1; // Bit 3 + uint8_t Error : 1; // Bit 2 + uint8_t Busy : 1; // Bit 1 + uint8_t Enable : 1; // Bit 0 +} PACKED; + +/** + * @brief Union simplifying manipulation of SCAP_STATUS bits + */ +union scap_status_union +{ + uint8_t full; + scap_status_bits bit; + + /** + * @brief Constructor + */ + scap_status_union() + : full(0) + {} +} PACKED; + +typedef scap_status_union scap_status_register_t; + /** * @brief Wrapper to call deviceOp to read the NV controller via I2C * diff --git a/src/usr/isteps/nvdimm/nvdimm_update.C b/src/usr/isteps/nvdimm/nvdimm_update.C index e69bb8c8776..3a732ef663a 100644 --- a/src/usr/isteps/nvdimm/nvdimm_update.C +++ b/src/usr/isteps/nvdimm/nvdimm_update.C @@ -2037,7 +2037,7 @@ bool NvdimmsUpdate::runUpdate(void) TRACFCOMP(g_trac_nvdimm, "Check/Update %d 32GB_TYPE NVDIMMs' BPM", v_NVDIMM_32GB_list.size()); - //@TODO RTC 210367 Add calls into bpm_update.C code. + //@TODO RTC 212448 Add calls into bpm_update.C code. } else if (( lid.id == NVDIMM_16GB_BPM_FW_LIDID) || (lid.id == NVDIMM_16GB_BPM_CONFIG_LIDID)) @@ -2045,7 +2045,7 @@ bool NvdimmsUpdate::runUpdate(void) TRACFCOMP(g_trac_nvdimm, "Check/Update %d 16GB_TYPE NVDIMMs' BPM", v_NVDIMM_16GB_list.size()); - //@TODO RTC 210367 Add calls into bpm_update.C code. + //@TODO RTC 212448 Add calls into bpm_update.C code. } else if (lid.id != NVDIMM_SIGNATURE_LIDID) { @@ -2056,6 +2056,7 @@ bool NvdimmsUpdate::runUpdate(void) } } + // @TODO RTC 212448 Add call to perform BPM updates // Destructor automatically unloads the NVDIMM flash binary } else