summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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