diff options
| author | Matt Derksen <mderkse1@us.ibm.com> | 2019-02-01 17:08:05 -0600 |
|---|---|---|
| committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2019-03-29 10:21:40 -0500 |
| commit | f5ab52ab71170611fbf1bdc2afd6321b13d931ad (patch) | |
| tree | 639ecd5ab61a9b902c5ba3ecba51b7d2682fdcdd /src | |
| parent | 33e7c6de0769ae7f408e64995e7976717ad47653 (diff) | |
| download | talos-hostboot-f5ab52ab71170611fbf1bdc2afd6321b13d931ad.tar.gz talos-hostboot-f5ab52ab71170611fbf1bdc2afd6321b13d931ad.zip | |
NVDIMM update code
This code does the actual NVDIMM update.
Refer to JEDEC BAEBI spec for details
https://www.jedec.org/standards-documents/docs/jesd245a
Change-Id: I8ac6f7695b8a056bb6d8e627d89b4e5e06398f1d
RTC:202536
CMVC-Prereq: 1080592
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71680
Reviewed-by: TSUNG K. YEUNG <tyeung@us.ibm.com>
Reviewed-by: Corey V. Swenson <cswenson@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H | 20 | ||||
| -rw-r--r-- | src/include/usr/util/utilmclmgr.H | 1 | ||||
| -rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.C | 5 | ||||
| -rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.H | 18 | ||||
| -rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.C | 1927 | ||||
| -rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.H | 237 | ||||
| -rwxr-xr-x | src/usr/isteps/nvdimm/nvdimmdd.C | 3 | ||||
| -rw-r--r-- | src/usr/isteps/nvdimm/runtime/nvdimm_rt.C | 15 | ||||
| -rw-r--r-- | src/usr/util/utilmclmgr.C | 1 |
9 files changed, 2000 insertions, 227 deletions
diff --git a/src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H b/src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H index f0da5af87..142d36bd9 100644 --- a/src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H +++ b/src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H @@ -74,6 +74,16 @@ enum nvdimmModuleId NVDIMM_CHECK_READY = 0x18, NOTIFY_NVDIMM_PROTECTION_CHG = 0x19, NVDIMM_RUN_UPDATE = 0x1A, + UPDATE_IMAGE = 0x1B, + UPDATE_IMAGE_DATA = 0x1C, + CHANGE_FW_UPDATE_MODE = 0x1D, + WAIT_FW_OPS_COMPLETE = 0x1E, + BYTE_REGION_BLOCK_TRANSFER = 0x1F, + VALIDATE_FW_HEADER = 0x20, + COMMIT_FW_REGION = 0x21, + CLEAR_FW_DATA_BLOCK = 0x22, + VALIDATE_FW_IMAGE = 0x23, + WAIT_FW_OPS_BLOCK_RECEIVED = 0x24, }; /** @@ -114,6 +124,16 @@ enum nvdimmReasonCode NVDIMM_NOT_READY = NVDIMM_COMP_ID | 0x1B, // NVDIMM not ready for host to access NVDIMM_NULL_FIRMWARE_REQUEST_PTR = NVDIMM_COMP_ID | 0x1C, // Firmware request is NULL NVDIMM_UNSUPPORTED_NVDIMM_TYPE = NVDIMM_COMP_ID | 0x1D, // Unsupported NVDIMM type for update + NVDIMM_OPERATION_IN_PROGRESS = NVDIMM_COMP_ID | 0x1E, // NV controller is busy + NVDIMM_CHECKSUM_ERROR = NVDIMM_COMP_ID | 0x1F, // Checksum error between host and nv calculated + NVDIMM_ZERO_TOTAL_REGIONS = NVDIMM_COMP_ID | 0x20, // Zero write regions calculated + NVDIMM_UPDATE_MODE_UNCHANGED = NVDIMM_COMP_ID | 0x21, // Unable to change update mode + NVDIMM_FW_OPS_IN_PROGRESS_TIMEOUT = NVDIMM_COMP_ID | 0x22, // Operations In Progress timeout + NVDIMM_DATA_SIZE_TOO_LARGE = NVDIMM_COMP_ID | 0x23, // Trying to write too much data + NVDIMM_DATA_SIZE_INVALID = NVDIMM_COMP_ID | 0x24, // Data size is invalid + NVDIMM_BLOCK_NOT_RECEIVED = NVDIMM_COMP_ID | 0x25, // Block data not received + NVDIMM_FW_OPS_NOT_SUCCESSFUL = NVDIMM_COMP_ID | 0x26, // Unsuccessful Firmware Operation + NVDIMM_BASE_SERVICES_NOT_READY = NVDIMM_COMP_ID | 0x27, // spBaseServices not ready for LID access }; enum UserDetailsTypes diff --git a/src/include/usr/util/utilmclmgr.H b/src/include/usr/util/utilmclmgr.H index 4f799a59b..357578d9f 100644 --- a/src/include/usr/util/utilmclmgr.H +++ b/src/include/usr/util/utilmclmgr.H @@ -72,6 +72,7 @@ extern const ComponentID g_MclCompId; extern const ComponentID g_PowervmCompId; extern const ComponentID g_OpalCompId; extern const ComponentID g_UcdCompId; +extern const ComponentID g_NvdimmCompId; // @enum Permission Types for MCL Component enum class CompFlags : uint16_t diff --git a/src/usr/isteps/nvdimm/nvdimm.C b/src/usr/isteps/nvdimm/nvdimm.C index a41cd7f18..5d16433e8 100644 --- a/src/usr/isteps/nvdimm/nvdimm.C +++ b/src/usr/isteps/nvdimm/nvdimm.C @@ -62,11 +62,6 @@ namespace NVDIMM #define NVDIMM_SET_USER_DATA_2_TIMEOUT(left_32_polled, right_32_timeout) \ NVDIMM_SET_USER_DATA_1(left_32_polled, right_32_timeout) -#define ADDRESS(uint16_address) \ - uint16_address & 0x00FF - -#define PAGE(uint16_address) \ - (uint16_address >> 8) & 0x000F typedef struct ops_timeoutInfo{ const char * desc; diff --git a/src/usr/isteps/nvdimm/nvdimm.H b/src/usr/isteps/nvdimm/nvdimm.H index 2d4a223f3..4d97a9c66 100644 --- a/src/usr/isteps/nvdimm/nvdimm.H +++ b/src/usr/isteps/nvdimm/nvdimm.H @@ -276,6 +276,13 @@ enum i2cReg : uint16_t TYPED_BLOCK_DATA_OFFSET = 0x3E0, }; +// i2cReg macros +#define ADDRESS(uint16_address) \ + uint16_address & 0x00FF + +#define PAGE(uint16_address) \ + (uint16_address >> 8) & 0x000F + // Up to 10 pages per BAEBI Spec, // but use page 0 mostly enum page : uint8_t @@ -416,6 +423,17 @@ errlHndl_t nvdimmOpenPage(TARGETING::Target *i_nvdimm, uint8_t i_page); * the error log. */ errlHndl_t nvdimmPollStatus(TARGETING::Target *i_nvdimm, ops_id i_ops_id, uint32_t &o_poll); + + +/** + * @brief This function sets the energy supply policy to device-managed + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmSetESPolicy(TARGETING::Target* i_nvdimm); } //End NVDIMM namespace diff --git a/src/usr/isteps/nvdimm/nvdimm_update.C b/src/usr/isteps/nvdimm/nvdimm_update.C index 27ab0be58..7dea83740 100644 --- a/src/usr/isteps/nvdimm/nvdimm_update.C +++ b/src/usr/isteps/nvdimm/nvdimm_update.C @@ -27,9 +27,18 @@ #include <isteps/nvdimm/nvdimm.H> #include <isteps/nvdimm/nvdimmreasoncodes.H> +#include <initservice/istepdispatcherif.H> // sendProgressCode +#include <util/utilmclmgr.H> // secure LID manager #include <errl/errlmanager.H> #include <devicefw/userif.H> #include <vpd/spdenums.H> +#include <sys/time.h> + +// Unique tracing for nvdimm update process +const char NVDIMM_UPD[] = "NVDIMM_UPD"; +trace_desc_t* g_trac_nvdimm_upd = NULL; +TRAC_INIT(&g_trac_nvdimm_upd, NVDIMM_UPD, 2*KILOBYTE); + // Easy macro replace for unit testing // #define TRACUCOMP(args...) TRACFCOMP(args) @@ -46,7 +55,8 @@ namespace NVDIMM */ inline void commitPredictiveNvdimmError(errlHndl_t & io_err) { - io_err->collectTrace(NVDIMM_COMP_NAME, 256); + io_err->collectTrace(NVDIMM_UPD, 512); + io_err->collectTrace(NVDIMM_COMP_NAME, 256); // for helper function traces if ( io_err->sev() < ERRORLOG::ERRL_SEV_PREDICTIVE ) { io_err->setSev( ERRORLOG::ERRL_SEV_PREDICTIVE ); @@ -54,27 +64,95 @@ inline void commitPredictiveNvdimmError(errlHndl_t & io_err) ERRORLOG::errlCommit(io_err, NVDIMM_COMP_ID); } +//////////////////////////////////////////////////////////////////////////////// +// Helper structs/enums for code update +// +// Refer to JEDEC BAEBI spec for details +// https://www.jedec.org/standards-documents/docs/jesd245a +//////////////////////////////////////////////////////////////////////////////// +// Definition of FIRMWARE_OPS_STATUS -- offset 0x71 +typedef union { + uint8_t whole; + struct + { + // [7:6] : reserved + uint8_t reserved : 2; + // [5] : the abort of the last fw operation failed + uint8_t fw_ops_abort_error : 1; + // [4] : the last fw operation was aborted by the host + uint8_t fw_ops_abort_success : 1; + // [3] : the last block has been received successfully by the NVDIMM + // and the host may proceed with sending the next block + uint8_t fw_ops_block_received : 1; + // [2] : the module is in FW update mode where firmware on the module + // can be changed. + // If cleared, firmware on the module cannot be changed + uint8_t fw_ops_update_mode : 1; + // [1] : the last firmware operation failed + uint8_t fw_ops_error : 1; + // [0] : the last firmware operation completed without any errors + uint8_t fw_ops_success : 1; + } PACKED; +} fw_ops_status_t; + +// Definition of FIRMWARE_OPS_CMD -- offset 0x4A +typedef union { + uint8_t whole; + struct + { + // [6-7] : Reserved for future use + uint8_t reserved : 2; + // [5] : Start a Validate Firmware Image operation + uint8_t start_validate_fw_image_op : 1; + // [4] : Start a Validate Firmware Header operation + uint8_t start_validate_fw_header_op : 1; + // [3] : Start a Commit Firmware operation + uint8_t start_commit_fw_op : 1; + // [2] : Start a Generate Firmware Checksum operation + uint8_t start_generate_fw_checksum_op : 1; + // [1] : Start a Clear Firmware operation + uint8_t start_clear_fw_op : 1; + // [0] : Enable / Disable firmware update mode. + // 0b0: fw update mode is disabled. + // 0b1: fw update mode is enabled. + uint8_t firmware_update_mode : 1; + } PACKED; +} fw_ops_cmd_t; + +// Definition of NVDIMM_CMD_STATUS0 -- offset 0x61 +typedef union { + uint8_t whole; + struct + { + // [7] : Firmware operations currently in progress + uint8_t firmware_ops_in_progress : 1; + // [6] : Arm operation in progress + uint8_t arm_in_progress : 1; + // [5] : Abort operation in progress + uint8_t abort_in_progress : 1; + // [4] : Erase operation in progress + uint8_t erase_in_progress : 1; + // [3] : Restore operation in progress + uint8_t restore_in_progress : 1; + // [2] : Catastrophic Save operation in progress + uint8_t catastrophic_save_in_progress : 1; + // [1] : Factory Default in progress + uint8_t factory_default_in_progress : 1; + // [0] : Operation in progress + uint8_t operation_in_progress : 1; + } PACKED; +} nvdimm_cmd_status0_t; + +// A code update block is composed of this many bytes +const uint8_t BYTES_PER_BLOCK = 32; + /////////////////////////////////////////////////////////////////////////////// // NVDIMM LID Image /////////////////////////////////////////////////////////////////////////////// -NvdimmLidImage::NvdimmLidImage(Util::LidId i_lidId, errlHndl_t& io_errHdl) +NvdimmLidImage::NvdimmLidImage(const void * i_lidImageAddr, size_t i_size) : + iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size) { - iv_lidImage = nullptr; - iv_lidImageSize = 0; - iv_ImageLoaded = false; - - iv_lidMgr = new UtilLidMgr(i_lidId); - io_errHdl = loadImage(); -} - -NvdimmLidImage::~NvdimmLidImage() -{ - errlHndl_t l_err = unloadImage(); - if (l_err) - { - ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); - } } uint32_t NvdimmLidImage::getType() @@ -83,8 +161,8 @@ uint32_t NvdimmLidImage::getType() if (iv_lidImageSize >= sizeof(nvdimm_image_header_t)) { - nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> - (iv_lidImage); + const nvdimm_image_header_t * pLid = + reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage); o_type = (uint32_t)pLid->module_mnfg_id_code << 16; o_type |= (uint32_t)pLid->module_product_id; } @@ -97,41 +175,67 @@ uint16_t NvdimmLidImage::getVersion() if (iv_lidImageSize >= sizeof(nvdimm_image_header_t)) { - nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> - (iv_lidImage); + const nvdimm_image_header_t * pLid = + reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage); o_version = pLid->controller_firmware_revision; } return o_version; } -void * NvdimmLidImage::getFlashImage() +const uint8_t * NvdimmLidImage::getHeaderAndSmartSignature(uint16_t & o_size) +{ + o_size = 0; + if (iv_lidImageSize > sizeof(nvdimm_image_header_t)) + { + const nvdimm_image_header_t * pLid = + reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage); + o_size = le16toh(pLid->SMART_digital_signature_size); + o_size += sizeof(nvdimm_image_header_t); + } + return reinterpret_cast<const uint8_t*>(iv_lidImage); +} + + +const void * NvdimmLidImage::getFlashImage() { void * o_image_ptr = nullptr; if (iv_lidImageSize > sizeof(nvdimm_image_header_t)) { - nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> - (iv_lidImage); + const nvdimm_image_header_t * pLid = + reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage); // make sure we don't point outside of lid memory // nvdimm flash image starts after the header and digital signature if ((sizeof(nvdimm_image_header_t) + le16toh(pLid->SMART_digital_signature_size)) < iv_lidImageSize) { - o_image_ptr = reinterpret_cast<uint8_t*>(iv_lidImage) + - sizeof(nvdimm_image_header_t) + - le16toh(pLid->SMART_digital_signature_size); + TRACUCOMP(g_trac_nvdimm_upd, + "NvdimmLidImage::getFlashImage() -> starts at offset 0x%X " + "(digital signature size: %d, header size: %d)", + sizeof(nvdimm_image_header_t) + + le16toh(pLid->SMART_digital_signature_size), + le16toh(pLid->SMART_digital_signature_size), + sizeof(nvdimm_image_header_t)); + + o_image_ptr = reinterpret_cast<void*>(const_cast<uint8_t *>( + reinterpret_cast<const uint8_t*>(iv_lidImage) + + sizeof(nvdimm_image_header_t) + + le16toh(pLid->SMART_digital_signature_size))); } } return o_image_ptr; } + size_t NvdimmLidImage::getFlashImageSize() { uint32_t o_flash_size = 0; if (iv_lidImageSize > sizeof(nvdimm_image_header_t)) { - nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> - (iv_lidImage); + const nvdimm_image_header_t * pLid = + reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage); + // Note: firmware_image_size does NOT include the Digital Signature + // (does include the 32-byte header though) o_flash_size = le32toh(pLid->firmware_image_size); o_flash_size -= sizeof(nvdimm_image_header_t); @@ -139,7 +243,7 @@ size_t NvdimmLidImage::getFlashImageSize() if ((o_flash_size + sizeof(nvdimm_image_header_t) + le16toh(pLid->SMART_digital_signature_size)) > iv_lidImageSize) { - TRACFCOMP(g_trac_nvdimm, + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"getFlashImageSize(): %ld flash size + %ld header + " "%ld digital signature is greater than %ld overall lid size", o_flash_size, sizeof(nvdimm_image_header_t), @@ -153,51 +257,14 @@ size_t NvdimmLidImage::getFlashImageSize() return o_flash_size; } -bool NvdimmLidImage::isImageLoaded() -{ - return iv_ImageLoaded; -} -/////////////////////////////////////////////////// -// Private member functions for NvdimmLidImage -/////////////////////////////////////////////////// -errlHndl_t NvdimmLidImage::loadImage() -{ - errlHndl_t l_err = nullptr; - if (!iv_ImageLoaded && (iv_lidMgr != nullptr)) - { - // @todo RTC 205015 -- need to use secure load - // iv_lidImage will point to memory allocated - // and controlled by iv_lidMgr - l_err = iv_lidMgr->getStoredLidImage(iv_lidImage, iv_lidImageSize); - if (l_err == nullptr) - { - // image successfully loaded into memory - iv_ImageLoaded = true; - } - } - return l_err; -} - -errlHndl_t NvdimmLidImage::unloadImage() -{ - errlHndl_t l_err = nullptr; - if (iv_ImageLoaded && (iv_lidMgr != nullptr)) - { - // use lidMgr to delete allocated memory for this lid - l_err = iv_lidMgr->releaseLidImage(); - iv_lidImage = nullptr; - iv_lidImageSize = 0; - iv_ImageLoaded = false; - } - return l_err; -} - /////////////////////////////////////////////////////////////////////////////// // NVDIMM Installed Image /////////////////////////////////////////////////////////////////////////////// NvdimmInstalledImage::NvdimmInstalledImage(TARGETING::Target * i_nvDimm) : iv_dimm(i_nvDimm), iv_version(INVALID_VERSION), - iv_manufacturer_id(INVALID_ID), iv_product_id(INVALID_ID) + iv_manufacturer_id(INVALID_ID), iv_product_id(INVALID_ID), + iv_timeout(INVALID_TIMEOUT), + iv_max_blocks_per_region(INVALID_REGION_BLOCK_SIZE) { // initialize to invalid values } @@ -215,7 +282,7 @@ errlHndl_t NvdimmInstalledImage::getType(uint32_t & o_type) DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_MANUFACTURER_ID)); if (l_err) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::getType()" + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::getType()" " nvdimm[%X] failed to read manufacturer ID", TARGETING::get_huid(iv_dimm)); iv_manufacturer_id = INVALID_ID; @@ -231,7 +298,7 @@ errlHndl_t NvdimmInstalledImage::getType(uint32_t & o_type) DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_PRODUCT_ID)); if (l_err) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::getType()" + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::getType()" " nvdimm[%X] failed to read product ID", TARGETING::get_huid(iv_dimm)); iv_product_id = INVALID_ID; @@ -245,38 +312,38 @@ errlHndl_t NvdimmInstalledImage::getType(uint32_t & o_type) return l_err; } -errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version) +errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version, + const bool i_force_recollect) { errlHndl_t l_err = nullptr; do { - if ((iv_version == INVALID_VERSION)) + if ((iv_version == INVALID_VERSION) || i_force_recollect) { // Return version in little-endian format uint8_t l_rev1 = 0xFF; uint8_t l_rev0 = 0xFF; - l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV1, l_rev1 ); + l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV0, l_rev0 ); if (l_err) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::" - "getVersion() nvdimm[%X] failed to read SLOT1_FWREV1", + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::" + "getVersion() nvdimm[%X] failed to read SLOT1_FWREV0", TARGETING::get_huid(iv_dimm)); iv_version = INVALID_VERSION; break; } - iv_version = (uint16_t)l_rev1 << 8; - - l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV0, l_rev0 ); + iv_version = (uint16_t)l_rev0 << 8; + l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV1, l_rev1 ); if (l_err) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::" - "getVersion() nvdimm[%X] failed to read SLOT1_FWREV0", + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::" + "getVersion() nvdimm[%X] failed to read SLOT1_FWREV1", TARGETING::get_huid(iv_dimm)); iv_version = INVALID_VERSION; break; } - iv_version |= (uint16_t)l_rev0; + iv_version |= (uint16_t)l_rev1; } } while (0); o_version = iv_version; @@ -284,6 +351,1425 @@ errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version) } +errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage) +{ + errlHndl_t l_err = nullptr; + // need to always disable this after it gets enabled + bool l_fw_update_mode_enabled = false; + do { + INITSERVICE::sendProgressCode(); + //////////////////////////////////////////////////////////////////////// + // Start of firmware update logic, section 9.7 in JESD245B + //////////////////////////////////////////////////////////////////////// + // 1. Validate module manufacturer ID and module product identifier + // Done before this was called, it is what selected i_lidImage + + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 2"); + // 2. Verify 'Operation In Progress' bit in the NVDIMM_CMD_STATUS0 + // register is cleared (ie. NV controller is NOT busy) + nvdimm_cmd_status0_t l_status; + l_err = nvdimmReadReg(iv_dimm, NVDIMM_CMD_STATUS0, l_status.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Read of NVDIMM_CMD_STATUS0 register on 0x%.8X NVDIMM failed", + TARGETING::get_huid(iv_dimm), l_status.whole); + break; + } + if (l_status.operation_in_progress) + { + TRACFCOMP(g_trac_nvdimm_upd,ERR_MRK"updateImage: " + "NV controller is busy (0x%08X) for NVDIMM 0x%.8X", + l_status.whole, TARGETING::get_huid(iv_dimm)); + /* + *@errortype + *@moduleid UPDATE_IMAGE + *@reasoncode NVDIMM_OPERATION_IN_PROGRESS + *@userdata1 NVDIMM Target Huid + *@userdata2 NVDIMM_CMD_STATUS0 + *@devdesc NV controller is busy so no update can run + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + UPDATE_IMAGE, + NVDIMM_OPERATION_IN_PROGRESS, + TARGETING::get_huid(iv_dimm), + l_status.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace( NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + break; + } + + // 3. Make sure we start from a cleared state + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 3"); + l_err = clearFwOpsStatus(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Unable to clear firmware ops status for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 4. Enable firmware update mode + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 4"); + l_err = changeFwUpdateMode(FW_UPDATE_MODE_ENABLED); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Unable to enable firmware update mode for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + // Set this flag so we will disable the update mode on error + l_fw_update_mode_enabled = true; + + // 5. Clear the Firmware Operation status + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 5"); + l_err = clearFwOpsStatus(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Unable to clear firmware ops status for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 6. Clear the firmware data block to ensure there is no residual data. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 6"); + l_err = clearFwDataBlock(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Step 6. clearFwDataBlock() failed for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 7. Send the first part (header + SMART signature) of + // the Firmware Image Data + + // 7a. Write the TYPED_BLOCK_DATA register with 0x1 (Firmware Image Data) + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7a"); + l_err = nvdimmWriteReg(iv_dimm, TYPED_BLOCK_DATA, 0x01); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Write of 0x01 to TYPED_BLOCK_DATA failed for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 7b. Write the BLOCK_ID, REGION_ID0 and REGION_ID1 registers with value 0. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7b"); + l_err = nvdimmWriteReg(iv_dimm, BLOCK_ID, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Write of 0x00 to BLOCK_ID failed for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Write of 0x00 to REGION_ID0 failed for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Write of 0x00 to REGION_ID1 failed for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 7c. Send the header (first 32 bytes of the image) + + // SMART digital signature to multiple blocks of + // TYPED_BLOCK_DATA_BYTE0 - TYPED_BLOCK_DATA_BYTE31. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7c"); + uint16_t header_plus_signature_size = 0; + const uint8_t * pHeaderAndDigitalSignature = + i_lidImage->getHeaderAndSmartSignature(header_plus_signature_size); + l_err = byteRegionBlockTransfer( pHeaderAndDigitalSignature, + header_plus_signature_size, false ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: Unable to send " + "header and digital signature update for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 7d. Validate the transfer by checksum + + // 7d.i. Host calculate checksum using the crc16 algo in JESD245B + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.i."); + uint16_t hostCksm = crc16(pHeaderAndDigitalSignature, + header_plus_signature_size); + + // 7d.ii. Set bit 1 (Clear the FIRMWARE_OPS_STATUS register) in + // the NVDIMM_MGT_CMD1 register. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.ii."); + l_err = clearFwOpsStatus(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "Unable to clear Firmware Operations Status for NVDIMM %.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 7d.iii - 7d.vii + // Write the FIRMWARE_OPS_CMD register with value 0x04 + // to start a Generate Firmware Checksum operation. + // Compare the module generated value with the host value. + // Abort the workflow if they do not match. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.iii."); + uint16_t nvCksm; + l_err = calcAndGetCksm(nvCksm); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: calcAndGetCksm() " + "failed for 0x%.8X nvdimm", TARGETING::get_huid(iv_dimm)); + break; + } + if (hostCksm != nvCksm) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: " + "NVDIMM 0x%.8X: data checksums mismatch (calc host: 0x%X " + "and nv: 0x%X) for first part (header + SMART signature)", + TARGETING::get_huid(iv_dimm), hostCksm, nvCksm); + /* + *@errortype + *@moduleid UPDATE_IMAGE + *@reasoncode NVDIMM_CHECKSUM_ERROR + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Host checksum calculated + *@userdata2[16:31] NV checksum returned + *@userdata2[32:47] size of data for checksum + *@devdesc Checksum failure when transferring region + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + UPDATE_IMAGE_DATA, + NVDIMM_CHECKSUM_ERROR, + TARGETING::get_huid(iv_dimm), + FOUR_UINT16_TO_UINT64( + hostCksm, nvCksm, + header_plus_signature_size, + 0x0000), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace( NVDIMM_COMP_NAME, 256 ); + + // maybe some data was altered on the NV controller + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + // possible code issue + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + break; + } + + // 8. Command the module to validate that the firmware image is valid + // for the module based on the header. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 8"); + l_err = validateFwHeader(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: firmware header " + "for 0x%.8X nvdimm cannot be validated", + TARGETING::get_huid(iv_dimm)); + break; + } + else + { + TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X " + "updated with valid header + SMART signature", + TARGETING::get_huid(iv_dimm)); + } + + // 9. Commit the first firmware data region + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 9"); + l_err = commitFwRegion(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: commitFwRegion() " + "of first data region failed for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 10. Send and commit the remaining firmware data in + // REGION_BLOCK_SIZE regions + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10"); + l_err = updateImageData(i_lidImage); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "updateImageData() failed sending full image for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + + // 11. Validate the firmware data + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 11"); + l_err = validateFwImage(); + if ( l_err ) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "validateFwImage() failed for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + else + { + TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X " + "updated with valid image data", TARGETING::get_huid(iv_dimm)); + } + + // 12. Disable firmware update mode + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 12"); + l_fw_update_mode_enabled = false; // don't retry the disable on error + l_err = changeFwUpdateMode(FW_UPDATE_MODE_DISABLED); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "Unable to disable FW update mode for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + + // There are two slots for firmware. + // Slot 0 is read-only hence this procedure only updates slot 1. + // At the end of the update, we should explicitly select slot 1, + // by writing 0x1 to FW_SLOT_INFO, to make sure the module is + // running on the latest code. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: Switch to slot 1"); + l_err = nvdimmWriteReg(iv_dimm, FW_SLOT_INFO, 0x01); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "Unable to switch to slot 1 for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + + // Reset controller to activate new firmware + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: resetController"); + l_err = resetController(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "Unable to activate new firmware for 0x%.8X nvdimm", + TARGETING::get_huid(iv_dimm)); + break; + } + + // force a recollect of the version of code installed + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: verify new code version running"); + uint16_t new_version = INVALID_VERSION; + l_err = getVersion(new_version, true); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "Unable to verify NVDIMM 0x%.8X was successfully updated", + TARGETING::get_huid(iv_dimm)); + break; + } + else + { + TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X of type " + "0x%04X%04X now running new code level 0x%04X", + TARGETING::get_huid(iv_dimm), + le16toh(iv_manufacturer_id), le16toh(iv_product_id), + le16toh(new_version)); + + } + + } while (0); + + // If update operation is aborted, we need to disable update mode + if (l_fw_update_mode_enabled) + { + TRACFCOMP(g_trac_nvdimm_upd, "updateImage: update was aborted, so disable FW_UPDATE_MODE"); + errlHndl_t l_err2 = changeFwUpdateMode(FW_UPDATE_MODE_DISABLED); + if (l_err2) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: " + "Attempt to disable Firmware Update Mode of 0x%.8X failed," + " RC=0x%X", TARGETING::get_huid(iv_dimm), + ERRL_GETRC_SAFE(l_err2)); + + // Should always have an error here, so link the two errors together + // Just a safety-check so we don't dereference a nullptr + if (l_err) + { + l_err2->plid(l_err->plid()); + l_err2->collectTrace(NVDIMM_COMP_NAME, 256); + l_err2->collectTrace(NVDIMM_UPD, 256); + errlCommit(l_err2, NVDIMM_COMP_ID); + } + else + { + // this path shouldn't get run + l_err = l_err2; + } + } + } + + return l_err; +} + + +// HELPER FUNCTIONS FOR UPDATE +errlHndl_t NvdimmInstalledImage::updateImageData(NvdimmLidImage * i_lidImage) +{ + errlHndl_t l_err = nullptr; + do + { + uint8_t blocks_per_region; + l_err = getBlocksPerRegion(blocks_per_region); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "getBlocksPerRegion() failed on NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // calculate the region size which equals block size * blocks per region + uint16_t region_size = BYTES_PER_BLOCK * blocks_per_region; + const uint8_t * fw_img_data = + reinterpret_cast<const uint8_t*>(i_lidImage->getFlashImage()); + size_t fw_img_data_len = i_lidImage->getFlashImageSize(); + + // Get the number of regions required for the amount of data. + // This sets the upper bound for the REGION_ID + uint16_t fw_img_total_regions = fw_img_data_len/region_size; + if (fw_img_data_len % region_size > 0) + { + // account for a partial region + fw_img_total_regions++; + } + if (fw_img_total_regions == 0) + { + /* + *@errortype + *@moduleid UPDATE_IMAGE_DATA + *@reasoncode NVDIMM_ZERO_TOTAL_REGIONS + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Firmware image size + *@userdata2[16:31] region_size + *@devdesc Firmware image size is not large enough + * (needs to be at least region_size) + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + UPDATE_IMAGE_DATA, + NVDIMM_ZERO_TOTAL_REGIONS, + TARGETING::get_huid(iv_dimm), + TWO_UINT16_ONE_UINT32_TO_UINT64( + fw_img_data_len, + region_size, + 0x00000000), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace( NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + break; + } + + // 10.a Write the BLOCK_ID, REGION_ID0 and REGION_ID1 registers + // with the appropriate value starting at 0 + TRACUCOMP(g_trac_nvdimm_upd, + "updateImage: Sending %d total regions of size %d bytes (total size: 0x%08X)", + fw_img_total_regions, region_size, fw_img_data_len); + l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Write of 0x00 to REGION_ID0 failed on NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Write of 0x00 to REGION_ID1 failed on NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + uint16_t region = 0; + while (region < fw_img_total_regions) + { + if (region % 10 == 0) + { + INITSERVICE::sendProgressCode(); + } + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.a - region 0x%04X", + region); + // For each region, start with BLOCK_ID of 0. BLOCK_ID + // is controlled in byteRegionBlockTransfer(). + l_err = nvdimmWriteReg(iv_dimm, BLOCK_ID, 0x00); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Write of 0x00 to BLOCK_ID failed on NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // Update REGION_ID0(lsb) and REGION_ID1(msb) + uint8_t l_data = region & 0x00FF; + l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Write of 0x%02X to REGION_ID0 failed on NVDIMM 0x%.8X", + l_data, TARGETING::get_huid(iv_dimm)); + break; + } + l_data = (((region & 0xFF00) >> 8) & 0x00FF); + l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d write of 0x%02X to REGION_ID1 failed on NVDIMM " + "0x%.8X", region, l_data, TARGETING::get_huid(iv_dimm)); + break; + } + + // 10.b Clear the firmware data block to ensure there is no + // residual data. + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.b"); + l_err = clearFwDataBlock(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d: clearFwDataBlock() failed on NVDIMM 0x%.8X", + region, TARGETING::get_huid(iv_dimm)); + break; + } + + // 10.c Send the data over to nvdimm region by region. + // Check if the remaining data is in the multiple of the region size + // If not, have the function to calculate the actual amount of data + // to send over to the nvdimm + const uint8_t * pImageData = &(fw_img_data[(region*region_size)]); + uint16_t data_len = region_size; + if ((fw_img_data_len-(region*region_size)) < region_size) + { + // send a final partial region + data_len = fw_img_data_len-(region*region_size); + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.c - send partial region (%d length)", data_len); + l_err = byteRegionBlockTransfer(pImageData, data_len, false); + } + else + { + // send a full region worth of data + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.c - send full region (%d length)", data_len); + l_err = byteRegionBlockTransfer(pImageData, data_len, true); + } + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d: byteRegionBlockTransfer() failed on NVDIMM 0x%.8X", + region, TARGETING::get_huid(iv_dimm)); + break; + } + + // 10.d-e After transferring each region, validate the transfer by checksum + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.d-e"); + uint16_t hostCksm = crc16(pImageData, data_len); + l_err = clearFwOpsStatus(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d: clearFwOpsStatus() failed on NVDIMM 0x%.8X", + region, TARGETING::get_huid(iv_dimm)); + break; + } + uint16_t nvCksm; + l_err = calcAndGetCksm(nvCksm); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d: calcAndGetCksm() failed on NVDIMM 0x%.8X", + region, TARGETING::get_huid(iv_dimm)); + break; + } + if (hostCksm != nvCksm) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d of NVDIMM 0x%.8X: data checksums mismatch " + "(calc host: 0x%X and nv: 0x%X)", + region, TARGETING::get_huid(iv_dimm), hostCksm, nvCksm); + + /* + *@errortype + *@moduleid UPDATE_IMAGE_DATA + *@reasoncode NVDIMM_CHECKSUM_ERROR + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Host checksum calculated + *@userdata2[16:31] NV checksum returned + *@userdata2[32:47] size of data for checksum + *@userdata2[48:63] region + *@devdesc Checksum failure when transferring region + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + UPDATE_IMAGE_DATA, + NVDIMM_CHECKSUM_ERROR, + TARGETING::get_huid(iv_dimm), + FOUR_UINT16_TO_UINT64( + hostCksm, nvCksm, + region, data_len), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace( NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + + break; + } + + // 10.f Commit the firmware data region transferred + TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.f"); + l_err = commitFwRegion(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: " + "Region %d: commitFwRegion() failed on NVDIMM 0x%.8X", + region, TARGETING::get_huid(iv_dimm)); + break; + } + region++; + } // End of FW image data transfer + } while (0); + + return l_err; +} + +errlHndl_t NvdimmInstalledImage::changeFwUpdateMode(fw_update_mode i_mode) +{ + errlHndl_t l_err = nullptr; + + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, i_mode); + if (!l_err) + { + // Wait for ops to complete + l_err = waitFwOpsComplete(); + if (!l_err) + { + // if ops completed successfully, check the status + // to make sure that FW update mode has been enabled/disabled + fw_ops_status_t opStatus; + l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_STATUS, opStatus.whole); + if (!l_err) + { + // Create an error if the mode was NOT set correctly + if (!(((i_mode == FW_UPDATE_MODE_ENABLED) && + (opStatus.fw_ops_update_mode == 1)) || + ((i_mode == FW_UPDATE_MODE_DISABLED) && + (opStatus.fw_ops_update_mode == 0))) ) + { + /* + *@errortype + *@moduleid CHANGE_FW_UPDATE_MODE + *@reasoncode NVDIMM_UPDATE_MODE_UNCHANGED + *@userdata1 NVDIMM Target Huid + *@userdata2[0:7] Mode setting + *@userdata2[8:15] FIRMWARE_OPS_STATUS byte + *@devdesc Firmware Update Mode not updated + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + CHANGE_FW_UPDATE_MODE, + NVDIMM_UPDATE_MODE_UNCHANGED, + TARGETING::get_huid(iv_dimm), + FOUR_UINT8_TO_UINT32( + i_mode, opStatus.whole, + 0x00, 0x00), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace( NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::waitFwOpsBlockReceived() +{ + errlHndl_t l_err = nullptr; + + // retry for a total of 100ms + uint32_t timeout_ms_val = 100; + + bool blockReceived = false; + fw_ops_status_t opStatus; + while (1) + { + l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_STATUS, + opStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"waitFwOpsBlockReceived: " + "FIRMWARE_OPS_STATUS read failed on NVDIMM 0x%.8X " + "(timeout: 0x%04X ms of 100 ms)", + TARGETING::get_huid(iv_dimm), timeout_ms_val); + break; + } + if (!opStatus.fw_ops_block_received) + { + // wait 1 millisecond between checking status + if (timeout_ms_val > 0) + { + timeout_ms_val -= 1; + nanosleep(0, NS_PER_MSEC); + } + else + { + // timeout hit + break; + } + } + else + { + // block received + blockReceived = true; + break; + } + } + + if (!blockReceived && !l_err) + { + /* + *@errortype + *@moduleid WAIT_FW_OPS_BLOCK_RECEIVED + *@reasoncode NVDIMM_BLOCK_NOT_RECEIVED + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Last FIRMWARE_OPS_STATUS read + *@userdata2[16:31] Timeout (msecs) + *@devdesc Firmware Operation timed out waiting for + * data block transfer confirmation + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + WAIT_FW_OPS_BLOCK_RECEIVED, + NVDIMM_FW_OPS_IN_PROGRESS_TIMEOUT, + TARGETING::get_huid(iv_dimm), + TWO_UINT16_ONE_UINT32_TO_UINT64 + ( + TWO_UINT8_TO_UINT16( 0x00, + opStatus.whole), + 100, + timeout_ms_val + ), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 512 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + + return l_err; +} + + +errlHndl_t NvdimmInstalledImage::waitFwOpsComplete() +{ + errlHndl_t l_err = nullptr; + + uint16_t timeout_val = INVALID_TIMEOUT; + l_err = getFwOpsTimeout(timeout_val); + uint32_t timeout_ms_val = timeout_val * 1000; + if (!l_err) + { + TRACUCOMP(g_trac_nvdimm_upd, "waitFwOpsComplete: timeout_val %d seconds", timeout_val); + bool opsComplete = false; + nvdimm_cmd_status0_t cmdStatus; + while (1) + { + l_err = nvdimmReadReg(iv_dimm, NVDIMM_CMD_STATUS0, cmdStatus.whole); + if (l_err) + { + break; + } + if (cmdStatus.firmware_ops_in_progress) + { + // wait 1 millisecond between checking status + if (timeout_ms_val > 0) + { + timeout_ms_val -= 1; + nanosleep(0, NS_PER_MSEC); + } + else + { + // timeout hit + break; + } + } + else + { + // ops completed + opsComplete = true; + break; + } + } + + if (!opsComplete && !l_err) + { + /* + *@errortype + *@moduleid WAIT_FW_OPS_COMPLETE + *@reasoncode NVDIMM_FW_OPS_IN_PROGRESS_TIMEOUT + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Last NVDIMM_CMD_STATUS0 read + *@userdata2[16:31] Timeout (seconds) + *@devdesc Firmware Operation timed out + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + WAIT_FW_OPS_COMPLETE, + NVDIMM_FW_OPS_IN_PROGRESS_TIMEOUT, + TARGETING::get_huid(iv_dimm), + TWO_UINT16_ONE_UINT32_TO_UINT64 + ( + TWO_UINT8_TO_UINT16( 0x00, + cmdStatus.whole), + iv_timeout, + timeout_ms_val + ), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::getFwOpsTimeout(uint16_t & o_timeout) +{ + errlHndl_t l_err = nullptr; + + do { + // Grab the timeout value once and reuse + if (iv_timeout == INVALID_TIMEOUT) + { + uint8_t lsb; + uint8_t msb; + l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_TIMEOUT0, lsb); + if (l_err) + { + break; + } + l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_TIMEOUT1, msb); + if (l_err) + { + break; + } + + // Look for whether this is in milliseconds or seconds + if (msb < 0x80) + { + // convert time value for milliseconds to seconds + iv_timeout = ((msb << 8) | lsb)/1000; + } + else + { + // timeout value is in seconds (remove the indicator bit) + iv_timeout = (((msb - 0x80) << 8) | lsb); + } + TRACUCOMP(g_trac_nvdimm_upd,"getFwOpsTimeout: msb:0x%02X lsb:0x%02X -> 0x%04X", + msb, lsb, iv_timeout); + } + o_timeout = iv_timeout; + } while (0); + + return l_err; +} + +errlHndl_t NvdimmInstalledImage::clearFwOpsStatus() +{ + errlHndl_t l_err = nullptr; + // NVDIMM_MGT_CMD1: + // Bit 1 set, the module shall clear all the bits except Bit 2 in + // the FIRMWARE_OPS_STATUS register + uint8_t l_data = 0x02; + l_err = nvdimmWriteReg(iv_dimm, NVDIMM_MGT_CMD1, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd,ERR_MRK"clearFwOpsStatus: " + "NVDIMM 0x%.8X clear FIRMWARE_OPS_STATUS register failed", + TARGETING::get_huid(iv_dimm)); + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::isFwOpsSuccess(bool & o_success) +{ + errlHndl_t l_err = nullptr; + o_success = false; + + fw_ops_status_t l_data; + l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_STATUS, l_data.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"isFwOpsSuccess: " + "NVDIMM 0x%.8X read FIRMWARE_OPS_STATUS register failed (0x%02X)", + TARGETING::get_huid(iv_dimm), l_data.whole); + } + else + { + if (l_data.fw_ops_success) + { + o_success = true; + } + else + { + // add this trace so we know what was returned from FIRMWARE_OP_STATUS read + TRACFCOMP(g_trac_nvdimm_upd, "isFwOpsSuccess() returning false (0x%02X)", + l_data.whole); + o_success = false; + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::getBlocksPerRegion + (uint8_t & io_blocks_per_region) +{ + errlHndl_t l_err = nullptr; + + io_blocks_per_region = iv_max_blocks_per_region; + if (iv_max_blocks_per_region == INVALID_REGION_BLOCK_SIZE) + { + l_err = nvdimmReadReg(iv_dimm, REGION_BLOCK_SIZE, io_blocks_per_region); + if (!l_err) + { + // If no error, update the class variable + // as this gets called multiple times + iv_max_blocks_per_region = io_blocks_per_region; + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::byteRegionBlockTransfer(const uint8_t * i_data, + const uint16_t i_data_size, + bool i_use_region_block_size) +{ + errlHndl_t l_err = nullptr; + uint8_t blocks_per_region = 0x00; + uint8_t max_blocks_per_region = 0x00; + + do { + l_err = getBlocksPerRegion(max_blocks_per_region); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "getBlocksPerRegion() failed on NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + + // If the data passed in does not equal the region block size, + // calculate the number of blocks required for the given data + if (!i_use_region_block_size) + { + blocks_per_region = i_data_size/BYTES_PER_BLOCK; + if (i_data_size % BYTES_PER_BLOCK) + { + // Add another block if data can't be evenly broken into + // BYTES_PER_BLOCK blocks + blocks_per_region++; + } + if (blocks_per_region > max_blocks_per_region) + { + /* + *@errortype + *@moduleid BYTE_REGION_BLOCK_TRANSFER + *@reasoncode NVDIMM_DATA_SIZE_TOO_LARGE + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Data size trying to transfer + *@userdata2[16:31] Maximum data size allowed + *@userdata2[32:47] Calculated blocks_per_region + *@userdata2[48-63] Maximum blocks_per_region allowed + *@devdesc Data size too big to transfer in one command + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + BYTE_REGION_BLOCK_TRANSFER, + NVDIMM_DATA_SIZE_TOO_LARGE, + TARGETING::get_huid(iv_dimm), + FOUR_UINT16_TO_UINT64( + i_data_size, + max_blocks_per_region*BYTES_PER_BLOCK, + blocks_per_region, + max_blocks_per_region), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + break; + } + } + else + { + blocks_per_region = max_blocks_per_region; + } + + TRACUCOMP(g_trac_nvdimm_upd,"byteRegionBlockTransfer: blocks_per_region = 0x%02X", blocks_per_region); + + if (i_data_size > (BYTES_PER_BLOCK*blocks_per_region)) + { + /* + *@errortype + *@moduleid BYTE_REGION_BLOCK_TRANSFER + *@reasoncode NVDIMM_DATA_SIZE_INVALID + *@userdata1 NVDIMM Target Huid + *@userdata2[0:15] Data size trying to transfer + *@userdata2[16:31] Calculated maximum data size transfer + *@userdata2[32:47] Blocks per region + *@userdata2[48-63] Bytes transferred per block + *@devdesc Data size too big to transfer + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + BYTE_REGION_BLOCK_TRANSFER, + NVDIMM_DATA_SIZE_INVALID, + TARGETING::get_huid(iv_dimm), + FOUR_UINT16_TO_UINT64( + i_data_size, + BYTES_PER_BLOCK*blocks_per_region, + blocks_per_region, + BYTES_PER_BLOCK), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + break; + } + + uint8_t blockNum = 0; + uint8_t block[BYTES_PER_BLOCK]; // used for partial transfer + uint8_t * pCurrentBlockData = nullptr; + while (blockNum < blocks_per_region && (!l_err)) + { + TRACUCOMP(g_trac_nvdimm_upd,"byteRegionBlockTransfer: block = 0x%02X", blockNum); + l_err = nvdimmWriteReg(iv_dimm, BLOCK_ID, blockNum); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Write 0x%02X to BLOCK_ID failed on NVDIMM 0x%.8X", + blockNum, TARGETING::get_huid(iv_dimm)); + break; + } + + // Data must be transferred in multiple of BYTES_PER_BLOCK bytes. + // If block_data is not BYTES_PER_BLOCK bytes, pad the end with 0s + if ((i_data_size - BYTES_PER_BLOCK * blockNum) < BYTES_PER_BLOCK) + { + // clear entire block of bytes + memset(block, 0x00, BYTES_PER_BLOCK); + + // add the last partial block of data bytes + if (i_data_size > (blockNum * BYTES_PER_BLOCK)) + { + memcpy(block, &i_data[blockNum * BYTES_PER_BLOCK], + (i_data_size - (blockNum * BYTES_PER_BLOCK))); + } + pCurrentBlockData = block; + } + else + { + pCurrentBlockData = const_cast<uint8_t *>(&i_data[blockNum * BYTES_PER_BLOCK]); + } + + // write out the 32-byte (BYTES_PER_BLOCK) data block + TRACUCOMP(g_trac_nvdimm_upd,"byteRegionBlockTransfer: write out 32byte block 0x%02X", blockNum); + l_err = nvdimmOpenPage(iv_dimm, PAGE(TYPED_BLOCK_DATA_BYTE0)); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Unable to open page for BLOCK %d transfer of NVDIMM " + "0x%.8X", blockNum, TARGETING::get_huid(iv_dimm)); + } + + size_t l_numBytes = BYTES_PER_BLOCK; + uint8_t l_reg_addr = ADDRESS(TYPED_BLOCK_DATA_BYTE0); + l_err = DeviceFW::deviceOp( DeviceFW::WRITE, + iv_dimm, + pCurrentBlockData, + l_numBytes, + DEVICE_NVDIMM_ADDRESS(l_reg_addr) ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Block %d write to 0x%02X failed on NVDIMM 0x%.8X", + blockNum, l_reg_addr, TARGETING::get_huid(iv_dimm)); + break; + } + // increment to next block + pCurrentBlockData += BYTES_PER_BLOCK; + + // After a block has been transferred, verify that the 32-byte block + // was received by polling FIRMWARE_OPS_STATUS offset for + // FIRMWARE_BLOCK_RECEIVED. + l_err = waitFwOpsBlockReceived(); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Block %d read of FIRMWARE_OPS_STATUS failed on NVDIMM " + " 0x%.8X", blockNum, TARGETING::get_huid(iv_dimm)); + break; + } + // block of data successfully sent to NV controller + TRACUCOMP(g_trac_nvdimm_upd,"byteRegionBlockTransfer: block 0x%02X successfully sent to NV controller", blockNum); + blockNum++; + } + + } while (0); + + return l_err; +} // end byteRegionBlockTransfer() + +errlHndl_t NvdimmInstalledImage::calcAndGetCksm(uint16_t & o_nvCksm) +{ + errlHndl_t l_err = nullptr; + + // Command the module to calculate the checksum + fw_ops_cmd_t opsCmd; + opsCmd.whole = 0x00; + opsCmd.start_generate_fw_checksum_op = 1; + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, opsCmd.whole); + if (!l_err) + { + l_err = waitFwOpsComplete(); + if (!l_err) + { + bool opSuccessful = false; + l_err = isFwOpsSuccess(opSuccessful); + if (!l_err && opSuccessful) + { + uint8_t lsb, msb; + l_err = nvdimmReadReg(iv_dimm, FW_REGION_CRC0, lsb); + if (!l_err) + { + l_err = nvdimmReadReg(iv_dimm, FW_REGION_CRC1, msb); + if (!l_err) + { + o_nvCksm = (msb << 8) | lsb; + } + } + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::validateFwHeader() +{ + errlHndl_t l_err = nullptr; + + l_err = clearFwOpsStatus(); + if (!l_err) + { + fw_ops_cmd_t opsCmd; + opsCmd.whole = 0x00; + opsCmd.start_validate_fw_header_op = 1; + + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, opsCmd.whole); + if (!l_err) + { + l_err = waitFwOpsComplete(); + if (!l_err) + { + bool opsSuccessful = false; + l_err = isFwOpsSuccess(opsSuccessful); + if (!l_err && !opsSuccessful) + { + /* + *@errortype + *@moduleid VALIDATE_FW_HEADER + *@reasoncode NVDIMM_FW_OPS_NOT_SUCCESSFUL + *@userdata1 NVDIMM Target Huid + *@userdata2 Operation command being verified + *@devdesc Firmware Operation not successful + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + VALIDATE_FW_HEADER, + NVDIMM_FW_OPS_NOT_SUCCESSFUL, + TARGETING::get_huid(iv_dimm), + opsCmd.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::commitFwRegion() +{ + errlHndl_t l_err = nullptr; + + l_err = clearFwOpsStatus(); + if (!l_err) + { + fw_ops_cmd_t opsCmd; + opsCmd.whole = 0x00; + opsCmd.start_commit_fw_op = 1; + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, opsCmd.whole); + if (!l_err) + { + l_err = waitFwOpsComplete(); + if (!l_err) + { + bool opsSuccessful = false; + l_err = isFwOpsSuccess(opsSuccessful); + if (!l_err && !opsSuccessful) + { + /* + *@errortype + *@moduleid COMMIT_FW_REGION + *@reasoncode NVDIMM_FW_OPS_NOT_SUCCESSFUL + *@userdata1 NVDIMM Target Huid + *@userdata2 Operation command being verified + *@devdesc Firmware Operation not successful + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + COMMIT_FW_REGION, + NVDIMM_FW_OPS_NOT_SUCCESSFUL, + TARGETING::get_huid(iv_dimm), + opsCmd.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::clearFwDataBlock() +{ + errlHndl_t l_err = nullptr; + + // clearFwOps + fw_ops_cmd_t opsCmd; + opsCmd.whole = 0x00; + opsCmd.start_clear_fw_op = 1; + TRACUCOMP(g_trac_nvdimm_upd, "clearFwDataBlock: clearFwOps cmd 0x%02X", opsCmd.whole); + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, opsCmd.whole); + if (!l_err) + { + TRACUCOMP(g_trac_nvdimm_upd, "clearFwDataBlock: start waitFwOpsComplete"); + l_err = waitFwOpsComplete(); + if (!l_err) + { + bool ops_success = false; + TRACUCOMP(g_trac_nvdimm_upd, "clearFwDataBlock: done waiting now check FwOpsStatus for success"); + l_err = isFwOpsSuccess(ops_success); + if (!l_err && !ops_success) + { + /* + *@errortype + *@moduleid CLEAR_FW_DATA_BLOCK + *@reasoncode NVDIMM_FW_OPS_NOT_SUCCESSFUL + *@userdata1 NVDIMM Target Huid + *@userdata2 Operation command being verified + *@devdesc Firmware Operation not successful + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + CLEAR_FW_DATA_BLOCK, + NVDIMM_FW_OPS_NOT_SUCCESSFUL, + TARGETING::get_huid(iv_dimm), + opsCmd.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::validateFwImage() +{ + errlHndl_t l_err = nullptr; + + l_err = clearFwOpsStatus(); + if (!l_err) + { + fw_ops_cmd_t opsCmd; + opsCmd.whole = 0x00; + opsCmd.start_validate_fw_image_op = 1; + l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, opsCmd.whole); + if (!l_err) + { + l_err = waitFwOpsComplete(); + if (!l_err) + { + bool opsSuccessful = false; + l_err = isFwOpsSuccess(opsSuccessful); + // create an error if operation not successful + if (!l_err && !opsSuccessful) + { + /* + *@errortype + *@moduleid VALIDATE_FW_IMAGE + *@reasoncode NVDIMM_FW_OPS_NOT_SUCCESSFUL + *@userdata1 NVDIMM Target Huid + *@userdata2 Operation command being verified + *@devdesc Firmware Operation not successful + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + VALIDATE_FW_IMAGE, + NVDIMM_FW_OPS_NOT_SUCCESSFUL, + TARGETING::get_huid(iv_dimm), + opsCmd.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addPartCallout( iv_dimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + + } + } + } + } + return l_err; +} + +errlHndl_t NvdimmInstalledImage::resetController() +{ + errlHndl_t l_err = nullptr; + + // If bit 0 is set, the module shall start a Reset Controller operation + l_err = nvdimmWriteReg(iv_dimm, NVDIMM_MGT_CMD0, 0x01); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd,ERR_MRK"resetController: NVDIMM 0x%.8X " + "write of 0x01 to NVDIMM_MGT_CMD0 register failed", + TARGETING::get_huid(iv_dimm)); + } + else + { + TRACUCOMP(g_trac_nvdimm_upd,"resetController: waiting 5 seconds after controller 0x%.8X reset", + TARGETING::get_huid(iv_dimm)); + + // sleep 5 seconds to allow for i2c controller to come back online + nanosleep(5,0); + + TRACUCOMP(g_trac_nvdimm_upd,"resetController: now check if NV controller is ready again", + TARGETING::get_huid(iv_dimm)); + + // Now wait until NV controller is ready again after reset + l_err = nvdimmReady(iv_dimm); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd,ERR_MRK"resetController: NV controller for " + "NVDIMM 0x%.8X is not reporting as ready after reset", + TARGETING::get_huid(iv_dimm)); + } + } + return l_err; +} + +uint16_t NvdimmInstalledImage::crc16(const uint8_t * i_data, int i_data_size) +{ + // From JEDEC JESD245B.01 document + // https://www.jedec.org/standards-documents/docs/jesd245a + int i, crc; + crc = 0; + while (--i_data_size >= 0) + { + crc = crc ^ (int)*i_data++ << 8; + for (i = 0; i < 8; ++i) + { + if (crc & 0x8000) + { + crc = crc << 1 ^ 0x1021; + } + else + { + crc = crc << 1; + } + } + } + return (crc & 0xFFFF); +} + /////////////////////////////////////////////////////////////////////////////// // NVDIMMS Update Functions /////////////////////////////////////////////////////////////////////////////// @@ -292,6 +1778,60 @@ NvdimmsUpdate::NvdimmsUpdate(TARGETING::TargetHandleList i_nvdimmList) iv_nvdimmList = i_nvdimmList; } +bool NvdimmsUpdate::runUpdateUsingLid(NvdimmLidImage * i_lidImage, + std::vector<NvdimmInstalledImage *> &i_list) +{ + bool o_no_error_found = true; + + errlHndl_t l_err = nullptr; + for (auto pInstalledImage : i_list) + { + INITSERVICE::sendProgressCode(); + bool updateNeeded = false; + l_err = isUpdateNeeded(updateNeeded, i_lidImage, pInstalledImage); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "NvdimmsUpdate::runUpdateUsingLid()" + " Unable to determine if nvdimm[%X] needs NV controller update." + " RC=0x%X, PLID=0x%.8X", + TARGETING::get_huid(pInstalledImage->getNvdimmTarget()), + ERRL_GETRC_SAFE(l_err), ERRL_GETPLID_SAFE(l_err)); + commitPredictiveNvdimmError(l_err); + o_no_error_found = false; + continue; + } + else if (updateNeeded) + { + // perform update for this DIMM with the current LID image + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdateUsingLid() - " + "now update nvdimm[0x%.8X]", + TARGETING::get_huid(pInstalledImage->getNvdimmTarget())); + + TRACFCOMP(g_trac_nvdimm_upd,"Updating with flash size: 0x%08X", + i_lidImage->getFlashImageSize()); + + l_err = pInstalledImage->updateImage(i_lidImage); + if (l_err) + { + // If update fails, the NV controller will run on its factory + // installed version in slot 0. + // We will call out the failed NVDIMM for replacement, but try + // to keep running with as much function as possible. + TRACFCOMP(g_trac_nvdimm_upd, + ERR_MRK"NvdimmsUpdate::runUpdateUsingLid() - " + "NVDIMM 0x%.8X NV controller update failed. " + "RC=0x%X, PLID=0x%.8X", + TARGETING::get_huid(pInstalledImage->getNvdimmTarget()), + ERRL_GETRC_SAFE(l_err), ERRL_GETPLID_SAFE(l_err)); + commitPredictiveNvdimmError(l_err); + l_err = nullptr; + o_no_error_found = false; + } + } // end of updateNeeded + } + return o_no_error_found; +} + bool NvdimmsUpdate::runUpdate(void) { bool o_no_error_found = true; // true if no error was found during update @@ -299,21 +1839,21 @@ bool NvdimmsUpdate::runUpdate(void) errlHndl_t l_err = nullptr; uint32_t l_installed_type = INVALID_TYPE; // current LID type installed - NvdimmLidImage * pCurLid = nullptr; // current LID being used for update - // These are kept to minimize lid memory loading/unloading and - // reported errors - NvdimmLidImage * pSmallLid = nullptr; - NvdimmLidImage * pLargeLid = nullptr; + // List of each installed NVDIMM type + std::vector<NvdimmInstalledImage*> v_NVDIMM_16GB_list; + std::vector<NvdimmInstalledImage*> v_NVDIMM_32GB_list; + // Build up installed NVDIMM image lists for (auto l_nvdimm : iv_nvdimmList) { - NvdimmInstalledImage l_installed_image(l_nvdimm); - l_err = l_installed_image.getType(l_installed_type); + NvdimmInstalledImage * l_installed_image = + new NvdimmInstalledImage(l_nvdimm); + l_err = l_installed_image->getType(l_installed_type); if (l_err) { // Continue updating other dimms - TRACFCOMP(g_trac_nvdimm, ERR_MRK "NvdimmsUpdate::runUpdate() - " + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "NvdimmsUpdate::runUpdate() - " "Unable to get nvdimm[0x%.8X] installed image type. " "RC=0x%X, PLID=0x%.8X", get_huid(l_nvdimm), ERRL_GETRC_SAFE(l_err), @@ -323,56 +1863,24 @@ bool NvdimmsUpdate::runUpdate(void) continue; } - if (l_installed_type == JEDEC_NVDIMM_16GB_TYPE) + if (l_installed_type == SMART_NVDIMM_16GB_TYPE) { - if (pSmallLid == nullptr) - { - pSmallLid = new NvdimmLidImage(Util::NVDIMM_16GB_LIDID, l_err); - if (l_err) - { - // Continue to try updating other dimms - TRACFCOMP(g_trac_nvdimm, - ERR_MRK "NvdimmsUpdate::runUpdate() - Unable to load " - "NVDIMM_16GB_LIDID(0x%X). RC=0x%X, PLID=0x%.8X", - Util::NVDIMM_16GB_LIDID, - ERRL_GETRC_SAFE(l_err), - ERRL_GETPLID_SAFE(l_err)); - commitPredictiveNvdimmError(l_err); - o_no_error_found = false; - // leaving pSmallLid object so don't continuously post the - // same error for each NVDIMM - continue; - } - } - pCurLid = pSmallLid; + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - " + "0x%.8X NVDIMM is SMART_NVDIMM_16GB_TYPE", + get_huid(l_nvdimm)); + v_NVDIMM_16GB_list.push_back(l_installed_image); } - else if (l_installed_type == JEDEC_NVDIMM_32GB_TYPE) + else if (l_installed_type == SMART_NVDIMM_32GB_TYPE) { - if (pLargeLid == nullptr) - { - pLargeLid = new NvdimmLidImage(Util::NVDIMM_32GB_LIDID, l_err); - if (l_err) - { - // Continue to try updating other dimms - TRACFCOMP(g_trac_nvdimm, - ERR_MRK "NvdimmsUpdate::runUpdate() - Unable to load " - "NVDIMM_32GB_LIDID(0x%X). RC=0x%X, PLID=0x%.8X", - Util::NVDIMM_32GB_LIDID, - ERRL_GETRC_SAFE(l_err), - ERRL_GETPLID_SAFE(l_err)); - commitPredictiveNvdimmError(l_err); - o_no_error_found = false; - // leaving pLargeLid object so don't continuously post the - // same error for each NVDIMM - continue; - } - } - pCurLid = pLargeLid; + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - " + "0x%.8X NVDIMM is SMART_NVDIMM_32GB_TYPE", + get_huid(l_nvdimm)); + v_NVDIMM_32GB_list.push_back(l_installed_image); } else { // unknown/unsupported Type - TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - unknown " + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - unknown " "nvdimm[%X] installed type 0x%04X, skipping update", TARGETING::get_huid(l_nvdimm), l_installed_type); /* @@ -393,60 +1901,126 @@ bool NvdimmsUpdate::runUpdate(void) l_installed_type, TARGETING::get_huid(l_nvdimm)), TWO_UINT32_TO_UINT64( - JEDEC_NVDIMM_16GB_TYPE, - JEDEC_NVDIMM_32GB_TYPE), + SMART_NVDIMM_16GB_TYPE, + SMART_NVDIMM_32GB_TYPE), ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); - l_err->addHwCallout( l_nvdimm, HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, HWAS::GARD_NULL ); + l_err->collectTrace(NVDIMM_UPD, 256); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH ); l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW ); ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); - o_no_error_found = false; - pCurLid = nullptr; continue; } + } - // Verify a valid LID was loaded and ready to read - if ((pCurLid == nullptr) || (!pCurLid->isImageLoaded())) + do { + // First check that updatable NVDIMMs exist on the system + if ((v_NVDIMM_16GB_list.size() == 0) && + (v_NVDIMM_32GB_list.size() == 0)) { - // Errors already logged, just continue to the next NVDIMM - continue; + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - " + "No updatable NVDIMMs present on the system"); + break; } - bool updateNeeded = false; - l_err = isUpdateNeeded(updateNeeded, pCurLid, &l_installed_image); - if (l_err) + ///////////////////////// + // @todo: remove this check when SMART provides updated 32GB image + // The current 32GB image will cause the future updating to fail + if (v_NVDIMM_16GB_list.size() == 0) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK "NvdimmsUpdate::runUpdate() - " - "Unable to determine if nvdimm[%X] needs NV controller update." - " RC=0x%X, PLID=0x%.8X", - TARGETING::get_huid(l_nvdimm), ERRL_GETRC_SAFE(l_err), - ERRL_GETPLID_SAFE(l_err)); - commitPredictiveNvdimmError(l_err); - o_no_error_found = false; + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - " + "Only 16GB NVDIMM type is supported right now for update"); + break; } - else if (updateNeeded) + ///////////////////////// + + if (INITSERVICE::spBaseServicesEnabled()) { - // perform update for this DIMM with the current LID image - TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - " - "now update nvdimm[0x%.8X]", TARGETING::get_huid(l_nvdimm)); + // Load the NVDIMM flash binary via the MCL in load-only mode + MCL::MasterContainerLidMgr mclManager(true); + MCL::CompInfo info; + l_err = mclManager.processSingleComponent(MCL::g_NvdimmCompId,info); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK "NvdimmsUpdate::runUpdate() - " + "unable to obtain NVDIMM lid images - "); + commitPredictiveNvdimmError(l_err); + o_no_error_found = false; + break; + } - TRACFCOMP(g_trac_nvdimm,"Updating with flash size: 0x%08X", - pCurLid->getFlashImageSize()); + for(const auto& lid : info.lidIds) + { + TRACFCOMP(g_trac_nvdimm,"LID ID=0x%08X, size=%d, vAddr=%p", + lid.id, lid.size, lid.vAddr); - // @todo RTC 202536 : Add update calls - } - } + if (lid.id == NVDIMM_16GB_LIDID) + { + if (v_NVDIMM_16GB_list.size() > 0) + { + // Grab the 16GB lid + TRACFCOMP(g_trac_nvdimm, + "Check/update %d 16GB_TYPE NVDIMMs", + v_NVDIMM_16GB_list.size()); + NvdimmLidImage lidImage(lid.vAddr, lid.size); + o_no_error_found &= runUpdateUsingLid(&lidImage, + v_NVDIMM_16GB_list); + } + } + else if (lid.id == NVDIMM_32GB_LIDID) + { + if (v_NVDIMM_32GB_list.size() > 0) + { + // Grab the 32GB lid + TRACFCOMP(g_trac_nvdimm, + "Check/update %d 32GB_TYPE NVDIMMs", + v_NVDIMM_32GB_list.size()); + NvdimmLidImage lidImage(lid.vAddr, lid.size); + o_no_error_found &= runUpdateUsingLid(&lidImage, + v_NVDIMM_32GB_list); + } + } + else if (lid.id != NVDIMM_SIGNATURE_LIDID) + { + TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - " + "Unknown NVDIMM LID: ID=0x%08X, size=%d", + lid.id, lid.size); + TRACFBIN(g_trac_nvdimm, "Unknown LID", lid.vAddr, 64); + } + } - if (pLargeLid) - { - delete pLargeLid; - } - if (pSmallLid) - { - delete pSmallLid; - } + // Destructor automatically unloads the NVDIMM flash binary + } + else + { + TRACFCOMP(g_trac_nvdimm_upd, "NvdimmsUpdate::runUpdate() - " + "spBaseServices not running, therefore NVDIMM LID images " + "cannot be accessed"); + /* + *@errortype + *@reasoncode NVDIMM_BASE_SERVICES_NOT_READY + *@moduleid NVDIMM_RUN_UPDATE + *@userdata1 NVDIMM_16GB_type list size + *@userdata2 NVDIMM_32GB_type list size + *@devdesc spBaseServices not available + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_RUN_UPDATE, + NVDIMM_BASE_SERVICES_NOT_READY, + v_NVDIMM_16GB_list.size(), + v_NVDIMM_32GB_list.size(), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->collectTrace(NVDIMM_UPD, 256); + ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); + o_no_error_found = false; + break; + } + } while (0); // end of flash update section return o_no_error_found; } @@ -469,7 +2043,7 @@ errlHndl_t NvdimmsUpdate::isUpdateNeeded(bool & o_update_needed, l_err = i_cur_image->getType(curType); if (l_err) { - TRACFCOMP(g_trac_nvdimm, + TRACFCOMP(g_trac_nvdimm_upd, "isUpdateNeeded(): failed to find type of NVDIMM[%X]", TARGETING::get_huid(l_dimm)); break; @@ -483,13 +2057,12 @@ errlHndl_t NvdimmsUpdate::isUpdateNeeded(bool & o_update_needed, l_err = i_cur_image->getVersion(curVersion); if (l_err) { - TRACFCOMP(g_trac_nvdimm, + TRACFCOMP(g_trac_nvdimm_upd, "isUpdateNeeded(): failed to find version of NVDIMM[%X]", TARGETING::get_huid(l_dimm)); break; } - if (curVersion != lidVersion) { // verify we are updating with a good version @@ -499,20 +2072,20 @@ errlHndl_t NvdimmsUpdate::isUpdateNeeded(bool & o_update_needed, // so an update can proceed o_update_needed = true; - TRACFCOMP(g_trac_nvdimm, + TRACFCOMP(g_trac_nvdimm_upd, "NVDIMM[%X] code level - current: 0x%04X, new: 0x%04X", TARGETING::get_huid(l_dimm), le16toh(curVersion), le16toh(lidVersion)); } else { - TRACFCOMP(g_trac_nvdimm, "NVDIMM[%X] has invalid version", + TRACFCOMP(g_trac_nvdimm_upd, "NVDIMM[%X] has invalid version", TARGETING::get_huid(l_dimm)); } } else { - TRACUCOMP(g_trac_nvdimm, + TRACFCOMP(g_trac_nvdimm_upd, "Keeping current NVDIMM[%X] level: 0x%04X", TARGETING::get_huid(l_dimm), le16toh(curVersion)); } diff --git a/src/usr/isteps/nvdimm/nvdimm_update.H b/src/usr/isteps/nvdimm/nvdimm_update.H index 6e2a137fc..37153b9c2 100644 --- a/src/usr/isteps/nvdimm/nvdimm_update.H +++ b/src/usr/isteps/nvdimm/nvdimm_update.H @@ -33,33 +33,46 @@ #include <cstdint> #include <errl/errlentry.H> -#include <util/utillidmgr.H> + namespace NVDIMM { // Some invalid constants const uint16_t INVALID_ID = 0xFFFF; const uint16_t INVALID_VERSION = 0xFFFF; +const uint16_t INVALID_TIMEOUT = 0xFFFF; const uint32_t INVALID_TYPE = 0xFFFFFFFF; // Type is combination of manufacturer id and product id -const uint32_t JEDEC_NVDIMM_16GB_TYPE = 0x01945377; -const uint32_t JEDEC_NVDIMM_32GB_TYPE = 0x01945378; +const uint32_t SMART_NVDIMM_16GB_TYPE = 0x01945377; +const uint32_t SMART_NVDIMM_32GB_TYPE = 0x01945378; + +// LID IDs for each NV controller type +const uint32_t NVDIMM_SIGNATURE_LIDID = 0x80D00025; // ignore this one + +// These LIDs are created from these two binaries supplied by SMART +// 16GB image -> src/extucode/NVDIDMM_SRN7A2G4IBM26MP1SC.bin --> 81e00640.lid +// 32GB image -> src/extucode/NVDIDMM_SRN7A4G4IBM24KP2SB.bin --> 81e00641.lid +const uint32_t NVDIMM_16GB_LIDID = 0x81e00640; +const uint32_t NVDIMM_32GB_LIDID = 0x81e00641; + + +// Firmware Update Mode settings for FIRMWARE_OPS_CMD +enum fw_update_mode : uint8_t +{ + FW_UPDATE_MODE_DISABLED = 0x00, + FW_UPDATE_MODE_ENABLED = 0x01, +}; class NvdimmLidImage { public: /** * @brief Constructor that sets access to LID information - * @param i_lidId - NVDIMM_16GB_LIDID or NVDIMM_32GB_LIDID - * @param io_errHndl - error filled in if lid not loadable + * @param i_lidImageAddr - virtual address where LID was loaded + * @param i_size - size of the loaded LID */ - explicit NvdimmLidImage(Util::LidId i_lidId, errlHndl_t& io_errHdl); - - /** - * @brief Cleanup any allocated memory holding the lid image - */ - ~NvdimmLidImage(); + explicit NvdimmLidImage(const void * i_lidImageAddr, size_t i_size); /** * @brief Grab the type of the image @@ -79,7 +92,7 @@ class NvdimmLidImage * @brief Get the actual NVDIMM flash image to load on the NVDIMM * @return Pointer to the start of the flash image */ - void * getFlashImage(); + const void * getFlashImage(); /** * @brief Get the size of the actual flash image @@ -87,12 +100,6 @@ class NvdimmLidImage */ size_t getFlashImageSize(); - /** - * @brief Check if the image was loaded into memory - * @return true, if image was loaded into memory - */ - bool isImageLoaded(); - //-----------------------------------------------------------// // Layout of NVDIMM lid image @@ -103,9 +110,9 @@ class NvdimmLidImage // Header of NVDIMM lid image typedef struct nvdimm_image_header { - // Byte 0-1 + // Byte 0-1 - 1st part of type uint16_t module_mnfg_id_code; - // Byte 2-3 + // Byte 2-3 - 2nd part of type uint16_t module_product_id; // Byte 4-5 uint16_t nv_memory_subsys_cntrlr_mnfg_id_code; @@ -115,7 +122,7 @@ class NvdimmLidImage uint8_t module_revision_code; // Byte 9 uint8_t nv_memory_subsys_cntrlr_revision_code; - // Byte 10-11 + // Byte 10-11 - this version of code for update uint16_t controller_firmware_revision; // Byte 12-13 uint16_t energy_source_firmware_revision; @@ -126,7 +133,7 @@ class NvdimmLidImage // Byte 16-17 uint16_t SMART_digital_signature_size; // Byte 18 - uint8_t SMART_digital_signature; + uint8_t SMART_digital_signature_type; // Byte 19 union { @@ -160,32 +167,24 @@ class NvdimmLidImage // (size: firmware_image_size bytes - 32-byte header) //-----------------------------------------------------------// - private: - /** - * @brief Load the lid image into memory - */ - errlHndl_t loadImage(); - /** - * @brief Unload the lid image from memory + * @brief Get a pointer to the header and smart digital signature + * @param o_size Byte size of the data being returned + * @return Pointer to 32-byte header + digital signature */ - errlHndl_t unloadImage(); + const uint8_t * getHeaderAndSmartSignature(uint16_t & o_size); - // force user to supply lid_id + private: + // force user to supply loaded image addr/size NvdimmLidImage(); - // status of whether or not the image was loaded into memory - bool iv_ImageLoaded; - // pointer to lid image - // note: memory allocated/deallocated by iv_lidMgr - void * iv_lidImage; + // note: memory is allocated outside of this class, + // should be left alone + const void * iv_lidImage; // size of lid image size_t iv_lidImageSize; - - // lid manager for nvdimm lid image - UtilLidMgr* iv_lidMgr; }; @@ -208,10 +207,12 @@ class NvdimmInstalledImage /** * @brief Grab the installed NVDIMM's version - * @param o_version - version of installed NVDIMM image + * @param o_version - version of installed NVDIMM image (little-endian format) + * @param i_force_recollect - force hw calls to recollect version * @return error if read operation fails */ - errlHndl_t getVersion(uint16_t & o_version); + errlHndl_t getVersion(uint16_t & o_version, + const bool i_force_recollect = false); /** * @brief Accessor to grab the current NVDIMM target @@ -222,6 +223,13 @@ class NvdimmInstalledImage return iv_dimm; } + /** + * @brief Update the current NV Controller + * @param Update using this image + * @return error pointer if failure to update, else nullptr + */ + errlHndl_t updateImage(NvdimmLidImage * i_lidImage); + private: // nvdimm target TARGETING::Target * iv_dimm; @@ -232,6 +240,137 @@ class NvdimmInstalledImage // Type contains these two concatentated little-endian IDs uint16_t iv_manufacturer_id; uint16_t iv_product_id; + + // timeout value (in seconds) for FIRMWARE_OPS_TIMEOUT0/1 + uint16_t iv_timeout; + + // constant for invalid setting + const uint8_t INVALID_REGION_BLOCK_SIZE = 0x00; + + // maximum blocks allowed per region (REGION_BLOCK_SIZE) + uint8_t iv_max_blocks_per_region; + + // Helper functions for updating the installed lid + /** + * @brief Transfer a region of bytes in multiple 32-byte blocks + * @param i_data - data to transfer + * @param i_data_size - size of the data to transfer + * @param i_use_region_block_size - use the register REGION_BLOCK_SIZE + * as number of blocks to send + * @return error if transfer fails, else nullptr + */ + errlHndl_t byteRegionBlockTransfer(const uint8_t * i_data, + const uint16_t i_data_size, + bool i_use_region_block_size = true); + + /** + * @brief NV controller calculate checksum of region of data bytes + * @param o_nvCksm - NV calculated checksum + * @return error if unable to get checksum, else nullptr + */ + errlHndl_t calcAndGetCksm(uint16_t & o_nvCksm); + + /** + * @brief Change the Firmware Update Mode + * @param i_mode - enable or disable mode + * @return error if unable to change mode, else nullptr + */ + errlHndl_t changeFwUpdateMode(fw_update_mode i_mode); + + /** + * @brief Clear the Firmware Data Block + * @return error if unable to clear data block, else nullptr + */ + errlHndl_t clearFwDataBlock(); + + /** + * @brief Clear the Firmware Operations Status + * @return error if unable to clear status, else nullptr + */ + errlHndl_t clearFwOpsStatus(); + + /** + * @brief Commit the Firmware Region Operation + * @return error if unable commit, else nullptr + */ + errlHndl_t commitFwRegion(); + + /** + * @brief Get the number of blocks per region + * @param io_blocks_per_region - value of REGION_BLOCK_SIZE register + * @return error if unable to get value, else nullptr + */ + errlHndl_t getBlocksPerRegion(uint8_t & io_blocks_per_region); + + /** + * @brief Get the Firmware Operations Timeout + * @param o_timeout - timeout in seconds for a Firmware Operation + * to complete + * @return error if unable to get timeout value, else nullptr + */ + errlHndl_t getFwOpsTimeout(uint16_t & o_timeout); + + /** + * @brief Is the Firmware Operation successful? + * @param o_success - true if FIRMWARE_OPS_STATUS reporting success + * @return error if unable to read register, else nullptr + */ + errlHndl_t isFwOpsSuccess(bool & o_success); + + /** + * @brief Reset NV controller. Resets controller and waits for it to + * come back online + * @return error if reset failed, else nullptr + */ + errlHndl_t resetController(); + + /** + * @brief Updates the NV controller with the lid's image data + * (minus header and signature) + * @param i_lidImage - lid object with image data + * @return error if unable to update, else nullptr + */ + errlHndl_t updateImageData(NvdimmLidImage * i_lidImage); + + /** + * @brief Run FW operation to validate the header and verify + * it was a successful operation + * @return error if unable to validate, else nullptr + */ + errlHndl_t validateFwHeader(); + + /** + * @brief Run FW operation to validate the firmware image and verify + * it was a successful operation + * @return error if unable to validate, else nullptr + */ + errlHndl_t validateFwImage(); + + /** + * @brief Wait until FW operation is no longer in progress + * @return error if failed i2c operation getting status or if + * timeout happened before status indicated operation completion, + * else nullptr + */ + errlHndl_t waitFwOpsComplete(); + + /** + * @brief Wait until FW operation reports data block was received + * Polls FIRMWARE_OPS_STATUS offset for FIRMWARE_BLOCK_RECEIVED + * @return error if failed i2c operation getting status or if + * timeout happened before status indicated operation completion, + * else nullptr + */ + errlHndl_t waitFwOpsBlockReceived(); + + /** + * @brief Checksum calculation + * See JESD245B for documentation of this checksum + * @param i_data - pointer to data + * @param i_data_size - size of data being pointed too + * @return checksum + */ + uint16_t crc16(const uint8_t * i_data, int i_data_size); }; @@ -255,16 +394,26 @@ class NvdimmsUpdate protected: /** * @brief Checks if an update is needed for an individual nvdimm - * @param[out] o_update_needed - true if lid image is good to - * update current image, else false + * @param[out] o_update_needed - + * true - lid image is good to update current image + * false - update not needed * @param[in] i_lid_image - lid image that is appropriate for nvdimm update * @param[in] i_cur_image - current installed image on the nvdimm - + * @return error if failure during check, else false */ errlHndl_t isUpdateNeeded(bool & o_update_needed, NvdimmLidImage * i_lid_image, NvdimmInstalledImage * i_cur_image); + /** + * @brief Update the list of NVDIMMs with the supplied LID image + * @param[in] i_lidImage - LID image for update + * @param[in] i_list - List of installed NVDIMMs to update + * @return true if no errors reported, else false + */ + bool runUpdateUsingLid(NvdimmLidImage * i_lidImage, + std::vector<NvdimmInstalledImage *> &i_list); + private: // Force user to supply NVDIMM list NvdimmsUpdate(); diff --git a/src/usr/isteps/nvdimm/nvdimmdd.C b/src/usr/isteps/nvdimm/nvdimmdd.C index 94bce8cc1..730fe0271 100755 --- a/src/usr/isteps/nvdimm/nvdimmdd.C +++ b/src/usr/isteps/nvdimm/nvdimmdd.C @@ -820,7 +820,8 @@ errlHndl_t nvdimmWrite ( TARGETING::Target * i_target, // Wait for NVDIMM to write data to its internal memory // i_i2cInfo.writeCycleTime value in milliseconds - nanosleep( 0, i_i2cInfo.writeCycleTime * NS_PER_MSEC ); + //nanosleep( 0, i_i2cInfo.writeCycleTime * NS_PER_MSEC ); + nanosleep( 0, 10000 ); // 10 microseconds (needed for nvdimm update) // Update how much data was written total_bytes_written += newBufLen; diff --git a/src/usr/isteps/nvdimm/runtime/nvdimm_rt.C b/src/usr/isteps/nvdimm/runtime/nvdimm_rt.C index a920f5ef9..68b9559f3 100644 --- a/src/usr/isteps/nvdimm/runtime/nvdimm_rt.C +++ b/src/usr/isteps/nvdimm/runtime/nvdimm_rt.C @@ -345,6 +345,21 @@ bool nvdimmArm(TARGETING::TargetHandleList &i_nvdimmTargetList) continue; } + l_err = nvdimmSetESPolicy(l_nvdimm); + if (l_err) + { + o_arm_successful = false; + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + + // Committing the error as we don't want this to interrupt + // the boot. This will notify the user that action is needed + // on this module + l_err->setSev(ERRORLOG::ERRL_SEV_PREDICTIVE); + l_err->collectTrace(NVDIMM_COMP_NAME, 1024); + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + l_err = NVDIMM::nvdimmChangeArmState(l_nvdimm, ARM_TRIGGER); // If we run into any error here we will just // commit the error log and move on. Let the diff --git a/src/usr/util/utilmclmgr.C b/src/usr/util/utilmclmgr.C index 89392baf7..334d0af1d 100644 --- a/src/usr/util/utilmclmgr.C +++ b/src/usr/util/utilmclmgr.C @@ -47,6 +47,7 @@ const ComponentID g_MclCompId {"MSTCONT"}; const ComponentID g_PowervmCompId {"POWERVM"}; const ComponentID g_OpalCompId {"OPAL"}; const ComponentID g_UcdCompId {"UCD9090"}; +const ComponentID g_NvdimmCompId {"NVDIMM"}; void compIdToString(const ComponentID i_compId, CompIdString o_compIdStr) { |

