diff options
Diffstat (limited to 'src/usr/isteps/nvdimm/bpm_update.H')
-rw-r--r-- | src/usr/isteps/nvdimm/bpm_update.H | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/src/usr/isteps/nvdimm/bpm_update.H b/src/usr/isteps/nvdimm/bpm_update.H new file mode 100644 index 000000000..4886a8abd --- /dev/null +++ b/src/usr/isteps/nvdimm/bpm_update.H @@ -0,0 +1,1078 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/nvdimm/bpm_update.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef BPM_UPDATE_H +#define BPM_UPDATE_H + +/* @file bpm_update.H + * + * + */ + +#include <vector> +#include <errl/errlentry.H> +#include <targeting/common/util.H> + +namespace NVDIMM +{ +namespace BPM +{ + +/* + * All of the various commands used for the BPM update. All commands can only be + * sent after write protection on the BPM has been disabled and the update magic + * values have been written to the BPM's magic registers. + * + * BSL: Bootstrap Loader commands + * BPM: Backup Power Module +*/ +enum COMMAND : uint8_t +{ + // A payload sent with this command will be interpreted and processed by the + // NVDIMM module. + BPM_LOCAL = 0xFF, + + /* + * These are LOCAL commands. These commands MUST be sent only outside of BSL + * BSL mode and must be paired with the BSP command BPM_LOCAL. Otherwise, + * unpredicatable errors will occur. + * + * When using issueCommand() for these commands they should always be sent + * with a 0 ms delay. This will ensure that the response packet is not + * checked from the BPM. Since these are processed by the NVDIMM it makes + * no sense to get a response from the BPM and attempting to do so will + * cause errors. + */ + BCL_ENTER_BSL_MODE = 0x01, + BCL_IS_BSL_MODE = 0x02, + BCL_WRITE_REG = 0x03, + BCL_START_UPDATE = 0x04, + BCL_END_UPDATE = 0x05, + BCL_IS_UPDATE_IN_PROGRESS = 0x06, + + // A payload sent with this command will be sent transparently to the BPM. + // This command must only be used while the BPM is in BSL mode. + BPM_PASSTHROUGH = 0xFE, + + /* + * These are PASSTHROUGH commands. These commands MUST be sent only while in + * BSL mode and must be paired with BSP command BPM_PASSTHROUGH. Otherwise, + * unpredicatable errors will occur. + */ + // Writes a block of data to the BPM. + // Delay 1ms (default) + BSL_RX_DATA_BLOCK = 0x10, + // Unlocks the BPM that is in BSL mode so that updates can occur. + // Delay 1ms (default) + BSL_RX_PASSWORD = 0x11, + // Erases 128 bytes at the given address offset. + // WARNING: Due to BSL memory limitations, BSL cannot verify the address + // is a valid config segment address offset and will blindly + // erase 128 bytes of data starting at that offset. If an invalid + // address is sent then the BPM will be bricked in a very + // unpredicatable/unrecoverable way. + // Delay 250ms + BSL_ERASE_SEGMENT = 0x12, + // Unknown, unused. + // Delay 0ms + BSL_TOGGLE_INFO = 0x13, + // Unknown, unused. + // Delay 1ms (default) + BSL_ERASE_BLOCK = 0x14, + // Erases the full firmware section on the BPM. The start of the firmware + // address must be supplied with this command. + // WARNING: Due to BSL memory limitations, BSL cannot verify the address + // is a valid firmware address offset and will blindly erase 128 + // data starting at that offset. If an invalid address is sent + // then the BPM will be bricked in a very + // unpredicatable/unrecoverable way. + // Delay 250ms + BSL_MASS_ERASE = 0x15, + // Sends the command to the BPM to perform the final CRC check on the + // firmware written to the BPM. If the CRC check doesn't match the expected + // CRC in the flash image then the firmware will not load on the BPM and it + // will remain in BSL mode until new firmware is loaded onto it. + // + // Delay 0 + // The response packet must be checked externally from the issueCommand() + // function because the response packet returned from this command is unique + // to this command and will return the results of this command. For more + // info see the checkFirmwareCrc() function description, implementation, and + // the COMMAND_BSL_CRC_CHECK_RESPONSE_CODES enum. + BSL_CRC_CHECK = 0x16, + // Unknown, unused. + // Delay 1ms (default) + BSL_LOAD_PC = 0x17, + // Unknown, unused. + // Delay 1ms (default) + BSL_TX_DATA_BLOCK = 0x18, + // Checks the Bootstrap Loader mode version on the BPM. Depending on this + // version, some parts of the update procedure may have changed. + // + // Delay 0ms + // The response packet must be checked externally from the issueCommand() + // function because the response packet returned from this command is unique + // to this command and will return the results of this command. For more + // info see the readBslVersion() function description and implementation. + BSL_TX_BSL_VERSION = 0x19, + // Unknown, unused. + // Delay 1ms (default) + BSL_TX_BUFFER_SIZE = 0x1A, + // Unknown, unused. + // Delay 1ms (default) + BSL_RX_DATA_BLOCK_FAST = 0x1B, + // Resets the BPM and exits BSL mode. + // Delay 0ms + // Never check for a response packet from the BPM after sending the reset + // command because the BPM may not be back up and if it is it will not be + // in BSL mode anymore. If the response packet is checked then errors will + // occur. + BSL_RESET_DEVICE = 0x1C, + // Verifies the block of data written to the BPM flash is identical to what + // was sent to it in a prior write. For more information see the + // verifyBlockWrite() description and implementation. + // + // Delay 0ms + // The response packet must be checked externally from the issueCommand() + // function because the response packet returned from this command is unique + // to this command and will return the results of this command. + BSL_VERIFY_BLOCK = 0x1D, +}; + +// These consts serve as reminders in the code for what was explained in the +// COMMAND enum. +const int NO_DELAY_NO_RESPONSE = 0; +const int NO_DELAY_EXTERNAL_RESPONSE = 0; +const int ERASE_SEGMENT_DELAY = 250; +const int ERASE_FIRMWARE_DELAY = 250; + +// These are the various response codes returned by the BPM after the +// BSL_CRC_CHECK command is sent at the end of the update procedure. +enum COMMAND_BSL_CRC_CHECK_RESPONSE_CODES : uint16_t +{ + // The updated firmware is set up with all necessary loading parameters to + // load and execute upon reset. + SUCCESSFUL_OPERATION = 0x00, + + // Error setting up the necessary loading parameters for the updated + // firmware image. + MEMORY_WRITE_CHECK_FAILED = 0x01, + + // The command was attempted without unlocking the BSL with the password. + BSL_LOCKED = 0x04, + + // Error setting up the necessary loading parameters for the updated + // firmware image. + WRITE_FORBIDDEN = 0x06, + + // The checksum validation of the updated firmware image failed. The + // calculated checksum doesn't match the checksum data provided @FF7A in the + // firmware image file. + VERIFY_MISMATCH = 0x09, + + // The firmware image start address given for the command is wrong. + PARAMETER_ERROR = 0x0A, + + // Firmware image file used for the update doesn't hae the checksum data + // defined @FF7A + MAIN_FW_NOT_SUPPORT_CRC_CHECK = 0x0B, +}; + +// BSL versions that this code supports. +const uint8_t BSL_VERSION_1_4 = 0x14; + +// The operator types for the BPM_CMD_STATUS register +enum COMMAND_STATUS_REGISTER_OP_TYPES : uint8_t +{ + NOP = 0x00, + READ = 0x01, + WRITE = 0x02, + NO_TRASFER = 0x03, +}; + +// Used to overlay onto the LID image +struct firmware_image_block +{ + // The block size is the sizeof(iv_addressOffset) plus sizeof(iv_data). + uint8_t iv_blockSize; + + // The address offset where the first byte in iv_data came from in the + // firmware image. + uint16_t iv_addressOffset; + + // A variable sized array of firmware data. The size of which is always + // iv_blockSize - sizeof(iv_addressOffset) and the max this can be is + // MAX_PAYLOAD_SIZE. + char iv_data[0]; + +} PACKED; + +typedef firmware_image_block firmware_image_block_t; + + +// Used to overlay onto the LID image +struct config_image_fragment +{ + // The fragment size is the size of iv_data. + uint8_t iv_fragmentSize; + + // The offset where the first byte in iv_data should begin overwritting the + // BPM config data in the BPM configuration segment dump buffer. + uint16_t iv_offset; + + // A variable sized array of config segment data. + char iv_data[0]; + +} PACKED; + +typedef config_image_fragment config_image_fragment_t; + + +/* Max payload size is 26 bytes + * 4 bytes: header + * 1 byte: sync byte + * 1 byte: command + * 1 byte: header size + data size + * 1 byte: header size + data size + * 2 bytes: address + * 2 bytes: extra + * 16 bytes: data + * 2 bytes: CRC + */ +constexpr size_t MAX_PAYLOAD_SIZE = 26; + +// Max number of bytes data section of payload can be. +constexpr size_t MAX_PAYLOAD_DATA_SIZE = 16; + +// Number of bytes for header, address, extra, and CRC +constexpr size_t MAX_PAYLOAD_OTHER_DATA_SIZE = 10; + +// Number of bytes for the header. +constexpr uint8_t PAYLOAD_HEADER_SIZE = 4; + +// Indices of where to find certain data within a constructed payload. +// These indices have been subtracted by 1 from the given payload format because +// after a payload is constructed the sync byte is removed from the front. +constexpr uint8_t PAYLOAD_COMMAND_INDEX = 0; +constexpr uint8_t PAYLOAD_ADDRESS_START_INDEX = 3; +constexpr uint8_t PAYLOAD_DATA_START_INDEX = 7; +constexpr uint8_t PAYLOAD_HEADER_DATA_LENGTH_INDEX = 1; + +// The sync byte that must always be at the front of a BPM payload. This is used +// calculate the CRC of the payload and then removed because the nvdimm +// automatically sends the sync byte ahead of the payload. +constexpr uint8_t SYNC_BYTE = 0x80; +constexpr uint8_t SYNC_BYTE_SIZE = sizeof(uint8_t); + +// Maximum size of any segment in the config data section +constexpr size_t SEGMENT_SIZE = 128; + +// Maximum size of the config data section. +constexpr size_t ALL_SEGMENTS_SIZE = 512; + +// Number of magic registers for the BPM +constexpr size_t NUM_MAGIC_REGISTERS = 2; + +// These are the production magic values for the BPM that should be written in +// BPM_MAGIC_REG1 and BPM_MAGIC_REG2 respectively. +const uint8_t PRODUCTION_MAGIC_VALUES[NUM_MAGIC_REGISTERS] = {0x55, 0xAA}; +// These magic values to enable nvdimm-bpm interface. They must be written to +// the magic registers BEFORE writing flash updates to the BPM in BSL mode. +const uint8_t UPDATE_MODE_MAGIC_VALUES[NUM_MAGIC_REGISTERS] = {0xB0, 0xDA}; +// These are the segment read magic values that allow dumping of the segment +// data from the BPM. +const uint8_t SEGMENT_READ_MAGIC_VALUES[NUM_MAGIC_REGISTERS] = {0xBA, 0xAB}; + +typedef std::vector<uint8_t> payload_t; + + +/** + * @brief BPM_CMD_STATUS register bits + */ +struct command_status_register_bits +{ + uint8_t Abort_Request : 1; // Bit 7 + uint8_t Abort_Acknowledge : 1; // Bit 6 + uint8_t Reserved1 : 1; // Bit 5 + uint8_t Reserved2 : 1; // Bit 4 + uint8_t Error_Flag : 1; // Bit 3 + uint8_t Bsp_Cmd_In_Progress : 1; // Bit 2 + uint8_t Operator_Type : 2; // Bit 1-0 +} PACKED; + +/** + * @brief Union simplifying manipulation of REG_CMD_STATUS value + */ +union command_status_register_union +{ + uint8_t value; + command_status_register_bits bits; + + /** + * @brief Constructor + */ + command_status_register_union() + : value(0) + {} + +} PACKED; + +typedef command_status_register_union command_status_register_t; + +class BpmFirmwareLidImage +{ +public: + + /** + * @brief Constructor that sets access to LID information + * + * @param[in] i_lidImageAddr virtual address where LID was loaded + * @param[in] i_size size of the loaded LID + */ + BpmFirmwareLidImage(void * const i_lidImageAddr, size_t i_size); + + /** + * @brief Returns the version of the firmware binary as a uint16_t + * + * @return uint16_t version of the firmware image as MMmm. + * MM = major version, mm = minor. + */ + uint16_t getVersion() const; + + /** + * @brief Returns the number of blocks in the LID image. + * + */ + uint16_t getNumberOfBlocks() const; + + /** + * @brief Returns a pointer to the first block in LID image. + */ + void const * getFirstBlock() const; + + /* Layout of the BPM Firmware image + * Byte 1: Major version number (MM) + * Byte 2: Minor version number (mm) + * Byte 3-4: N number of blocks in the file (NN NN) + * Byte 5-EOF: Blocks of the form: + * BLOCK_SIZE Byte 1: X number of bytes in block excluding + * this byte. (XX) + * ADDRESS_OFFSET Byte 2-3: Original address offset of the + * first data byte. (AD DR) + * DATA_BYTES Byte 4-X: Firmware data bytes (DD) + * + * Example file: + * 01 03 00 01 06 80 00 6a 14 31 80 + * MM mm NN NN XX AD DR DD DD DD DD + */ + typedef struct firmware_image_header + { + uint8_t iv_versionMajor; + uint8_t iv_versionMinor; + uint16_t iv_numberOfBlocks; + } firmware_image_header_t; + +private: + + // Pointer to the LID image allocated outside of the class + void * const iv_lidImage; + + // The size of the LID image. + size_t iv_lidImageSize; +}; + + +class BpmConfigLidImage +{ +public: + + /** + * @brief Constructor that sets access to LID information + * + * @param[in] i_lidImageAddr virtual address where LID was loaded + * @param[in] i_size size of the loaded LID + */ + BpmConfigLidImage(void * const i_lidImageAddr, size_t i_size); + + /** + * @brief Returns the version of the config binary as a uint16_t. There isn't + * a way to check the version of the config data on the BPM but the + * config binary still has the version of the flash image it + * originally came from. + * + * @return uint16_t version of the firmware image as MMmm. + * MM = major version, mm = minor. + */ + uint16_t getVersion() const; + + /** + * @brief Returns the number of fragments in the LID image. + * + */ + uint16_t getNumberOfFragments() const; + + /** + * @brief Returns a pointer to the first fragment in LID image. + */ + void const * getFirstFragment() const; + + /* The binary will be organized in the following way: + * Byte 1: Major version number (MM) + * Byte 2: Minor version number (mm) + * Byte 3: N number of fragments in the file (NN) + * Byte 4-EOF: Fragments of the form: + * FRAGMENT_SIZE Byte 1: X number of bytes in fragment data + * section. (XX) + * INDEX_OFFSET Byte 2-3: Each BPM's config section is unique + * to itself. So, during the update + * the contents of a BPM's config data + * will be dumped into a buffer. + * These two bytes will be used as an + * offset into that buffer from which + * overwritting will take place. + * (IN DX) + * DATA_BYTES Byte 4-X: Fragment data bytes to be written + * at the INDEX_OFFSET in the dumped + * config data buffer. (DD) + * + * Example file output: + * 01 05 01 04 01 28 6a 14 31 80 + * MM mm NN XX IN DX DD DD DD DD + */ + typedef struct config_image_header + { + uint8_t iv_versionMajor; + uint8_t iv_versionMinor; + uint16_t iv_numberOfFragments; + } config_image_header_t; + +private: + + // Pointer to the LID image allocated outside of the class + void * const iv_lidImage; + + // The size of the LID image. + size_t iv_lidImageSize; +}; + +class Bpm +{ + /* + * The Bpm can either be in Bootstrap Loader (BSL) mode or not. Many of + * member functions utilize BSL mode for the update procedure and must + * therefore be in BSL mode to succeed. Other functions perform operations + * that will not work in BSL mode since that mode is strictly for updating + * the device and turns of some functionality while in that mode. The "mode" + * the BPM must be in is given in the function brief description. + */ +public: + + + explicit Bpm(const TARGETING::TargetHandle_t i_nvdimm); + + // Force User to supply a nvdimm target. + Bpm() = delete; + + /** + * @brief Runs the BPM firmware update using the given image. + * + * @param[in] i_image The BPM firmware image. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t runUpdate(BpmFirmwareLidImage i_fwImage, + BpmConfigLidImage i_configImage); + + /** + * @brief At most, one full update retry should occur in some + * circumstances. If one of those occurances happens then the + * member iv_attemptAnotherUpdate will be set to true. Otherwise, it + * will remain false. + * + * @return bool true if another update should be attempted. + * Otherwise, false. + */ + bool attemptAnotherUpdate(); + + /** + * @brief Returns if an update has been attempted on this BPM. + * + * @return bool true if an update has been attempted before. + * Otherwise, false. + */ + bool hasAttemptedUpdate(); + + /** + * @brief returns the nvdimm that is associated with this BPM. + */ + const TARGETING::TargetHandle_t getNvdimm(); + +private: + + // The nvdimm whose battery firmware will be updated. + const TARGETING::TargetHandle_t iv_nvdimm; + + // The Bootstrap Loader version of the BPM + uint8_t iv_bslVersion; + + // The firmware address for the BPM image can be either 0x8000 or 0xA000. + // This member will keep track of which one it is. + uint16_t iv_firmwareStartAddress; + + // Keeps track of if the update should be attempted again. + bool iv_attemptAnotherUpdate; + + // Buffers for the segment data in case another update attempt is needed. + // If the first update fails there won't be any running firmware on the + // device which is required to dump the segment data. + uint8_t iv_segmentD[SEGMENT_SIZE]; + uint8_t iv_segmentB[SEGMENT_SIZE]; + + // Keeps track if the segments have been merged with the flash image data + // yet. + bool iv_segmentDMerged; + bool iv_segmentBMerged; + + // Keeps track of if an update has been attempted at least once. + bool iv_updateAttempted; + + /** + * @brief Determines if another update attempt should occur for this BPM. + */ + void setAttemptAnotherUpdate(); + + /** + * @brief Gets the BSL version from the BPM and sets the iv_bslVersion + * member. Only needs to be called once. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t readBslVersion(); + + /** + * @brief Gets the Firmware version from the BPM + * + * @param[out] o_fwVersion The firmware version currently on the BPM. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t getFwVersion(uint16_t & o_fwVersion) const; + + /** + * @brief This function issues a command to the BPM using a payload as the + * means of sending the command. + * + * @param[in] i_command The BSP command to send to the BPM. + * @param[in] i_payload The payload to write to the + * BPM_REG_PAYLOAD_START register. + * @param[in] i_opType The operation type of the command. Must be one + * of the COMMAND_STATUS_REGISTER_OP_TYPES + * + * @param[in] i_msDelay How long to wait before the response from the + * BPM should be checked. Default 1 ms. If a delay + * of 0 ms is given then the response will not be + * read and it is the caller's responsibilty to + * check the response status. See COMMAND enum for + * required delays. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t issueCommand(uint8_t i_command, + payload_t i_payload, + uint8_t i_opType, + int i_msDelay = 1); + + /** + * @brief This function issues a BSP command to the BPM by setting up a + * payload containing only that command and then calling the + * issueCommand function that accepts a payload as an argument. + * + * NOTE: Since the BSP command is not a BSL command, it doesn't need + * to be formatted as a BSL payload but it still must be written to + * the BPM_REG_PAYLOAD_START register. + * + * @param[in] i_bspCommand The BSP command to send to the BPM. + * @param[in] i_command The BCL command to be written to the + * BPM_REG_PAYLOAD_START register. Must be one + * of the BCL_ commands. + * @param[in] i_opType The operation type of the BSP command. Must + * be a COMMAND_STATUS_REGISTER_OP_TYPES + * + * @param[in] i_msDelay How long to wait before the response from the + * BPM should be checked. Default 1 ms. If a delay + * of 0 ms is given then the response will not be + * read and it is the caller's responsibilty to + * check the response status. See COMMAND enum for + * required delays. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t issueCommand(uint8_t i_bspCommand, + uint8_t i_command, + uint8_t i_opType, + int i_msDelay = 1); + + /** + * @brief This function checks if the BPM has entered update mode + * + * @return errlHndl_t nullptr on success. + * Otherwise, pointer to an errlEntry. + */ + errlHndl_t inUpdateMode(); + + /** + * @brief Send the command to the BPM to enter update mode + * + * @return errlHndl_t nullptr if no errors occurred during command + * execution. Otherwise, pointer to an errlEntry. + */ + errlHndl_t enterUpdateMode(); + + /** + * @brief Send the command to the BPM to exit update mode + * + * @return errlHndl_t nullptr if no errors occurred during command + * execution. Otherwise, pointer to an errlEntry. + */ + errlHndl_t exitUpdateMode(); + + /** + * @brief Executes the firmware portion of the BPM update. + * + * @param[in] i_image The BPM firmware LID image to apply to the BPM. + * + * @return errlHndl_t nullptr if no errors occurred. + * Otherwise, pointer to an errlEntry. + */ + errlHndl_t updateFirmware(BpmFirmwareLidImage i_image); + + /** + * @brief Helper function that executes the firmware portion of the BPM + * update by calling all necessary functions in order. + * + * @param[in] i_image The BPM firmware LID image to apply to the BPM. + * + * @return errlHndl_t nullptr if no errors occurred. + * Otherwise, pointer to an errlEntry. + */ + errlHndl_t runFirmwareUpdates(BpmFirmwareLidImage i_image); + + /** + * @brief Executes the config portion of the BPM update. + * + * @return errlHndl_t nullptr on success. Otherwise, an Error. + */ + errlHndl_t updateConfig(); + + /** + * @brief Helper function that executes the config portion of the BPM + * update by calling all necessary functions in order. + * + * @param[in] i_image The BPM config LID image to apply to the BPM. + * + * @return errlHndl_t nullptr on success. Otherwise, an Error. + */ + errlHndl_t runConfigUpdates(BpmConfigLidImage i_image); + + /** + * @brief Commands the BPM to enter BSL mode to allow for BSL commands to be + * executed. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t enterBootstrapLoaderMode(); + + /** + * @brief Creates a valid BSL payload given a firmware_image_block_t. + * + * @param[out] o_payload The BSL payload + * @param[in] i_block A pointer to a firmware image block. + * @param[in] i_command The BSL command to be included with the payload + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t setupPayload(payload_t & o_payload, + const firmware_image_block_t * i_block, + uint8_t i_command); + + /** + * @brief Creates a valid BSL payload given a BSL command, address, and + * optionally data to include with the command. This function is used + * to create firmware_image_block_t objects which are then passed + * onto the version of setupPayload that turns them into payloads. + * + * @param[out] o_payload The BSL payload + * @param[in] i_command The BSL command to be included with the payload + * @param[in] i_address The address to execute the command from. This + * will be zero or the address to execute the + * command from. + * @param[in] i_data The array of data to be included with the BSL + * command. Default nullptr. + * @param[in] i_length Length of the i_data array parameter. Default 0. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t setupPayload(payload_t & o_payload, + uint8_t i_command, + uint16_t i_address, + const uint8_t i_data[] = nullptr, + size_t i_length = 0); + + /** + * @brief This function unlocks the BPM. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t unlockDevice(); + + /** + * @brief This function will send the command to reset the BPM. This will + * exit BSL mode if the BPM was in that mode. + * + * @return errlHndl_t nullptr on success. Otherwise, pointer to an + * errlEntry. + */ + errlHndl_t resetDevice(); + + /** + * @brief Write to the BPM register via the SCAP registers + * + * @param[in] i_reg The BPM register to write to. + * + * @param[in] i_data The data to write to the given register. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeViaScapRegister(uint8_t i_reg, uint8_t i_data); + + /** + * @brief Reads the BPM register via the SCAP registers + * + * @param[in] i_reg The BPM register to read from. + * + * @param[in/out] io_data The data that was in the given register. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t readViaScapRegister(uint8_t i_reg, uint8_t & io_data); + + /** + * @brief Disables write protection on the BPM by sending the password + * sequence to I2C_REG_PROTECT + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t disableWriteProtection(); + + /** + * @brief Many operations performed on the BPM require the magic registers + * to have specific values written in them. This function acts as a + * helper to facilitate that process. + * + * NOTE: Write protection on the BPM must be disabled, otherwise + * this function will fail. + * + * @param[in] i_magicValues The pair of magic values to be written to + * BPM_MAGIC_REG1 and BPM_MAGIC_REG2 + * respectively. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeToMagicRegisters( + uint8_t const (&i_magicValues)[NUM_MAGIC_REGISTERS]); + + /** + * @brief Switches the page on the BPM to the given page. This function + * must be executed only after the segment read magic values have + * been written to the BPM's magic registers. + * + * @param[in] i_segmentCode The segment code that corresponds to the + * page to switch to on the BPM. + * + * @return errlHndl_t nullptr on success. Otherwise, an error + * + */ + errlHndl_t switchBpmPage(uint16_t i_segmentCode); + + /** + * @brief Dumps the given segment data from the BPM. CANNOT be in BSL mode. + * + * @param[in] i_segmentCode The segment code that corresponds to the + * segment to dump from the BPM. + * + * @param[out] o_buffer A pointer to the buffer to fill with segment + * data. Must be SEGMENT_SIZE in size. + * + * @return errlHndl_t nullptr on success. Otherwise, an error + * + */ + errlHndl_t dumpSegment(uint16_t i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]); + + /** + * @brief Merges the segment data dumped from the BPM with the segment data + * fragments present in the BpmConfigLidImage that correspond to the + * given segment code. + * + * @param[in] i_configImage The image that holds the fragments of + * segment data. + * + * @param[in] i_segmentCode The segment code that corresponds to the + * segment to dump from the BPM. + * + * @param[out] o_buffer The merged segment data for the BPM. + * Must be SEGMENT_SIZE in length. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t mergeSegment(BpmConfigLidImage i_configImage, + uint16_t i_segmentCode, + uint8_t (&o_buffer)[SEGMENT_SIZE]); + + /** + * @brief Commands the BPM to erase the segment data on the BPM using the + * given segment code to tell it which to erase. + * The BPM must be in BSL mode for this function to work. + * + * @param[in] i_segmentCode The segment from the config data section to + * erase. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t eraseSegment(uint16_t i_segmentCode); + + /** + * @brief Writes the segment data from the buffer to the BPM using the + * given segment code to determine which segment the data belongs + * to. The BPM must be in BSL mode for this function to work. + * + * @param[in] i_buffer The segment data to write to the BPM. + * + * @param[in] i_segmentCode The segment from the config data section the + * data belongs to. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t writeSegment(uint8_t const (&i_buffer)[SEGMENT_SIZE], + uint16_t i_segmentCode); + + /** + * @brief Dumps segment D and B data from the BPM and merges it with the + * data from the config image to create the unique updated segments + * for this BPM. The BPM CANNOT be in BSL mode for this function to + * work because the data is dumped using SCAP registers. There must + * also be working firmware on the device otherwise this will fail. + * + * @param[in] i_configImage The config image that has the fragments to + * merge into the BPM's existing segment data. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t preprocessSegments(BpmConfigLidImage i_configImage); + + /** + * @brief Verifies that the data written into the flash on the BPM is what + * was sent by hostboot in a payload. + * + * @param[in] i_payload The payload that was just sent to the BPM to + * be verified. + * + * @param[in] i_dataLength The length of the data section of the + * payload. + * + * @param[in] o_status The status code returned from the BPM. + * A status of 0 indicates success, all other + * values are a failure. + * + * @return errlHndl_t nullptr if no errors. Otherwise, an error. + */ + errlHndl_t verifyBlockWrite(payload_t i_payload, + uint8_t i_dataLength, + uint8_t & o_status); + + /** + * @brief Attempts a BSL_RX_DATA_BLOCK command up to three times by calling + * blockWriteRetry. + * + * @param[in] i_payload The payload containing the BSL_RX_DATA_BLOCK + * command and the data to be attempted to be + * written. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t blockWrite(payload_t i_payload); + + /** + * @brief Attempts a BSL_RX_DATA_BLOCK command up to three times. + * + * @param[in] i_payload The payload containing the BSL_RX_DATA_BLOCK + * command and the data to be attempted to be + * written. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t blockWriteRetry(payload_t i_payload); + + /** + * @brief A helper function used to wait for the command status bit to reset + * after a command is executed. + * + * @param[in] i_commandStatus The command status register union made + * by the caller to identify the type of + * command that was sent. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t waitForCommandStatusBitReset( + command_status_register_t i_commandStatus); + + errlHndl_t verifyGoodBpmState(); + + /** + * @brief Helper function for the SCAP register functions that will poll + * the busy bit in SCAP_STATUS until it is zero. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t waitForBusyBit(); + + /** + * @brief Starting with BSL version 1.4 it is necessary to check the CRC of + * the firmware image once it has been written to the BPM. If this + * is not done or fails to succeed then the firmware image will not + * be loaded and executed by the BPM. If the CRC check fails then + * the update must be attempted again. + * Must be in BSL mode. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t checkFirmwareCrc(); + + /** + * @brief After a command is sent to the BPM to request info from it this + * function processes the response and returns it to the caller. + * A response packet can only be received once per command sent to + * the BPM. Which means that the caller must resend the command + * again to get another response packet. Simply calling the function + * repeatedly will not work. BPM must be in BSL mode. + * + * @param[in] o_responseData The buffer to be filled with the + * response data from the BPM. + * + * @param[in] i_responseSize The size of the buffer to be filled. + * + * @return errlHndl_t nullptr on success. Otherwise, an error. + */ + errlHndl_t getResponse(uint8_t * o_responseData, + uint8_t i_responseSize); + + + /** + * @brief 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 handleMultipleErrors(errlHndl_t& io_returnErrl, + errlHndl_t& io_secondErrl); + + /** + * @brief Calculates the CRC16 bytes for the BSL payload. This CRC differs + * from the NVDIMM CRC calculation in that the initial value is + * 0xFFFF instead of 0x0000. + * + * NOTE: To calculate a correct CRC for the BSL payload the SYNC_BYTE + * must be included in the payload despite the fact that it + * should be removed from the payload before sending to the BPM + * because the NVDIMM sends the SYNC_BYTE automatically. + * + * @param[in] i_ptr A pointer to the start of the data to calculate the + * CRC for. + * @param[in] i_size This size of the data pointed at by i_ptr. + * + * @return uint16_t The CRC bytes. + */ + uint16_t crc16_calc(const void* const i_ptr, int i_size); + + +}; + +typedef std::vector<Bpm> bpmList_t; + +/** + * @brief Runs the firmware and config updates on the list of BPMs given. + * + * @param[in] i_16gb_BPMs The list of BPMs sitting on 16gb NVDIMMs that + * potentially need to be updated. + * + * @param[in] i_32gb_BPMs The list of BPMs sitting on 32gb NVDIMMs that + * potentially need to be updated. + * + * @param[in] i_16gb_fwImage The firmware image associated with BPMs sitting + * on 16gb NVDIMMs. + * + * @param[in] i_32gb_fwImage The firmware image associated with BPMs sitting + * on 32gb NVDIMMs. + * + * @param[in] i_16gb_configImage The configuration data associated with BPMs + * sitting on 16gb NVDIMMs. + * + * @param[in] i_32gb_configImage The configuration data associated with BPMs + * sitting on 32gb NVDIMMs. + * + */ +void runBpmUpdates(bpmList_t * const i_16gb_BPMs, + bpmList_t * const i_32gb_BPMs, + BpmFirmwareLidImage * const i_16gb_fwImage, + BpmFirmwareLidImage * const i_32gb_fwImage, + BpmConfigLidImage * const i_16gb_configImage, + BpmConfigLidImage * const i_32gb_configImage); + +}; // end of BPM namespace +}; // end of NVDIMM namespace + +#endif + |