summaryrefslogtreecommitdiffstats
path: root/src/usr/isteps/nvdimm/bpm_update.C
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr/isteps/nvdimm/bpm_update.C')
-rw-r--r--src/usr/isteps/nvdimm/bpm_update.C4108
1 files changed, 4108 insertions, 0 deletions
diff --git a/src/usr/isteps/nvdimm/bpm_update.C b/src/usr/isteps/nvdimm/bpm_update.C
new file mode 100644
index 000000000..3ffdb595b
--- /dev/null
+++ b/src/usr/isteps/nvdimm/bpm_update.C
@@ -0,0 +1,4108 @@
+/* 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 <isteps/nvdimm/nvdimm.H>
+#include <errl/hberrltypes.H>
+#include <errl/errlmanager.H>
+#include <endian.h>
+#include <sys/time.h>
+#include <hbotcompid.H>
+#include <trace/interface.H>
+#include <initservice/istepdispatcherif.H>
+#include <isteps/nvdimm/bpmreasoncodes.H>
+
+#include <hwas/common/hwasCallout.H>
+
+#include <targeting/common/targetservice.H>
+#include <attributeenums.H>
+
+namespace NVDIMM
+{
+namespace BPM
+{
+
+trace_desc_t* g_trac_bpm = nullptr;
+TRAC_INIT(&g_trac_bpm, BPM_COMP_NAME, 4*KILOBYTE);
+
+// For debug traces
+#define TRACUCOMP(args...)
+//#define TRACUCOMP(args...) TRACFCOMP(args)
+#define TRACUBIN(args...)
+//#define TRACUBIN(args...) TRACUBIN(args)
+
+// These constants are kept out of the header file since they aren't relevant
+// outside of this file.
+const uint16_t BPM_ADDRESS_ZERO = 0;
+const uint16_t BPM_CONFIG_START_ADDRESS = 0x1800;
+// There are two potential start addresses for the firmware section.
+// They are:
+const uint16_t MAIN_PROGRAM_ADDRESS = 0x8000;
+const uint16_t MAIN_PROGRAM_ADDRESS_ALT = 0xA000;
+
+// 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;
+
+// These are the segment codes used to dump out a particular config data segment
+// on the BPM.
+const uint16_t DEFAULT_REG_PAGE = 0x905E;
+const uint16_t SEGMENT_A_CODE = 0x9A5E;
+const uint16_t SEGMENT_B_CODE = 0x9B5E;
+const uint16_t SEGMENT_C_CODE = 0x9C5E;
+const uint16_t SEGMENT_D_CODE = 0x9D5E;
+
+// Starting addresses relative to address 0x1800.
+// Segments appear in reverse order on BPM.
+// Each segment is SEGMENT_SIZE long.
+const size_t SEGMENT_D_START_ADDR = 0x000;
+const size_t SEGMENT_C_START_ADDR = 0x080;
+const size_t SEGMENT_B_START_ADDR = 0x100;
+const size_t SEGMENT_A_START_ADDR = 0x180;
+
+const std::map<uint16_t, size_t> segmentMap
+{
+ {SEGMENT_A_CODE, SEGMENT_A_START_ADDR},
+ {SEGMENT_B_CODE, SEGMENT_B_START_ADDR},
+ {SEGMENT_C_CODE, SEGMENT_C_START_ADDR},
+ {SEGMENT_D_CODE, SEGMENT_D_START_ADDR},
+};
+
+const uint8_t MAX_RETRY = 3;
+
+/**
+ * @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;
+}
+
+/**
+ * @brief Helper function to pull out the BPM address offset in the given
+ * payload.
+ *
+ * @param[in] i_payload The payload from which to extract the address
+ * offset.
+ */
+uint16_t getPayloadAddressBE(payload_t i_payload)
+{
+ // Get the payload address and convert back to big endian.
+ uint16_t payloadAddress = (i_payload[PAYLOAD_ADDRESS_START_INDEX])
+ | (i_payload[PAYLOAD_ADDRESS_START_INDEX + 1] << 8);
+ return payloadAddress;
+}
+
+/**
+ * @brief Helper function to extract the Segement ID from the segment code.
+ *
+ * @param[in] i_segmentCode The Segment code to pull the segment ID from
+ *
+ * @return uint8_t The Segment ID (A, B, C, D) as a hex value.
+ * For example 0xA, 0xB, etc.
+ */
+uint8_t getSegmentIdentifier(uint16_t i_segmentCode)
+{
+ uint8_t segmentId = (i_segmentCode >> 8) & 0xF;
+ return segmentId;
+}
+
+/**
+ * @brief Helper function to sleep for longer durations in 5 second increments.
+ *
+ * @param[in] i_sleepInSeconds How many seconds to sleep.
+ */
+void longSleep(uint8_t const i_sleepInSeconds)
+{
+ int iterations = i_sleepInSeconds / 5;
+ do
+ {
+ // Send progress code.
+ INITSERVICE::sendProgressCode();
+
+ // Sleep for 5 seconds
+ nanosleep(5, 0);
+
+ --iterations;
+ } while (iterations > 0);
+}
+
+void runBpmUpdates(bpmList_t * const i_16gb_BPMs,
+ bpmList_t * const i_32gb_BPMs,
+ BpmFirmwareLidImage * const i_16gb_fwImage,
+ BpmFirmwareLidImage * const i_32gb_fwImage,
+ BpmConfigLidImage * const i_16gb_configImage,
+ BpmConfigLidImage * const i_32gb_configImage)
+{
+
+ assert( (i_16gb_BPMs == nullptr)
+ || i_16gb_BPMs->empty()
+ || ((i_16gb_fwImage != nullptr) && (i_16gb_configImage != nullptr)),
+ "BPM::runBpmUpdates(): Update images for 16gb BPMs was nullptr and "
+ "there are 16gb BPMs in the system to may require updates.");
+ assert( (i_32gb_BPMs == nullptr)
+ || i_32gb_BPMs->empty()
+ || ((i_32gb_fwImage != nullptr) && (i_32gb_configImage != nullptr)),
+ "BPM::runBpmUpdates(): Update images for 32gb BPMs was nullptr and "
+ "there are 32gb BPMs in the system to may require updates.");
+
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ if ( (i_16gb_BPMs != nullptr)
+ && (i_16gb_fwImage != nullptr)
+ && (i_16gb_configImage != nullptr))
+ {
+ TRACFCOMP(g_trac_bpm,
+ "Check/update %d BPMs on 16GB_TYPE NVDIMMs",
+ i_16gb_BPMs->size());
+
+ for(auto& bpm : *i_16gb_BPMs)
+ {
+ errl = bpm.runUpdate(*i_16gb_fwImage, *i_16gb_configImage);
+ if (errl != nullptr)
+ {
+ uint32_t nvdimmHuid = TARGETING::get_huid(bpm.getNvdimm());
+ if (bpm.attemptAnotherUpdate())
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "An error occurred during a 16GB_TYPE BPM "
+ "update for NVDIMM 0x%.8X. "
+ "Try again.",
+ nvdimmHuid);
+
+ delete errl;
+ errl = bpm.runUpdate(*i_16gb_fwImage,
+ *i_16gb_configImage);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "Another error occurred while attempting "
+ "to update the same 16GB_TYPE BPM for "
+ "NVDIMM 0x%.8X. Commit and move onto the "
+ "next BPM",
+ nvdimmHuid);
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "An error occurred during a 16GB_TYPE BPM "
+ "update for NVDIMM 0x%.8X. "
+ "Commit and move onto the next BPM",
+ nvdimmHuid);
+ }
+ ERRORLOG::errlCommit(errl, BPM_COMP_ID);
+ }
+ }
+ }
+
+ if ( (i_32gb_BPMs != nullptr)
+ && (i_32gb_fwImage != nullptr)
+ && (i_32gb_configImage != nullptr))
+ {
+ TRACFCOMP(g_trac_bpm,
+ "Check/update %d BPMs on 32GB_TYPE NVDIMMs",
+ i_32gb_BPMs->size());
+
+ for(auto& bpm : *i_32gb_BPMs)
+ {
+ errl = bpm.runUpdate(*i_32gb_fwImage, *i_32gb_configImage);
+ if (errl != nullptr)
+ {
+ uint32_t nvdimmHuid = TARGETING::get_huid(bpm.getNvdimm());
+ if (bpm.attemptAnotherUpdate())
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "An error occurred during a 32GB_TYPE BPM "
+ "update for NVDIMM 0x%.8X. "
+ "Try again.",
+ nvdimmHuid);
+
+ delete errl;
+ errl = bpm.runUpdate(*i_32gb_fwImage,
+ *i_32gb_configImage);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "Another error occurred while attempting "
+ "to update the same 32GB_TYPE BPM for "
+ "NVDIMM 0x%.8X. Commit and move onto the "
+ "next BPM",
+ nvdimmHuid);
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "An error occurred during a 32GB_TYPE BPM "
+ "update for NVDIMM 0x%.8X. "
+ "Commit and move onto the next BPM",
+ nvdimmHuid);
+ }
+ ERRORLOG::errlCommit(errl, BPM_COMP_ID);
+ }
+ }
+ }
+ } while(0);
+}
+
+// =============================================================================
+// BpmFirmwareLidImage Class Functions
+// =============================================================================
+
+BpmFirmwareLidImage::BpmFirmwareLidImage(void * const i_lidImageAddr,
+ size_t i_size)
+ : iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size)
+{
+ assert(i_lidImageAddr != nullptr,
+ "BPM::BpmFirmwareLidImage(): Provided LID image must not be nullptr");
+}
+
+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;
+}
+
+// =============================================================================
+// BpmConfigLidImage Class Functions
+// =============================================================================
+
+BpmConfigLidImage::BpmConfigLidImage(void * const i_lidImageAddr,
+ size_t i_size)
+ : iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size)
+{
+ assert(i_lidImageAddr != nullptr,
+ "BPM::BpmConfigLidImage(): Provided LID image must not be nullptr");
+}
+
+uint16_t BpmConfigLidImage::getVersion() const
+{
+ uint16_t version = INVALID_VERSION;
+
+ if (iv_lidImageSize >= sizeof(config_image_header_t))
+ {
+ const config_image_header_t * header =
+ reinterpret_cast<const config_image_header_t*>(iv_lidImage);
+
+ version = TWO_UINT8_TO_UINT16(header->iv_versionMajor,
+ header->iv_versionMinor);
+ }
+
+ return version;
+}
+
+uint16_t BpmConfigLidImage::getNumberOfFragments() const
+{
+ uint16_t numberOfFragments = 0;
+
+ if (iv_lidImageSize >= sizeof(config_image_header_t))
+ {
+ const config_image_header_t * header =
+ reinterpret_cast<const config_image_header_t*>(iv_lidImage);
+
+ numberOfFragments = header->iv_numberOfFragments;
+ }
+
+ return numberOfFragments;
+}
+
+void const * BpmConfigLidImage::getFirstFragment() const
+{
+ void * fragment = nullptr;
+
+ if (getNumberOfFragments() > 0)
+ {
+ fragment = reinterpret_cast<uint8_t* const>(iv_lidImage)
+ + sizeof(config_image_header_t);
+ }
+
+ return fragment;
+}
+
+// =============================================================================
+// Bpm Class Functions
+// =============================================================================
+
+Bpm::Bpm(const TARGETING::TargetHandle_t i_nvdimm)
+ : iv_nvdimm(i_nvdimm),
+ iv_bslVersion(0),
+ iv_firmwareStartAddress(0),
+ iv_attemptAnotherUpdate(false),
+ iv_segmentDMerged(false),
+ iv_segmentBMerged(false),
+ iv_updateAttempted(false)
+{
+ assert((i_nvdimm != nullptr) && (isNVDIMM(i_nvdimm)),
+ "BPM::Bpm(): An nvdimm target must be given.");
+
+ memset(&iv_segmentD, 0, SEGMENT_SIZE);
+ memset(&iv_segmentB, 0, SEGMENT_SIZE);
+
+}
+
+bool Bpm::attemptAnotherUpdate()
+{
+ return iv_attemptAnotherUpdate;
+}
+
+bool Bpm::hasAttemptedUpdate()
+{
+ return iv_updateAttempted;
+}
+
+void Bpm::setAttemptAnotherUpdate()
+{
+
+ if (iv_updateAttempted)
+ {
+ // Since iv_updateAttempted is true that means that this function was
+ // called on a subsequent update attempt, meaning we should no longer
+ // attempt updates if the current attempt fails.
+ iv_attemptAnotherUpdate = false;
+ }
+ else
+ {
+ // Since iv_updateAttempted is false that means that this function was
+ // called on the first update attempt because by default
+ // iv_updateAttempted is false and is only set to true as the last part
+ // of the update procedure.
+ iv_attemptAnotherUpdate = true;
+ }
+
+}
+
+const TARGETING::TargetHandle_t Bpm::getNvdimm()
+{
+ return iv_nvdimm;
+}
+
+errlHndl_t Bpm::readBslVersion()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::readBslVersion()");
+ errlHndl_t errl = nullptr;
+
+ do {
+ // Enter Update mode
+ errl = enterUpdateMode();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Verify in Update mode
+ errl = inUpdateMode();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Enter Bootstrap Loader (BSL) mode
+ 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;
+ }
+
+ // 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,
+ NO_DELAY_EXTERNAL_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Get the result from the BPM.
+ errl = getResponse(&iv_bslVersion, sizeof(uint8_t));
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): "
+ "Failed to determine BSL Version.");
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::readBslVersion(): BSL Version is 0x%X",
+ iv_bslVersion);
+ } while(0);
+
+ // Reset the device. This will exit BSL mode.
+ errlHndl_t exitErrl = resetDevice();
+ if (exitErrl != nullptr)
+ {
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ // Exit update mode
+ exitErrl = exitUpdateMode();
+ if (exitErrl != nullptr)
+ {
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ ES_FWREV0,
+ bpmMinor);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getFwVersion(): "
+ "Failed to read BPM minor version byte");
+ errl->collectTrace(BPM_COMP_NAME);
+ 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,
+ const int i_msDelay)
+{
+ 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, i_msDelay);
+
+ return errl;
+}
+
+errlHndl_t Bpm::issueCommand(const uint8_t i_command,
+ payload_t i_payload,
+ const uint8_t i_opType,
+ const int i_msDelay)
+{
+ 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 {
+
+ // Check the full payload size to make sure it's not too large. Add the
+ // size of the SYNC_BYTE that was dropped during payload creation to
+ // verify that the full payload sent by the NVDIMM won't exceed the max
+ // size the BPM is able to receive.
+ if ((i_payload.size() + SYNC_BYTE_SIZE) > MAX_PAYLOAD_SIZE)
+ {
+ uint8_t payloadSize = i_payload.size() + SYNC_BYTE_SIZE;
+ uint8_t payloadHeaderDataSize =
+ i_payload[PAYLOAD_HEADER_DATA_LENGTH_INDEX];
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::issueCommand(): "
+ "payload size %d exceeds max payload size of %d",
+ payloadSize, MAX_PAYLOAD_SIZE);
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_ISSUE_COMMAND
+ * @reasoncode BPM_RC::BPM_INVALID_PAYLOAD_SIZE
+ * @userdata1[00:31] Full Payload Size, including SYNC_BYTE
+ * @userdata1[32:63] MAX_PAYLOAD_SIZE
+ * @userdata2[00:31] Payload Header + Data size
+ * @userdata2[32:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The maximum payload size to be sent to the BPM
+ * was exceeded.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_ISSUE_COMMAND,
+ BPM_RC::BPM_INVALID_PAYLOAD_SIZE,
+ TWO_UINT16_TO_UINT32(payloadSize,
+ MAX_PAYLOAD_SIZE),
+ TWO_UINT32_TO_UINT64(
+ payloadHeaderDataSize,
+ TARGETING::get_huid(iv_nvdimm))
+ );
+ errl->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Set the payload length. This is the actual length of the payload
+ // excluding the size of the SYNC_BYTE that was dropped during payload
+ // creation which is already missing from size().
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ errl = waitForCommandStatusBitReset(commandStatus);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // If a delay was given then wait for the delay and check the response.
+ // Otherwise, do not wait and do not check the response. For a list of
+ // commands and delays, see bpm_update.H for more info.
+ if (i_msDelay > 0)
+ {
+ // Wait the given time in ms. Default 1ms for most commands.
+ nanosleep(0, i_msDelay * NS_PER_MSEC);
+
+ // Check the response from the BPM. A non-zero response value
+ // indicates failure. So, assume a failure and check for success.
+ uint8_t data = 0xFF;
+ errl = getResponse(&data, sizeof(uint8_t));
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // If the data read from the response is a non-zero value then the
+ // issued command failed.
+ if (data != 0)
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_ISSUE_COMMAND
+ * @reasoncode BPM_RC::BPM_BAD_RESPONSE
+ * @userdata1 The command that failed to execute.
+ * See bpm_update.H for list of commands.
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc The command sent to the BPM failed.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_ISSUE_COMMAND,
+ BPM_RC::BPM_BAD_RESPONSE,
+ i_payload[PAYLOAD_COMMAND_INDEX],
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::runUpdate(BpmFirmwareLidImage i_fwImage,
+ BpmConfigLidImage i_configImage)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::runUpdate(): "
+ "Running BPM Update for NVDIMM 0x%.8X",
+ TARGETING::get_huid(iv_nvdimm));
+
+ errlHndl_t errl = nullptr;
+ // Assume an update is necessary for the BPM and determine if it isn't.
+ bool shouldPerformUpdate = true;
+
+ // Get the sys target to check for attribute overrides.
+ TARGETING::Target* sys = nullptr;
+ TARGETING::targetService().getTopLevelTarget(sys);
+
+ auto updateOverride =
+ sys->getAttr<TARGETING::ATTR_BPM_UPDATE_OVERRIDE>();
+ uint16_t firmwareOverrideFlag = (updateOverride & 0xFF00);
+ uint16_t configOverrideFlag = (updateOverride & 0x00FF);
+
+ do {
+
+ // First check if there is a BPM connected
+ errl = verifyGoodBpmState();
+ if (errl != nullptr)
+ {
+ // Either there isn't a BPM connected to this NVDIMM or it's not
+ // functional. Don't bother with updates.
+ shouldPerformUpdate = false;
+ iv_attemptAnotherUpdate = false;
+ break;
+ }
+
+ // 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;
+ }
+
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM 0x%.4X, "
+ "Firmware version of image 0x%.4X.",
+ bpmFwVersion, i_fwImage.getVersion());
+
+ if (i_fwImage.getVersion() == bpmFwVersion)
+ {
+ shouldPerformUpdate = false;
+ if (updateOverride == TARGETING::BPM_UPDATE_BEHAVIOR_DEFAULT_ALL)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM matches the version in "
+ "the image. Skipping update.");
+ break;
+ }
+ }
+
+ if (updateOverride == TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_ALL)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "ATTR_BPM_UPDATE_OVERRIDE set to SKIP_ALL. "
+ "Skipping update.");
+ break;
+ }
+
+ // Depending on the BSL version a CRC check may be necessary
+ errl = readBslVersion();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // If the BSL version read from the BPM isn't a supported version then
+ // don't perform the updates as the update flow may have changed between
+ // BSL versions.
+ if (iv_bslVersion != BSL_VERSION_1_4)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::runUpdate(): "
+ "Unsupported BSL Version 0x%.2X detected on BPM. "
+ "Cancelling Update.");
+
+ break;
+ }
+
+ if ((shouldPerformUpdate
+ || (firmwareOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_FW))
+ && !(firmwareOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_FW))
+ {
+ if (firmwareOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_FW)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "ATTR_BPM_UPDATE_OVERRIDE set to force firmware "
+ "portion of BPM updates. Running Firmware Update...");
+ }
+
+ errl = runFirmwareUpdates(i_fwImage);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (firmwareOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_FW)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "ATTR_BPM_UPDATE_OVERRIDE set to skip firmware "
+ "portion of BPM updates. Skipping Firmware Update...");
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware Data on BPM already up-to-date. "
+ "Skipping Firmware Update...");
+ }
+ }
+
+ if ((shouldPerformUpdate
+ || (configOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_CONFIG))
+ && !(configOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_CONFIG))
+ {
+ if (configOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_CONFIG)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "ATTR_BPM_UPDATE_OVERRIDE set to force config "
+ "portion of BPM updates. Running Config Update...");
+ }
+ errl = runConfigUpdates(i_configImage);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (configOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_CONFIG)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "ATTR_BPM_UPDATE_OVERRIDE set to skip config "
+ "portion of BPM updates. Skipping Config Update...");
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Configuration Data on BPM already up-to-date. "
+ "Skipping Config Update...");
+ }
+ }
+
+ } while(0);
+
+ if ((shouldPerformUpdate
+ || (configOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_CONFIG)
+ || (firmwareOverrideFlag == TARGETING::BPM_UPDATE_BEHAVIOR_FORCE_FW))
+ && (updateOverride != TARGETING::BPM_UPDATE_BEHAVIOR_SKIP_ALL))
+ {
+ // Reset controller and unlock encryption if necessary
+ errlHndl_t exitErrl = nvdimmResetController(iv_nvdimm);
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate() "
+ "Couldn't reset NVDIMM controller.");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ // If the update was successful then we must wait for 15 seconds before
+ // polling the status of the BPM since it has to finish updating its
+ // firmware and resetting.
+ TRACFCOMP(g_trac_bpm, "Bpm::runUpdate(): "
+ "Wait for the BPM to finish update and reset procedure, "
+ "sleep for 15 seconds");
+ longSleep(15);
+
+ // Poll SCAP_STATUS register for BPM state before we check final
+ // firmware version.
+ exitErrl = verifyGoodBpmState();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Could not verify that BPM was present and enabled!");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ 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");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM 0x%.4X, "
+ "Firmware version of image 0x%.4X.",
+ bpmFwVersion, i_fwImage.getVersion());
+
+ if (i_fwImage.getVersion() == bpmFwVersion)
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::runUpdate(): "
+ "Firmware version on the BPM matches the version in the "
+ "image. Firmware Update Successful.");
+ iv_attemptAnotherUpdate = false;
+ }
+ else
+ {
+ // Attempt another update if one hasn't already been attempted.
+ setAttemptAnotherUpdate();
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runUpdate(): "
+ "Version on BPM didn't match image. %s ",
+ iv_attemptAnotherUpdate ?
+ "Attempt another update..."
+ : "Attempts to update the BPM have failed.");
+ if (iv_attemptAnotherUpdate == false)
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE
+ * @moduleid BPM_RC::BPM_RUN_FW_UPDATES
+ * @reasoncode BPM_RC::BPM_VERSION_MISMATCH
+ * @userdata1[00:31] Version on the BPM
+ * @userdata1[32:63] Version of the flash image
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc The version on the BPM didn't match the
+ * version in the flash image.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ exitErrl = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ BPM_RC::BPM_RUN_FW_UPDATES,
+ BPM_RC::BPM_VERSION_MISMATCH,
+ TWO_UINT32_TO_UINT64(bpmFwVersion,
+ i_fwImage.getVersion()),
+ TARGETING::get_huid(iv_nvdimm));
+ exitErrl->collectTrace(BPM_COMP_NAME);
+ handleMultipleErrors(errl, exitErrl);
+ }
+ }
+
+ TRACFCOMP(g_trac_bpm, EXIT_MRK"Bpm::runUpdate(): "
+ "Concluding BPM Update for NVDIMM 0x%.8X %s",
+ TARGETING::get_huid(iv_nvdimm),
+ (errl != nullptr) ? "with errors" : "without errors");
+ }
+
+ // An update has been attempted at least once. Set member variable to true
+ // to dictate future update attempts. This variable should only be set at
+ // the end of the update procedure in order to properly control future
+ // update attempts.
+ iv_updateAttempted = true;
+
+ if (errl == nullptr)
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_INFORMATIONAL
+ * @moduleid BPM_RC::BPM_RUN_UPDATE
+ * @reasoncode BPM_RC::BPM_UPDATE_SUCCESSFUL
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc BPM Update finished without errors.
+ * @custdesc Informational log associated with DIMM updates.
+ */
+ errlHndl_t infoErrl = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ BPM_RC::BPM_RUN_UPDATE,
+ BPM_RC::BPM_UPDATE_SUCCESSFUL,
+ TARGETING::get_huid(iv_nvdimm));
+ infoErrl->collectTrace(BPM_COMP_NAME);
+ ERRORLOG::errlCommit(infoErrl, BPM_COMP_ID);
+ }
+
+ 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,
+ NO_DELAY_NO_RESPONSE);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ if (!isUpdateInProgress)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::inUpdateMode(): "
+ "Failed to enter update mode");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_IN_UPDATE_MODE
+ * @reasoncode BPM_RC::BPM_UPDATE_MODE_VERIFICATION_FAIL
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to verify update mode was entered using
+ * the BSL interface.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_IN_UPDATE_MODE,
+ BPM_RC::BPM_UPDATE_MODE_VERIFICATION_FAIL,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::enterUpdateMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::enterUpdateMode()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // Disable write protection on the BPM. Otherwise, we can't write the
+ // magic values that enable the nvdimm-bpm interface.
+ errl = disableWriteProtection();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Write the magic values to enable nvdimm-bpm interface
+ errl = writeToMagicRegisters(UPDATE_MODE_MAGIC_VALUES);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): "
+ "Failed to write magic numbers that enable "
+ "update mode");
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::enterUpdateMode(): "
+ "Issuing BPM_LOCAL BCL_START_UPDATE command.");
+
+ errl = issueCommand(BPM_LOCAL,
+ BCL_START_UPDATE,
+ WRITE,
+ NO_DELAY_NO_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ nanosleep(2,0);
+
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_INFORMATIONAL
+ * @moduleid BPM_RC::BPM_START_UPDATE
+ * @reasoncode BPM_RC::BPM_ENTER_UPDATE_MODE
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc BPM has entered update mode.
+ * @custdesc Informational log associated with DIMM updates.
+ */
+ errlHndl_t infoErrl = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ BPM_RC::BPM_START_UPDATE,
+ BPM_RC::BPM_ENTER_UPDATE_MODE,
+ TARGETING::get_huid(iv_nvdimm));
+ infoErrl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ infoErrl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddVendorLog(iv_nvdimm, infoErrl);
+ nvdimmAddPage4Regs(iv_nvdimm, infoErrl);
+ ERRORLOG::errlCommit(infoErrl, BPM_COMP_ID);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::exitUpdateMode()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::exitUpdateMode()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ errl = writeToMagicRegisters(UPDATE_MODE_MAGIC_VALUES);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): "
+ "Failed to write the update magic values to "
+ " be able to send BPM_LOCAL commands.");
+ break;
+ }
+
+ errl = issueCommand(BPM_LOCAL,
+ BCL_IS_UPDATE_IN_PROGRESS,
+ READ,
+ NO_DELAY_NO_RESPONSE);
+ 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::exitUpdateMode(): "
+ "Failed to read BPM_REG_ERR_STATUS register to determine "
+ "if BPM is in update mode.");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Sending the exit update command when the BPM isn't in update mode can
+ // cause unpredicatable behavior and errors.
+ if (isUpdateInProgress)
+ {
+ errl = issueCommand(BPM_LOCAL,
+ BCL_END_UPDATE,
+ WRITE,
+ NO_DELAY_NO_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): "
+ "Not in update mode. "
+ "Exit update command will not be sent.");
+ }
+
+ // Write back the production magic values
+ errl = writeToMagicRegisters(PRODUCTION_MAGIC_VALUES);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::exitUpdateMode(): "
+ "Failed to write the production magic values to "
+ "disable update mode.");
+ break;
+ }
+
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_INFORMATIONAL
+ * @moduleid BPM_RC::BPM_END_UPDATE
+ * @reasoncode BPM_RC::BPM_EXIT_UPDATE_MODE
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc BPM has exited update mode.
+ * @custdesc Informational log associated with DIMM updates.
+ */
+ errlHndl_t infoErrl = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ BPM_RC::BPM_END_UPDATE,
+ BPM_RC::BPM_EXIT_UPDATE_MODE,
+ TARGETING::get_huid(iv_nvdimm));
+ infoErrl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ infoErrl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddVendorLog(iv_nvdimm, infoErrl);
+ nvdimmAddPage4Regs(iv_nvdimm, infoErrl);
+ ERRORLOG::errlCommit(infoErrl, BPM_COMP_ID);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::updateFirmware(BpmFirmwareLidImage i_image)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::updateFirmware()");
+ errlHndl_t errl = nullptr;
+
+ // The reset vector address is near the end of the firmware section.
+ // We must do a special operation on it when it shows up during the update.
+ const uint16_t RESET_VECTOR_ADDRESS = 0xFFFE;
+
+ bool mainAddressEncountered = false;
+
+ // Get the number of blocks in the image
+ 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)
+ || (block->iv_addressOffset == MAIN_PROGRAM_ADDRESS_ALT))
+ && !mainAddressEncountered)
+ {
+ // Only execute this once.
+ mainAddressEncountered = true;
+
+ // Save the firmware start address for later. This will be needed
+ // for the final CRC check when the update is completed.
+ iv_firmwareStartAddress = block->iv_addressOffset;
+
+ payload_t payload;
+ errl = setupPayload(payload,
+ BSL_MASS_ERASE,
+ iv_firmwareStartAddress);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH,
+ payload,
+ WRITE,
+ ERASE_FIRMWARE_DELAY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Performing BSL_MASS_ERASE on BPM, sleep for 5 seconds.");
+ longSleep(5);
+
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Begin writing flash image to BPM "
+ "with a starting address of 0x%.4X",
+ iv_firmwareStartAddress);
+
+ }
+
+ if (block->iv_addressOffset % 0x400 == 0)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Writing to address offset 0x%.4X. "
+ "Firmware blocks written: %d; Remaining: %d",
+ block->iv_addressOffset,
+ i, (NUMBER_OF_BLOCKS - i));
+ }
+
+ // Construct the payload for this block in the image
+ payload_t payload;
+ errl = setupPayload(payload, block, BSL_RX_DATA_BLOCK);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ if (block->iv_addressOffset == RESET_VECTOR_ADDRESS)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Encountered RESET_VECTOR_ADDRESS 0x%.4X. "
+ "Attempt to write RESET_VECTOR to BPM up to %d times.",
+ RESET_VECTOR_ADDRESS,
+ MAX_RETRY);
+ // Attempting to BSL_VERIFY_BLOCK on the reset vector data will
+ // fail. To verify that this data is written correctly we will check
+ // the response packet sent by the BPM.
+ const uint8_t RESET_VECTOR_RECEIVE_SUCCESS = 0x80;
+ uint8_t retry = 1;
+ do
+ {
+ // Issue the write command to the BPM.
+ // The RESET_VECTOR is special in that its response is checked
+ // externally.
+ errl = issueCommand(BPM_PASSTHROUGH,
+ payload,
+ WRITE,
+ NO_DELAY_EXTERNAL_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Get the response packet and verify that the status is
+ // RESET_VECTOR_RECEIVE_SUCCESS.
+ //
+ // Any status besides RESET_VECTOR_RECEIVE_SUCCESS is considered
+ // a fail. So, assume a failure and check.
+ uint8_t status = 0xFF;
+ errl = getResponse(&status,
+ sizeof(uint8_t));
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ if (status != RESET_VECTOR_RECEIVE_SUCCESS)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "status %d from BPM was not "
+ "RESET_VECTOR_RECEIVE_SUCCESS value of %d. "
+ "Retrying...",
+ status,
+ RESET_VECTOR_RECEIVE_SUCCESS);
+
+ if (++retry > MAX_RETRY)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Never received RESET_VECTOR_RECEIVE_SUCCESS "
+ "status from BPM in three attempts. "
+ "Aborting Update");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_UPDATE_FIRMWARE
+ * @reasoncode BPM_RC::BPM_RESET_VECTOR_NEVER_RECEIVED
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc RESET_VECTOR_RECEIVE_SUCCESS status was not
+ * received in three attempts.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_UPDATE_FIRMWARE,
+ BPM_RC::BPM_RESET_VECTOR_NEVER_RECEIVED,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+
+ // Change the state of iv_attemptAnotherUpdate to signal
+ // if another update attempt should occur.
+ setAttemptAnotherUpdate();
+
+ break;
+ }
+ }
+ else
+ {
+ // RESET_VECTOR was written and received successfully.
+ // Exit retry loop.
+ break;
+ }
+
+ // Sleep for 0.001 second before attempting again.
+ nanosleep(0, 1 * NS_PER_MSEC);
+
+ } while(retry <= MAX_RETRY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+ else
+ {
+ // Attempt to write the data using a retry loop. This will also
+ // verify that the data was correctly written to the BPM.
+ errl = blockWrite(payload);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+
+ // 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);
+ }
+
+ TRACFCOMP(g_trac_bpm, EXIT_MRK"Bpm::updateFirmware(): "
+ "Firmware flash image write and verification completed "
+ "%s",
+ (errl == nullptr) ? "without errors" : "with errors");
+
+ return errl;
+}
+
+errlHndl_t Bpm::updateConfig()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::updateConfig()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // Erase Segment D on the BPM via the BSL interface.
+ errl = eraseSegment(SEGMENT_D_CODE);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): "
+ "Failed to erase Segment D.");
+ break;
+ }
+
+ // Write the updated Segment D buffer to the BPM via the BSL interface.
+ errl = writeSegment(iv_segmentD, SEGMENT_D_CODE);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): "
+ "Failed to write Segment D.");
+ break;
+ }
+
+
+ // Erase Segment B on the BPM via the BSL interface.
+ errl = eraseSegment(SEGMENT_B_CODE);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): "
+ "Failed to erase Segment B.");
+ break;
+ }
+
+ // Write the updated Segment B buffer to the BPM via the BSL interface.
+ errl = writeSegment(iv_segmentB, SEGMENT_B_CODE);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::updateConfig(): "
+ "Failed to write Segment B.");
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::enterBootstrapLoaderMode()
+{
+ 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 = 5;
+ bool inBslMode = false;
+
+ while (retry != 0)
+ {
+
+ errl = issueCommand(BPM_LOCAL,
+ BCL_IS_BSL_MODE,
+ WRITE,
+ NO_DELAY_NO_RESPONSE);
+ 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.");
+ errl->collectTrace(BPM_COMP_NAME);
+ 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.");
+ longSleep(5);
+ break;
+ }
+
+ // Sleep for 0.001 second.
+ nanosleep(0, 1 * NS_PER_MSEC);
+
+ errl = issueCommand(BPM_LOCAL,
+ BCL_ENTER_BSL_MODE,
+ WRITE,
+ NO_DELAY_NO_RESPONSE);
+ if (errl != nullptr)
+ {
+ 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);
+ --retry;
+
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ if (!inBslMode)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::enterBootstrapLoaderMode(): "
+ "Failed to enter BSL mode on the BPM");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_ENTER_BSL_MODE
+ * @reasoncode BPM_RC::BPM_FAILED_TO_ENTER_BSL_MODE
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to enter BSL mode after several attempts.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_ENTER_BSL_MODE,
+ BPM_RC::BPM_FAILED_TO_ENTER_BSL_MODE,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ 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)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "Bpm::setupPayload(): Block Data Size %d exceeds max payload "
+ "size of %d",
+ blockDataSize,
+ MAX_PAYLOAD_DATA_SIZE);
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_SETUP_PAYLOAD
+ * @reasoncode BPM_RC::BPM_INVALID_PAYLOAD_DATA_SIZE
+ * @userdata1[0:7] Block Data Size
+ * @userdata1[8:15] MAX_PAYLOAD_DATA_SIZE
+ * @userdata2[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to enter BSL mode after several attempts.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_SETUP_PAYLOAD,
+ BPM_RC::BPM_INVALID_PAYLOAD_DATA_SIZE,
+ TWO_UINT8_TO_UINT16(blockDataSize,
+ MAX_PAYLOAD_DATA_SIZE),
+ TARGETING::get_huid(iv_nvdimm));
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ 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 {
+
+ // Verify we are in BSL mode by checking SCAP_STATUS because if we aren't
+ // then we don't need to do anything.
+ scap_status_register_t status;
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_STATUS,
+ status.full);
+ if (errl != nullptr)
+ {
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (status.bit.Bpm_Bsl_Mode)
+ {
+ // 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;
+ }
+
+ // Despite this being a BSL command we cannot check the response
+ // because the BPM will either be offline and cannot respond or
+ // the command will have completed and we won't be in BSL mode
+ // anymore and therefor shouldn't check the response.
+ errl = issueCommand(BPM_PASSTHROUGH,
+ payload,
+ WRITE,
+ NO_DELAY_NO_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // If we wait less than 15 seconds for the reset to occur it is
+ // possible that BPM won't be ready for more commands via the NVDIMM
+ TRACFCOMP(g_trac_bpm, "Bpm::resetDevice(): "
+ "Resetting BPM for NVDIMM 0x%.8X, sleep for 15 seconds.",
+ TARGETING::get_huid(iv_nvdimm));
+ longSleep(15);
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::resetDevice(): "
+ "Not in BSL Mode. Don't send the reset command.");
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::readViaScapRegister(uint8_t const i_reg, uint8_t & io_data)
+{
+ TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::readViaScapRegister()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Write to SCAP register which register we're attempting to access on
+ // the BPM
+ errl = nvdimmWriteReg(iv_nvdimm,
+ SCAP_REG,
+ i_reg);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::readViaScapRegister(): "
+ "Failed to set SCAP_REG to register 0x%.2X",
+ i_reg);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Read out the data from the requested register
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_DATA,
+ io_data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "BPM::readViaScapRegister(): "
+ "Failed to read data from SCAP_DATA for register 0x%.2X.",
+ i_reg);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::writeViaScapRegister(uint8_t const i_reg, uint8_t const i_data)
+{
+ TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeViaScapRegister()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // The SCAP_REG and SCAP_DATA registers require a few retries to get the
+ // values to stick. This loop sets SCAP_REG to i_reg
+ uint8_t retry = 0;
+ uint8_t data = 0;
+ do {
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Write to SCAP register which register we're attempting to access
+ // on the BPM
+ errl = nvdimmWriteReg(iv_nvdimm,
+ SCAP_REG,
+ i_reg);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::writeViaScapRegister(): "
+ "Failed to set SCAP_REG to register 0x%.2X",
+ i_reg);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Wait 100ms
+ nanosleep(0, 100 * NS_PER_MSEC);
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_REG,
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "BPM::writeViaScapRegister(): "
+ "Failed to read from SCAP_REG to verify that "
+ "requested register 0x%.2X was written successfully.",
+ i_reg);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (data == i_reg)
+ {
+ TRACUCOMP(g_trac_bpm, "Bpm::writeViaScapRegister(): "
+ "REG 0x%X was successfully written to SCAP_REG 0x434. "
+ "Stop retries.",
+ i_reg);
+ break;
+ }
+
+ } while(++retry < MAX_RETRY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ if ((retry >= MAX_RETRY) && (data != i_reg))
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WRITE_VIA_SCAP
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT_REG
+ * @userdata1[0:31] The register that we were attempting to write to
+ * SCAP_REG.
+ * @userdata1[32:63] The data that was found in the register on the
+ * final attempt.
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc The command sent to the BPM failed.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WRITE_VIA_SCAP,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT_REG,
+ TWO_UINT32_TO_UINT64(i_reg,
+ data),
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ // The SCAP_REG and SCAP_DATA registers require a few retries to get the
+ // values to stick. This loop sets SCAP_DATA to i_data
+ retry = 0;
+ data = 0;
+ do {
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Write the data to the register we're attempting to access
+ // on the BPM.
+ errl = nvdimmWriteReg(iv_nvdimm,
+ SCAP_DATA,
+ i_data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"BPM::writeViaScapRegister(): "
+ "Failed to write data 0x%.2X to SCAP_DATA for "
+ "register 0x%.2X.",
+ i_data,
+ i_reg);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Wait for the SCAP_STATUS Busy bit to be zero.
+ errl = waitForBusyBit();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Wait 100ms
+ nanosleep(0, 100 * NS_PER_MSEC);
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_DATA,
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"BPM::writeViaScapRegister(): "
+ "Failed to read from SCAP_DATA to verify "
+ "that requested data 0x%.2X was written successfully.",
+ i_data);
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (data == i_data)
+ {
+ TRACUCOMP(g_trac_bpm, "Bpm::writeViaScapRegister(): "
+ "DATA 0x%X was successfully written to SCAP_DATA 0x435."
+ " Stop retries.",
+ i_data);
+ break;
+ }
+
+ } while(++retry < MAX_RETRY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ if ((retry >= MAX_RETRY) && (data != i_data))
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WRITE_VIA_SCAP
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT_DATA
+ * @userdata1[0:31] The data that we were attempting to write to
+ * SCAP_DATA.
+ * @userdata1[32:63] The data that was found in the register on the
+ * final attempt.
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc The command sent to the BPM failed.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WRITE_VIA_SCAP,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT_DATA,
+ TWO_UINT32_TO_UINT64(i_data,
+ data),
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::disableWriteProtection()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::disableWriteProtection()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // The following write sequence to the I2C_REG_PROTECT register
+ // indirectly removes write protection from registers 0x40-0x7F on
+ // page 4.
+ for ( size_t i = 0; i < BPM_PASSWORD_LENGTH; ++i)
+ {
+ errl = nvdimmWriteReg(iv_nvdimm,
+ I2C_REG_PROTECT,
+ BPM_PASSWORD[i]);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::disableWriteProtection(): "
+ "Failed to write the unlock sequence to "
+ "I2C_REG_PROTECT");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ nanosleep(0, 100 * NS_PER_MSEC);
+
+ // Make sure protection was removed
+ uint8_t data = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ I2C_REG_PROTECT,
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::disableWriteProtection(): "
+ "Failed to verify that write protection was removed");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+ const uint8_t WRITE_PROTECT_DISABLED = 0x80;
+ if (!(data & WRITE_PROTECT_DISABLED))
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::disableWriteProtection(): "
+ "Failed to disable write protection. I2C_REG_PROTECT");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_DISABLE_WRITE_PROTECTION
+ * @reasoncode BPM_RC::BPM_DISABLE_WRITE_PROTECTION_FAILED
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to enter BSL mode after several attempts.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_DISABLE_WRITE_PROTECTION,
+ BPM_RC::BPM_DISABLE_WRITE_PROTECTION_FAILED,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::switchBpmPage(uint16_t const i_segmentCode)
+{
+
+ errlHndl_t errl = nullptr;
+
+ do {
+ const uint8_t SPECIAL_CONTROL_COMMAND1 = 0x3E;
+ const uint8_t SPECIAL_CONTROL_COMMAND2 = 0x3F;
+ // Next, switch to the desired BPM segment by writing the segment code
+ // to the BPM's Special Control Command registers.
+ //
+ // Since the SCAP_DATA register can only hold 1 byte at a time we must
+ // do this in two steps.
+ // According to SMART, the segment code must be written in the following
+ // form to those registers:
+ // Register 0x3E gets LO(i_segmentCode) byte
+ // Register 0x3F gets HI(i_segmentCode) byte
+ // Example: 0x9D5E is the segment code for Segment D. It must be written
+ // as follows
+ // 0x3E, 0x5E
+ // 0x3F, 0x9D
+ const uint8_t loSegCode = i_segmentCode & 0xFF;
+ const uint8_t hiSegCode = (i_segmentCode >> 8) & 0xFF;
+
+ TRACUCOMP(g_trac_bpm, "Bpm::switchBpmPage(): "
+ "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND1 and "
+ "0x%.2X to SPECIAL_CONTROL_COMMAND2",
+ loSegCode,
+ hiSegCode);
+
+ // First, clear the SPECIAL_CONTROL_COMMAND2 register so that we can
+ // write the full sequence without issue.
+ errl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND2, 0x00);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::switchBpmPage(): "
+ "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND2 "
+ "FAILED. BPM page will not have switched properly!!",
+ hiSegCode);
+ break;
+ }
+
+ // Write the LO segment code.
+ errl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND1, loSegCode);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::switchBpmPage(): "
+ "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND1 "
+ "FAILED. BPM page will not have switched properly!!",
+ loSegCode);
+ break;
+ }
+
+ // Write the HI segment code.
+ errl = writeViaScapRegister(SPECIAL_CONTROL_COMMAND2, hiSegCode);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::switchBpmPage(): "
+ "Writing 0x%.2X to SPECIAL_CONTROL_COMMAND2 "
+ "FAILED. BPM page will not have switched properly!!",
+ hiSegCode);
+ break;
+ }
+
+
+ // Request to open segment page is sent.
+ // Wait a few seconds for the operation to complete.
+ nanosleep(2,0);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::writeToMagicRegisters(
+ uint8_t const (&i_magicValues)[NUM_MAGIC_REGISTERS])
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeToMagicRegisters() 0x%.2X 0x%.2X",
+ i_magicValues[0],
+ i_magicValues[1]);
+ errlHndl_t errl = nullptr;
+
+ do {
+ const uint16_t magic_registers[NUM_MAGIC_REGISTERS] =
+ {BPM_MAGIC_REG1, BPM_MAGIC_REG2};
+
+ for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i)
+ {
+ errl = nvdimmWriteReg(iv_nvdimm,
+ magic_registers[i],
+ i_magicValues[i]);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): "
+ "Failed to write the magic values to the magic "
+ "registers");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Verify the magic values were written
+ uint8_t magic_data[NUM_MAGIC_REGISTERS] = {0};
+ for (size_t i = 0; i < NUM_MAGIC_REGISTERS; ++i)
+ {
+ errl = nvdimmReadReg(iv_nvdimm,
+ magic_registers[i],
+ magic_data[i]);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): "
+ "Failed to read back magic values to verify that "
+ "they were written.");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // If either of the magic values stored in magic_data don't match the
+ // corresponding expected values in magic_values then an error occurred.
+ if ( (magic_data[0] != i_magicValues[0])
+ || (magic_data[1] != i_magicValues[1]))
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::writeToMagicRegisters(): "
+ "Magic values read from BPM didn't match expected values "
+ "BPM_MAGIC_REG1 Expected 0x%.2X Actual 0x%.2X "
+ "BPM_MAGIC_REG2 Expected 0x%.2X Actual 0x%.2X",
+ i_magicValues[0], magic_data[0],
+ i_magicValues[1], magic_data[1]);
+
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WRITE_MAGIC_REG
+ * @reasoncode BPM_RC::BPM_WRITE_TO_MAGIC_REG_FAILED
+ * @userdata1[0:7] BPM_MAGIC_REG1 expected value
+ * @userdata1[8:15] BPM_MAGIC_REG1 actual value
+ * @userdata1[16:23] BPM_MAGIC_REG2 expected value
+ * @userdata1[24:31] BPM_MAGIC_REG2 actual value
+ * @userdata2[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to write values to the magic registers on
+ * the BPM.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WRITE_MAGIC_REG,
+ BPM_RC::BPM_WRITE_TO_MAGIC_REG_FAILED,
+ TWO_UINT16_TO_UINT32(
+ TWO_UINT8_TO_UINT16(i_magicValues[0],
+ magic_data[0]),
+ TWO_UINT8_TO_UINT16(i_magicValues[1],
+ magic_data[1])),
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ TRACUCOMP(g_trac_bpm, "Bpm::writeToMagicRegisters(): "
+ "Magic values successfully written to BPM "
+ "BPM_MAGIC_REG1 0x%.2X "
+ "BPM_MAGIC_REG2 0x%.2X ",
+ magic_data[0],
+ magic_data[1]);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::dumpSegment(uint16_t const i_segmentCode,
+ uint8_t (&o_buffer)[SEGMENT_SIZE])
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::dumpSegment(): Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ assert(i_segmentCode == SEGMENT_B_CODE, "Bpm::dumpSegment(): Only Segment B is supported.");
+
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ errl = disableWriteProtection();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // We cannot be in BSL mode when dumping the config segments. Verify we
+ // aren't in BSL mode by checking SCAP_STATUS
+ scap_status_register_t status;
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_STATUS,
+ status.full);
+ if (errl != nullptr)
+ {
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (status.bit.Bpm_Bsl_Mode)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::dumpSegment(): "
+ "BSL Mode is enabled. Attempting to exit BSL mode.");
+
+ // Try to exit BSL mode. This function will exit BSL.
+ errl = resetDevice();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Exit update mode if on and write back production magic values.
+ errl = exitUpdateMode();
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_STATUS,
+ status.full);
+ if (errl != nullptr)
+ {
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+ if (status.bit.Bpm_Bsl_Mode)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::dumpSegment(): "
+ "Couldn't dump Segment %X. BSL Mode is enabled.",
+ getSegmentIdentifier(i_segmentCode));
+
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_DUMP_SEGMENT
+ * @reasoncode BPM_RC::BPM_BSL_MODE_ENABLED
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc Couldn't dump segment data because BSL mode
+ * was enabled.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_DUMP_SEGMENT,
+ BPM_RC::BPM_BSL_MODE_ENABLED,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+
+ break;
+ }
+ }
+
+ // First the NVDIMM MAGIC registers BPM_MAGIC_REG1 and BPM_MAGIC_REG2
+ // must be programmed to 0xBA and 0xAB respectively.
+ const uint8_t magic_values[NUM_MAGIC_REGISTERS] = {0xBA, 0xAB};
+ errl = writeToMagicRegisters(magic_values);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): "
+ "Failed to write magic numbers that enable "
+ "reading of segment data.");
+ break;
+ }
+
+ uint8_t retry = 1;
+ // Attempt to switch to the correct page and dump data twice.
+ do {
+ // Set buffer to be all zeroes.
+ memset(&o_buffer, 0, SEGMENT_SIZE);
+
+ // Open this segments page on the BPM.
+ errl = switchBpmPage(i_segmentCode);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): "
+ "Dumping Segment %X to buffer.",
+ getSegmentIdentifier(i_segmentCode));
+
+ // Dump the segment data
+ bool wrongPage = false;
+ for (uint8_t reg = 0; reg < SEGMENT_SIZE; ++reg)
+ {
+ errl = readViaScapRegister(reg, o_buffer[reg]);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // We can determine if the page switch succeeded based on the
+ // first three bytes from regs 0x10-0x12. If Segment B was
+ // opened, then 0x10-0x1F is serial number for the BPM.
+ // SMART guarantees the first three bytes to be as follows:
+ if ((reg == 0x10) && (o_buffer[reg] != 0x53))
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::dumpSegment "
+ "data 0x%.2X at offset 0x%.2x wasn't expected "
+ "value 0x53",
+ o_buffer[reg], reg);
+ wrongPage = true;
+ }
+ if ((reg == 0x11) && (o_buffer[reg] != 0x46))
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::dumpSegment "
+ "data 0x%.2X at offset 0x%.2x wasn't expected "
+ "value 0x46",
+ o_buffer[reg], reg);
+ wrongPage = true;
+ }
+ if ((reg == 0x12) && (o_buffer[reg] != 0x52))
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::dumpSegment "
+ "data 0x%.2X at offset 0x%.2x wasn't expected "
+ "value 0x52",
+ o_buffer[reg], reg);
+ wrongPage = true;
+ }
+
+ if (wrongPage && (reg == 0x20))
+ {
+ break;
+ }
+
+ }
+
+ TRACUBIN(g_trac_bpm, "Segment BIN DUMP", o_buffer, SEGMENT_SIZE);
+
+ if ((errl != nullptr) || (wrongPage == false))
+ {
+ break;
+ }
+
+ // Close this segments page on the BPM before making another
+ // attempt.
+ errl = switchBpmPage(DEFAULT_REG_PAGE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ } while(++retry < MAX_RETRY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ if (retry >= MAX_RETRY)
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_DUMP_SEGMENT
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1 The segment code for the page that failed to
+ * open.
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc Failed to open the segment page in the given
+ * amount of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_DUMP_SEGMENT,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ i_segmentCode,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ setAttemptAnotherUpdate();
+ break;
+ }
+
+ } while(0);
+
+ TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): "
+ "Closing Segment %X's page.",
+ getSegmentIdentifier(i_segmentCode));
+
+ // Close the Segment page by switching back to the default page.
+ errlHndl_t closeSegmentErrl = switchBpmPage(DEFAULT_REG_PAGE);
+ if (closeSegmentErrl != nullptr)
+ {
+ handleMultipleErrors(errl, closeSegmentErrl);
+ }
+
+ // Write back the production magic values.
+ errlHndl_t magicErrl = writeToMagicRegisters(PRODUCTION_MAGIC_VALUES);
+ if (magicErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::dumpSegment(): "
+ "Failed to write update mode magic numbers.");
+ handleMultipleErrors(errl, magicErrl);
+ }
+
+ return errl;
+}
+
+errlHndl_t Bpm::mergeSegment(BpmConfigLidImage const i_configImage,
+ uint16_t const i_segmentCode,
+ uint8_t (&o_buffer)[SEGMENT_SIZE])
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::mergeSegment(): Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ errlHndl_t errl = nullptr;
+
+ size_t segmentStartOffset = 0;
+ auto it = segmentMap.find(i_segmentCode);
+ if (it != segmentMap.end())
+ {
+ segmentStartOffset = it->second;
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::mergeSegment(): "
+ "Couldn't find start offset for Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ assert(false, "Add the missing Segment %X Start Offset to the offset map", getSegmentIdentifier(i_segmentCode));
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::mergeSegment(): "
+ "Segment %X Start offset: 0x%X",
+ getSegmentIdentifier(i_segmentCode),
+ segmentStartOffset);
+
+ do {
+
+ const size_t NUMBER_OF_FRAGMENTS = i_configImage.getNumberOfFragments();
+ char const * data = reinterpret_cast<char const *>(
+ i_configImage.getFirstFragment());
+
+ config_image_fragment_t const * fragment =
+ reinterpret_cast<config_image_fragment_t const *>(data);
+
+ TRACUCOMP(g_trac_bpm, "mergeSegment(): "
+ "NUMBER_OF_FRAGMENTS = 0x%.4X", NUMBER_OF_FRAGMENTS);
+
+ for(size_t i = 0; i < NUMBER_OF_FRAGMENTS; ++i)
+ {
+ // The fragment offsets are given as offsets within the
+ // configuration segment data. So, if the fragment offset is less
+ // than the starting offset of this segment then the fragment is not
+ // relevant to this segment.
+ if (fragment->iv_offset < segmentStartOffset)
+ {
+ // This fragment is not for the segment we are dealing with.
+ TRACUCOMP(g_trac_bpm, "mergeSegment(): "
+ "Fragment with offset 0x%.4X not related to "
+ "Segment %X, skipping",
+ fragment->iv_offset,
+ getSegmentIdentifier(i_segmentCode));
+
+ // Move to the next fragment
+ data += sizeof(config_image_fragment_t)
+ + fragment->iv_fragmentSize;
+ fragment =
+ reinterpret_cast<config_image_fragment_t const *>(data);
+ continue;
+ }
+ // Each segment is 128 bytes in size. So, if the offset given for
+ // the fragment is greater than the upper boundry then no more
+ // fragments exist for this segment.
+ if (fragment->iv_offset >= segmentStartOffset + SEGMENT_SIZE)
+ {
+ // This fragment and all other fragments afterward are not for
+ // this segment.
+ TRACUCOMP(g_trac_bpm, "mergeSegment(): "
+ "Fragment with offset 0x%.4X greater than/equal to "
+ "Segment %X ending offset, skipping",
+ fragment->iv_offset,
+ getSegmentIdentifier(i_segmentCode));
+ break;
+ }
+
+ // The fragment offset may be out of bounds for the buffer so
+ // scale it down to be within the buffer size.
+ size_t offset = fragment->iv_offset % SEGMENT_SIZE;
+
+ // Overwrite the BPM segment data at the offset specified by the
+ // fragment.
+ memcpy(&o_buffer[offset],
+ &(fragment->iv_data),
+ fragment->iv_fragmentSize);
+
+ // Move to the next fragment
+ data += sizeof(config_image_fragment_t) + fragment->iv_fragmentSize;
+ fragment = reinterpret_cast<config_image_fragment_t const *>(data);
+ }
+
+ TRACUBIN(g_trac_bpm, "Merged Segment", o_buffer, SEGMENT_SIZE);
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::eraseSegment(uint16_t i_segmentCode)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::eraseSegment(): Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ payload_t payload;
+
+ size_t segmentStartOffset = 0;
+ auto it = segmentMap.find(i_segmentCode);
+ if (it != segmentMap.end())
+ {
+ segmentStartOffset = it->second;
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::mergeSegment(): "
+ "Couldn't find start offset for Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ assert(false, "Add the missing Segment %X Start Offset to the offset map", getSegmentIdentifier(i_segmentCode));
+ }
+ errl = setupPayload(payload,
+ BSL_ERASE_SEGMENT,
+ BPM_CONFIG_START_ADDRESS + segmentStartOffset);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH,
+ payload,
+ WRITE,
+ ERASE_SEGMENT_DELAY);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Wait 1 second for the operation to complete.
+ TRACFCOMP(g_trac_bpm, "Bpm::eraseSegment(): "
+ "Erasing Segment %X. "
+ "Waiting 1 second for operation to complete.",
+ getSegmentIdentifier(i_segmentCode));
+ nanosleep(1,0);
+
+ } while(0);
+
+ TRACFCOMP(g_trac_bpm, EXIT_MRK"Bpm::eraseSegment(): "
+ "Segment %X erase operation completed "
+ "%s",
+ getSegmentIdentifier(i_segmentCode),
+ (errl == nullptr) ? "without errors" : "with errors");
+
+ return errl;
+}
+
+errlHndl_t Bpm::writeSegment(uint8_t const (&i_buffer)[SEGMENT_SIZE],
+ uint16_t const i_segmentCode)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::writeSegment(): Segment %X",
+ getSegmentIdentifier(i_segmentCode));
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ auto it = segmentMap.find(i_segmentCode);
+ size_t segmentStartOffset = 0;
+ if (it != segmentMap.end())
+ {
+ segmentStartOffset = it->second;
+ }
+
+ // To update the given segment, we have to send over the data as
+ // payloads. Since the max size of a payload's data is 16 bytes, there
+ // will be 8 payloads sent to update a given segment because each
+ // segment is 128 bytes.
+ for (size_t offset = 0;
+ offset < SEGMENT_SIZE;
+ offset += MAX_PAYLOAD_DATA_SIZE)
+ {
+ // Construct a payload for the data at this offset up to the
+ // MAX_PAYLOAD_DATA_SIZE.
+ payload_t payload;
+ // Each segment is 128 bytes and the segment start addresses
+ // are their relative position to BPM_CONFIG_START_ADDRESS. To
+ // arrive at the correct address offset for this data we must
+ // calculate the addressOffset in the following way.
+ uint16_t addressOffset = BPM_CONFIG_START_ADDRESS
+ + segmentStartOffset
+ + offset;
+ errl = setupPayload(payload,
+ BSL_RX_DATA_BLOCK,
+ addressOffset,
+ &i_buffer[offset],
+ MAX_PAYLOAD_DATA_SIZE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ if (addressOffset % 0x20 == 0)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::writeSegment(): "
+ "Writing to address offset 0x%.4X. "
+ "Config bytes written: 0x%X; Remaining: 0x%X",
+ addressOffset,
+ offset, (SEGMENT_SIZE - offset));
+ }
+
+ // Attempt to write the payload using a retry loop.
+ errl = blockWrite(payload);
+ if (errl != nullptr)
+ {
+ break;
+ }
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ } while(0);
+
+ TRACFCOMP(g_trac_bpm, EXIT_MRK"Bpm::writeSegment(): "
+ "Segment %X write and verification completed "
+ "%s",
+ getSegmentIdentifier(i_segmentCode),
+ (errl == nullptr) ? "without errors" : "with errors");
+
+ return errl;
+}
+
+errlHndl_t Bpm::preprocessSegments(BpmConfigLidImage const i_configImage)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::preprocessSegments()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ if (iv_attemptAnotherUpdate && iv_segmentDMerged && iv_segmentBMerged)
+ {
+ // The segment data has already been merged with the flash image
+ // data. Doing it again has the potential to fail depending on where
+ // the last update attempt failed.
+ TRACFCOMP(g_trac_bpm, "Bpm::preprocessSegments(): "
+ "Segment data was merged in a previous update attempt, "
+ "skipping preprocessing and using existing data.");
+ break;
+ }
+
+ // Merge the fragments for D with the data from the BPM. For D, this
+ // will just populate the empty segment with the data from the flash
+ // image.
+ if (!iv_segmentDMerged)
+ {
+ errl = mergeSegment(i_configImage, SEGMENT_D_CODE, iv_segmentD);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::preprocessSegments(): "
+ "Failed to merge Segment D.");
+ break;
+ }
+ iv_segmentDMerged = true;
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::preprocessSegments(): "
+ "Segment %X has been merged already. Skipping merge...",
+ getSegmentIdentifier(SEGMENT_D_CODE));
+ }
+
+ // Merge the fragments for B with the data from the BPM.
+ if (!iv_segmentBMerged)
+ {
+ // Dump the segment into a buffer. This is only necessary for
+ // segment B as segment D comes straight from the flash image file.
+ errl = dumpSegment(SEGMENT_B_CODE, iv_segmentB);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = mergeSegment(i_configImage, SEGMENT_B_CODE, iv_segmentB);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::preprocessSegments(): "
+ "Failed to merge Segment B.");
+ break;
+ }
+ iv_segmentBMerged = true;
+ }
+ else
+ {
+ TRACFCOMP(g_trac_bpm, INFO_MRK"Bpm::preprocessSegments(): "
+ "Segment %X has been merged already. Skipping merge...",
+ getSegmentIdentifier(SEGMENT_B_CODE));
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::getResponse(uint8_t * const o_responseData,
+ uint8_t const i_responseSize)
+{
+ TRACUCOMP(g_trac_bpm, ENTER_MRK"Bpm::getResponse()");
+
+ errlHndl_t errl = nullptr;
+ memset(o_responseData, 0xFF, i_responseSize);
+
+ do {
+
+ // Get the result from the BPM.
+ // First clear the error status register
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_REG_ERR_STATUS,
+ 0x00);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): "
+ "Failed to clear error status register");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Set the payload length
+ // The 4 header bytes plus 2 CRC bytes make up the other data size in
+ // the response payload.
+ const uint8_t RESPONSE_PAYLOAD_OTHER_DATA_SIZE = 6;
+ uint8_t responsePayloadSize = RESPONSE_PAYLOAD_OTHER_DATA_SIZE
+ + i_responseSize;
+
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_PAYLOAD_LENGTH,
+ responsePayloadSize);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): "
+ "Failed to set payload length");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Setup the command status register
+ command_status_register_t commandStatus;
+ commandStatus.bits.Bsp_Cmd_In_Progress = 1;
+ commandStatus.bits.Operator_Type = READ;
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_CMD_STATUS,
+ commandStatus.value);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): "
+ "Failed to setup command status register");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Setup command type.
+ errl = nvdimmWriteReg(iv_nvdimm,
+ BPM_REG_CMD,
+ BPM_PASSTHROUGH);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): "
+ "Failed to setup command type.");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ errl = waitForCommandStatusBitReset(commandStatus);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Read out the response payload.
+ payload_t responsePayload;
+
+ for (size_t i = 0; i < responsePayloadSize; ++i)
+ {
+ uint8_t data = 0;
+ errl = nvdimmReadReg(iv_nvdimm,
+ (BPM_REG_PAYLOAD_START + (i * sizeof(uint8_t))),
+ data);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::getResponse(): "
+ "Failed to read response payload");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ responsePayload.push_back(data);
+ }
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Verify the data from the response was good.
+ uint8_t* responseIterator = responsePayload.data();
+ uint16_t responseCrc = *(reinterpret_cast<uint16_t *>
+ (&responseIterator[PAYLOAD_HEADER_SIZE + i_responseSize]));
+ // The BPM is going to give the response CRC in LE. So convert it to BE.
+ responseCrc = le16toh(responseCrc);
+ uint16_t expectedCrc = crc16_calc(responseIterator,
+ PAYLOAD_HEADER_SIZE + i_responseSize);
+ if (responseCrc != expectedCrc)
+ {
+ memset(o_responseData, 0xFF, i_responseSize);
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::getResponse(): "
+ "Response CRC verification failed. "
+ "Received invalid data from BPM.");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_GET_RESPONSE
+ * @reasoncode BPM_RC::BPM_RESPONSE_CRC_MISMATCH
+ * @userdata1[00:31] Expected Response CRC (in Big Endian)
+ * @userdata1[32:63] Actual Response CRC (in Big Endian)
+ * @userdata2 NVDIMM Target HUID associated with this BPM
+ * @devdesc The response CRC calculated by the BPM didn't
+ * match the CRC calculated by hostboot.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_GET_RESPONSE,
+ BPM_RC::BPM_RESPONSE_CRC_MISMATCH,
+ TWO_UINT32_TO_UINT64(expectedCrc,
+ responseCrc),
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ // Write the data to the output buffer
+ for (size_t i = 0; i < i_responseSize; ++i)
+ {
+ // Only copy the response data from the payload to the output buffer
+ o_responseData[i] = responsePayload[i + PAYLOAD_HEADER_SIZE];
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::verifyBlockWrite(payload_t i_payload,
+ uint8_t i_dataLength,
+ uint8_t & o_status)
+{
+ errlHndl_t errl = nullptr;
+ // Assume a bad status.
+ o_status = 0xFF;
+
+ do {
+
+ // Pull the address to verify out of the payload. It was inserted in
+ // little endian form so it needs to be converted back to big endian
+ // because setupPayload expects an address in big endian.
+ uint16_t address = getPayloadAddressBE(i_payload);
+
+ // The data section of the payload is organized in the following way:
+ // 2 bytes: uint16_t size of data to verify in little endian format
+ // 2 bytes: CRC of the data to be verified on the BPM in little endian.
+ const size_t VERIFY_BLOCK_PAYLOAD_DATA_SIZE = 4;
+ uint8_t data[VERIFY_BLOCK_PAYLOAD_DATA_SIZE] = {0};
+
+ // Since the data length is stored as uint16_t but the length we deal
+ // with is uint8_t we can easily convert this to little endian by
+ // storing our uint8_t data length in the first index of the array and
+ // leaving the next index 0.
+ data[0] = i_dataLength;
+
+ // Calculate the uint16_t CRC for the data that was written to the BPM.
+ // The BPM will compare its calculated CRC with this one to verify if
+ // the block was written correctly.
+ uint16_t crc = htole16(crc16_calc(&i_payload[PAYLOAD_DATA_START_INDEX],
+ i_dataLength));
+
+ memcpy(&data[2], &crc, sizeof(uint16_t));
+
+ payload_t verifyPayload;
+ errl = setupPayload(verifyPayload,
+ BSL_VERIFY_BLOCK,
+ address,
+ data,
+ VERIFY_BLOCK_PAYLOAD_DATA_SIZE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Issue the command to the BPM.
+ errl = issueCommand(BPM_PASSTHROUGH,
+ verifyPayload,
+ WRITE,
+ NO_DELAY_EXTERNAL_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = getResponse(&o_status, sizeof(uint8_t));
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::blockWrite(payload_t i_payload)
+{
+ assert(i_payload[PAYLOAD_COMMAND_INDEX] == BSL_RX_DATA_BLOCK,
+ "Bpm::blockWrite(): "
+ "Can only write BSL_RX_DATA_BLOCK commands");
+
+ errlHndl_t errl = nullptr;
+ uint8_t retry = 0;
+
+ // Get the payload address for trace output.
+ uint16_t payloadAddress = getPayloadAddressBE(i_payload);
+
+ // Any status from verifyBlockWrite that is non-zero is considered a
+ // fail. So, assume a fail and check.
+ uint8_t wasVerified = 0xFF;
+ do {
+
+
+ // Since the write command has its response packet checked within the
+ // issueCommand() function we must attempt to retry the write if we get
+ // a bad response from the BPM.
+ errl = blockWriteRetry(i_payload);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Sleep for 0.001 second
+ nanosleep(0, 1 * NS_PER_MSEC);
+
+ uint8_t dataLength = i_payload[PAYLOAD_HEADER_DATA_LENGTH_INDEX]
+ - PAYLOAD_HEADER_SIZE;
+ errl = verifyBlockWrite(i_payload,
+ dataLength,
+ wasVerified);
+ if ( (errl != nullptr)
+ && (errl->reasonCode() == BPM_RC::BPM_RESPONSE_CRC_MISMATCH)
+ && ((retry + 1) < MAX_RETRY))
+ {
+ // Delete the retryable error and continue
+ TRACFCOMP(g_trac_bpm, "Bpm::blockWrite(): "
+ "Encountered a retryable error. Delete and continue.");
+ delete errl;
+ errl = nullptr;
+ }
+ else if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::blockWrite(): "
+ "BSL_VERIFY_BLOCK failed for address 0x%.4X. "
+ "A non-retryable error occurred on attempt %d/%d",
+ payloadAddress,
+ (retry + 1),
+ MAX_RETRY);
+ // A non-retryable error occurred. Break from retry loop.
+ break;
+ }
+
+ if (wasVerified != 0)
+ {
+ TRACUCOMP(g_trac_bpm, "Bpm::blockWrite(): "
+ "BSL_VERIFY_BLOCK failed for address 0x%.4X. "
+ "Attempt %d/%d",
+ payloadAddress,
+ (retry + 1),
+ MAX_RETRY);
+ }
+ else
+ {
+ // Write verified successfully, stop retries.
+ break;
+ }
+
+ } while (++retry < MAX_RETRY);
+ if ((errl == nullptr) && (retry >= MAX_RETRY) && (wasVerified != 0))
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::blockWrite(): "
+ "Failed to write payload data to BPM after %d retries.",
+ MAX_RETRY);
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_BLOCK_WRITE
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The block of data to be written to the BPM
+ * failed to write successfully in the given number
+ * of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_BLOCK_WRITE,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+
+ }
+
+ if (errl != nullptr)
+ {
+ // Change the state of iv_attemptAnotherUpdate. This will signal
+ // another update attempt or cease further attempts.
+ setAttemptAnotherUpdate();
+ }
+
+ return errl;
+}
+
+errlHndl_t Bpm::blockWriteRetry(payload_t i_payload)
+{
+ assert(i_payload[PAYLOAD_COMMAND_INDEX] == BSL_RX_DATA_BLOCK,
+ "Bpm::blockWriteRetry(): "
+ "Can only retry BSL_RX_DATA_BLOCK commands");
+
+ errlHndl_t errl = nullptr;
+ uint8_t retry = 0;
+
+ // Get the payload address for trace output.
+ uint16_t payloadAddress = getPayloadAddressBE(i_payload);
+
+ do {
+
+ // Send the payload data over as a pass-through command. The response
+ // will be checked internally.
+ errl = issueCommand(BPM_PASSTHROUGH, i_payload, WRITE);
+ if (errl == nullptr)
+ {
+ // Command was a success. Stop retries.
+ break;
+ }
+
+ if ( (errl != nullptr)
+ && (errl->reasonCode() == BPM_RC::BPM_RESPONSE_CRC_MISMATCH)
+ && ((retry + 1) < MAX_RETRY))
+ {
+ // Delete the retryable error and continue
+ TRACFCOMP(g_trac_bpm, "Bpm::blockWriteRetry(): "
+ "Encountered a retryable error. Delete and continue.");
+ delete errl;
+ errl = nullptr;
+ }
+ else if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::blockWriteRetry(): "
+ "BSL_RX_DATA_BLOCK failed for address 0x%.4X. "
+ "A non-retryable error occurred on attempt %d/%d",
+ payloadAddress,
+ (retry + 1),
+ MAX_RETRY);
+ // A non-retryable error occurred. Break from retry loop.
+ break;
+ }
+
+ } while (++retry < MAX_RETRY);
+ if ((errl == nullptr) && (retry >= MAX_RETRY))
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::blockWriteRetry(): "
+ "Failed to write payload data to BPM after %d retries.",
+ MAX_RETRY);
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_RETRY_BLOCK_WRITE
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The block of data to be written to the BPM
+ * failed to write successfully in the given number
+ * of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_RETRY_BLOCK_WRITE,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+
+ }
+
+ if (errl != nullptr)
+ {
+ // Change the state of iv_attemptAnotherUpdate. This will signal
+ // another update attempt or cease further attempts.
+ setAttemptAnotherUpdate();
+ }
+
+ 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)
+ {
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ // Give the BPM 20 seconds to complete any given command before we time
+ // out and cancel the update procedure.
+ int retry = 20 * MS_PER_SEC;
+
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (--retry <= 0)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK
+ "BPM::waitForCommandStatusBitReset(): "
+ "BSP_CMD_IN_PROGRESS bit has not reset in allotted "
+ "number of retries. Cancel update procedure");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WAIT_FOR_CMD_BIT_RESET
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The command status bit failed to reset in
+ * the given number of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WAIT_FOR_CMD_BIT_RESET,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ 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");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::waitForCommandStatusBitReset(): "
+ "BPM_CMD_STATUS Error Flag is set");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WAIT_FOR_CMD_BIT_RESET
+ * @reasoncode BPM_RC::BPM_CMD_STATUS_ERROR_BIT_SET
+ * @userdata1[0:7] Error status code returned by BPM
+ * @userdata2[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The command status register returned an error.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WAIT_FOR_CMD_BIT_RESET,
+ BPM_RC::BPM_CMD_STATUS_ERROR_BIT_SET,
+ error,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+
+ }
+
+ } while(0);
+
+ return errl;
+}
+
+errlHndl_t Bpm::verifyGoodBpmState()
+{
+ errlHndl_t errl = nullptr;
+ int retry = 100;
+ scap_status_register_t status;
+ const uint8_t BPM_PRESENT_AND_ENABLED = 0x11;
+
+ while (retry > 0)
+ {
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_STATUS,
+ status.full);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::verifyGoodBpmState(): "
+ "Failed to read SCAP_STATUS to determine "
+ "state of BPM.");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if ((status.full & 0xFF) == BPM_PRESENT_AND_ENABLED)
+ {
+ // BPM is present and enabled. Stop retries.
+ break;
+ }
+
+ --retry;
+ nanosleep(0, 1 * NS_PER_MSEC);
+ }
+ if (retry <= 0)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::verifyGoodBpmState(): "
+ "BPM failed to become present and enabled "
+ "in 100 retries.");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_VERIFY_GOOD_BPM_STATE
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1 NVDIMM Target HUID associated with this BPM
+ * @userdata2 SCAP_STATUS register contents. See nvdimm.H
+ * for bits associated with this register.
+ * @devdesc The BPM did not become present and enabled
+ * in given number of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_VERIFY_GOOD_BPM_STATE,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ TARGETING::get_huid(iv_nvdimm),
+ status.full);
+ errl->collectTrace(BPM_COMP_NAME);
+ errl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ }
+
+ return errl;
+}
+
+errlHndl_t Bpm::waitForBusyBit()
+{
+ errlHndl_t errl = nullptr;
+ int retry = 10;
+ scap_status_register_t status;
+
+ while (retry > 0)
+ {
+
+ errl = nvdimmReadReg(iv_nvdimm,
+ SCAP_STATUS,
+ status.full);
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, "Bpm::waitForBusyBit(): "
+ "Failed to read from SCAP_STATUS to determine "
+ "state of Busy bit.");
+ errl->collectTrace(BPM_COMP_NAME);
+ break;
+ }
+
+ if (!status.bit.Busy)
+ {
+ // SCAP Register is no longer busy. Stop retries.
+ break;
+ }
+
+ if (retry <= 0)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::waitForBusyBit(): "
+ "SCAP_STATUS Busy bit failed to reset to 0 "
+ "in 10 retries.");
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_WAIT_FOR_BUSY_BIT_RESET
+ * @reasoncode BPM_RC::BPM_EXCEEDED_RETRY_LIMIT
+ * @userdata1[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The SCAP status register busy bit failed to
+ * reset in given number of retries.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_WAIT_FOR_BUSY_BIT_RESET,
+ BPM_RC::BPM_EXCEEDED_RETRY_LIMIT,
+ TARGETING::get_huid(iv_nvdimm));
+ errl->collectTrace(BPM_COMP_NAME);
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ --retry;
+ nanosleep(0, 2 * NS_PER_MSEC);
+ }
+
+ return errl;
+}
+
+errlHndl_t Bpm::runConfigUpdates(BpmConfigLidImage i_configImage)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::runConfigUpdates()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // Before the entering BSL mode, we must do preprocessing prior to the
+ // config part of the update. Segment B needs to be dumped from the
+ // BPM into a buffer and then the config data from the image needs to be
+ // inserted into it. To dump segment data, it is required to have
+ // working firmware which will not be the case during BSL mode.
+ errl = preprocessSegments(i_configImage);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // 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;
+ }
+
+ // Perform the configuration data segment updates.
+ // As of BSL 1.4 this is done via the BSL interface instead of SCAP
+ // registers.
+ errl = updateConfig();
+ if (errl != nullptr)
+ {
+ // We are returning with an error. Since the error is from the
+ // config part of the updates it's best to erase the firmware on the
+ // BPM so that updates will be attempted on it in the future.
+ // Because there isn't a way to determine the validity of the config
+ // section on the BPM we're completely reliant on what the firmware
+ // version reports to decide if we need to update or not. If we see
+ // that the firmware version matches the image but for some reason
+ // the config data wasn't updated properly we could believe we
+ // updated successfully when, in fact, we just left the BPM in a bad
+ // state.
+ if ( (iv_firmwareStartAddress == MAIN_PROGRAM_ADDRESS)
+ || (iv_firmwareStartAddress == MAIN_PROGRAM_ADDRESS_ALT))
+ {
+ payload_t payload;
+ errlHndl_t fwEraseErrl = setupPayload(payload,
+ BSL_MASS_ERASE,
+ iv_firmwareStartAddress);
+ if (fwEraseErrl != nullptr)
+ {
+ handleMultipleErrors(errl, fwEraseErrl);
+ break;
+ }
+
+ fwEraseErrl = issueCommand(BPM_PASSTHROUGH,
+ payload,
+ WRITE,
+ ERASE_FIRMWARE_DELAY);
+ if (fwEraseErrl != nullptr)
+ {
+ handleMultipleErrors(errl, fwEraseErrl);
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::updateFirmware(): "
+ "Performing BSL_MASS_ERASE on BPM to force full "
+ "update on any subsequent attempt. Sleep for 5 "
+ "seconds.");
+ longSleep(5);
+ }
+ break;
+ }
+
+ } while(0);
+
+ // Reset the device. This will exit BSL mode.
+ errlHndl_t exitErrl = resetDevice();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runConfigUpdates(): "
+ "Failed to reset the device");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ // Exit update mode
+ exitErrl = exitUpdateMode();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runConfigUpdates(): "
+ "Failed to exit update mode");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+
+ return errl;
+}
+
+errlHndl_t Bpm::runFirmwareUpdates(BpmFirmwareLidImage i_image)
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::runFirmwareUpdates()");
+ errlHndl_t errl = nullptr;
+
+ do {
+
+ // 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;
+ }
+
+ // Run Firmware Update
+ errl = updateFirmware(i_image);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::runFirmwareUpdates(): "
+ "Perform final CRC check on entire BPM flash to load "
+ "new firmware.");
+
+ errl = checkFirmwareCrc();
+ if (errl != nullptr)
+ {
+ setAttemptAnotherUpdate();
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm:: runFirmwareUpdates(): "
+ "Final CRC check failed. %s ",
+ (iv_attemptAnotherUpdate == false) ?
+ "Attempt another update..."
+ : "Attempts to update the BPM have failed. Firmware will not load.");
+ break;
+ }
+
+ } while(0);
+
+ // Reset the device. This will exit BSL mode.
+ errlHndl_t exitErrl = resetDevice();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runFirmwareUpdates(): "
+ "Failed to reset the device");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ // Exit update mode
+ exitErrl = exitUpdateMode();
+ if (exitErrl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::runFirmwareUpdates(): "
+ "Failed to exit update mode");
+ handleMultipleErrors(errl, exitErrl);
+ }
+
+ return errl;
+}
+
+errlHndl_t Bpm::checkFirmwareCrc()
+{
+ TRACFCOMP(g_trac_bpm, ENTER_MRK"Bpm::checkFirmwareCrc()");
+ errlHndl_t errl = nullptr;
+
+ // The COMMAND_CRC_CHECK would return a 3 byte response in the following
+ // format:
+ //
+ // ========================================================================
+ // [Status Code] [Computed_CRC_Lo] [Computed_CRC_Hi]
+ // ========================================================================
+ // BSL_LOCKED 0x00 0x00
+ // PARAMETER_ERROR 0x00 0x00
+ // MAIN_FW_NOT_SUPPORT_CRC_CHECK 0x00 0x00
+ // MEMORY_WRITE_CHECK_FAILED CRC_Low CRC_Hi
+ // WRITE_FORBIDDEN CRC_Low CRC_Hi
+ // VERIFY_MISMATCH CRC_Low CRC_Hi
+ // SUCCESSFUL_OPERATION CRC_Low CRC_Hi
+ //
+ // For status codes BSL_LOCKED, PARAMETER_ERROR, and
+ // MAIN_FW_NOT_SUPPORT_CRC_CHECK the response CRC values are considered
+ // as DONT CARE.
+ //
+ // For the remainder of the status codes the CRC values are the
+ // computed CRC of the image.
+ //
+ // For SUCCESSFUL_OPERATION, the RESET_VECTOR was written.
+ // See bpm_update.H for more info on the status codes
+ const uint8_t CRC_CHECK_RESPONSE_SIZE = 3;
+ uint8_t responseData[CRC_CHECK_RESPONSE_SIZE] = {0};
+
+ do {
+
+ TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): "
+ "Performing final CRC check.");
+ payload_t crcPayload;
+ errl = setupPayload(crcPayload,
+ BSL_CRC_CHECK,
+ iv_firmwareStartAddress);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ errl = issueCommand(BPM_PASSTHROUGH,
+ crcPayload,
+ WRITE,
+ NO_DELAY_EXTERNAL_RESPONSE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ // Wait 10 seconds for the CRC check to complete.
+ TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): "
+ "Allow CRC check to complete on BPM by waiting 10 seconds.");
+ longSleep(10);
+
+ errl = getResponse(responseData, CRC_CHECK_RESPONSE_SIZE);
+ if (errl != nullptr)
+ {
+ break;
+ }
+
+ TRACFCOMP(g_trac_bpm, "Bpm::checkFirmwareCrc(): "
+ "Response Packet CRC check status = 0x%X, CRC_Low = 0x%X, "
+ "CRC_Hi = 0x%X",
+ responseData[0],
+ responseData[1],
+ responseData[2]);
+
+ if (responseData[0] != SUCCESSFUL_OPERATION)
+ {
+ /*@
+ * @errortype
+ * @severity ERRORLOG::ERRL_SEV_PREDICTIVE
+ * @moduleid BPM_RC::BPM_CHECK_FIRMWARE_CRC
+ * @reasoncode BPM_RC::BPM_FIRMWARE_CRC_VERIFY_FAILURE
+ * @userdata1[0:7] CRC check response status code. See bpm_update.H
+ * @userdata1[8:15] CRC low byte
+ * @userdata1[16:23] CRC high byte
+ * @userdata2[0:63] NVDIMM Target HUID associated with this BPM
+ * @devdesc The firmware CRC check failed. Cross check the
+ * CRC check response status code for more details.
+ * @custdesc A problem occurred during IPL of the system.
+ */
+ errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_PREDICTIVE,
+ BPM_RC::BPM_CHECK_FIRMWARE_CRC,
+ BPM_RC::BPM_FIRMWARE_CRC_VERIFY_FAILURE,
+ FOUR_UINT8_TO_UINT32(responseData[0],
+ responseData[1],
+ responseData[2],
+ 0),
+ TARGETING::get_huid(iv_nvdimm));
+ nvdimmAddPage4Regs(iv_nvdimm,errl);
+ nvdimmAddVendorLog(iv_nvdimm, errl);
+ break;
+ }
+
+ } while(0);
+
+ if (errl != nullptr)
+ {
+ TRACFCOMP(g_trac_bpm, ERR_MRK"Bpm::checkFirmwareCrc(): "
+ "Error occurred during BPM Firmware CRC check. "
+ "Firmware image will not load on BPM and update must be "
+ "attempted again.");
+ errl->collectTrace(BPM_COMP_NAME);
+ }
+
+ return errl;
+}
+
+/**
+ * @brief Helper function to handle two potential errors that might occur in a
+ * function that only returns a single error log. If the return error is
+ * not nullptr then the second error will be linked to it and committed
+ * if this is the final update attempt. Otherwise, it will be deleted
+ * since the update procedure will occur again and may be successful.
+ * If the return error is nullptr then the return error will point to
+ * the second's error and the second error will point to nullptr.
+ *
+ * @param[in/out] io_returnErrl A pointer to the error that would be
+ * returned by the function that called
+ * this one. If nullptr, then it will be
+ * set point to the secondary error and
+ * that error will become nullptr.
+ *
+ * @param[in/out] io_secondErrl The secondary error that occurred which
+ * in addition to the usual returned error.
+ */
+void Bpm::handleMultipleErrors(errlHndl_t& io_returnErrl,
+ errlHndl_t& io_secondErrl)
+{
+ if (iv_updateAttempted && (io_returnErrl != nullptr))
+ {
+ io_secondErrl->plid(io_returnErrl->plid());
+ TRACFCOMP(g_trac_bpm, "Committing second error eid=0x%X with plid of "
+ "returned error: 0x%X",
+ io_secondErrl->eid(),
+ io_returnErrl->plid());
+ io_secondErrl->collectTrace(BPM_COMP_NAME);
+ io_secondErrl->addPartCallout(iv_nvdimm,
+ HWAS::BPM_PART_TYPE,
+ HWAS::SRCI_PRIORITY_HIGH);
+ ERRORLOG::errlCommit(io_secondErrl, BPM_COMP_ID);
+ }
+ else if (io_returnErrl == nullptr)
+ {
+ io_returnErrl = io_secondErrl;
+ io_secondErrl = nullptr;
+ }
+ else
+ {
+ // Another update attempt will be made, delete this secondary error.
+ delete io_secondErrl;
+ io_secondErrl = nullptr;
+ }
+}
+
+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);
+
+ 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
OpenPOWER on IntegriCloud