summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Raybuck <matthew.raybuck@ibm.com>2019-06-20 12:36:59 -0500
committerDaniel M Crowell <dcrowell@us.ibm.com>2019-07-23 13:52:48 -0500
commitf18f9f2d14773dae84a57c9be8dfdbaa665ce799 (patch)
tree64626bf1b01fb73ae15b7ac9c5100d29ad2180ea
parentc865c731af71ca5218eaedd6707120c90f234213 (diff)
downloadblackbird-hostboot-f18f9f2d14773dae84a57c9be8dfdbaa665ce799.tar.gz
blackbird-hostboot-f18f9f2d14773dae84a57c9be8dfdbaa665ce799.zip
Add BPM firmware update procedure
Adds the necessary code to update the firmware data of the BPM. This commit doesn't support the config data portion of the update. The BPM code hasn't been hooked into the existing NVDIMM code yet either. These things, along with error paths, will be added in future commits. Change-Id: I11aa8b8afda26a093682765e488a4de0e31c0f6c RTC:210367 Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/79277 Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Reviewed-by: Matt Derksen <mderkse1@us.ibm.com> Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com> Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Reviewed-by: Roland Veloz <rveloz@us.ibm.com> Reviewed-by: Daniel M Crowell <dcrowell@us.ibm.com>
-rwxr-xr-xsrc/build/buildpnor/buildBpmFlashImages.pl10
-rw-r--r--src/include/usr/hbotcompid.H10
-rw-r--r--src/include/usr/util/utillidmgr.H4
-rw-r--r--src/usr/isteps/nvdimm/bpm_update.C1373
-rw-r--r--src/usr/isteps/nvdimm/bpm_update.H450
-rw-r--r--src/usr/isteps/nvdimm/nvdimm.H8
-rw-r--r--src/usr/isteps/nvdimm/nvdimm.mk4
-rw-r--r--src/usr/isteps/nvdimm/nvdimm_update.C16
-rw-r--r--src/usr/isteps/nvdimm/nvdimm_update.H7
9 files changed, 1877 insertions, 5 deletions
diff --git a/src/build/buildpnor/buildBpmFlashImages.pl b/src/build/buildpnor/buildBpmFlashImages.pl
index d2449031d..28e0e2b70 100755
--- a/src/build/buildpnor/buildBpmFlashImages.pl
+++ b/src/build/buildpnor/buildBpmFlashImages.pl
@@ -145,6 +145,8 @@ sub generateFirmwareImage
# The address offsets in the file are prefixed with the @ symbol.
use constant ADDRESS_LINE => "\@";
+ use constant FW_START_ADDRESS_8000 => "\@8000";
+ use constant FW_START_ADDRESS_A000 => "\@A000";
# Each line is plain text where each byte is separated by spaces.
# To determine how many bytes are on the line divide by characters per byte
@@ -173,8 +175,9 @@ sub generateFirmwareImage
last;
}
- # Ignore data from addresses below @8000 because the firmware data
- # only begins from @8000 onward.
+ # There are two possible addresses where the firmware data section can
+ # start: @8000 or @A000. Ignore all data until we reach either of those
+ # addresses since it's only after that, that the firmware data begins.
if (substr($line, 0, 1) eq ADDRESS_LINE)
{
$currentAddress = hex substr($line, 1, 4);
@@ -183,7 +186,8 @@ sub generateFirmwareImage
printf("Found address offset: 0x%04x\n", $currentAddress);
}
- if ($line eq "\@8000")
+ if ( ($line eq FW_START_ADDRESS_8000)
+ || ($line eq FW_START_ADDRESS_A000))
{
$inFirmwareSection = 1;
}
diff --git a/src/include/usr/hbotcompid.H b/src/include/usr/hbotcompid.H
index 4afcaa80e..924091fc6 100644
--- a/src/include/usr/hbotcompid.H
+++ b/src/include/usr/hbotcompid.H
@@ -496,6 +496,16 @@ const char EXPUPD_COMP_NAME[] = "expupd";
//@}
//
+/** @name BPM
+ * BPM flash update component
+ */
+//@{
+const compId_t BPM_COMP_ID = 0x4300;
+const char BPM_COMP_NAME[] = "bpm";
+
+//@}
+
+//
//
/** @name HDAT
* HDAT component
diff --git a/src/include/usr/util/utillidmgr.H b/src/include/usr/util/utillidmgr.H
index e78eba133..bd1c587c0 100644
--- a/src/include/usr/util/utillidmgr.H
+++ b/src/include/usr/util/utillidmgr.H
@@ -63,6 +63,10 @@ enum LidId
TARGETING_BINARY_LIDID = 0x81e00630,
NVDIMM_16GB_LIDID = 0x81e00640,
NVDIMM_32GB_LIDID = 0x81e00641,
+ NVDIMM_16GB_BPM_FW_LIDID = 0x81e00642,
+ NVDIMM_32GB_BPM_FW_LIDID = 0x81e00643,
+ NVDIMM_16GB_BPM_CONFIG_LIDID = 0x81e00644,
+ NVDIMM_32GB_BPM_CONFIG_LIDID = 0x81e00645,
UCD_LIDID = 0x81e00650,
INVALID_LIDID = 0xFFFFFFFF
diff --git a/src/usr/isteps/nvdimm/bpm_update.C b/src/usr/isteps/nvdimm/bpm_update.C
new file mode 100644
index 000000000..404fcbecc
--- /dev/null
+++ b/src/usr/isteps/nvdimm/bpm_update.C
@@ -0,0 +1,1373 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/isteps/nvdimm/bpm_update.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2019 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/* */
+/* IBM_PROLOG_END_TAG */
+
+#include "nvdimm.H"
+#include "bpm_update.H"
+#include "nvdimm_update.H"
+
+#include <errl/hberrltypes.H>
+#include <endian.h>
+#include <sys/time.h>
+#include <hbotcompid.H>
+#include <trace/interface.H>
+#include <targeting/common/targetservice.H>
+
+namespace NVDIMM
+{
+namespace BPM
+{
+
+trace_desc_t* g_trac_bpm = nullptr;
+TRAC_INIT(&g_trac_bpm, BPM_COMP_NAME, 2*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_OTHER_DATA_SIZE = 10;
+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;
+
+// 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;
+
+/**
+ * @brief A helper function used in assert statements to verify the correct
+ * BSP commands were passed into the correct function arguments.
+ *
+ * @param[in] i_command The command that will verified to be a BSP command.
+ *
+ * @return bool true if i_command is a BSP command.
+ * false if it's not a BSP command.
+ */
+bool isBspCommand(const uint8_t i_command)
+{
+ bool result = ((i_command == BPM_PASSTHROUGH) || (i_command == BPM_LOCAL))
+ ? true : false;
+
+ return result;
+}
+
+/**
+ * @brief A helper function used in assert statements to verify the correct
+ * BCL commands were passed into the correct function arguments.
+ *
+ * @param[in] i_command The command that will verified to be a BCL command.
+ *
+ * @return bool true if i_command is a BCL command.
+ * false if it's not a BCL command.
+ */
+bool isBclCommand(const uint8_t i_command)
+{
+ bool result = false;
+ switch(i_command)
+ {
+ case BCL_ENTER_BSL_MODE:
+ case BCL_IS_BSL_MODE:
+ case BCL_WRITE_REG:
+ case BCL_START_UPDATE:
+ case BCL_END_UPDATE:
+ case BCL_IS_UPDATE_IN_PROGRESS:
+ {
+ result = true;
+ break;
+ }
+ default:
+ {
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief A helper function used in assert statements to verify the correct
+ * BSL commands were passed into the correct function arguments.
+ *
+ * @param[in] i_command The command that will verified to be a BSL command.
+ *
+ * @return bool true if i_command is a BSL command.
+ * false if it's not a BSL command.
+ */
+bool isBslCommand(const uint8_t i_command)
+{
+ bool result = false;
+ switch(i_command)
+ {
+ case BSL_RX_DATA_BLOCK:
+ case BSL_RX_PASSWORD:
+ case BSL_ERASE_SEGMENT:
+ case BSL_TOGGLE_INFO:
+ case BSL_ERASE_BLOCK:
+ case BSL_MASS_ERASE:
+ case BSL_CRC_CHECK:
+ case BSL_LOAD_PC:
+ case BSL_TX_DATA_BLOCK:
+ case BSL_TX_BSL_VERSION:
+ case BSL_TX_BUFFER_SIZE:
+ case BSL_RX_DATA_BLOCK_FAST:
+ case BSL_RESET_DEVICE:
+ case BSL_VERIFY_BLOCK:
+ {
+ result = true;
+ break;
+ }
+ default:
+ {
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+// =============================================================================
+// BpmFirmwareLidImage Class Functions
+// =============================================================================
+
+BpmFirmwareLidImage::BpmFirmwareLidImage(void * const i_lidImageAddr,
+ size_t i_size)
+ : iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size)
+{
+}
+
+uint16_t BpmFirmwareLidImage::getVersion() const
+{
+ uint16_t version = INVALID_VERSION;
+
+ if (iv_lidImageSize >= sizeof(firmware_image_header_t))
+ {
+ const firmware_image_header_t * header =
+ reinterpret_cast<const firmware_image_header_t*>(iv_lidImage);
+
+ version = TWO_UINT8_TO_UINT16(header->iv_versionMajor,
+ header->iv_versionMinor);
+ }
+
+ return version;
+}
+
+uint16_t BpmFirmwareLidImage::getNumberOfBlocks() const
+{
+ uint16_t numberOfBlocks = 0;
+
+ if (iv_lidImageSize >= sizeof(firmware_image_header_t))
+ {
+ const firmware_image_header_t * header =
+ reinterpret_cast<const firmware_image_header_t*>(iv_lidImage);
+
+ numberOfBlocks = header->iv_numberOfBlocks;
+ }
+
+ return numberOfBlocks;
+}
+
+void const * BpmFirmwareLidImage::getFirstBlock() const
+{
+ void * block = nullptr;
+
+ if (getNumberOfBlocks() > 0)
+ {
+ block = reinterpret_cast<uint8_t* const>(iv_lidImage)
+ + sizeof(firmware_image_header_t);
+ }
+
+ return block;
+}
+
+// =============================================================================
+// Bpm Class Functions
+// =============================================================================
+
+Bpm::Bpm(const TARGETING::TargetHandle_t i_nvdimm)
+ : iv_nvdimm(i_nvdimm), iv_bslVersion(0)
+{
+ assert((i_nvdimm != nullptr) && (isNVDIMM(i_nvdimm)),
+ "An nvdimm target must be given.");
+}
+
+errlHndl_t Bpm::readBslVersion()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"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);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Issue the BSL command
+ errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+
+ // Read out the version payload.
+ payload_t versionPayload;
+
+ 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)
+ {
+ 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);
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ iv_bslVersion = versionPayload[VERSION_PAYLOAD_VERSION_INDEX];
+
+ TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): BSL Version is 0x%X",
+ iv_bslVersion);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::getFwVersion(uint16_t & o_fwVersion) const
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::getFwVersion()");
+ errlHndl_t errl = nullptr;
+
+ do {
+ uint8_t bpmMajor = 0, bpmMinor = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ ES_FWREV1,
+ bpmMajor);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getFwVersion(): "
+ "Failed to read BPM major version byte");
+ break;
+ }
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ ES_FWREV0,
+ bpmMinor);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getFwVersion(): "
+ "Failed to read BPM minor version byte");
+ break;
+ }
+
+ o_fwVersion = TWO_UINT8_TO_UINT16(bpmMajor, bpmMinor);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::issueCommand(const uint8_t i_bspCommand,
+ const uint8_t i_command,
+ const uint8_t i_opType)
+{
+ assert(isBspCommand(i_bspCommand),
+ "i_bspCommand must be a valid BSP command");
+ assert(isBclCommand(i_command),
+ "i_command must be a valid BCL command");
+ // i_opType gets set in the BPM_CMD_STATUS register where it is only given
+ // two bits. So any value above 3 is not valid.
+ assert(i_opType <= 3, "i_opType can only range between 0 and 3");
+
+ errlHndl_t errl = nullptr;
+
+ // i_command must be sent in BPM_REG_PAYLOAD_START, but it doesn't need to
+ // be formatted into a typical payload since the command isn't a BSL
+ // command. So, just create a payload_t, push_back the command, and let the
+ // issueCommand function that takes a payload_t parameter handle the rest.
+ payload_t payloadCommand;
+ payloadCommand.push_back(i_command);
+
+ errl = issueCommand(i_bspCommand, payloadCommand, i_opType);
+
+ return errl;
+}
+
+errlHndl_t Bpm::issueCommand(const uint8_t i_command,
+ payload_t i_payload,
+ const uint8_t i_opType)
+{
+ assert(isBspCommand(i_command),
+ "i_bspCommand must be a valid BSP command");
+
+ // i_opType gets set in the BPM_CMD_STATUS register where it is only given
+ // two bits. So any value above 3 is not valid.
+ assert(i_opType <= 3, "i_opType can only range between 0 and 3");
+
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ if (i_payload.size() > MAX_PAYLOAD_SIZE)
+ {
+ //@TODO RTC 212447: Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::issueCommand(): "
+ "payload size %d exceeds max payload size of %d",
+ i_payload.size(), MAX_PAYLOAD_SIZE);
+ break;
+ }
+
+ // Load the payload
+ int i = 0;
+ for (const auto& byte : i_payload)
+ {
+ errl = nvdimmWriteReg(iv_nvdimm,
+ (BPM_REG_PAYLOAD_START + (i * sizeof(uint8_t))),
+ byte);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::issueCommand(): "
+ "Failed to write payload to BPM_REG_PAYLOAD_START");
+ break;
+ }
+
+ ++i;
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Clear the error status register
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_REG_ERR_STATUS,
+ 0x00);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::issueCommand(): "
+ "Failed to clear error status register");
+ break;
+ }
+
+ // Set the payload length
+ uint8_t data = i_payload.size();
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_PAYLOAD_LENGTH,
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::issueCommand(): "
+ "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 = i_opType;
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_CMD_STATUS,
+ commandStatus.value);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::issueCommand(): "
+ "Failed to setup the command status register");
+ break;
+ }
+
+ // Setup command type. The basically executes the command
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_REG_CMD,
+ i_command);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::issueCommand(): "
+ "Failed to set the command type. "
+ "The command was not issued to the BPM");
+ break;
+ }
+
+ errl = waitForCommandStatusBitReset(commandStatus);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_image)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::runUpdate(): "
+ "Running BPM Update for NVDIMM 0x%.8X",
+ TARGETING::get_huid(iv_nvdimm));
+
+ errlHndl_t errl = nullptr;
+ do {
+
+ // Check the version on the BPM against the version in the image.
+ uint16_t bpmFwVersion = INVALID_VERSION;
+ errl = getFwVersion(bpmFwVersion);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Could not determine firmware version on BPM "
+ "Skipping update.");
+ break;
+ }
+
+ if (i_image.getVersion() == bpmFwVersion)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM matches the version in the "
+ "image. Skipping update.");
+
+ // @TODO RTC 212448: disable forced updates.
+ //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());
+
+ // Enter Update mode
+ errl = enterUpdateMode();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Verify in Update mode
+ errl = inUpdateMode();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Enter Bootstrap Loader (BSL) mode to perform firmware update
+ 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;
+ }
+
+ // Depending on the BSL version a CRC check may be necessary
+ // @TODO RTC 212446: Add CRC check to update procedure
+ errl = readBslVersion();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Run Firmware Update
+ errl = updateFirmware(i_image);
+ 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;
+// }
+
+ } while(0);
+
+ // Reset the device. This will exit BSL mode.
+ errlHndl_t exitErrl = resetDevice();
+ if (exitErrl != nullptr)
+ {
+ //@TODO RTC 212447 Do something with the error.
+ delete exitErrl;
+ }
+
+ // Exit update mode
+ exitErrl = exitUpdateMode();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Failed to exit update mode");
+ //@TODO RTC 212447 Do something with the error.
+ delete exitErrl;
+ }
+
+ // To see the BPM firmware level updated we must reset the controller
+ exitErrl = nvdimmWriteReg(iv_nvdimm,
+ NVDIMM_MGT_CMD0,
+ 0x01);
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Could not reset NVDIMM Controller");
+ //@TODO RTC 212447: Do something with the error.
+ delete exitErrl;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::runUpdate(): "
+ "Reset command sent to NVDIMM controller, sleep for 15 seconds");
+ nanosleep(15,0);
+
+ uint16_t bpmFwVersion = INVALID_VERSION;
+ exitErrl = getFwVersion(bpmFwVersion);
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Could not determine firmware version on the BPM");
+ //@TODO RTC 212447 Do something with the error.
+ delete exitErrl;
+ }
+
+ // @TODO RTC 212446: Depending on BSL version, need to do CRC check.
+ if (i_image.getVersion() == bpmFwVersion)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM matches the version in the "
+ "image. Update Successful.");
+ }
+
+ TRACFCOMP(g_trac_bpm, EXIT_MRK"Bpm::runUpdate(): "
+ "Concluding BPM Update for NVDIMM 0x%.8X",
+ TARGETING::get_huid(iv_nvdimm));
+
+ return errl;
+}
+
+errlHndl_t Bpm::inUpdateMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::inUpdateMode()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ errl = issueCommand(BPM_LOCAL, BCL_IS_UPDATE_IN_PROGRESS, READ);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ uint8_t isUpdateInProgress = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ BPM_REG_ERR_STATUS,
+ isUpdateInProgress);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::inUpdateMode(): "
+ "Failed to read error status register");
+ break;
+ }
+
+ if (!isUpdateInProgress)
+ {
+ // @TODO RTC 212447 Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::inUpdateMode(): "
+ "Failed to enter update mode");
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::enterUpdateMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::enterUpdateMode()");
+ errlHndl_t errl = nullptr;
+
+ 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);
+ 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;
+ }
+ }
+ 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]);
+ break;
+ }
+
+ errl = issueCommand(BPM_LOCAL, BCL_START_UPDATE, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ nanosleep(2,0);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::exitUpdateMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::exitUpdateMode()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ errl = issueCommand(BPM_LOCAL, BCL_END_UPDATE, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // 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;
+ }
+ }
+ 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]);
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::updateFirmware()");
+ errlHndl_t errl = nullptr;
+
+ const uint16_t MAIN_PROGRAM_ADDRESS = 0x8000;
+ const uint16_t NUMBER_OF_BLOCKS = i_image.getNumberOfBlocks();
+
+ char const * data =
+ reinterpret_cast<char const *>(i_image.getFirstBlock());
+
+ firmware_image_block_t const * block =
+ reinterpret_cast<firmware_image_block_t const *>
+ (data);
+
+ 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)
+ {
+ payload_t payload;
+ errl = setupPayload(payload, BSL_MASS_ERASE, MAIN_PROGRAM_ADDRESS);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Performing BSL_MASS_ERASE, sleep for 5 seconds.");
+ nanosleep(5,0);
+ }
+
+ // Construct the payload for this block in the image
+ payload_t payload;
+ errl = setupPayload(payload, block, BSL_RX_DATA_BLOCK);
+ 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.01 second
+ nanosleep(0, 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;
+}
+
+errlHndl_t Bpm::enterBootstrapLoaderMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::enterBootstrapLoaderMode()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // Entering BSL mode depends on the state of the BPM and it may need
+ // several retries in order to successfully enter BSL mode.
+ int retry = 10;
+ bool inBslMode = false;
+
+ while (retry != 0)
+ {
+
+ errl = issueCommand(BPM_LOCAL, BCL_IS_BSL_MODE, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ uint8_t data = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ BPM_REG_ERR_STATUS,
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::enterBootstrapLoaderMode(): "
+ "Failed to read BPM_REG_ERR_STATUS to verify that "
+ "BSL mode was enabled.");
+ break;
+ }
+ // data will be 1 if the BPM successfully entered BSL mode.
+ if (data == 1)
+ {
+ inBslMode = true;
+ TRACFCOMP(g_trac_bpm, "Bpm::enterBootstrapLoaderMode(): "
+ "BSL Mode entered, sleep for 5 seconds.");
+ nanosleep(5,0);
+ break;
+ }
+
+ TRACUCOMP(g_trac_bpm, "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)
+ {
+ // @TODO RTC 212447 Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::enterBootstrapLoaderMode(): "
+ "Failed to enter BSL mode on the BPM");
+
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::setupPayload(payload_t & o_payload,
+ const uint8_t i_command,
+ const uint16_t i_address,
+ const uint8_t i_data[],
+ const size_t i_length)
+{
+ // Enforce sane inputs
+ assert(( (i_data == nullptr && i_length == 0)
+ || (i_data != nullptr && i_length != 0)),
+ "if i_length is non-zero then i_data must not be nullptr, otherwise i_data must be nullptr.");
+ assert(isBslCommand(i_command),
+ "i_command must be a valid BSL command");
+
+ errlHndl_t errl = nullptr;
+
+ // Calculate the block size.
+ size_t blockSize = sizeof(uint16_t) + i_length;
+
+ // Allocate memory for the block
+ firmware_image_block_t* myBlock = reinterpret_cast<firmware_image_block_t*>(
+ malloc(sizeof(firmware_image_block_t) + i_length));
+
+ // Setup the block "header" info
+ myBlock->iv_blockSize = blockSize;
+ myBlock->iv_addressOffset = i_address;
+
+ // Copy the data if any exists.
+ if (i_data != nullptr)
+ {
+ memcpy(&myBlock->iv_data, i_data, i_length);
+ }
+
+ // Setup the return payload
+ errl = setupPayload(o_payload, myBlock, i_command);
+
+ // Block is no longer needed.
+ free(myBlock);
+
+ return errl;
+}
+
+errlHndl_t Bpm::setupPayload(payload_t & o_payload,
+ const firmware_image_block_t * i_block,
+ const uint8_t i_command)
+{
+ assert(i_block != nullptr, "i_block must not be nullptr.");
+ assert(isBslCommand(i_command),
+ "i_command must be a valid BSL command");
+
+ errlHndl_t errl = nullptr;
+
+ // The data size in the block is the total block size
+ // minus the 2 bytes for the address offset.
+ const uint8_t blockDataSize = i_block->iv_blockSize - sizeof(uint16_t);
+
+ // The header plus payload data section size. This excludes the address
+ // offset, extra bytes, and CRC bytes.
+ const uint8_t headerDataSize = PAYLOAD_HEADER_SIZE + blockDataSize;
+
+ do {
+
+ if (blockDataSize > MAX_PAYLOAD_DATA_SIZE)
+ {
+ // @TODO RTC 212447 Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "Bpm::setupPayload(): Block Data Size %d exceeds max payload "
+ "size of %d",
+ blockDataSize,
+ MAX_PAYLOAD_DATA_SIZE);
+ break;
+ }
+
+ // Create the payload with the exact size needed.
+ payload_t payload(MAX_PAYLOAD_OTHER_DATA_SIZE + blockDataSize);
+
+ // Instead of using push_back, use a pointer to an element in the vector.
+ // Since the size of the vector is declared and intialized to zero ahead of
+ // time push_back will not work. Also, some of the data is larger than
+ // uint8_t and so it's easier to just use memcpy for insertion.
+ // NOTE: Because push_back isn't being used the size() of the vector doesn't
+ // change along with the data being added to the vector. This was
+ // corrected by explicitly setting the payload size in the constructor
+ // call above.
+ uint8_t * payloadIterator = payload.data();
+
+ // According to SMART, we must supply the header + data size twice.
+ uint8_t header[PAYLOAD_HEADER_SIZE] = { SYNC_BYTE,
+ i_command,
+ headerDataSize,
+ headerDataSize };
+
+ memcpy(payloadIterator, &header, PAYLOAD_HEADER_SIZE);
+
+ // Move past the header
+ payloadIterator += PAYLOAD_HEADER_SIZE;
+
+ // Write the address offset in little endian form.
+ uint16_t addressLE = htole16(i_block->iv_addressOffset);
+ uint8_t* addressOffset = reinterpret_cast<uint8_t*>(&addressLE);
+ memcpy(payloadIterator, addressOffset, sizeof(uint16_t));
+
+ // Move past the address
+ payloadIterator += sizeof(uint16_t);
+
+ // The extra bytes vary based on the given command.
+ // These are the extra bytes for their corresponding bootstrap loader
+ // commands. They are arranged in little endian form so that no byte
+ // swapping is required.
+ const uint8_t BSL_ERASE_SEGMENT_EXTRA_BYTES[] = {0x02, 0xA5};
+ const uint8_t BSL_MASS_ERASE_EXTRA_BYTES[] = {0x06, 0xA5};
+ switch(i_command)
+ {
+ case BSL_ERASE_SEGMENT:
+ {
+ memcpy(payloadIterator,
+ &BSL_ERASE_SEGMENT_EXTRA_BYTES,
+ sizeof(uint16_t));
+
+ break;
+ }
+ case BSL_MASS_ERASE:
+ {
+ memcpy(payloadIterator,
+ &BSL_MASS_ERASE_EXTRA_BYTES,
+ sizeof(uint16_t));
+ break;
+ }
+ default:
+ {
+ // Give the size of the data section as a uint16_t in little
+ // endian form.
+ uint8_t dataLength[] = {blockDataSize, 0x0};
+ memcpy(payloadIterator, &dataLength, sizeof(uint16_t));
+ break;
+ }
+ }
+
+ // Move past the payload's extra bytes.
+ payloadIterator += sizeof(uint16_t);
+
+ if (blockDataSize > 0)
+ {
+ // Copy the payload data from the LID image block to the payload's data
+ // section.
+ memcpy(payloadIterator, &i_block->iv_data, blockDataSize);
+
+ // Move past the payload's data section.
+ payloadIterator += blockDataSize;
+ }
+
+ // Calculate the CRC bytes
+ // Pass in the size of the payload excluding the two reserved bytes
+ // for the CRC.
+ uint16_t crc = htole16(crc16_calc(payload.data(), payload.size()-2));
+
+ // Write the CRC bytes
+ uint8_t* crcBytes = reinterpret_cast<uint8_t*>(&crc);
+ memcpy(payloadIterator, crcBytes, sizeof(uint16_t));
+
+ // The sync byte is automatically sent by the NVDIMM to the BPM so
+ // including it in the payload isn't necessary. It is only needed to
+ // calculate the CRC bytes.
+ payload.erase(payload.begin());
+ // Force the returned payload to have the exact capacity and size of the
+ // payload.
+ o_payload.swap(payload);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::unlockDevice()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::unlockDevice()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // This is a BSL command, so it must be formatted into a payload.
+ payload_t payload;
+
+ // This command must send the password in order to unlock the device.
+ errl = setupPayload(payload,
+ BSL_RX_PASSWORD,
+ BPM_ADDRESS_ZERO,
+ BPM_PASSWORD,
+ BPM_PASSWORD_LENGTH);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::resetDevice()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::resetDevice()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // This is a BSL command, so it must be formatted into a payload.
+ payload_t payload;
+ errl = setupPayload(payload, BSL_RESET_DEVICE, BPM_ADDRESS_ZERO);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH, payload, WRITE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+
+ TRACFCOMP(g_trac_bpm, "Bpm::resetDevice(): "
+ "Resetting BPM for NVDIMM 0x%.8X, sleep for 10 seconds.",
+ TARGETING::get_huid(iv_nvdimm));
+ nanosleep(10,0);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::waitForCommandStatusBitReset(
+ command_status_register_t i_commandStatus)
+{
+ errlHndl_t errl = nullptr;
+
+ do {
+ // Wait until the COMMAND_IN_PROGRESS bit is reset
+ errl = nvdimmReadReg(iv_nvdimm,
+ BPM_CMD_STATUS,
+ i_commandStatus.value);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ int retry = 10;
+
+ while (i_commandStatus.bits.Bsp_Cmd_In_Progress)
+ {
+ nanosleep(0, 1 * NS_PER_MSEC);
+ errl = nvdimmReadReg(iv_nvdimm,
+ BPM_CMD_STATUS,
+ i_commandStatus.value);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): "
+ "Failed to read BPM_CMD_STATUS register");
+ break;
+ }
+
+ if (--retry <= 0)
+ {
+ //@TODO RTC 212447: Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "BPM::waitForCommandStatusBitReset(): "
+ "BSP_CMD_IN_PROGRESS bit has not reset in allotted "
+ "number of retries. Cancel update procedure");
+ break;
+ }
+
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Check for error
+ if (i_commandStatus.bits.Error_Flag)
+ {
+ uint8_t error = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ BPM_REG_ERR_STATUS,
+ error);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::waitForCommandStatusBitReset(): "
+ "Failed to read BPM_REG_ERR_STATUS");
+ break;
+ }
+
+ // @TODO RTC 212447 Error
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::getBslVersion(): "
+ "BPM_CMD_STATUS Error Flag is set");
+ break;
+
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+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;
+ for (size_t i = 0; i < 8; ++i)
+ {
+ if (crc & 0x8000)
+ {
+ crc = crc << 1 ^ 0x1021;
+ }
+ else
+ {
+ crc = crc << 1;
+ }
+ }
+ }
+
+ return (crc & 0xFFFF);
+}
+
+}; // End of BPM namespace
+}; // End of NVDIMM namespace
diff --git a/src/usr/isteps/nvdimm/bpm_update.H b/src/usr/isteps/nvdimm/bpm_update.H
new file mode 100644
index 000000000..a1ed79c05
--- /dev/null
+++ b/src/usr/isteps/nvdimm/bpm_update.H
@@ -0,0 +1,450 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/isteps/nvdimm/bpm_update.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2019 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/* */
+/* IBM_PROLOG_END_TAG */
+
+#ifndef BPM_UPDATE_H
+#define BPM_UPDATE_H
+
+/* @file bpm_update.H
+ *
+ *
+ */
+
+#include <vector>
+#include <errl/errlentry.H>
+#include <targeting/common/util.H>
+
+namespace NVDIMM
+{
+namespace BPM
+{
+
+// All of the various commands used for the BPM update
+// BSL: Bootstrap Loader commands
+// BPM: Backup Power Module
+enum COMMAND : uint8_t
+{
+ BCL_ENTER_BSL_MODE = 0x01,
+ BCL_IS_BSL_MODE = 0x02,
+ BCL_WRITE_REG = 0x03,
+ BCL_START_UPDATE = 0x04,
+ BCL_END_UPDATE = 0x05,
+ BCL_IS_UPDATE_IN_PROGRESS = 0x06,
+ BSL_RX_DATA_BLOCK = 0x10,
+ BSL_RX_PASSWORD = 0x11,
+ BSL_ERASE_SEGMENT = 0x12,
+ BSL_TOGGLE_INFO = 0x13,
+ BSL_ERASE_BLOCK = 0x14,
+ BSL_MASS_ERASE = 0x15,
+ BSL_CRC_CHECK = 0x16,
+ BSL_LOAD_PC = 0x17,
+ BSL_TX_DATA_BLOCK = 0x18,
+ BSL_TX_BSL_VERSION = 0x19,
+ BSL_TX_BUFFER_SIZE = 0x1A,
+ BSL_RX_DATA_BLOCK_FAST = 0x1B,
+ BSL_RESET_DEVICE = 0x1C,
+ BSL_VERIFY_BLOCK = 0x1D,
+ BPM_PASSTHROUGH = 0xFE,
+ BPM_LOCAL = 0xFF,
+};
+
+// The operator types for the BPM_CMD_STATUS register
+enum COMMAND_STATUS_REGISTER_OP_TYPES : uint8_t
+{
+ NOP = 0x00,
+ READ = 0x01,
+ WRITE = 0x02,
+ NO_TRASFER = 0x03,
+};
+
+// Used to overlay onto the LID image
+struct firmware_image_block
+{
+ // The block size is the sizeof(iv_addressOffset) plus sizeof(iv_data).
+ uint8_t iv_blockSize;
+
+ // The address offset where the first byte in iv_data came from in the
+ // firmware image.
+ uint16_t iv_addressOffset;
+
+ // A variable sized array of firmware data. The size of which is always
+ // iv_blockSize - sizeof(iv_addressOffset) and the max this can be is
+ // MAX_PAYLOAD_SIZE.
+ char iv_data[0];
+
+} PACKED;
+
+typedef firmware_image_block firmware_image_block_t;
+
+
+/* Max payload size is 26 bytes
+ * 4 bytes: header
+ * 1 byte: sync byte
+ * 1 byte: command
+ * 1 byte: header size + data size
+ * 1 byte: header size + data size
+ * 2 bytes: address
+ * 2 bytes: extra
+ * 16 bytes: data
+ * 2 bytes: CRC
+ */
+extern const size_t MAX_PAYLOAD_SIZE;
+
+// Max number of bytes data section of payload can be.
+extern const size_t MAX_PAYLOAD_DATA_SIZE;
+
+// Number of bytes for header, address, extra, and CRC
+extern const size_t MAX_PAYLOAD_OTHER_DATA_SIZE;
+
+// Number of bytes for the header.
+extern const uint8_t PAYLOAD_HEADER_SIZE;
+
+// The sync byte that must always be at the front of a BPM payload. This is used
+// calculate the CRC of the payload and then removed because the nvdimm
+// automatically sends the sync byte ahead of the payload.
+extern const uint8_t SYNC_BYTE;
+
+typedef std::vector<uint8_t> payload_t;
+
+
+/**
+ * @brief BPM_CMD_STATUS register bits
+ */
+struct command_status_register_bits
+{
+ uint8_t Abort_Request : 1; // Bit 7
+ uint8_t Abort_Acknowledge : 1; // Bit 6
+ uint8_t Reserved1 : 1; // Bit 5
+ uint8_t Reserved2 : 1; // Bit 4
+ uint8_t Error_Flag : 1; // Bit 3
+ uint8_t Bsp_Cmd_In_Progress : 1; // Bit 2
+ uint8_t Operator_Type : 2; // Bit 1-0
+} PACKED;
+
+/**
+ * @brief Union simplifying manipulation of REG_CMD_STATUS value
+ */
+union command_status_register_union
+{
+ uint8_t value;
+ command_status_register_bits bits;
+
+ /**
+ * @brief Constructor
+ */
+ command_status_register_union()
+ : value(0)
+ {}
+
+} PACKED;
+
+typedef command_status_register_union command_status_register_t;
+
+class BpmFirmwareLidImage
+{
+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
+ */
+ BpmFirmwareLidImage(void * const i_lidImageAddr, size_t i_size);
+
+ /**
+ * @brief Returns the version of the firmware binary as a uint16_t
+ *
+ * @return uint16_t version of the firmware image as MMmm.
+ * MM = major version, mm = minor.
+ */
+ uint16_t getVersion() const;
+
+ /**
+ * @brief Returns the number of blocks in the LID image.
+ *
+ */
+ uint16_t getNumberOfBlocks() const;
+
+ /**
+ * @brief Returns a pointer to the first block in LID image.
+ */
+ void const * getFirstBlock() const;
+
+ /* Layout of the BPM Firmware image
+ * Byte 1: Major version number (MM)
+ * Byte 2: Minor version number (mm)
+ * Byte 3-4: N number of blocks in the file (NN NN)
+ * Byte 5-EOF: Blocks of the form:
+ * BLOCK_SIZE Byte 1: X number of bytes in block excluding
+ * this byte. (XX)
+ * ADDRESS_OFFSET Byte 2-3: Original address offset of the
+ * first data byte. (AD DR)
+ * DATA_BYTES Byte 4-X: Firmware data bytes (DD)
+ *
+ * Example file:
+ * 01 03 00 01 06 80 00 6a 14 31 80
+ * MM mm NN NN XX AD DR DD DD DD DD
+ */
+ typedef struct firmware_image_header
+ {
+ uint8_t iv_versionMajor;
+ uint8_t iv_versionMinor;
+ uint16_t iv_numberOfBlocks;
+ } firmware_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
+{
+public:
+
+
+ explicit Bpm(const TARGETING::TargetHandle_t i_nvdimm);
+
+ // Force User to supply a nvdimm target.
+ Bpm() = delete;
+
+ /**
+ * @brief Runs the BPM firmware update using the given image.
+ *
+ * @param[in] i_image The BPM firmware image.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t runUpdate(BpmFirmwareLidImage i_image);
+
+private:
+
+ // The nvdimm whose battery firmware will be updated.
+ const TARGETING::TargetHandle_t iv_nvdimm;
+
+ // The Bootstrap Loader version of the BPM
+ uint8_t iv_bslVersion;
+
+ /**
+ * @brief Gets the BSL version from the BPM and sets the iv_bslVersion
+ * member. Only needs to be called once.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t readBslVersion();
+
+ /**
+ * @brief Gets the Firmware version from the BPM
+ *
+ * @param[out] o_fwVersion The firmware version currently on the BPM.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t getFwVersion(uint16_t & o_fwVersion) const;
+
+ /**
+ * @brief This function issues a command to the BPM using a payload as the
+ * means of sending the command.
+ *
+ * @param[in] i_command The BSP command to send to the BPM.
+ * @param[in] i_payload The payload to write to the
+ * BPM_REG_PAYLOAD_START register.
+ * @param[in] i_opType The operation type of the command. Must be one
+ * of the COMMAND_STATUS_REGISTER_OP_TYPES
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t issueCommand(uint8_t i_command,
+ payload_t i_payload,
+ uint8_t i_opType);
+
+ /**
+ * @brief This function issues a BCL 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
+ * to be formatted as a BSL payload but it still must be written to
+ * the BPM_REG_PAYLOAD_START register.
+ *
+ * @param[in] i_bspCommand The BSP command to send to the BPM.
+ * @param[in] i_command The BCL command to be written to the
+ * BPM_REG_PAYLOAD_START register. Must be one
+ * of the BCL_ commands.
+ * @param[in] i_opType The operation type of the BSP command. Must
+ * be a COMMAND_STATUS_REGISTER_OP_TYPES
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t issueCommand(uint8_t i_bspCommand,
+ uint8_t i_command,
+ uint8_t i_opType);
+
+ /**
+ * @brief This function checks if the BPM has entered update mode
+ *
+ * @return errlHndl_t nullptr on success.
+ * Otherwise, pointer to an errlEntry.
+ */
+ errlHndl_t inUpdateMode();
+
+ /**
+ * @brief Send the command to the BPM to enter update mode
+ *
+ * @return errlHndl_t nullptr if no errors occurred during command
+ * execution. Otherwise, pointer to an errlEntry.
+ */
+ errlHndl_t enterUpdateMode();
+
+ /**
+ * @brief Send the command to the BPM to exit update mode
+ *
+ * @return errlHndl_t nullptr if no errors occurred during command
+ * execution. Otherwise, pointer to an errlEntry.
+ */
+ errlHndl_t exitUpdateMode();
+
+ /**
+ * @brief Executes the firmware portion of the BPM update.
+ *
+ * @param[in] i_image The BPM firmware LID image to apply to the BPM.
+ *
+ * @return errlHndl_t nullptr if no errors occurred.
+ * Otherwise, pointer to an errlEntry.
+ */
+ errlHndl_t updateFirmware(BpmFirmwareLidImage i_image);
+
+ /**
+ * @brief Commands the BPM to enter BSL mode to allow for BSL commands to be
+ * executed.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t enterBootstrapLoaderMode();
+
+ /**
+ * @brief Creates a valid BSL payload given a firmware_image_block_t.
+ *
+ * @param[out] o_payload The BSL payload
+ * @param[in] i_block A pointer to a firmware image block.
+ * @param[in] i_command The BSL command to be included with the payload
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t setupPayload(payload_t & o_payload,
+ const firmware_image_block_t * i_block,
+ uint8_t i_command);
+
+ /**
+ * @brief Creates a valid BSL payload given a BSL command, address, and
+ * optionally data to include with the command. This function is used
+ * to create firmware_image_block_t objects which are then passed
+ * onto the version of setupPayload that turns them into payloads.
+ *
+ * @param[out] o_payload The BSL payload
+ * @param[in] i_command The BSL command to be included with the payload
+ * @param[in] i_address The address to execute the command from. This
+ * will be zero or the address to execute the
+ * command from.
+ * @param[in] i_data The array of data to be included with the BSL
+ * command. Default nullptr.
+ * @param[in] i_length Length of the i_data array parameter. Default 0.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t setupPayload(payload_t & o_payload,
+ uint8_t i_command,
+ uint16_t i_address,
+ const uint8_t i_data[] = nullptr,
+ size_t i_length = 0);
+
+ /**
+ * @brief This function unlocks the BPM.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t unlockDevice();
+
+ /**
+ * @brief This function will send the command to reset the BPM. This will
+ * exit BSL mode if the BPM was in that mode.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, pointer to an
+ * errlEntry.
+ */
+ errlHndl_t resetDevice();
+
+ /**
+ * @brief A helper function used to wait for the command status bit to reset
+ * after a command is executed.
+ *
+ * @param[in] i_commandStatus The command status register union made
+ * by the caller to identify the type of
+ * command that was sent.
+ *
+ * @return errlHndl_t nullptr on success. Otherwise, an error.
+ */
+ errlHndl_t waitForCommandStatusBitReset(
+ command_status_register_t i_commandStatus);
+
+ /**
+ * @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.
+ *
+ * NOTE: To calculate a correct CRC for the BSL payload the SYNC_BYTE
+ * must be included in the payload despite the fact that it
+ * should be removed from the payload before sending to the BPM
+ * because the NVDIMM sends the SYNC_BYTE automatically.
+ *
+ * @param[in] i_ptr A pointer to the start of the data to calculate the
+ * CRC for.
+ * @param[in] i_size This size of the data pointed at by i_ptr.
+ *
+ * @return uint16_t The CRC bytes.
+ */
+ uint16_t crc16_calc(const void* const i_ptr, int i_size);
+
+
+};
+
+
+}; // end of BPM namespace
+}; // end of NVDIMM namespace
+
+#endif
+
diff --git a/src/usr/isteps/nvdimm/nvdimm.H b/src/usr/isteps/nvdimm/nvdimm.H
index d5253889d..11642217a 100644
--- a/src/usr/isteps/nvdimm/nvdimm.H
+++ b/src/usr/isteps/nvdimm/nvdimm.H
@@ -275,6 +275,14 @@ enum i2cReg : uint16_t
TYPED_BLOCK_DATA_BYTE30 = 0x39E,
TYPED_BLOCK_DATA_BYTE31 = 0x39F,
TYPED_BLOCK_DATA_OFFSET = 0x3E0,
+ BPM_MAGIC_REG1 = 0x430,
+ BPM_MAGIC_REG2 = 0x431,
+ I2C_REG_PROTECT = 0x43D,
+ BPM_REG_CMD = 0x440,
+ BPM_CMD_STATUS = 0x441,
+ BPM_PAYLOAD_LENGTH = 0x442,
+ BPM_REG_ERR_STATUS = 0x443,
+ BPM_REG_PAYLOAD_START = 0x444,
ENCRYPTION_COMMAND = 0x51F,
ENCRYPTION_CONFIG_STATUS = 0x520,
ENCRYPTION_ACCESS_KEY_SET = 0x521,
diff --git a/src/usr/isteps/nvdimm/nvdimm.mk b/src/usr/isteps/nvdimm/nvdimm.mk
index 397b27814..f26c8232b 100644
--- a/src/usr/isteps/nvdimm/nvdimm.mk
+++ b/src/usr/isteps/nvdimm/nvdimm.mk
@@ -5,7 +5,7 @@
#
# OpenPOWER HostBoot Project
#
-# Contributors Listed Below - COPYRIGHT 2018
+# Contributors Listed Below - COPYRIGHT 2018,2019
# [+] International Business Machines Corp.
#
#
@@ -52,6 +52,8 @@ ifneq (${HOSTBOOT_RUNTIME},1)
# code update path for NVDIMMs (not at RUNTIME)
OBJS += nvdimm_update.o
+# code update path for BPMs (not at runtime)
+OBJS += bpm_update.o
endif
diff --git a/src/usr/isteps/nvdimm/nvdimm_update.C b/src/usr/isteps/nvdimm/nvdimm_update.C
index e7fbad4a8..e69bb8c87 100644
--- a/src/usr/isteps/nvdimm/nvdimm_update.C
+++ b/src/usr/isteps/nvdimm/nvdimm_update.C
@@ -2031,6 +2031,22 @@ bool NvdimmsUpdate::runUpdate(void)
v_NVDIMM_32GB_list);
}
}
+ else if (( lid.id == NVDIMM_32GB_BPM_FW_LIDID)
+ || (lid.id == NVDIMM_32GB_BPM_CONFIG_LIDID))
+ {
+ 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.
+ }
+ else if (( lid.id == NVDIMM_16GB_BPM_FW_LIDID)
+ || (lid.id == NVDIMM_16GB_BPM_CONFIG_LIDID))
+ {
+ 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.
+ }
else if (lid.id != NVDIMM_SIGNATURE_LIDID)
{
TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - "
diff --git a/src/usr/isteps/nvdimm/nvdimm_update.H b/src/usr/isteps/nvdimm/nvdimm_update.H
index ea606d89d..e544d98b3 100644
--- a/src/usr/isteps/nvdimm/nvdimm_update.H
+++ b/src/usr/isteps/nvdimm/nvdimm_update.H
@@ -56,8 +56,13 @@ const uint32_t NVDIMM_SIGNATURE_LIDID = 0x80D00025; // ignore this one
const uint32_t NVDIMM_16GB_LIDID = 0x81e00640;
const uint32_t NVDIMM_32GB_LIDID = 0x81e00641;
+const uint32_t NVDIMM_16GB_BPM_FW_LIDID = 0x81e00642;
+const uint32_t NVDIMM_16GB_BPM_CONFIG_LIDID = 0x81e00644;
+
+const uint32_t NVDIMM_32GB_BPM_FW_LIDID = 0x81e00643;
+const uint32_t NVDIMM_32GB_BPM_CONFIG_LIDID = 0x81e00645;
+
-// Firmware Update Mode settings for FIRMWARE_OPS_CMD
enum fw_update_mode : uint8_t
{
FW_UPDATE_MODE_DISABLED = 0x00,
OpenPOWER on IntegriCloud