diff options
author | Matthew Raybuck <matthew.raybuck@ibm.com> | 2019-07-23 15:17:20 -0500 |
---|---|---|
committer | Daniel M Crowell <dcrowell@us.ibm.com> | 2019-08-09 09:55:40 -0500 |
commit | c03117c7bb4915a4aa9dd2bc22140ded2e301b84 (patch) | |
tree | aacfcc7ac6947b93bc66171b50c6cdb14df04d71 /src | |
parent | 0f0b7c641abb16561970c51371ecbfb9f254481b (diff) | |
download | talos-hostboot-c03117c7bb4915a4aa9dd2bc22140ded2e301b84.tar.gz talos-hostboot-c03117c7bb4915a4aa9dd2bc22140ded2e301b84.zip |
Add BPM config update procedure
Adds the config portion of the BPM updates. This includes missing
requirements from BSL version 1.4. Currently, only BSL 1.4 is supported
by the BPM update procedure. If a BSL version is unsupported, then the
update will be skipped.
Change-Id: I063786bad0723441da52db95b815a32b6498df4c
RTC: 212446
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/80925
Reviewed-by: Christian R Geddes <crgeddes@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Reviewed-by: Roland Veloz <rveloz@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/usr/isteps/nvdimm/bpm_update.C | 1515 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/bpm_update.H | 377 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.H | 33 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.C | 5 |
4 files changed, 1662 insertions, 268 deletions
diff --git a/src/usr/isteps/nvdimm/bpm_update.C b/src/usr/isteps/nvdimm/bpm_update.C index 404fcbecc..25bf29234 100644 --- a/src/usr/isteps/nvdimm/bpm_update.C +++ b/src/usr/isteps/nvdimm/bpm_update.C @@ -33,6 +33,7 @@ #include <hbotcompid.H> #include <trace/interface.H> #include <targeting/common/targetservice.H> +#include <initservice/istepdispatcherif.H> 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<uint16_t, size_t> 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 // ============================================================================= @@ -212,14 +276,89 @@ void const * BpmFirmwareLidImage::getFirstBlock() const } // ============================================================================= +// 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<const config_image_header_t*>(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<const config_image_header_t*>(iv_lidImage); + + numberOfFragments = header->iv_numberOfFragments; + } + + return numberOfFragments; +} + +void const * BpmConfigLidImage::getFirstFragment() const +{ + void * fragment = nullptr; + + if (getNumberOfFragments() > 0) + { + fragment = reinterpret_cast<uint8_t* const>(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<firmware_image_block_t const *>(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,7 +1330,804 @@ 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::readViaScapRegister(uint8_t const i_reg, uint8_t & io_data) +{ + TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::readViaScapRegister()"); + errlHndl_t errl = nullptr; + + do { + + // 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, + 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; + } + + } while(0); + + 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) + { + 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::disableWriteProtection(): " + "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); + 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) + { + TRACFCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): " + "Failed to write the magic values to the magic " + "registers"); + break; + } + } + if (errl != nullptr) + { + break; + } + + // Verify the magic values were written + uint8_t magic_data[NUM_MAGIC_REGISTERS] = {0}; + 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::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::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; +} + +errlHndl_t Bpm::dumpSegment(uint16_t const i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]) +{ + 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; + } + + + } 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<char const *>( + i_configImage.getFirstFragment()); + + config_image_fragment_t const * fragment = + reinterpret_cast<config_image_fragment_t const *>(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<config_image_fragment_t const *>(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<config_image_fragment_t const *>(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<uint16_t *> + (&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); @@ -1341,15 +2206,139 @@ errlHndl_t Bpm::waitForCommandStatusBitReset( 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<const uint8_t*>(i_ptr); - 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 (--i_size >= 0) { crc = crc ^ *(data++) << 8; diff --git a/src/usr/isteps/nvdimm/bpm_update.H b/src/usr/isteps/nvdimm/bpm_update.H index a1ed79c05..f602968fe 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<uint8_t> 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. * @@ -345,6 +521,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. * @@ -409,6 +592,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. * @@ -422,6 +728,41 @@ private: 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 * 0xFFFF instead of 0x0000. @@ -442,6 +783,36 @@ private: }; +typedef std::vector<Bpm> 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 947cfdc99..8304486f6 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 e69bb8c87..3a732ef66 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 |