summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatt Derksen <mderkse1@us.ibm.com>2019-02-01 17:08:05 -0600
committerDaniel M. Crowell <dcrowell@us.ibm.com>2019-03-29 10:21:40 -0500
commitf5ab52ab71170611fbf1bdc2afd6321b13d931ad (patch)
tree639ecd5ab61a9b902c5ba3ecba51b7d2682fdcdd /src
parent33e7c6de0769ae7f408e64995e7976717ad47653 (diff)
downloadtalos-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.H20
-rw-r--r--src/include/usr/util/utilmclmgr.H1
-rw-r--r--src/usr/isteps/nvdimm/nvdimm.C5
-rw-r--r--src/usr/isteps/nvdimm/nvdimm.H18
-rw-r--r--src/usr/isteps/nvdimm/nvdimm_update.C1927
-rw-r--r--src/usr/isteps/nvdimm/nvdimm_update.H237
-rwxr-xr-xsrc/usr/isteps/nvdimm/nvdimmdd.C3
-rw-r--r--src/usr/isteps/nvdimm/runtime/nvdimm_rt.C15
-rw-r--r--src/usr/util/utilmclmgr.C1
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)
{
OpenPOWER on IntegriCloud