Skip to content

Commit

Permalink
NVDIMM update - Clear FW Ops Status verification
Browse files Browse the repository at this point in the history
SMART is recommending after 0x41=0x02 (Clear FW Ops Status) that
the IBM utility read 0x71 to verify that the OPS_SUCCESS and
OPS_ERROR bits have been cleared and that only
Bit 2 (FIRMWARE_UPDATE_MODE) is set.

First of two fixes for SW469894

Change-Id: Id9e9d7fa5abd5bcd3bae806b27332513f48956b2
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/83946
Reviewed-by: TSUNG K YEUNG <tyeung@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@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: Corey V Swenson <cswenson@us.ibm.com>
Reviewed-by: Daniel M Crowell <dcrowell@us.ibm.com>
  • Loading branch information
mderkse1 authored and dcrowell77 committed Nov 5, 2019
1 parent 51aae24 commit e01fc4b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/include/usr/isteps/nvdimm/nvdimmreasoncodes.H
Expand Up @@ -109,6 +109,7 @@ enum nvdimmModuleId
NVDIMM_CHECK_FW_SLOT = 0x3B,
NVDIMM_ARM_PRE_CHECK = 0x3C,
NVDIMM_ARM = 0x3D,
CLEAR_FW_OPS_STATUS = 0x3E,
};

/**
Expand Down Expand Up @@ -204,6 +205,7 @@ enum nvdimmReasonCode
NVDIMM_ARM_PRE_CHECK_FAILED = NVDIMM_COMP_ID | 0x52,
NVDIMM_ARM_ENCRYPTION_UNLOCK_FAILED = NVDIMM_COMP_ID | 0x53,
NVDIMM_ARM_RETRY = NVDIMM_COMP_ID | 0x54,
NVDIMM_CLEAR_FW_OPS_STATUS_TIMEOUT = NVDIMM_COMP_ID | 0x55, // Failed to clear FW_OPS_STATUS
};

enum UserDetailsTypes
Expand Down
119 changes: 112 additions & 7 deletions src/usr/isteps/nvdimm/nvdimm_update.C
Expand Up @@ -266,7 +266,8 @@ 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_timeout(INVALID_TIMEOUT),
iv_max_blocks_per_region(INVALID_REGION_BLOCK_SIZE)
iv_max_blocks_per_region(INVALID_REGION_BLOCK_SIZE),
iv_fw_update_mode_enabled(false)
{
// initialize to invalid values
}
Expand Down Expand Up @@ -356,8 +357,7 @@ 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();
////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -432,8 +432,6 @@ errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage)
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");
Expand Down Expand Up @@ -649,7 +647,6 @@ errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage)

// 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)
{
Expand Down Expand Up @@ -709,7 +706,7 @@ errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage)
} while (0);

// If update operation is aborted, we need to disable update mode
if (l_fw_update_mode_enabled)
if (iv_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);
Expand Down Expand Up @@ -1034,6 +1031,17 @@ errlHndl_t NvdimmInstalledImage::changeFwUpdateMode(fw_update_mode i_mode)
nvdimmAddPage4Regs(iv_dimm,l_err);
nvdimmAddUpdateRegs(iv_dimm,l_err);
}
else
{
if (opStatus.fw_ops_update_mode == 1)
{
iv_fw_update_mode_enabled = true;
}
else
{
iv_fw_update_mode_enabled = false;
}
}
}
}
}
Expand Down Expand Up @@ -1259,6 +1267,103 @@ errlHndl_t NvdimmInstalledImage::clearFwOpsStatus()
"NVDIMM 0x%.8X clear FIRMWARE_OPS_STATUS register failed",
TARGETING::get_huid(iv_dimm));
}
else
{
// Verify expected bits cleared

// Setup expected cleared status byte
fw_ops_status_t l_cleared_ops_status;
l_cleared_ops_status.whole = 0x00;
if (iv_fw_update_mode_enabled)
{
// set BIT 2 -- this should not be cleared by the command
l_cleared_ops_status.fw_ops_update_mode = 1;
}

// Set some timeout so this doesn't cause endless loop
uint16_t timeout_val = INVALID_TIMEOUT;
l_err = getFwOpsTimeout(timeout_val);
// Note: potential error will just exit the while loop and be returned

// convert seconds to ms value
// double the timeout to ensure enough time has elapsed for the clear
// note: doubling here instead of just doubling timeout_val since that
// variable is only a bit16 vs bit32
uint32_t timeout_ms_val = timeout_val * 1000 * 2;

fw_ops_status_t l_ops_status;

while (!l_err)
{
l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_STATUS, l_ops_status.whole);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"clearFwOpsStatus: "
"NVDIMM 0x%.8X read FIRMWARE_OPS_STATUS register failed "
" (0x%02X)",
TARGETING::get_huid(iv_dimm), l_ops_status.whole);
break;
}

// Exit if expected cleared status is found
if (l_ops_status.whole == l_cleared_ops_status.whole)
{
break;
}

// wait 1 millisecond between checking status
if (timeout_ms_val > 0)
{
timeout_ms_val -= 1;
nanosleep(0, NS_PER_MSEC);
}
else
{
// timeout hit
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"clearFwOpsStatus: "
"NVDIMM 0x%.8X FIRMWARE_OPS_STATUS register reads 0x%02X "
"instead of cleared value of 0x%02X after %lld seconds",
TARGETING::get_huid(iv_dimm), l_ops_status.whole,
l_cleared_ops_status.whole, timeout_val*2);

/*@
*@errortype
*@moduleid CLEAR_FW_OPS_STATUS
*@reasoncode NVDIMM_CLEAR_FW_OPS_STATUS_TIMEOUT
*@userdata1 NVDIMM Target Huid
*@userdata2[0:7] Last FIRMWARE_OPS_STATUS read
*@userdata2[8:15] Expected cleared status
*@userdata2[16:31] Reserved
*@userdata2[32:63] Timeout (seconds)
*@devdesc FIRMWARE_OPS_STATUS not cleared
*@custdesc NVDIMM not updated
*/
l_err = new ERRORLOG::ErrlEntry(
ERRORLOG::ERRL_SEV_PREDICTIVE,
CLEAR_FW_OPS_STATUS,
NVDIMM_CLEAR_FW_OPS_STATUS_TIMEOUT,
TARGETING::get_huid(iv_dimm),
TWO_UINT16_ONE_UINT32_TO_UINT64
(
TWO_UINT8_TO_UINT16(
l_ops_status.whole,
l_cleared_ops_status.whole),
0x0000,
timeout_val * 2
),
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;
}
} // end of while (!l_err) loop
} // end of Verify expected bits cleared

return l_err;
}

Expand Down
3 changes: 3 additions & 0 deletions src/usr/isteps/nvdimm/nvdimm_update.H
Expand Up @@ -255,6 +255,9 @@ class NvdimmInstalledImage
// maximum blocks allowed per region (REGION_BLOCK_SIZE)
uint8_t iv_max_blocks_per_region;

// set to true when doing update
bool iv_fw_update_mode_enabled;

// Helper functions for updating the installed lid
/**
* @brief Transfer a region of bytes in multiple 32-byte blocks
Expand Down

0 comments on commit e01fc4b

Please sign in to comment.