/* 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 #include #include #include #include #include #include #include #include #include #include #include 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 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(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(iv_lidImage); numberOfBlocks = header->iv_numberOfBlocks; } return numberOfBlocks; } void const * BpmFirmwareLidImage::getFirstBlock() const { void * block = nullptr; if (getNumberOfBlocks() > 0) { block = reinterpret_cast(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(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(iv_lidImage); numberOfFragments = header->iv_numberOfFragments; } return numberOfFragments; } void const * BpmConfigLidImage::getFirstFragment() const { void * fragment = nullptr; if (getNumberOfFragments() > 0) { fragment = reinterpret_cast(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(); 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(i_image.getFirstBlock()); firmware_image_block_t const * block = reinterpret_cast (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(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( 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(&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(&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( i_configImage.getFirstFragment()); config_image_fragment_t const * fragment = reinterpret_cast(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(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(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 (&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(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