summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/usr/isteps/nvdimm/bpm_update.C1515
-rw-r--r--src/usr/isteps/nvdimm/bpm_update.H377
-rw-r--r--src/usr/isteps/nvdimm/nvdimm.H33
-rw-r--r--src/usr/isteps/nvdimm/nvdimm_update.C5
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
OpenPOWER on IntegriCloud