diff options
Diffstat (limited to 'src/usr/isteps/nvdimm/nvdimm.C')
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.C | 5026 |
1 files changed, 4687 insertions, 339 deletions
diff --git a/src/usr/isteps/nvdimm/nvdimm.C b/src/usr/isteps/nvdimm/nvdimm.C index 79d7b679d..e93271e5e 100644 --- a/src/usr/isteps/nvdimm/nvdimm.C +++ b/src/usr/isteps/nvdimm/nvdimm.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2014,2019 */ +/* Contributors Listed Below - COPYRIGHT 2014,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -28,6 +28,8 @@ #include <errl/errlentry.H> #include <errl/errlmanager.H> #include <errl/errludtarget.H> +#include <errl/errludlogregister.H> +#include <errl/errludstring.H> #include <targeting/common/commontargeting.H> #include <targeting/common/util.H> #include <targeting/common/utilFilter.H> @@ -36,33 +38,39 @@ #include <fapi2.H> #include <fapi2/plat_hwp_invoker.H> #include <lib/shared/nimbus_defaults.H> +#include <lib/ccs/ccs_nimbus.H> #include <lib/dimm/ddr4/nvdimm_utils.H> #include <lib/mc/port.H> #include <isteps/nvdimm/nvdimmreasoncodes.H> +#include "errlud_nvdimm.H" +#include "nvdimmErrorLog.H" #include <isteps/nvdimm/nvdimm.H> #include <vpd/spdenums.H> +#include <secureboot/trustedbootif.H> +#include <targeting/common/targetUtil.H> +#ifdef __HOSTBOOT_RUNTIME +#include <runtime/hbrt_utilities.H> +#include <targeting/runtime/rt_targeting.H> +#else +#include <initservice/istepdispatcherif.H> +#endif using namespace TARGETING; using namespace DeviceFW; using namespace EEPROM; +using namespace ERRORLOG; trace_desc_t* g_trac_nvdimm = NULL; TRAC_INIT(&g_trac_nvdimm, NVDIMM_COMP_NAME, 2*KILOBYTE); // Easy macro replace for unit testing -#define TRACUCOMP(args...) TRACFCOMP(args) -//#define TRACUCOMP(args...) +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) namespace NVDIMM { #define NUM_OFFSET 2 -#define NVDIMM_SET_USER_DATA_1(left_32_ops_id, right_32_huid) \ - TWO_UINT32_TO_UINT64(left_32_ops_id, right_32_huid) - -#define NVDIMM_SET_USER_DATA_2_TIMEOUT(left_32_polled, right_32_timeout) \ - NVDIMM_SET_USER_DATA_1(left_32_polled, right_32_timeout) - typedef struct ops_timeoutInfo{ const char * desc; @@ -83,6 +91,173 @@ constexpr ops_timeoutInfo_t timeoutInfoTable[] = {"CHARGE", {ES_CHARGE_TIMEOUT1, ES_CHARGE_TIMEOUT0}, CHARGE , MODULE_HEALTH_STATUS1, CHARGE_IN_PROGRESS}, }; +// Definition of ENCRYPTION_CONFIG_STATUS -- page 5 offset 0x20 +typedef union { + uint8_t whole; + struct + { + uint8_t reserved : 1; // [7] + uint8_t unsupported_field : 1; // [6] + uint8_t erase_pending : 1; // [5] + uint8_t encryption_unlocked : 1; // [4] + uint8_t encryption_enabled : 1; // [3] + uint8_t erase_key_present : 1; // [2] + uint8_t random_string_present : 1; // [1] + uint8_t encryption_supported : 1; // [0] + } PACKED; +} encryption_config_status_t; + +// Valid bits to check against (skips reserved and unsupported) +static constexpr uint8_t ENCRYPTION_STATUS_CHECK_MASK = 0x3F; +static constexpr uint8_t ENCRYPTION_STATUS_DISABLED = 0x01; +static constexpr uint8_t ENCRYPTION_STATUS_ENABLED = 0x1F; + +// NV_STATUS masks +static constexpr uint8_t NV_STATUS_OR_MASK = 0xFB; +static constexpr uint8_t NV_STATUS_AND_MASK = 0x04; +static constexpr uint8_t NV_STATUS_UNPROTECTED_SET = 0x01; +static constexpr uint8_t NV_STATUS_UNPROTECTED_CLR = 0xFE; +static constexpr uint8_t NV_STATUS_ENCRYPTION_SET = 0x10; +static constexpr uint8_t NV_STATUS_ENCRYPTION_CLR = 0xEF; +static constexpr uint8_t NV_STATUS_ERASE_VERIFY_SET = 0x20; +static constexpr uint8_t NV_STATUS_ERASE_VERIFY_CLR = 0xDF; +static constexpr uint8_t NV_STATUS_POSSIBLY_UNPROTECTED_SET = 0x40; + +// NVDIMM key consts +static constexpr size_t NUM_KEYS_IN_ATTR = 3; +static constexpr size_t MAX_TPM_SIZE = 34; +static constexpr uint8_t KEY_TERMINATE_BYTE = 0x00; +static constexpr uint8_t KEY_ABORT_BYTE = 0xFF; + +// NVDIMM CSAVE_FAIL_INFO1 Bit mask +// Currently only bits 1:6 need to be checked during init +static constexpr uint8_t CSAVE_FAIL_BITS_MASK = 0x7E; + +// LOG PAGE INFO +static constexpr size_t VENDOR_LOG_UNIT_SIZE = 256; +static constexpr size_t VENDOR_LOG_BLOCK_SIZE = 32; +static constexpr size_t VENDOR_BLOCK_DATA_BYTES = 32; + +// TYPED_BLOCK_DATA +static constexpr uint8_t VENDOR_DATA_TYPE = 0x04; +static constexpr uint8_t VENDOR_DEFAULT = 0x00; +static constexpr uint8_t FIRMWARE_IMAGE_DATA = 0x02; + +// Commands to OPERATIONAL_UNIT_OPS_CMD +static constexpr uint8_t GET_OPERATIONAL_UNIT = 0x01; +static constexpr uint8_t GENERATE_OPERATIONAL_UNIT_CKSUM = 0x08; + +static constexpr uint8_t MSBIT_SET_MASK = 0x80; +static constexpr uint8_t MSBIT_CLR_MASK = 0x7F; +static constexpr uint8_t OPERATION_SLEEP_SECONDS = 0x1; + +// Bit mask for checking the fw slot running +static constexpr uint8_t RUNNING_FW_SLOT = 0xF0; + +// NOTE: If the ARM_MAX_RETRY_COUNT is greater than 1 then +// previous error logs may be lost and not reported +static constexpr size_t ARM_MAX_RETRY_COUNT = 1; +static constexpr uint8_t FW_OPS_UPDATE = 0x04; + +// Secure erase verify operations +static constexpr uint8_t ERASE_VERIFY_CLEAR = 0x00; +static constexpr uint8_t ERASE_VERIFY_START = 0xC0; +static constexpr uint8_t ERASE_VERIFY_TRIGGER = 0x80; + +#ifndef __HOSTBOOT_RUNTIME +// Warning thresholds +static constexpr uint8_t THRESHOLD_ES_LIFETIME = 0x07; // 7% +static constexpr uint8_t THRESHOLD_NVM_LIFETIME = 0x31; // 49% + +// 12 bit fixed point temperature in celsius degrees +// with following bit format: +// [15:13]Reserved +// [12]Sign 0 = positive, 1 = negative The value of 0 C should be expressed as a positive value +// [11]128 [10]64 [9]32 [8]16 [7]8 [6]4 [5]2 [4]1 [3]0.5 [2]0.25 +// [1]0.125 Optional for temperature reporting fields; not used for temperature threshold fields +// [0]0.0625 Optional for temperature reporting fields; not used for temperature threshold fields +static constexpr uint8_t THRESHOLD_ES_TEMP_HIGH_1 = 0x03; // 52.5 C +static constexpr uint8_t THRESHOLD_ES_TEMP_HIGH_0 = 0x48; // 52.5 C +static constexpr uint8_t THRESHOLD_ES_TEMP_LOW_1 = 0x00; // 2.5 C +static constexpr uint8_t THRESHOLD_ES_TEMP_LOW_0 = 0x28; // 2.5 C +#endif + +// Definition of ENCRYPTION_KEY_VALIDATION -- page 5 offset 0x2A +typedef union { + uint8_t whole; + struct + { + uint8_t reserved : 5; // [7:3] + uint8_t keys_validated : 1; // [2] + uint8_t access_key_valid : 1; // [1] + uint8_t erase_key_valid : 1; // [0] + } PACKED; +} encryption_key_validation_t; + +/** + * @brief Utility function to send the value of + * ATTR_NVDIMM_ARMED to the FSP + */ +void send_ATTR_NVDIMM_ARMED( Target* i_nvdimm, + ATTR_NVDIMM_ARMED_type& i_val ); + +/** + * @brief Utility function to set ATTR_NVDIMM_ENCRYPTION_KEYS_FW + * and send the value to the FSP + */ +void set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW( + ATTR_NVDIMM_ENCRYPTION_KEYS_FW_typeStdArr& i_val ) +{ + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW: no TopLevelTarget"); + + l_sys->setAttrFromStdArr + <ATTR_NVDIMM_ENCRYPTION_KEYS_FW>(i_val); + +#ifdef __HOSTBOOT_RUNTIME + errlHndl_t l_err = nullptr; + + // Send attr to HWSV if at runtime + AttributeTank::Attribute l_attr = {}; + if( !makeAttributeStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_FW> + (l_sys, l_attr) ) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW() Could not create Attribute"); + /*@ + *@errortype + *@reasoncode NVDIMM_CANNOT_MAKE_ATTRIBUTE + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid SET_ATTR_NVDIMM_ENCRYPTION_KEYS_FW + *@devdesc Couldn't create an Attribute to send the data + * to the FSP + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + SET_ATTR_NVDIMM_ENCRYPTION_KEYS_FW, + NVDIMM_CANNOT_MAKE_ATTRIBUTE, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + else + { + std::vector<TARGETING::AttributeTank::Attribute> l_attrList; + l_attrList.push_back(l_attr); + l_err = sendAttributes( l_attrList ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW() Error sending ATTR_NVDIMM_ENCRYPTION_KEYS_FW down to FSP"); + l_err->setSev(ERRORLOG::ERRL_SEV_PREDICTIVE); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + } +#endif //__HOSTBOOT_RUNTIME + +} + /** * @brief Wrapper to call deviceOp to read the NV controller via I2C * @@ -103,8 +278,8 @@ errlHndl_t nvdimmReadReg(Target* i_nvdimm, uint8_t & o_data, const bool page_verify) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Read HUID %X, addr 0x%X", - TARGETING::get_huid(i_nvdimm), i_addr); + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Read HUID 0x%X, addr 0x%X", + get_huid(i_nvdimm), i_addr); errlHndl_t l_err = nullptr; size_t l_numBytes = 1; @@ -123,7 +298,7 @@ errlHndl_t nvdimmReadReg(Target* i_nvdimm, if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReadReg() nvdimm[%X] - failed to read the current page", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } @@ -134,7 +309,7 @@ errlHndl_t nvdimmReadReg(Target* i_nvdimm, if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReadReg() nvdimm[%X] - failed to verify page", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } } @@ -144,11 +319,16 @@ errlHndl_t nvdimmReadReg(Target* i_nvdimm, i_nvdimm, &o_data, l_numBytes, - DEVICE_NVDIMM_ADDRESS(l_reg_addr)); + DEVICE_NVDIMM_RAW_ADDRESS(l_reg_addr)); }while(0); - TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Read HUID %X, page 0x%X, addr 0x%X = %X", - TARGETING::get_huid(i_nvdimm), l_reg_page, l_reg_addr, o_data); + if (l_err) + { + nvdimmAddPage4Regs(i_nvdimm,l_err); + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Read HUID 0x%X, page 0x%X, addr 0x%X = 0x%X", + get_huid(i_nvdimm), l_reg_page, l_reg_addr, o_data); return l_err; } @@ -173,8 +353,8 @@ errlHndl_t nvdimmWriteReg(Target* i_nvdimm, uint8_t i_data, const bool page_verify) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Write HUID %X, addr 0x%X = %X", - TARGETING::get_huid(i_nvdimm), i_addr, i_data); + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Write HUID 0x%X, addr 0x%X = 0x%X", + get_huid(i_nvdimm), i_addr, i_data); errlHndl_t l_err = nullptr; size_t l_numBytes = 1; @@ -193,7 +373,7 @@ errlHndl_t nvdimmWriteReg(Target* i_nvdimm, if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmWriteReg() nvdimm[%X] - failed to read the current page", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } @@ -204,7 +384,7 @@ errlHndl_t nvdimmWriteReg(Target* i_nvdimm, if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmWriteReg() nvdimm[%X] - failed to verify page", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } } @@ -214,11 +394,16 @@ errlHndl_t nvdimmWriteReg(Target* i_nvdimm, i_nvdimm, &i_data, l_numBytes, - DEVICE_NVDIMM_ADDRESS(l_reg_addr)); + DEVICE_NVDIMM_RAW_ADDRESS(l_reg_addr)); }while(0); - TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Write HUID %X, page = 0x%X, addr 0x%X = %X", - TARGETING::get_huid(i_nvdimm), l_reg_page, l_reg_addr, i_data); + if (l_err) + { + nvdimmAddPage4Regs(i_nvdimm,l_err); + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Write HUID 0x%X, page = 0x%X, addr 0x%X = 0x%X", + get_huid(i_nvdimm), l_reg_page, l_reg_addr, i_data); return l_err; } @@ -234,43 +419,48 @@ errlHndl_t nvdimmWriteReg(Target* i_nvdimm, void nvdimmSetStatusFlag(Target *i_nvdimm, const uint8_t i_status_flag) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X]" - ,TARGETING::get_huid(i_nvdimm), i_status_flag); + ,get_huid(i_nvdimm), i_status_flag); - auto l_statusFlag = i_nvdimm->getAttr<TARGETING::ATTR_NV_STATUS_FLAG>(); + auto l_statusFlag = i_nvdimm->getAttr<ATTR_NV_STATUS_FLAG>(); switch(i_status_flag) { - // Make sure NSTD_VAL_PRSV (content preserved) is unset before setting NSTD_VAL_NOPRSV - // (data not preserved) or NSTD_ERR_NOPRSV (error preserving data) + // Make sure NSTD_VAL_RESTORED (content preserved) is unset before setting NSTD_VAL_ERASED + // (data not preserved) or NSTD_VAL_SR_FAILED (error preserving data) case NSTD_ERR: - case NSTD_VAL_NOPRSV: - case NSTD_ERR_NOPRSV: - l_statusFlag &= NSTD_VAL_PRSV_MASK; + case NSTD_VAL_ERASED: + case NSTD_VAL_SR_FAILED: + l_statusFlag &= NSTD_VAL_RESTORED_MASK; l_statusFlag |= i_status_flag; break; // If the content preserved(restore sucessfully), make sure - // NSTD_VAL_NOPRSV (not preserved) and NSTD_ERR_NOPRSV (error preserving) + // NSTD_VAL_ERASED (not preserved) and NSTD_VAL_SR_FAILED (error preserving) // are unset before setting this flag. - case NSTD_VAL_PRSV: - l_statusFlag &= (NSTD_VAL_NOPRSV_MASK & NSTD_ERR_NOPRSV_MASK); + case NSTD_VAL_RESTORED: + l_statusFlag &= (NSTD_VAL_ERASED_MASK & NSTD_VAL_SR_FAILED_MASK); l_statusFlag |= i_status_flag; break; - case NSTD_ERR_NOBKUP: + case NSTD_VAL_DISARMED: + l_statusFlag |= i_status_flag; + break; + + // Error detected but save/restore might work. May coexsit with other bits. + case NSTD_ERR_VAL_SR: l_statusFlag |= i_status_flag; break; default: assert(0, "nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X] invalid flag!", - TARGETING::get_huid(i_nvdimm), i_status_flag); + get_huid(i_nvdimm), i_status_flag); break; } - i_nvdimm->setAttr<TARGETING::ATTR_NV_STATUS_FLAG>(l_statusFlag); + i_nvdimm->setAttr<ATTR_NV_STATUS_FLAG>(l_statusFlag); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X]" - ,TARGETING::get_huid(i_nvdimm), i_status_flag); + ,get_huid(i_nvdimm), i_status_flag); } @@ -284,10 +474,11 @@ void nvdimmSetStatusFlag(Target *i_nvdimm, const uint8_t i_status_flag) */ errlHndl_t nvdimmReady(Target *i_nvdimm) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmReady() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmReady() HUID[%X]",get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; - uint8_t l_data = 0x0; + nvdimm_reg_t l_RegInfo; + uint8_t l_data; uint8_t l_nvm_init_time = 0; size_t l_numBytes = 1; @@ -300,17 +491,17 @@ errlHndl_t nvdimmReady(Target *i_nvdimm) DEVICE_SPD_ADDRESS(SPD::NVM_INIT_TIME)); TRACUCOMP(g_trac_nvdimm, "nvdimmReady() HUID[%X] l_nvm_init_time = %u", - TARGETING::get_huid(i_nvdimm), l_nvm_init_time); + get_huid(i_nvdimm), l_nvm_init_time); if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReady() nvdimm[%X] - failed to retrieve NVM_INIT_TIME from SPD", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } - // Convert to ms for polling - uint32_t l_nvm_init_time_ms = l_nvm_init_time * MS_PER_SEC; + // Convert to ms for polling and double the value to avoid edge condition + uint32_t l_nvm_init_time_ms = l_nvm_init_time * MS_PER_SEC * 2; uint32_t l_poll = 0; do @@ -320,7 +511,7 @@ errlHndl_t nvdimmReady(Target *i_nvdimm) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReady() nvdimm[%X] - error getting ready status[%d]", - TARGETING::get_huid(i_nvdimm), l_data); + get_huid(i_nvdimm), l_data); break; } @@ -336,8 +527,50 @@ errlHndl_t nvdimmReady(Target *i_nvdimm) if ((l_data != NV_READY) && !l_err) { + + // Collect available status registers for error log + do + { + // Read and save NVDIMM_READY for traces + l_err = nvdimmReadReg(i_nvdimm, NVDIMM_READY, l_data); + if(l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + break; + } + l_RegInfo.NVDimm_Ready = l_data; + + // Read and save MODULE_HEALTH for traces + l_err = nvdimmReadReg(i_nvdimm, MODULE_HEALTH, l_data); + if(l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + break; + } + l_RegInfo.Module_Health = l_data; + + // Read and save MODULE_HEALTH_STATUS0 for traces + l_err = nvdimmReadReg(i_nvdimm, MODULE_HEALTH_STATUS0, l_data); + if(l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + break; + } + l_RegInfo.Module_Health_Status0 = l_data; + + // Read and save MODULE_HEALTH_STATUS1 for traces + l_err = nvdimmReadReg(i_nvdimm, MODULE_HEALTH_STATUS1, l_data); + if(l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + break; + } + l_RegInfo.Module_Health_Status1 = l_data; + + }while(0); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReady() nvdimm[%X] - nvdimm not ready[%d]", - TARGETING::get_huid(i_nvdimm), l_data); + get_huid(i_nvdimm), l_data); /*@ *@errortype *@reasoncode NVDIMM_NOT_READY @@ -350,26 +583,33 @@ errlHndl_t nvdimmReady(Target *i_nvdimm) * for host access. (userdata1 != 0xA5) *@custdesc NVDIMM not ready */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, - NVDIMM_CHECK_READY, - NVDIMM_NOT_READY, - NVDIMM_SET_USER_DATA_1(l_data, TARGETING::get_huid(i_nvdimm)), - 0x0, - ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_CHECK_READY, + NVDIMM_NOT_READY, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); - l_err->collectTrace(NVDIMM_COMP_NAME, 1024 ); + l_err->collectTrace(NVDIMM_COMP_NAME); // If nvdimm is not ready for access by now, this is // a failing indication on the NV controller - l_err->addPartCallout( i_nvdimm, - HWAS::NV_CONTROLLER_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + + // Add Register Traces to error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); } }while(0); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmReady() HUID[%X] ready[%X]", - TARGETING::get_huid(i_nvdimm), l_data); + get_huid(i_nvdimm), l_data); return l_err; } @@ -386,7 +626,7 @@ errlHndl_t nvdimmReady(Target *i_nvdimm) */ errlHndl_t nvdimmResetController(Target *i_nvdimm) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmResetController() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmResetController() HUID[%X]",get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; do @@ -396,7 +636,7 @@ errlHndl_t nvdimmResetController(Target *i_nvdimm) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmResetController() nvdimm[%X] - error reseting the controller", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } @@ -404,12 +644,17 @@ errlHndl_t nvdimmResetController(Target *i_nvdimm) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmResetController() nvdimm[%X] - not ready after reset.", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); } }while(0); - TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmResetController() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + // Reset will lock encryption so unlock again + TargetHandleList l_nvdimmTargetList; + l_nvdimmTargetList.push_back(i_nvdimm); + nvdimm_encrypt_unlock(l_nvdimmTargetList); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmResetController() HUID[%X]",get_huid(i_nvdimm)); return l_err; } @@ -436,8 +681,8 @@ errlHndl_t nvdimmPollStatus ( Target *i_nvdimm, bool l_done = false; // Get the timeout value for ops_id - assert(i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), - "nvdimmPollStatus() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", TARGETING::get_huid(i_nvdimm)); + assert(i_nvdimm->tryGetAttr<ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), + "nvdimmPollStatus() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", get_huid(i_nvdimm)); uint32_t l_timeout = l_target_timeout_values[i_ops_id]; do @@ -461,13 +706,13 @@ errlHndl_t nvdimmPollStatus ( Target *i_nvdimm, o_poll += OPS_POLL_TIME_MS; - } while (o_poll < l_timeout); + } while (o_poll <= l_timeout); if (!l_done && !l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmPollStatus() nvdimm[%X] - Status timed out ops_id[%d]", - TARGETING::get_huid(i_nvdimm), i_ops_id); + get_huid(i_nvdimm), i_ops_id); /*@ *@errortype *@reasoncode NVDIMM_STATUS_TIMEOUT @@ -481,20 +726,17 @@ errlHndl_t nvdimmPollStatus ( Target *i_nvdimm, * Refer to userdata1 for which operation it timed out. *@custdesc NVDIMM timed out */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, - NVDIMM_POLL_STATUS, - NVDIMM_STATUS_TIMEOUT, - NVDIMM_SET_USER_DATA_1(i_ops_id, TARGETING::get_huid(i_nvdimm)), - NVDIMM_SET_USER_DATA_2_TIMEOUT(o_poll, l_timeout), - ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); - - l_err->collectTrace(NVDIMM_COMP_NAME, 1024 ); - - // May have to move the error handling to the caller - // as different op could have different error severity - l_err->addPartCallout( i_nvdimm, - HWAS::NV_CONTROLLER_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_STATUS, + NVDIMM_STATUS_TIMEOUT, + NVDIMM_SET_USER_DATA_1(i_ops_id, get_huid(i_nvdimm)), + NVDIMM_SET_USER_DATA_2_TIMEOUT(o_poll, l_timeout), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); } return l_err; @@ -516,14 +758,46 @@ errlHndl_t nvdimmPollBackupDone(Target* i_nvdimm, uint32_t &o_poll) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollBackupDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; + nvdimm_reg_t l_RegInfo = nvdimm_reg_t(); l_err = nvdimmPollStatus ( i_nvdimm, SAVE, o_poll); + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + + /*@ + *@errortype + *@reasoncode NVDIMM_BACKUP_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_POLL_BACKUP + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@devdesc Encountered timeout while performing NVDIMM Restore operation + *@custdesc NVDIMM timed out + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_BACKUP, + NVDIMM_BACKUP_TIMEOUT, + NVDIMM_SET_USER_DATA_1(SAVE, TARGETING::get_huid(i_nvdimm)), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + } + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollBackupDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } @@ -543,18 +817,57 @@ errlHndl_t nvdimmPollRestoreDone(Target* i_nvdimm, uint32_t &o_poll) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollRestoreDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; + nvdimm_reg_t l_RegInfo = nvdimm_reg_t(); l_err = nvdimmPollStatus ( i_nvdimm, RESTORE, o_poll ); + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + + /*@ + *@errortype + *@reasoncode NVDIMM_RESTORE_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_POLL_RESTORE + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@devdesc Encountered timeout while performing NVDIMM Restore operation + *@custdesc NVDIMM timed out + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_RESTORE, + NVDIMM_RESTORE_TIMEOUT, + NVDIMM_SET_USER_DATA_1(RESTORE, TARGETING::get_huid(i_nvdimm)), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // May have to move the error handling to the caller + // as different op could have different error severity + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + } + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollRestoreDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } + /** * @brief This function polls the command status register for erase * completion (does not indicate success or fail) @@ -570,14 +883,39 @@ errlHndl_t nvdimmPollEraseDone(Target* i_nvdimm, uint32_t &o_poll) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollEraseDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; - l_err = nvdimmPollStatus ( i_nvdimm, ERASE, o_poll); + l_err = nvdimmPollStatus( i_nvdimm, ERASE, o_poll); + + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + + /*@ + *@errortype + *@reasoncode NVDIMM_ERASE_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_POLL_ERASE + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@devdesc Encountered timeout while performing NVDIMM Restore operation + *@custdesc NVDIMM timed out + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_ERASE, + NVDIMM_ERASE_TIMEOUT, + NVDIMM_SET_USER_DATA_1(ERASE, TARGETING::get_huid(i_nvdimm)), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); + } TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollEraseDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } @@ -598,14 +936,18 @@ errlHndl_t nvdimmPollESChargeStatus(Target* i_nvdimm, uint32_t &o_poll) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollESChargeDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; - l_err = nvdimmPollStatus ( i_nvdimm, CHARGE, o_poll ); + l_err = nvdimmPollStatus( i_nvdimm, CHARGE, o_poll ); + + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollESChargeDone() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } @@ -623,7 +965,7 @@ errlHndl_t nvdimmPollESChargeStatus(Target* i_nvdimm, errlHndl_t nvdimmGetRestoreValid(Target* i_nvdimm, uint8_t & o_rstrValid) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmGetRestoreValid() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; @@ -631,11 +973,11 @@ errlHndl_t nvdimmGetRestoreValid(Target* i_nvdimm, uint8_t & o_rstrValid) if (l_err){ TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], Error getting restore status!", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); } TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmGetRestoreValid() nvdimm[%X], restore_status[%x],", - TARGETING::get_huid(i_nvdimm), o_rstrValid); + get_huid(i_nvdimm), o_rstrValid); return l_err; } @@ -651,10 +993,11 @@ errlHndl_t nvdimmGetRestoreValid(Target* i_nvdimm, uint8_t & o_rstrValid) errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmSetESPolicy() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; - uint8_t l_data; + uint8_t l_data = 0x0; + nvdimm_reg_t l_RegInfo = nvdimm_reg_t(); do { @@ -663,9 +1006,9 @@ errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) if (l_err) { - nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOBKUP); + nvdimmSetStatusFlag(i_nvdimm, NSTD_VAL_DISARMED); TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSetESPolicy() nvdimm[%X]" - "failed to write ES register!",TARGETING::get_huid(i_nvdimm)); + "failed to write ES register!",get_huid(i_nvdimm)); break; } @@ -677,16 +1020,16 @@ errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) if (l_err) { - nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOBKUP); + nvdimmSetStatusFlag(i_nvdimm, NSTD_VAL_DISARMED); TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSetESPolicy() nvdimm[%X]" - "failed to read ES register!",TARGETING::get_huid(i_nvdimm)); + "failed to read ES register!",get_huid(i_nvdimm)); break; } - if ((l_data & ES_SUCCESS) != ES_SUCCESS) + if (((l_data & ES_SUCCESS) != ES_SUCCESS) || ((l_data & ES_POLICY_ERROR) == ES_POLICY_ERROR)) { TRACFCOMP(g_trac_nvdimm, EXIT_MRK"NDVIMM HUID[%X], nvdimmSetESPolicy() " - "failed!",TARGETING::get_huid(i_nvdimm)); + "failed!",get_huid(i_nvdimm)); /*@ *@errortype *@reasoncode NVDIMM_SET_ES_ERROR @@ -700,28 +1043,28 @@ errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) * NVDIMM is intact *@custdesc NVDIMM encountered error setting the energy source policy */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, - NVDIMM_SET_ES, - NVDIMM_SET_ES_ERROR, - NVDIMM_SET_USER_DATA_1(CHARGE, TARGETING::get_huid(i_nvdimm)), - 0x0, - ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); - - l_err->collectTrace(NVDIMM_COMP_NAME, 1024 ); - - // Failure setting the energy source policy could mean error on the - // battery or even the cabling - l_err->addPartCallout( i_nvdimm, - HWAS::BPM_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); - l_err->addPartCallout( i_nvdimm, - HWAS::BPM_CABLE_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_ES, + NVDIMM_SET_ES_ERROR, + NVDIMM_SET_USER_DATA_1(CHARGE, get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + + // Read relevant regs for trace data + nvdimmTraceRegs(i_nvdimm, l_RegInfo); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); } }while(0); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NDVIMM HUID[%X], nvdimmSetESPolicy()," - ,TARGETING::get_huid(i_nvdimm)); + ,get_huid(i_nvdimm)); return l_err; } @@ -739,7 +1082,7 @@ errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) errlHndl_t nvdimmChangeArmState(Target *i_nvdimm, bool i_state) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmChangeArmState() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; @@ -753,11 +1096,11 @@ errlHndl_t nvdimmChangeArmState(Target *i_nvdimm, bool i_state) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmChangeArmState() nvdimm[%X] error %s nvdimm!!", - TARGETING::get_huid(i_nvdimm), i_state? "arming" : "disarming"); + get_huid(i_nvdimm), i_state? "arming" : "disarming"); } TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmChangeArmState() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } @@ -774,7 +1117,7 @@ errlHndl_t nvdimmChangeArmState(Target *i_nvdimm, bool i_state) errlHndl_t nvdimmValidImage(Target *i_nvdimm, bool &o_imgValid) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmValidImage(): nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; uint8_t l_data = 0x0; @@ -785,7 +1128,7 @@ errlHndl_t nvdimmValidImage(Target *i_nvdimm, bool &o_imgValid) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmValidImage() nvdimm[%X]" - "failed to for image!",TARGETING::get_huid(i_nvdimm) ); + "failed to for image!",get_huid(i_nvdimm) ); } else if(l_data & VALID_IMAGE) { @@ -793,55 +1136,70 @@ errlHndl_t nvdimmValidImage(Target *i_nvdimm, bool &o_imgValid) } TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmValidImage(): nvdimm[%X] ret[%X]", - TARGETING::get_huid(i_nvdimm), l_data); + get_huid(i_nvdimm), l_data); return l_err; } +void maskMbacalfir_eventn(TARGETING::Target* i_nvdimm) +{ + errlHndl_t l_err = nullptr; + TargetHandleList l_mcaList; + uint64_t l_writeData; + uint32_t l_writeAddress; + size_t l_writeSize = sizeof(l_writeData); + + getParentAffinityTargets(l_mcaList, i_nvdimm, CLASS_UNIT, TYPE_MCA); + assert(l_mcaList.size(), "maskMbacalfir_eventn() failed to find parent MCA."); + + l_writeAddress = MBACALFIR_OR_MASK_REG; + l_writeData = MBACALFIR_EVENTN_OR_BIT; + l_err = deviceWrite(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK "Failed to mask MBACALFIR EventN using address " + "0x%08x on NVDIMM 0x%08X MCA 0x%08X", + l_writeAddress, get_huid(i_nvdimm), get_huid(l_mcaList[0])); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit( l_err, NVDIMM_COMP_ID ); + } +} + #ifndef __HOSTBOOT_RUNTIME /** * @brief This function handles all the restore related operations. * SRE -> restore -> SRX/RCD/MRS * - * @param[in] i_nvdimmList - list of nvdimms + * @param[in,out] io_nvdimmList - list of nvdimms. Each nvdimm is removed + * from the list after a successful restore. Leftover nvdimm + * is returned to the caller for error handling. * * @param[in] i_mpipl - MPIPL mode * * @return errlHndl_t - Null if successful, otherwise a pointer to * the error log. */ -errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) +errlHndl_t nvdimmRestore(TargetHandleList& io_nvdimmList, uint8_t &i_mpipl) { errlHndl_t l_err = nullptr; - bool l_imgValid; uint8_t l_rstrValid; uint32_t l_poll = 0; + TargetHandleList l_nvdimmList = io_nvdimmList; do { // Put NVDIMM into self-refresh - for (TargetHandleList::iterator it = i_nvdimmList.begin(); - it != i_nvdimmList.end();) + for (TargetHandleList::iterator it = io_nvdimmList.begin(); + it != io_nvdimmList.end();) { - l_err = nvdimmValidImage(*it, l_imgValid); - // No reason to run if we can't figure out - // if there is an image or not - if (l_err) - { - nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); - break; - } + // Default state during boot is unarmed, therefore not preserved + nvdimmSetStatusFlag(*it, NSTD_VAL_DISARMED); - if (!l_imgValid) - { - nvdimmSetStatusFlag(*it, NSTD_VAL_NOPRSV); - i_nvdimmList.erase(it); - continue; - } - - TARGETING::TargetHandleList l_mcaList; - getParentAffinityTargets(l_mcaList, *it, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, CLASS_UNIT, TYPE_MCA); assert(l_mcaList.size(), "nvdimmRestore() failed to find parent MCA."); fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); @@ -850,21 +1208,40 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) // is de-asserted before kicking off the restore if (i_mpipl) { + TRACFCOMP(g_trac_nvdimm, "nvdimmRestore(): in MPIPL"); + + // To avoid PRD error during mpipl need to Mask MBACALFIR EventN + // Note: a regular IPL will already have this masked + maskMbacalfir_eventn(*it); + + // Call init for error checking skipped in the SAVE step + nvdimm_init(*it); + FAPI_INVOKE_HWP(l_err, mss::ddr_resetn, l_fapi_mca, HIGH); if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] i_mpipl[%u] failed to de-assert resetn!", - TARGETING::get_huid(*it), i_mpipl); - - nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); - //@TODO RTC 199645 - add HW callout on dimm target - // If we failed to de-assert reset_n, the dimm is pretty much useless. - // Let's not restore if that happens - // The callout will be added inside the HWP - // Leaving this comment here as a reminder, will remove later + get_huid(*it), i_mpipl); break; } + + // In MPIPL, invalidate the BAR to prevent any traffic from stepping on + // the restore + FAPI_INVOKE_HWP(l_err, mss::nvdimm::change_bar_valid_state, l_fapi_mca, LOW); + + // This should not fail at all (scom read/write). If it does, post an informational log + // to leave some breadcrumbs + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] i_mpipl[%u] failed to invalidate BAR!", + get_huid(*it), i_mpipl); + + l_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + l_err->collectTrace( NVDIMM_COMP_NAME ); + ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); + } + } // Self-refresh is done at the port level @@ -873,13 +1250,7 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] self_refresh_entry failed!", - TARGETING::get_huid(*it)); - - nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); - //@TODO RTC 199645 - add HW callout on dimm target - // Without SRE the data could be not reliably restored - // The callout will be added inside the HWP - // Leaving this comment here as a reminder, will remove later + get_huid(*it)); break; } it++; @@ -890,21 +1261,14 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) break; } - // Nothing to do. Move on. - if (i_nvdimmList.empty()) - { - break; - } - // Kick off the restore on each nvdimm in the nvdimm list - for (const auto & l_nvdimm : i_nvdimmList) + for (const auto & l_nvdimm : io_nvdimmList) { l_err = nvdimmWriteReg(l_nvdimm, NVDIMM_FUNC_CMD, RESTORE_IMAGE); if (l_err) { - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], error initiating restore!!", - TARGETING::get_huid(l_nvdimm)); + get_huid(l_nvdimm)); break; } } @@ -915,7 +1279,7 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) } // Make sure the restore completed - for (const auto & l_nvdimm : i_nvdimmList) + for (const auto & l_nvdimm : io_nvdimmList) { // Since we kicked off the restore on all the modules at once, the restore // should complete on all of the modules in one restore window. Use the @@ -923,10 +1287,8 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) l_err = nvdimmPollRestoreDone(l_nvdimm, l_poll); if (l_err) { - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], error restoring!", - TARGETING::get_huid(l_nvdimm)); - errlCommit(l_err, NVDIMM_COMP_ID); + get_huid(l_nvdimm)); break; } } @@ -936,22 +1298,23 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) break; } - // Make sure the restore is valid - for (const auto & l_nvdimm : i_nvdimmList) + // Check for restore errors + for (TargetHandleList::iterator it = io_nvdimmList.begin(); + it != io_nvdimmList.end();) { - l_err = nvdimmGetRestoreValid(l_nvdimm, l_rstrValid); + l_err = nvdimmGetRestoreValid(*it, l_rstrValid); if (l_err) { - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore Target[%X] error validating restore status!", - TARGETING::get_huid(l_nvdimm)); + get_huid(*it)); break; } - if ((l_rstrValid & RSTR_SUCCESS) != RSTR_SUCCESS){ + if ((l_rstrValid & RSTR_ERROR) == RSTR_ERROR) + { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] restoreValid[%d], restore failed!", - TARGETING::get_huid(l_nvdimm), l_rstrValid); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] restore failed due to errors", + get_huid(*it)); /*@ *@errortype *@reasoncode NVDIMM_RESTORE_FAILED @@ -964,36 +1327,21 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) * restore timeout (Controller error) *@custdesc NVDIMM failed to restore data */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, - NVDIMM_RESTORE, - NVDIMM_RESTORE_FAILED, - TARGETING::get_huid(l_nvdimm), - 0x0, - ERRORLOG::ErrlEntry::NO_SW_CALLOUT); - - l_err->collectTrace(NVDIMM_COMP_NAME, 1024 ); - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); - - // Invalid restore could be due to dram not in self-refresh - // or controller issue. Data should not be trusted at this point - l_err->addPartCallout( l_nvdimm, - HWAS::NV_CONTROLLER_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_RESTORE, + NVDIMM_RESTORE_FAILED, + get_huid(*it), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT); + nvdimmAddPage4Regs(*it,l_err); + nvdimmAddVendorLog(*it, l_err); break; } - } - - if (l_err) - { - break; - } - // Exit self-refresh - for (const auto & l_nvdimm : i_nvdimmList) - { - - TARGETING::TargetHandleList l_mcaList; - getParentAffinityTargets(l_mcaList, l_nvdimm, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + // Exit self-refresh + TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, CLASS_UNIT, TYPE_MCA); assert(l_mcaList.size(), "nvdimmRestore() failed to find parent MCA."); fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); @@ -1005,16 +1353,48 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] post_restore_transition failed!", - TARGETING::get_huid(l_nvdimm)); - - // Commit the error from the HWP - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + get_huid(*it)); + nvdimmAddPage4Regs(*it,l_err); break; } else { // Restore success! - nvdimmSetStatusFlag(l_nvdimm, NSTD_VAL_PRSV); + // Remove dimm from list for error handling + it = io_nvdimmList.erase(it); + } + } + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmRestore() HUID[%X] encounrterd an error during restore"); + break; + } + + if (i_mpipl) + { + for (const auto & l_nvdimm : l_nvdimmList) + { + TargetHandleList l_mcaList; + errlHndl_t err = nullptr; + getParentAffinityTargets(l_mcaList, l_nvdimm, CLASS_UNIT, TYPE_MCA); + assert(l_mcaList.size(), "nvdimmRestore() failed to find parent MCA."); + + // Re-validate the BAR after restore + fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); + FAPI_INVOKE_HWP(err, mss::nvdimm::change_bar_valid_state, l_fapi_mca, HIGH); + + // This should not fail at all (scom read/write). If it does, post an informational log + // to leave some breadcrumbs + if (err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] i_mpipl[%u] failed to invalidate BAR!", + get_huid(l_nvdimm), i_mpipl); + + err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + err->collectTrace( NVDIMM_COMP_NAME ); + ERRORLOG::errlCommit(err, NVDIMM_COMP_ID); + } } } @@ -1027,66 +1407,124 @@ errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList, uint8_t &i_mpipl) #endif /** - * @brief This function checks the erase status register to make sure - * the last erase completed witout error + * @brief This function checks the status and success of an erase * * @param[in] i_nvdimm - nvdimm target with NV controller + * @param[in] i_statusOnly - check just the status register (not the image) * * @return errlHndl_t - Null if successful, otherwise a pointer to * the error log. */ -errlHndl_t nvdimmCheckEraseSuccess(Target *i_nvdimm) +errlHndl_t nvdimmEraseCheck(Target *i_nvdimm, bool i_statusOnly) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmCheckEraseSuccess() : nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); - - uint8_t l_data = 0; errlHndl_t l_err = nullptr; + nvdimm_reg_t l_RegInfo; + uint8_t l_data = 0; + bool l_valid = false; - l_err = nvdimmReadReg(i_nvdimm, ERASE_STATUS, l_data); + // Erase happens one module at a time. No need to set any offset on the counter + uint32_t l_poll = 0; + l_err = nvdimmPollEraseDone(i_nvdimm, l_poll); + // Add part callout, currently all erase calls have same callout + // Dump traces to the error log if error exists if (l_err) { - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckEraseSuccess() nvdimm[%X]" - "failed to read erase status reg!",TARGETING::get_huid(i_nvdimm)); + // For both Erase timeout and Erase fail + // Callout nvdimm on high, gard and deconfig + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); } - else if ((l_data & ERASE_SUCCESS) != ERASE_SUCCESS) + else { + do + { + // Read Erase Status register + l_err = nvdimmReadReg ( i_nvdimm, ERASE_STATUS, l_data); + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_VAL_DISARMED); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm[%X], failed to read erase status", + get_huid(i_nvdimm)); + break; + } - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckEraseSuccess() nvdimm[%X]" - "failed to erase!",TARGETING::get_huid(i_nvdimm)); - /*@ - *@errortype - *@reasoncode NVDIMM_ERASE_FAILED - *@severity ERRORLOG_SEV_PREDICTIVE - *@moduleid NVDIMM_CHECK_ERASE - *@userdata1[0:31] Related ops (0xff = NA) - *@userdata1[32:63] Target Huid - *@userdata2 <UNUSED> - *@devdesc Encountered error erasing previously stored data image - * on NVDIMM. Likely due to timeout and/or controller error - *@custdesc NVDIMM error erasing data image - */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, - NVDIMM_CHECK_ERASE, - NVDIMM_ERASE_FAILED, - NVDIMM_SET_USER_DATA_1(ERASE, TARGETING::get_huid(i_nvdimm)), - 0x0, - ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + if (i_statusOnly) + { + // assume image is cleared, do not check + TRACFCOMP(g_trac_nvdimm, "nvdimmEraseCheck() - skipping image check for nvdimm[%X]", + get_huid(i_nvdimm)); + l_valid = false; + } + else + { + // Check for a valid image + l_err = nvdimmValidImage( i_nvdimm, l_valid ); + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_VAL_DISARMED); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm[%X] Failed to detect valid image", + get_huid(i_nvdimm)); + break; + } + } - l_err->collectTrace(NVDIMM_COMP_NAME, 1024 ); - errlCommit( l_err, NVDIMM_COMP_ID ); + if ( (l_data & ERASE_ERROR) || l_valid ) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_VAL_DISARMED); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm[%X] NVDimm Erase failed due to error (ERASE_STATUS: 0x%02X, Image %s)", + get_huid(i_nvdimm), l_data, l_valid?"not erased":"erased"); + /*@ + *@errortype + *@reasoncode NVDIMM_ERASE_ERROR + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_ERASE + *@userdata1[0:31] ERASE_STATUS register + *@userdata1[32:63] Target Huid + *@userdata2 ERASE_ERROR status bit + *@userdata2 Image validity + *@devdesc Encountered error during image erase function + * on NVDIMM. Check error register trace for details + *@custdesc NVDIMM error during nvdimm erase + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_ERASE, + NVDIMM_ERASE_ERROR, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + NVDIMM_SET_USER_DATA_1(ERASE_ERROR, l_valid), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + break; + } - // Failure to erase could mean internal NV controller error and/or - // HW error on nand flash. NVDIMM will lose persistency if failed to - // erase nand flash - l_err->addPartCallout( i_nvdimm, - HWAS::NV_CONTROLLER_PART_TYPE, - HWAS::SRCI_PRIORITY_HIGH); - } + } while(0); - TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmCheckEraseSuccess(): nvdimm[%X] ret[%X]", - TARGETING::get_huid(i_nvdimm), l_data); + if(l_err) + { + // Callout nvdimm on high, gard and deconfig + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + } + } return l_err; } @@ -1102,7 +1540,7 @@ errlHndl_t nvdimmCheckEraseSuccess(Target *i_nvdimm) errlHndl_t nvdimmEraseNF(Target *i_nvdimm) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmEraseNF() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; @@ -1112,22 +1550,17 @@ errlHndl_t nvdimmEraseNF(Target *i_nvdimm) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] error initiating erase!!", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); break; } - // Erase happens one module at a time. No need to set any offset on the counter - uint32_t l_poll = 0; - l_err = nvdimmPollEraseDone(i_nvdimm, l_poll); - if (!l_err) - { - l_err = nvdimmCheckEraseSuccess(i_nvdimm); - } + // Poll for success, then check the status and image + l_err = nvdimmEraseCheck(i_nvdimm, false); }while(0); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmEraseNF() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); return l_err; } @@ -1146,15 +1579,15 @@ errlHndl_t nvdimmEraseNF(Target *i_nvdimm) errlHndl_t nvdimmOpenPage(Target *i_nvdimm, uint8_t i_page) { - TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmOpenPage nvdimm[%X]", TARGETING::get_huid(i_nvdimm)); + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmOpenPage nvdimm[%X]", get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; bool l_success = false; uint8_t l_data; uint32_t l_poll = 0; uint32_t l_target_timeout_values[6]; - assert(i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), - "nvdimmOpenPage() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", TARGETING::get_huid(i_nvdimm)); + assert(i_nvdimm->tryGetAttr<ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), + "nvdimmOpenPage() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", get_huid(i_nvdimm)); uint32_t l_timeout = l_target_timeout_values[PAGE_SWITCH]; @@ -1167,7 +1600,7 @@ errlHndl_t nvdimmOpenPage(Target *i_nvdimm, if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmOpenPage nvdimm[%X]" - "error writing to page change reg", TARGETING::get_huid(i_nvdimm)); + "error writing to page change reg", get_huid(i_nvdimm)); break; } @@ -1200,7 +1633,7 @@ errlHndl_t nvdimmOpenPage(Target *i_nvdimm, if (!l_success && !l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmOpenPage nvdimm[%X] openpage_success[%d]," - "failure to open page!", TARGETING::get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); + "failure to open page!", get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); /*@ *@errortype @@ -1215,25 +1648,28 @@ errlHndl_t nvdimmOpenPage(Target *i_nvdimm, *@custdesc Encountered error performing internal operaiton * on NVDIMM */ - l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, - NVDIMM_POLL_STATUS, - NVDIMM_STATUS_TIMEOUT, - NVDIMM_SET_USER_DATA_1(PAGE_SWITCH, TARGETING::get_huid(i_nvdimm)), - NVDIMM_SET_USER_DATA_2_TIMEOUT(l_poll, l_timeout), - ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_OPEN_PAGE, + NVDIMM_OPEN_PAGE_TIMEOUT, + NVDIMM_SET_USER_DATA_1(PAGE_SWITCH, get_huid(i_nvdimm)), + NVDIMM_SET_USER_DATA_2_TIMEOUT(l_poll, l_timeout), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + nvdimmAddVendorLog(i_nvdimm, l_err); // Failure to open page most likely means problem with // the NV controller. l_err->addPartCallout( i_nvdimm, HWAS::NV_CONTROLLER_PART_TYPE, HWAS::SRCI_PRIORITY_HIGH); + nvdimmAddPage4Regs(i_nvdimm,l_err); } }while(0); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmOpenPage nvdimm[%X] nvdimmOpenPage.success[%d]," - ,TARGETING::get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); + ,get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); return l_err; } @@ -1250,12 +1686,12 @@ errlHndl_t nvdimmOpenPage(Target *i_nvdimm, errlHndl_t nvdimmGetTimeoutVal(Target* i_nvdimm) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmGetTimeoutVal() HUID[%X]" - ,TARGETING::get_huid(i_nvdimm)); + ,get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; uint8_t l_data = 0; uint32_t timeout_map[6]; - i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); + i_nvdimm->tryGetAttr<ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); //Get the 6 main timeout values for (uint8_t i = SAVE; i <= CHARGE; i++) @@ -1282,28 +1718,33 @@ errlHndl_t nvdimmGetTimeoutVal(Target* i_nvdimm) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmGetTimeoutVal() HUID[%X] " - "error reading timeout value for op[%d]!", TARGETING::get_huid(i_nvdimm), i); + "error reading timeout value for op[%d]!", get_huid(i_nvdimm), i); break; } //Converting to msec depending on bit 15. 1 = sec, 0 = msec //except for charge. Charge is only in seconds so convert anyway + //Double the timeout values for margins if (timeout_map[i] >= 0x8000 || i == CHARGE){ timeout_map[i] = timeout_map[i] & 0x7FFF; - timeout_map[i] = timeout_map[i] * MS_PER_SEC; + timeout_map[i] = timeout_map[i] * MS_PER_SEC * 2; + } + else + { + timeout_map[i] = timeout_map[i] * 2; } TRACUCOMP(g_trac_nvdimm, "nvdimmGetTimeoutVal() HUID[%X], timeout_idx[%d], timeout_ms[%d]" - ,TARGETING::get_huid(i_nvdimm), timeoutInfoTable[i].idx, timeout_map[i]); + ,get_huid(i_nvdimm), timeoutInfoTable[i].idx, timeout_map[i]); } if (!l_err) { - i_nvdimm->setAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); + i_nvdimm->setAttr<ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); } TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmGetTimeoutVal() HUID[%X]" - ,TARGETING::get_huid(i_nvdimm)); + ,get_huid(i_nvdimm)); return l_err; } @@ -1327,8 +1768,8 @@ errlHndl_t nvdimmEpowSetup(TargetHandleList &i_nvdimmList) for (TargetHandleList::iterator it = i_nvdimmList.begin(); it != i_nvdimmList.end();) { - TARGETING::TargetHandleList l_mcaList; - getParentAffinityTargets(l_mcaList, *it, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, CLASS_UNIT, TYPE_MCA); assert(l_mcaList.size(), "nvdimmEpowSetup() failed to find parent MCA."); fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); @@ -1340,9 +1781,10 @@ errlHndl_t nvdimmEpowSetup(TargetHandleList &i_nvdimmList) if (l_err) { TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmEpowSetup() HUID[%X] failed to setup epow!", - TARGETING::get_huid(*it)); + get_huid(*it)); - nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); + nvdimmSetStatusFlag(*it, NSTD_VAL_SR_FAILED); + nvdimmAddPage4Regs(*it,l_err); break; } it++; @@ -1354,6 +1796,7 @@ errlHndl_t nvdimmEpowSetup(TargetHandleList &i_nvdimmList) return l_err; } + /** * @brief Entry function to NVDIMM restore * - Restore image from NVDIMM NAND flash to DRAM @@ -1365,31 +1808,21 @@ errlHndl_t nvdimmEpowSetup(TargetHandleList &i_nvdimmList) void nvdimm_restore(TargetHandleList &i_nvdimmList) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_restore()"); + errlHndl_t l_err = nullptr; + bool l_valid = false; + bool l_continue = true; TARGETING::Target* l_sys = nullptr; TARGETING::targetService().getTopLevelTarget( l_sys ); assert(l_sys, "nvdimm_restore: no TopLevelTarget"); uint8_t l_mpipl = l_sys->getAttr<ATTR_IS_MPIPL_HB>(); + nvdimm_reg_t l_RegInfo = nvdimm_reg_t(); + TargetHandleList l_nvdimm_restore_list = i_nvdimmList; + uint8_t l_rstrValid; do { - // Set the energy policy to device-managed - // Don't think this is needed for the supercaps to start charging - // but do it anyway to get the charging going - for (const auto & l_nvdimm : i_nvdimmList) - { - l_err = nvdimmSetESPolicy(l_nvdimm); - if (l_err) - { - // Failing this is an indication of power pack issue. - // This will prevent future backup, but let's continue - // since we can still restore the data if there is any - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmSetESPolicy()"); - errlCommit( l_err, NVDIMM_COMP_ID ); - } - } - + // Check MPIPL case first to make sure any on-going backup is complete if (l_mpipl) { // During MPIPL, make sure any in-progress save is completed before proceeding @@ -1401,41 +1834,118 @@ void nvdimm_restore(TargetHandleList &i_nvdimmList) if (l_err) { - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + nvdimmSetStatusFlag(l_nvdimm, NSTD_VAL_ERASED); TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() nvdimm[%X], error backing up the DRAM!", - TARGETING::get_huid(l_nvdimm)); + get_huid(l_nvdimm)); errlCommit(l_err, NVDIMM_COMP_ID); break; } } } + // Compile a list of nvdimms with valid image + // TODO: Reach out to RAS on how to handle odd number of nvdimms + // since we always operate in pairs + for (TargetHandleList::iterator it = l_nvdimm_restore_list.begin(); + it != l_nvdimm_restore_list.end();) + { + // Check for a valid image + l_err = nvdimmValidImage( *it, l_valid ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() nvdimm[%X] Failed to detect valid image", get_huid(*it)); + errlCommit(l_err, NVDIMM_COMP_ID); + } + + // Remove it from the restore list if there is no valid image + if (!l_valid) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() nvdimm[%X] No valid image discovered", get_huid(*it)); + // Set ATTR NV STATUS FLAG to Erased + nvdimmSetStatusFlag(*it, NSTD_VAL_ERASED); + it = l_nvdimm_restore_list.erase(it); + + } + else + { + it++; + } + } + + // Exit if there is nothing to restore + if (l_nvdimm_restore_list.empty()) + { + break; + } + // Start the restore - l_err = nvdimmRestore(i_nvdimmList, l_mpipl); + l_err = nvdimmRestore(l_nvdimm_restore_list, l_mpipl); + // Check if restore completed successfully if (l_err) { + const auto l_nvdimm = l_nvdimm_restore_list.front(); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmRestore()"); - errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetStatusFlag(l_nvdimm, NSTD_VAL_SR_FAILED); + + // Invalid restore could be due to dram not in self-refresh + // or controller issue. Data should not be trusted at this point + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( l_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(l_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); break; } - // Make sure the energy source is fully charged before erasing the images - // Doing this on all the nvdimms since the ones w/o image will need - // to be fully charged before arming the trigger - uint32_t l_poll = 0; + // Check health status registers and exit if required for (const auto & l_nvdimm : i_nvdimmList) { - l_err = nvdimmPollESChargeStatus(l_nvdimm, l_poll); + // Post restore health check. l_continue gets set per the health check logic + // and used later to determine if boot shall continue on error condition + l_err = nvdimmHealthStatusCheck( l_nvdimm, HEALTH_RESTORE, l_continue ); - if (l_err){ - nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() nvdimm[%X] failed during health status check", get_huid(l_nvdimm)); errlCommit( l_err, NVDIMM_COMP_ID ); + if (!l_continue) + { + break; + } } + + // Make sure the restore is valid + l_err = nvdimmGetRestoreValid(l_nvdimm, l_rstrValid); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore Target[%X] error validating restore status!", + get_huid(l_nvdimm)); + break; + } + + if ((l_rstrValid & RSTR_SUCCESS) == RSTR_SUCCESS) + { + // Restore success! + nvdimmSetStatusFlag(l_nvdimm, NSTD_VAL_RESTORED); + } + } }while(0); + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + } + // At the end, pre-load CCS with commands for EPOW. This will stage the CCS // with the require commands to trigger the save on NVDIMMs. The actual // triggering will be done by OCC when EPOW is detected. @@ -1455,6 +1965,7 @@ void nvdimm_restore(TargetHandleList &i_nvdimmList) * - Checks for ready state * - Gathers timeout values * - Waits for the ongoing backup to complete + * - Unlocks encryption * - Disarms the trigger for draminit * * @param[in] i_nvdimm - nvdimm target @@ -1463,66 +1974,3903 @@ void nvdimm_restore(TargetHandleList &i_nvdimmList) void nvdimm_init(Target *i_nvdimm) { TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_init() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); errlHndl_t l_err = nullptr; + bool l_continue = true; + uint8_t l_data = 0; + uint8_t l_failinfo0 = 0; + uint8_t l_failinfo1 = 0; + nvdimm_reg_t l_RegInfo; + uint32_t l_poll = 0; do { - l_err = nvdimmReady(i_nvdimm); + // Force a factory reset if told to via attribute override + // This will allow us to recover from bad images, lost keys, etc + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_init: no TopLevelTarget"); + if( l_sys->getAttr<ATTR_FORCE_NVDIMM_RESET>() ) + { + l_err = nvdimm_factory_reset(i_nvdimm); + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], factory reset failed", + get_huid(i_nvdimm)); + errlCommit(l_err, NVDIMM_COMP_ID); + } + } + // Set ATTR_NV_STATUS_FLAG to default disarmed state + l_err = notifyNvdimmProtectionChange(i_nvdimm, NVDIMM_DISARMED); if (l_err) { nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_int() nvdimm[%X], controller not ready", - TARGETING::get_huid(i_nvdimm)); errlCommit(l_err, NVDIMM_COMP_ID); + } + + // Check if the nvdimm ready status + l_err = nvdimmReady(i_nvdimm); + + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], controller not ready", + get_huid(i_nvdimm)); break; } + // Check if the firmware slot is 0 + l_err = nvdimmGetRunningSlot(i_nvdimm, l_data); + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], failed to read slot info", + get_huid(i_nvdimm)); + break; + } + + if (l_data == 0) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_VAL_SR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], running on fw slot 0", + get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_INVALID_FW_SLOT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_FW_SLOT + *@userdata1[0:31] Slot running + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Encountered error when checking the firmware slot running + * on NVDIMM. Firmware is running on slot 0 instead of 1 + *@custdesc NVDIMM incorrect firmware slot + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_FW_SLOT, + NVDIMM_INVALID_FW_SLOT, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Add callout of nvdimm with no deconfig/gard + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + errlCommit(l_err, NVDIMM_COMP_ID); + } + // Get the timeout values for the major ops at init l_err = nvdimmGetTimeoutVal(i_nvdimm); if (l_err) { nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_int() nvdimm[%X], error retrieving timeout values", - TARGETING::get_huid(i_nvdimm)); - errlCommit(l_err, NVDIMM_COMP_ID); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], error retrieving timeout values", + get_huid(i_nvdimm)); break; } - //Check save progress - uint32_t l_poll = 0; - l_err = nvdimmPollBackupDone(i_nvdimm, l_poll); + // Check for Erase in progress and verify good status + l_err = nvdimmEraseCheck(i_nvdimm, true); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], error checking erase status", + get_huid(i_nvdimm)); + break; + } + // Check NO_RESET_N bit for power loss without save + l_err = nvdimmReadReg ( i_nvdimm, CSAVE_FAIL_INFO1, l_data); if (l_err) { - nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOPRSV); - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_int() nvdimm[%X], error backing up the DRAM!", - TARGETING::get_huid(i_nvdimm)); - errlCommit(l_err, NVDIMM_COMP_ID); break; } + else if ((l_data & NO_RESET_N) == NO_RESET_N) + { + // Set ATTR_NV_STATUS_FLAG to partial working as data may persist + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_VAL_SR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmInit() nvdimm[%X]" + "failed to save due to power loss!",get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_POWER_SAVE_FAILURE + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_RESETN + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc NO_RESET_N: The NVDIMM experienced a power loss, but no CSAVE + * was triggered since the NVDIMM did not detect an asserted + * RESET_N. If there is a prior predicitve log for OCC in safe + * mode, than this would be the reason for NO_RESET_N. Otherwise + * there could be a problem with the RESET_N signal between proc + * and NVDIMM. + *@custdesc NVDIMM error erasing data image + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_RESETN, + NVDIMM_POWER_SAVE_FAILURE, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Failure to erase could mean internal NV controller error and/or + // HW error on nand flash. NVDIMM will lose persistency if failed to + // erase nand flash + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); - // Disarm the ddr_resetn here in case it came in armed. When the nvdimm is - // armed the reset_n is masked off from the host, meaning the drams won't - // be able to get reset properly later, causing training to fail. - l_err = nvdimmChangeArmState(i_nvdimm, DISARM_TRIGGER); + errlCommit(l_err, NVDIMM_COMP_ID); + } + else + { + // Check save progress + l_err = nvdimmPollBackupDone(i_nvdimm, l_poll); + if (l_err) + { + // May have to move the error handling to the caller + // as different op could have different error severity + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_int() nvdimm[%X], error backing up the DRAM!", + get_huid(i_nvdimm)); + break; + } + } + // Check CSAVE FAIL INFO registers for fail errors + l_err = nvdimmReadReg( i_nvdimm, CSAVE_FAIL_INFO0, l_failinfo0 ); + if (l_err) + { + break; + } + l_err = nvdimmReadReg ( i_nvdimm, CSAVE_FAIL_INFO1, l_failinfo1 ); + if (l_err) + { + break; + } + // Apply mask for relevant 1:6 bits to failinfo1 + l_failinfo1 &= CSAVE_FAIL_BITS_MASK; + + // Check CSAVE_STATUS Register + l_err = nvdimmReadReg( i_nvdimm, CSAVE_STATUS, l_data ); if (l_err) { - nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOPRSV); - TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_init() nvdimm[%X], error disarming the nvdimm!", - TARGETING::get_huid(i_nvdimm)); - errlCommit(l_err, NVDIMM_COMP_ID); break; } + else if ((l_data == SAVE_ERROR) && ((l_failinfo0 != ZERO) || (l_failinfo1 != ZERO))) + { + /*@ + *@errortype + *@reasoncode NVDIMM_CSAVE_ERROR + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_CSAVE + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Encountered error saving during catastrophic save + * on NVDIMM. Check error register trace for details + *@custdesc NVDIMM error during Catastrophic Save + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_CSAVE, + NVDIMM_CSAVE_ERROR, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Collect register data for FFDC Traces + nvdimmTraceRegs ( i_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + + // Check if the image is still valid + if ( l_RegInfo.CSave_Info != VALID_IMAGE ) + { + // Callout and gard dimm if image is not valid + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::DECONFIG, + HWAS::GARD_Fatal); + break; + } + else + { + // Callout dimm without gard if image is valid + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + // Set ATTR_NV_STATUS_FLAG to partial working as data may persist + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_VAL_SR); + errlCommit(l_err, NVDIMM_COMP_ID); + } + } + + // Check Health Status Registers + l_err = nvdimmHealthStatusCheck(i_nvdimm, HEALTH_SAVE, l_continue); + if(!l_continue) + { + break; + } + + // Unlock encryption if enabled + TargetHandleList l_nvdimmTargetList; + l_nvdimmTargetList.push_back(i_nvdimm); + NVDIMM::nvdimm_encrypt_unlock(l_nvdimmTargetList); }while(0); TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_init() nvdimm[%X]", - TARGETING::get_huid(i_nvdimm)); + get_huid(i_nvdimm)); + + if (l_err) + { + l_err->collectTrace( NVDIMM_COMP_NAME ); + errlCommit(l_err, NVDIMM_COMP_ID); + } } + + +void nvdimm_thresholds(TARGETING::TargetHandleList &i_nvdimmList) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_thresholds()"); + + errlHndl_t l_err = nullptr; + + for (const auto & l_nvdimm : i_nvdimmList) + { + // ES_LIFETIME_WARNING_THRESHOLD + l_err = nvdimmWriteReg(l_nvdimm, + ES_LIFETIME_WARNING_THRESHOLD, + THRESHOLD_ES_LIFETIME); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting ES_LIFETIME_WARNING_THRESHOLD", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // NVM_LIFETIME_WARNING_THRESHOLD + l_err = nvdimmWriteReg(l_nvdimm, + NVM_LIFETIME_WARNING_THRESHOLD, + THRESHOLD_NVM_LIFETIME); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting NVM_LIFETIME_WARNING_THRESHOLD", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // ES_TEMP_WARNING_HIGH_THRESHOLD1 + l_err = nvdimmWriteReg(l_nvdimm, + ES_TEMP_WARNING_HIGH_THRESHOLD1, + THRESHOLD_ES_TEMP_HIGH_1); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting ES_TEMP_WARNING_HIGH_THRESHOLD1", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // ES_TEMP_WARNING_HIGH_THRESHOLD0 + l_err = nvdimmWriteReg(l_nvdimm, + ES_TEMP_WARNING_HIGH_THRESHOLD0, + THRESHOLD_ES_TEMP_HIGH_0); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting ES_TEMP_WARNING_HIGH_THRESHOLD0", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // ES_TEMP_WARNING_LOW_THRESHOLD1 + l_err = nvdimmWriteReg(l_nvdimm, + ES_TEMP_WARNING_LOW_THRESHOLD1, + THRESHOLD_ES_TEMP_LOW_1); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting ES_TEMP_WARNING_LOW_THRESHOLD1", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // ES_TEMP_WARNING_LOW_THRESHOLD0 + l_err = nvdimmWriteReg(l_nvdimm, + ES_TEMP_WARNING_LOW_THRESHOLD0, + THRESHOLD_ES_TEMP_LOW_0); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimm_thresholds() nvdimm[%X] " + "error setting ES_TEMP_WARNING_LOW_THRESHOLD0", + get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_thresholds()"); +} + + +errlHndl_t nvdimm_getRandom(uint8_t* o_genData) +{ + errlHndl_t l_err = nullptr; + uint8_t l_xtraData[ENC_KEY_SIZE] = {0}; + + do + { + // Get a pointer to the TPM + Target* l_tpm = nullptr; + l_err = nvdimm_getTPM(l_tpm); + if (l_err) + { + break; + } + + // Get a random number from the TPM + l_err = TRUSTEDBOOT::GetRandom(l_tpm, ENC_KEY_SIZE, o_genData); + if (l_err) + { + break; + } + + // Validate and update the random number + // Retry if more randomness required + do + { + //Get replacement data + l_err = TRUSTEDBOOT::GetRandom(l_tpm, ENC_KEY_SIZE, l_xtraData); + if (l_err) + { + break; + } + + }while (nvdimm_keyifyRandomNumber(o_genData, l_xtraData)); + + } while(0); + + return l_err; +} + + +errlHndl_t nvdimm_getTPM(Target*& o_tpm) +{ + errlHndl_t l_err = nullptr; + + do + { + // Get all functional TPMs + TargetHandleList l_tpmList; + TRUSTEDBOOT::getTPMs(l_tpmList, + TRUSTEDBOOT::TPM_FILTER::ALL_FUNCTIONAL); + + if (l_tpmList.size()) + { + o_tpm = l_tpmList[0]; + break; + } + + // No TPMs, generate error + TRACFCOMP(g_trac_nvdimm,ERR_MRK"nvdimm_getTPM() No functional TPMs found"); + + /*@ + *@errortype + *@reasoncode NVDIMM_TPM_NOT_FOUND + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_GET_TPM + *@devdesc Functional TPM required to generate encryption keys + *@custdesc NVDIMM error generating encryption keys + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_GET_TPM, + NVDIMM_TPM_NOT_FOUND, + 0x0, + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + + // Get all TPMs + TRUSTEDBOOT::getTPMs(l_tpmList, + TRUSTEDBOOT::TPM_FILTER::ALL_IN_BLUEPRINT); + if (l_tpmList.size() == 0) + { + // No TPMs, we probably have nvdimms enabled + // when they should not be + l_err->addProcedureCallout( + HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_HIGH); + } + else + { + // If a TPM exists it must be deconfigured + l_err->addProcedureCallout( + HWAS::EPUB_PRC_FIND_DECONFIGURED_PART, + HWAS::SRCI_PRIORITY_HIGH); + l_err->addProcedureCallout( + HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_MED); + } + + }while(0); + + // Functional TPM not found + return l_err; +} + + +#endif + + +/** + * @brief Force a factory reset of the NV logic and flash + * + * @param[in] i_nvdimm - NVDIMM Target + */ +errlHndl_t nvdimm_factory_reset(Target *i_nvdimm) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_factory_reset() nvdimm[%X]", + get_huid(i_nvdimm)); + errlHndl_t l_err = nullptr; + + do + { + // Send the reset command + l_err = nvdimmWriteReg(i_nvdimm, NVDIMM_FUNC_CMD, FACTORY_DEFAULT); + if( l_err ) + { + break; + } + + // Poll 2 minutes for completion + // We could get the timeout value from the dimm but since we're + // doing a hard reset anyway I just want to use a big number that + // can handle any lies that the controller might tell us. + uint8_t l_data = 0; + constexpr uint64_t MAX_POLL_SECONDS = 120; + uint64_t poll = 0; + for( poll = 0; poll < MAX_POLL_SECONDS; poll++ ) + { + l_err = nvdimmReadReg(i_nvdimm, NVDIMM_CMD_STATUS0, l_data); + if( l_err ) + { + break; + } + + if( l_data != FACTORY_RESET_IN_PROGRESS ) + { + break; + } + +#ifndef __HOSTBOOT_RUNTIME + // kick the watchdog since this can take awhile + INITSERVICE::sendProgressCode(); #endif + // sleep 1 second + nanosleep(1, 0); + } + if( l_err ) { break; } + + // Make an error if it never finished + if( poll >= MAX_POLL_SECONDS ) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_factory_reset() nvdimm[%X] - factory reset never completed[%d]", + get_huid(i_nvdimm), l_data); + /*@ + *@errortype + *@reasoncode NVDIMM_NOT_READY + *@severity ERRORLOG_SEV_UNRECOVERABLE + *@moduleid NVDIMM_FACTORY_RESET + *@userdata1[0:31] Ret value from ready register + *@userdata1[32:63] Target Huid + *@userdata2 Number of seconds waited + *@devdesc NVDIMM factory reset never completed + *@custdesc NVDIMM still in reset + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_FACTORY_RESET, + NVDIMM_NOT_READY, + NVDIMM_SET_USER_DATA_1(l_data, get_huid(i_nvdimm)), + MAX_POLL_SECONDS, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // If nvdimm is not ready for access by now, this is + // a failing indication on the NV controller + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + nvdimmAddPage4Regs(i_nvdimm,l_err); + } + } while(0); + + return l_err; +} + + +bool nvdimm_encrypt_unlock(TargetHandleList &i_nvdimmList) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_encrypt_unlock()"); + errlHndl_t l_err = nullptr; + bool l_success = true; + + do + { + // Do not check ATTR_NVDIMM_ENCRYPTION_ENABLE + // The attribute could have been reset by flashing the FSP + // Unlock if the keys are valid and NVDIMM hw encryption is enabled + + // Get the sys pointer, attribute keys are system level + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_encrypt_unlock() no TopLevelTarget"); + + // Get the FW key attributes + auto l_attrKeysFw = + l_sys->getAttrAsStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_FW>(); + + // Cast to key data struct type for easy access to each key + nvdimmKeyData_t* l_keysFw = + reinterpret_cast<nvdimmKeyData_t*>(&l_attrKeysFw); + + // Check encryption unlock for all nvdimms + for (const auto & l_nvdimm : i_nvdimmList) + { + // Get encryption state in the config/status reg + encryption_config_status_t l_encStatus = {0}; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_unlock() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Already unlocked or not enabled then exit + if (l_encStatus.encryption_unlocked || + !l_encStatus.encryption_enabled) + { + break; + } + + // Check for valid key attribute data + l_err = nvdimm_checkValidAttrKeys(l_keysFw); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + break; + } + + // Else encryption is enabled but needs unlock + TRACFCOMP(g_trac_nvdimm, "nvdimm_encrypt_unlock() nvdimm[%X] enabled, unlocking...",get_huid(l_nvdimm)); + + // Set the Unlock Access Key Reg + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->ak, + ENCRYPTION_ACCESS_KEY_UNLOCK, + ENCRYPTION_ACCESS_KEY_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Verify encryption is unlocked + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_unlock() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS after unlock",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + if (!l_encStatus.encryption_unlocked) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_unlock() nvdimm[%X] encryption unlock failed, expected ENCRYPTION_CONFIG_STATUS=0x%.02X, expected=0x1F ",get_huid(l_nvdimm),l_encStatus.whole); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_UNLOCK_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_ENCRYPT_UNLOCK + *@userdata1 NVDIMM HUID + *@userdata2 ENCRYPTION_CONFIG_STATUS + *@devdesc NVDIMM failed to unlock encryption + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_ENCRYPT_UNLOCK, + NVDIMM_ENCRYPTION_UNLOCK_FAILED, + get_huid(l_nvdimm), + l_encStatus.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(l_nvdimm, l_err); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_MED, + HWAS::DELAYED_DECONFIG, + HWAS::GARD_NULL ); + + nvdimmAddPage4Regs(l_nvdimm,l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + } + else + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_encrypt_unlock() nvdimm[%X] encryption is unlocked 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + } + } + }while(0); + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_encrypt_unlock()"); + return l_success; +} + + +void nvdimmSetEncryptionError(Target *i_nvdimm) +{ + ATTR_NVDIMM_ARMED_type l_armed_state = {}; + l_armed_state = i_nvdimm->getAttr<ATTR_NVDIMM_ARMED>(); + + l_armed_state.encryption_error_detected = 1; + + i_nvdimm->setAttr<ATTR_NVDIMM_ARMED>(l_armed_state); +} + + +bool nvdimm_keyifyRandomNumber(uint8_t* o_genData, uint8_t* i_xtraData) +{ + bool l_failed = false; + uint32_t l_xtraByte = 0; + + for (uint32_t l_byte = 0; l_byte < ENC_KEY_SIZE; l_byte++) + { + if ((o_genData[l_byte] != KEY_TERMINATE_BYTE) && + (o_genData[l_byte] != KEY_ABORT_BYTE)) + { + // This byte is valid + continue; + } + + // This byte is not valid, replace it + // Find a valid byte in the replacement data + while ((i_xtraData[l_xtraByte] == KEY_TERMINATE_BYTE) || + (i_xtraData[l_xtraByte] == KEY_ABORT_BYTE)) + { + l_xtraByte++; + + if (l_xtraByte == ENC_KEY_SIZE) + { + l_failed = true; + break; + } + } + + if (l_failed) + { + break; + } + + // Replace the invalid byte with the valid extra byte + o_genData[l_byte] = i_xtraData[l_xtraByte]; + } + + return l_failed; +} + + +bool nvdimm_validRandomNumber(uint8_t* i_genData) +{ + bool l_valid = true; + for (uint32_t l_byte = 0; l_byte < ENC_KEY_SIZE; l_byte++) + { + if ((i_genData[l_byte] == KEY_TERMINATE_BYTE) || + (i_genData[l_byte] == KEY_ABORT_BYTE)) + { + l_valid = false; + break; + } + } + return l_valid; +} + + +errlHndl_t nvdimm_checkValidAttrKeys( nvdimmKeyData_t* i_attrData ) +{ + errlHndl_t l_err = nullptr; + bool l_valid = false; + + do + { + l_valid = nvdimm_validRandomNumber(i_attrData->rs); + if (!l_valid) + { + break; + } + l_valid = nvdimm_validRandomNumber(i_attrData->ek); + if (!l_valid) + { + break; + } + l_valid = nvdimm_validRandomNumber(i_attrData->ak); + if (!l_valid) + { + break; + } + }while(0); + + if (!l_valid) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_checkValidAttrKeys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW contains invalid data"); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_INVALID_ATTRIBUTE + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_VALID_ATTR_DATA + *@devdesc ATTR_NVDIMM_ENCRYPTION_KEYS_FW has invalid data + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_VALID_ATTR_DATA, + NVDIMM_ENCRYPTION_INVALID_ATTRIBUTE, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + } + + return l_err; +} + + +errlHndl_t nvdimm_handleConflictingKeys( + ATTR_NVDIMM_ENCRYPTION_KEYS_FW_typeStdArr& i_attrKeysFw, + ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR_typeStdArr& i_attrKeysAnchor) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_handleConflictingKeys()"); + errlHndl_t l_err = nullptr; + bool l_validKeyFound = false; + + // Recast to key data type to simplify parsing + nvdimmKeyData_t* l_keysFw = + reinterpret_cast<nvdimmKeyData_t*>(&i_attrKeysFw); + nvdimmKeyData_t* l_keysAnchor = + reinterpret_cast<nvdimmKeyData_t*>(&i_attrKeysAnchor); + + // Get the nvdimm target pointers + TargetHandleList l_nvdimmTargetList; + nvdimm_getNvdimmList(l_nvdimmTargetList); + for (const auto & l_nvdimm : l_nvdimmTargetList) + { + // Check encryption state in the config/status reg + encryption_config_status_t l_encStatus = {0}; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_handleConflictingKeys() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + continue; + } + + // Encryption is not enabled + // Keys are not in use so could use either set of keys + // Use the ANCHOR card keys + if (!l_encStatus.encryption_enabled) + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_handleConflictingKeys() nvdimm[%X] copying ANCHOR keys to FW",get_huid(l_nvdimm)); + l_validKeyFound = true; + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(i_attrKeysAnchor); + continue; + } + + // Encryption is enabled, test the keys + // Write the EK test reg with the FW attr value + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->ek, + ENCRYPTION_ERASE_KEY_TEST, + ENCRYPTION_ERASE_KEY_TEST_VERIFY, + false); + if (l_err) + { + break; + } + + // Check for erase key valid in the validation reg + encryption_key_validation_t l_keyValid = {0}; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_KEY_VALIDATION, + l_keyValid.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_handleConflictingKeys() nvdimm[%X] error reading ENCRYPTION_KEY_VALIDATION",get_huid(l_nvdimm)); + break; + } + if (l_keyValid.erase_key_valid) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_handleConflictingKeys() nvdimm[%X] ATTR_NVDIMM_ENCRYPTION_KEYS_FW valid",get_huid(l_nvdimm)); + l_validKeyFound = true; + // Re-write the FW keys, this will also update the ANCHOR keys + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(i_attrKeysFw); + break; + } + + // Write the EK test reg with the Anchor attr value + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysAnchor->ek, + ENCRYPTION_ERASE_KEY_TEST, + ENCRYPTION_ERASE_KEY_TEST_VERIFY, + false); + if (l_err) + { + break; + } + + // Check for erase key valid in the validation reg + l_keyValid.whole = 0; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_KEY_VALIDATION, + l_keyValid.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_handleConflictingKeys() nvdimm[%X] error reading ENCRYPTION_KEY_VALIDATION",get_huid(l_nvdimm)); + break; + } + if (l_keyValid.erase_key_valid) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_handleConflictingKeys() nvdimm[%X] ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR valid",get_huid(l_nvdimm)); + l_validKeyFound = true; + // Copy anchor attr value to FW attribute + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(i_attrKeysAnchor); + + break; + } + } + + if (!l_validKeyFound) + { + // Neither key attribute is valid + TRACFCOMP(g_trac_nvdimm,ERR_MRK"nvdimm_handleConflictingKeys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW and ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR invalid."); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_KEY_ATTRS_INVALID + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_HANDLE_CONFLICTING_KEYS + *@devdesc NVDIMM encryption key attributes invalid + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_HANDLE_CONFLICTING_KEYS, + NVDIMM_ENCRYPTION_KEY_ATTRS_INVALID, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + } + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_handleConflictingKeys()"); + return l_err; +} + + +void nvdimm_getNvdimmList(TargetHandleList &o_nvdimmTargetList) +{ + // Check for any NVDIMMs after the mss_power_cleanup + TargetHandleList l_dimmTargetList; + getAllLogicalCards(l_dimmTargetList, TYPE_DIMM); + + // Walk the dimm list and collect all the nvdimm targets + for (auto const l_dimm : l_dimmTargetList) + { + if (isNVDIMM(l_dimm)) + { + o_nvdimmTargetList.push_back(l_dimm); + } + } +} + + +bool nvdimm_gen_keys(void) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_gen_keys()"); + errlHndl_t l_err = nullptr; + bool l_success = true; + + do + { + // Determine if key generation required + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_gen_keys: no TopLevelTarget"); + + // Key size must be less that max TPM random generator size + static_assert(ENC_KEY_SIZE <= MAX_TPM_SIZE, + "nvdimm_gen_keys() ENC_KEY_SIZE is greater than MAX_TPM_SIZE"); + + // Key attributes should be same size + static_assert( sizeof(ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR_type) == + sizeof(ATTR_NVDIMM_ENCRYPTION_KEYS_FW_type), + "nvdimm_gen_keys() size of ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR_type does not match ATTR_NVDIMM_ENCRYPTION_KEYS_FW_type"); + + // Get the key attributes + auto l_attrKeysFw = + l_sys->getAttrAsStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_FW>(); + auto l_attrKeysAn = + l_sys->getAttrAsStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR>(); + + // Check the attribute sizes + static_assert(sizeof(l_attrKeysFw) == (NUM_KEYS_IN_ATTR * ENC_KEY_SIZE), + "nvdimm_gen_keys() Size of ATTR_NVDIMM_ENCRYPTION_KEYS_FW does not match NUM_KEYS_IN_ATTR * ENC_KEY_SIZE"); + static_assert(sizeof(l_attrKeysAn) == (NUM_KEYS_IN_ATTR * ENC_KEY_SIZE), + "nvdimm_gen_keys() Size of ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR does not match NUM_KEYS_IN_ATTR * ENC_KEY_SIZE"); + + // Compare attributes to zero + std::array<uint8_t,sizeof(l_attrKeysFw)> l_zero = {0}; + bool l_fwZero = (l_attrKeysFw == l_zero); + bool l_anZero = (l_attrKeysAn == l_zero); + + // Compare the attribute values + if (!l_fwZero && !l_anZero) + { + if (l_attrKeysFw != l_attrKeysAn) + { + // Handle conflicting keys + TRACFCOMP(g_trac_nvdimm, "nvdimm_gen_keys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW != ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR"); + l_err = nvdimm_handleConflictingKeys(l_attrKeysFw,l_attrKeysAn); + } + else + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_gen_keys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW == ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR"); + } + break; + } + else if (!l_fwZero && l_anZero) + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_gen_keys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW != 0 and ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR = 0"); + break; + } + else if (l_fwZero && !l_anZero) + { + // Set FW attr = Anchor attr + TRACFCOMP(g_trac_nvdimm, "nvdimm_gen_keys() Setting ATTR_NVDIMM_ENCRYPTION_KEYS_FW = ATTR_NVDIMM_ENCRYPTION_KEYS_ANCHOR"); + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(l_attrKeysAn); + break; + } + + // If we get here then both key attributes are zero, generate new keys + assert(sizeof(l_attrKeysFw) == sizeof(nvdimmKeyData_t), + "nvdimm_gen_keys() ATTR_NVDIMM_ENCRYPTION_KEYS_FW size does not match nvdimmKeyData_t"); + nvdimmKeyData_t* l_keys = + reinterpret_cast<nvdimmKeyData_t*>(&l_attrKeysFw); + + // Generate Random String (RS) + l_err = nvdimm_getRandom(l_keys->rs); + if (l_err) + { + break; + } + + // Generate Erase Key (EK) + l_err = nvdimm_getRandom(l_keys->ek); + if (l_err) + { + break; + } + + // Generate Access Key (AK) + l_err = nvdimm_getRandom(l_keys->ak); + if (l_err) + { + break; + } + + // Set the FW attribute + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(l_attrKeysFw); + + }while(0); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_gen_keys() Failed to generate keys, will not set ATTR_NVDIMM_ENCRYPTION_KEYS_FW"); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_success = false; + + // Set the encryption error for all nvdimms + TargetHandleList l_nvdimmTargetList; + nvdimm_getNvdimmList(l_nvdimmTargetList); + for (const auto & l_nvdimm : l_nvdimmTargetList) + { + nvdimmSetEncryptionError(l_nvdimm); + } + } + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_gen_keys()"); + return l_success; +} + + +bool nvdimm_remove_keys(void) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_remove_keys()"); + bool l_success = true; + + // Get the sys pointer, attribute keys are system level + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_remove_keys() no TopLevelTarget"); + + // Set the FW attribute = 0 + TRACFCOMP(g_trac_nvdimm, "nvdimm_remove_keys() Setting ATTR_NVDIMM_ENCRYPTION_KEYS_FW=0"); + ATTR_NVDIMM_ENCRYPTION_KEYS_FW_typeStdArr l_attrKeysFw = {0}; + set_ATTR_NVDIMM_ENCRYPTION_KEYS_FW(l_attrKeysFw); + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_remove_keys()"); + return l_success; +} + + +errlHndl_t nvdimm_setKeyReg(Target* i_nvdimm, + uint8_t* i_keyData, + uint32_t i_keyReg, + uint32_t i_verifyReg, + bool i_secondAttempt) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_setKeyReg(0x%X) reg=0x%X",get_huid(i_nvdimm),i_keyReg); + errlHndl_t l_err = nullptr; + + do + { + uint32_t l_byte = 0; + uint8_t l_verifyData = 0x0; + + // Before setting the key reg we need to + // init the verif reg with a random value + uint8_t l_genData[ENC_KEY_SIZE] = {0}; + l_err = nvdimm_getRandom(l_genData); + if (l_err) + { + break; + } + + // Write the verif reg one byte at a time + for (l_byte = 0; l_byte < ENC_KEY_SIZE; l_byte++) + { + // Write the verification byte + l_err = nvdimmWriteReg(i_nvdimm, i_verifyReg, l_genData[l_byte]); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_setKeyReg() huid=0x%X, error writing verif reg=0x%.03X byte=0x%d", get_huid(i_nvdimm), i_verifyReg, l_byte); + break; + } + } + + // Delay to allow verif write to complete + nanosleep(0, KEY_WRITE_DELAY_MS*NS_PER_MSEC); + + // Write the reg, one byte at a time + for (l_byte = 0; l_byte < ENC_KEY_SIZE; l_byte++) + { + // Write the key byte + l_err = nvdimmWriteReg(i_nvdimm, i_keyReg, i_keyData[l_byte]); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_setKeyReg() huid=0x%X, error writing key reg 0x%.03X byte=0x%d", get_huid(i_nvdimm), i_keyReg, l_byte); + break; + } + + // Read the verification byte + l_err = nvdimmReadReg(i_nvdimm, i_verifyReg, l_verifyData); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_setKeyReg() huid=0x%X, error reading verif reg=0x%.03X byte=0x%d", get_huid(i_nvdimm), i_verifyReg, l_byte); + break; + } + + // Verify the key byte + if (l_verifyData != i_keyData[l_byte]) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_setKeyReg() huid=0x%X, key verification failed reg=0x%.03X byte=0x%d set=0x%.02x get=0x%.02x", get_huid(i_nvdimm), i_keyReg, l_byte, i_keyData[l_byte], l_verifyData); + // Write KEY_ABORT_BYTE to abort the key write sequence + l_err = nvdimmWriteReg(i_nvdimm, i_keyReg, KEY_ABORT_BYTE); + if (i_secondAttempt) + { + // Verify check byte failed for the second time + TRACFCOMP(g_trac_nvdimm,ERR_MRK"nvdimm_getTPM() Key verification byte check failed on second attempt."); + /*@ + *@errortype + *@reasoncode NVDIMM_VERIF_BYTE_CHECK_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SET_KEY_REG + *@userdata1 NVDIMM HUID + *@userdata2[0:31] Key Register + *@userdata2[32:63] Verif Register + *@devdesc NVDIMM failed to set encryption register + *@custdesc NVDIMM register error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_KEY_REG, + NVDIMM_VERIF_BYTE_CHECK_FAILED, + get_huid(i_nvdimm), + NVDIMM_SET_USER_DATA_1(i_keyReg,i_verifyReg), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(i_nvdimm, l_err); + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + nvdimmAddPage4Regs(i_nvdimm,l_err); + } + else + { + // Try writing the reg again + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_setKeyReg() huid=0x%X, writing reg=0x%.03X again", get_huid(i_nvdimm), i_keyReg); + l_err = nvdimm_setKeyReg(i_nvdimm, + i_keyData, + i_keyReg, + i_verifyReg, + true); + } + break; + } + } + + // Delay to allow write to complete + nanosleep(0, KEY_WRITE_DELAY_MS*NS_PER_MSEC); + + }while(0); + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_setKeyReg(0x%X) reg=0x%X",get_huid(i_nvdimm),i_keyReg); + return l_err; +} + + +bool nvdimm_encrypt_enable(TargetHandleList &i_nvdimmList) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_encrypt_enable()"); + errlHndl_t l_err = nullptr; + bool l_success = true; + + do + { + // Get the sys pointer, attribute keys are system level + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_encrypt_enable() no TopLevelTarget"); + + // Exit if encryption is not enabled via the attribute + if (!l_sys->getAttr<ATTR_NVDIMM_ENCRYPTION_ENABLE>()) + { + TRACFCOMP(g_trac_nvdimm,"ATTR_NVDIMM_ENCRYPTION_ENABLE=0"); + break; + } + + // Get the FW key attributes + auto l_attrKeysFw = + l_sys->getAttrAsStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_FW>(); + + // Cast to key data struct type for easy access to each key + nvdimmKeyData_t* l_keysFw = + reinterpret_cast<nvdimmKeyData_t*>(&l_attrKeysFw); + + // Check for valid key attribute key data + l_err = nvdimm_checkValidAttrKeys(l_keysFw); + if (l_err) + { + break; + } + + // Handle encryption for all nvdimms + for (const auto & l_nvdimm : i_nvdimmList) + { + // Check encryption state in the config/status reg + encryption_config_status_t l_encStatus = {0}; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_enable() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Encryption is enabled and unlocked + if (l_encStatus.encryption_unlocked && + l_encStatus.encryption_enabled) + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_encrypt_enable() nvdimm[%X] enabled and unlocked",get_huid(l_nvdimm)); + continue; + } + + // Need to handle these cases? + if (!((l_encStatus.whole & ENCRYPTION_STATUS_CHECK_MASK) + == ENCRYPTION_STATUS_DISABLED)) + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_encrypt_enable() nvdimm[%X] unsupported state 0x%.02X",get_huid(l_nvdimm),l_encStatus.whole); + continue; + } + + // Status = 0x01, enable encryption + // Set the Random String (RS) reg + TRACFCOMP(g_trac_nvdimm,"nvdimm_encrypt_enable() nvdimm[%X] status=0x01 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->rs, + ENCRYPTION_RAMDOM_STRING_SET, + ENCRYPTION_RANDOM_STRING_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Set the Erase Key (EK) Reg + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->ek, + ENCRYPTION_ERASE_KEY_SET, + ENCRYPTION_ERASE_KEY_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Set the Access Key (AK) Reg + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->ak, + ENCRYPTION_ACCESS_KEY_SET, + ENCRYPTION_ACCESS_KEY_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Verify encryption is enabled + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_enable() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS after enable",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + if (!((l_encStatus.whole & ENCRYPTION_STATUS_CHECK_MASK) + == ENCRYPTION_STATUS_ENABLED)) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_encrypt_enable() nvdimm[%X] encryption enable failed, ENCRYPTION_CONFIG_STATUS=0x%.02X, expected=0x1F ",get_huid(l_nvdimm),l_encStatus.whole); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_ENABLE_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_ENCRYPT_ENABLE + *@userdata1 NVDIMM HUID + *@userdata2 ENCRYPTION_CONFIG_STATUS + *@devdesc NVDIMM failed to enable encryption + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_ENCRYPT_ENABLE, + NVDIMM_ENCRYPTION_ENABLE_FAILED, + get_huid(l_nvdimm), + l_encStatus.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(l_nvdimm, l_err); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + + nvdimmAddPage4Regs(l_nvdimm,l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + } + else + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_encrypt_enable() nvdimm[%X] encryption is enabled 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + + l_err = notifyNvdimmProtectionChange(l_nvdimm, + ENCRYPTION_ENABLED); + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + } + } + } + }while(0); + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_encrypt_enable()"); + return l_success; +} + + +bool nvdimm_crypto_erase(TargetHandleList &i_nvdimmList) +{ + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_crypto_erase()"); + errlHndl_t l_err = nullptr; + bool l_success = true; + + do + { + // Get the sys pointer, attribute keys are system level + Target* l_sys = nullptr; + targetService().getTopLevelTarget( l_sys ); + assert(l_sys, "nvdimm_crypto_erase: no TopLevelTarget"); + + // Exit if encryption is not enabled via the attribute + if (!l_sys->getAttr<ATTR_NVDIMM_ENCRYPTION_ENABLE>()) + { + TRACFCOMP(g_trac_nvdimm,"ATTR_NVDIMM_ENCRYPTION_ENABLE=0"); + break; + } + + // Get the FW key attributes + auto l_attrKeysFw = + l_sys->getAttrAsStdArr<ATTR_NVDIMM_ENCRYPTION_KEYS_FW>(); + + // Cast to key data struct type for easy access to each key + nvdimmKeyData_t* l_keysFw = + reinterpret_cast<nvdimmKeyData_t*>(&l_attrKeysFw); + + // Check for valid key attribute key data + l_err = nvdimm_checkValidAttrKeys(l_keysFw); + if (l_err) + { + break; + } + + // Handle erase for all nvdimms + for (const auto & l_nvdimm : i_nvdimmList) + { + // Check encryption state in the config/status reg + encryption_config_status_t l_encStatus = {0}; + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + // Encryption enabled must be set to crypto erase + if (!l_encStatus.encryption_enabled) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] encryption not enabled, will not cypto erase 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + l_success = false; + continue; + } + else + { + TRACFCOMP(g_trac_nvdimm, "nvdimm_crypto_erase() nvdimm[%X] encryption enabled 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + } + + // Set the Erase Key (EK) Reg + l_err = nvdimm_setKeyReg(l_nvdimm, + l_keysFw->ek, + ENCRYPTION_ERASE_KEY_SET, + ENCRYPTION_ERASE_KEY_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Check encryption state in the config/status reg + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + // Erase pending bit should be set + if (!l_encStatus.erase_pending) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] expected erase pending = 1 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_ERASE_PENDING_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CRYPTO_ERASE + *@userdata1 NVDIMM HUID + *@userdata2 ENCRYPTION_CONFIG_STATUS + *@devdesc NVDIMM failed to set encryption register + *@custdesc NVDIMM register error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CRYPTO_ERASE, + NVDIMM_ENCRYPTION_ERASE_PENDING_FAILED, + get_huid(l_nvdimm), + l_encStatus.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(l_nvdimm, l_err); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + + nvdimmAddPage4Regs(l_nvdimm,l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + else + { + TRACFCOMP(g_trac_nvdimm,"nvdimm_crypto_erase() nvdimm[%X] erase pending 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + } + + // Generate a generic erase key + uint8_t l_genData[ENC_KEY_SIZE] = {0}; + l_err = nvdimm_getRandom(l_genData); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Set the Erase Key (EK) Reg + l_err = nvdimm_setKeyReg(l_nvdimm, + l_genData, + ENCRYPTION_ERASE_KEY_SET, + ENCRYPTION_ERASE_KEY_VERIFY, + false); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + + // Check encryption state in the config/status reg + l_err = nvdimmReadReg(l_nvdimm, + ENCRYPTION_CONFIG_STATUS, + l_encStatus.whole); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] error reading ENCRYPTION_CONFIG_STATUS",get_huid(l_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + // Encryption enabled bit should not be set + if (l_encStatus.encryption_enabled) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_crypto_erase() nvdimm[%X] expected encryption enabled = 0 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + /*@ + *@errortype + *@reasoncode NVDIMM_ENCRYPTION_ERASE_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CRYPTO_ERASE + *@userdata1 NVDIMM HUID + *@userdata2 ENCRYPTION_CONFIG_STATUS + *@devdesc NVDIMM failed to set encryption register + *@custdesc NVDIMM register error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CRYPTO_ERASE, + NVDIMM_ENCRYPTION_ERASE_FAILED, + get_huid(l_nvdimm), + l_encStatus.whole, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + nvdimmAddVendorLog(l_nvdimm, l_err); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + + nvdimmAddPage4Regs(l_nvdimm,l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetEncryptionError(l_nvdimm); + l_success = false; + continue; + } + else + { + TRACFCOMP(g_trac_nvdimm,"nvdimm_crypto_erase() nvdimm[%X] erase complete 0x%.02x",get_huid(l_nvdimm),l_encStatus.whole); + + l_err = notifyNvdimmProtectionChange(l_nvdimm, + ENCRYPTION_DISABLED); + if (l_err) + { + errlCommit(l_err, NVDIMM_COMP_ID); + } + } + } + }while(0); + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_crypto_erase()"); + return l_success; +} + + +errlHndl_t notifyNvdimmProtectionChange(Target* i_target, + const nvdimm_protection_t i_state) +{ + TRACFCOMP( g_trac_nvdimm, ENTER_MRK + "notifyNvdimmProtectionChange: Target huid 0x%.8X, state %d", + get_huid(i_target), i_state); + + errlHndl_t l_err = nullptr; + + do + { + // Get the type of target passed in + // It could be proc_type for OCC state + // Or dimm_type for ARM/ERROR state + ATTR_TYPE_type l_type = i_target->getAttr<ATTR_TYPE>(); + assert((l_type == TYPE_PROC)||(l_type == TYPE_DIMM), + "notifyNvdimmProtectionChange invalid target type"); + + // Load the nvdimm list + TargetHandleList l_nvdimmTargetList; + Target* l_proc = nullptr; + if (l_type == TYPE_PROC) + { + // Get the nvdimms under this proc target + l_nvdimmTargetList = getProcNVDIMMs(i_target); + + // Only send command if the processor has an NVDIMM under it + if (l_nvdimmTargetList.empty()) + { + TRACFCOMP( g_trac_nvdimm, "notifyNvdimmProtectionChange: " + "No NVDIMM found under processor 0x%.8X", + get_huid(i_target)); + break; + } + + // The proc target is the passed-in target + l_proc = i_target; + } + else + { + // Only a list of one but keep consistent with proc type + l_nvdimmTargetList.push_back(i_target); + + // Find the proc target from nvdimm target passed in + TargetHandleList l_procList; + getParentAffinityTargets(l_procList, + i_target, + CLASS_CHIP, + TYPE_PROC, + UTIL_FILTER_ALL); + assert(l_procList.size() == 1, "notifyNvdimmProtectionChange:" + "getParentAffinityTargets size != 1"); + l_proc = l_procList[0]; + } + + + // Update the nvdimm status attributes + for (auto const l_nvdimm : l_nvdimmTargetList) + { + // Get the armed status attr and update it + ATTR_NVDIMM_ARMED_type l_armed_state = {}; + l_armed_state = l_nvdimm->getAttr<ATTR_NVDIMM_ARMED>(); + + // If we change the armed state, need to tell FSP + bool l_armed_change = false; + bool l_set_encryption = false; + bool l_clr_encryption = false; + bool l_sev_started = false; + bool l_sev_completed = false; + + switch (i_state) + { + case NVDIMM_ARMED: + l_armed_state.armed = 1; + l_armed_change = true; + break; + case NVDIMM_DISARMED: + l_armed_state.armed = 0; + l_armed_change = true; + break; + case OCC_ACTIVE: + l_armed_state.occ_active = 1; + break; + case OCC_INACTIVE: + l_armed_state.occ_active = 0; + break; + case NVDIMM_FATAL_HW_ERROR: + l_armed_state.fatal_error_detected = 1; + break; + case NVDIMM_RISKY_HW_ERROR: + l_armed_state.risky_error_detected = 1; + break; + case NVDIMM_ENCRYPTION_ERROR: + l_armed_state.encryption_error_detected = 1; + break; + case ENCRYPTION_ENABLED: + l_set_encryption = true; + break; + case ENCRYPTION_DISABLED: + l_clr_encryption = true; + break; + case ERASE_VERIFY_STARTED: + l_sev_started = true; + break; + case ERASE_VERIFY_COMPLETED: + l_sev_completed = true; + break; + case SEND_NV_STATUS: + // no action, just send status + break; + } + + // Set the attribute and send it to the FSP if needed + l_nvdimm->setAttr<ATTR_NVDIMM_ARMED>(l_armed_state); + if( l_armed_change ) + { + send_ATTR_NVDIMM_ARMED( l_nvdimm, l_armed_state ); + } + + // Get the nv status flag attr and update it + ATTR_NV_STATUS_FLAG_type l_nv_status = + l_nvdimm->getAttr<ATTR_NV_STATUS_FLAG>(); + + // Clear bit 0 if protected nv state + if (l_armed_state.armed && + l_armed_state.occ_active && + !l_armed_state.fatal_error_detected) + { + l_nv_status &= NV_STATUS_UNPROTECTED_CLR; + } + + // Set bit 0 if unprotected nv state + else + { + l_nv_status |= NV_STATUS_UNPROTECTED_SET; + } + + // Set bit 4 if encryption enabled + if (l_set_encryption) + { + l_nv_status |= NV_STATUS_ENCRYPTION_SET; + } + + // Clear bit 4 if encryption disabled + if (l_clr_encryption) + { + l_nv_status &= NV_STATUS_ENCRYPTION_CLR; + } + + // Clear bit 5 if secure erase verify started + if (l_sev_started) + { + l_nv_status &= NV_STATUS_ERASE_VERIFY_CLR; + } + + // Set bit 5 if secure erase verify comlpleted + if (l_sev_completed) + { + l_nv_status |= NV_STATUS_ERASE_VERIFY_SET; + } + + // Set bit 6 if risky error + if (l_armed_state.risky_error_detected) + { + l_nv_status |= NV_STATUS_POSSIBLY_UNPROTECTED_SET; + } + + l_nvdimm->setAttr<ATTR_NV_STATUS_FLAG>(l_nv_status); + + } // for nvdimm list + + // Generate combined nvdimm status for the proc + // Bit 2 of NV_STATUS_FLAG is 'Device contents are persisted' + // and must be ANDed for all nvdimms + // the rest of the bits are ORed for all nvdimms + ATTR_NV_STATUS_FLAG_type l_combined_or = 0x00; + ATTR_NV_STATUS_FLAG_type l_combined_and = 0xFF; + ATTR_NV_STATUS_FLAG_type l_combined_status = 0x00; + l_nvdimmTargetList = getProcNVDIMMs(l_proc); + for (auto const l_nvdimm : l_nvdimmTargetList) + { + l_combined_or |= l_nvdimm->getAttr<ATTR_NV_STATUS_FLAG>(); + l_combined_and &= l_nvdimm->getAttr<ATTR_NV_STATUS_FLAG>(); + } + + // Bit 2 of NV_STATUS_FLAG is 'Device contents are persisted' + l_combined_status = + (l_combined_or & NV_STATUS_OR_MASK) | + (l_combined_and & NV_STATUS_AND_MASK); + + TRACFCOMP( g_trac_nvdimm, + "notifyNvdimmProtectionChange: NV_STATUS for proc %X 0x%.02X", + get_huid(l_proc), l_combined_status); + +#ifdef __HOSTBOOT_RUNTIME + + // Send combined status notification + // Get the Proc Chip Id + TARGETING::rtChipId_t l_chipId = 0; + + l_err = TARGETING::getRtTarget(l_proc, l_chipId); + if(l_err) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"notifyNvdimmProtectionChange: getRtTarget ERROR" ); + break; + } + + // Check for valid interface + if ((nullptr == g_hostInterfaces) || + (nullptr == g_hostInterfaces->firmware_request)) + { + TRACFCOMP( g_trac_nvdimm, ERR_MRK"notifyNvdimmProtectionChange: " + "Hypervisor firmware_request interface not linked"); + + /*@ + * @errortype + * @severity ERRL_SEV_PREDICTIVE + * @moduleid NOTIFY_NVDIMM_PROTECTION_CHG + * @reasoncode NVDIMM_NULL_FIRMWARE_REQUEST_PTR + * @userdata1 HUID of processor target + * @userdata2[0:31] NV_STATUS to PHYP + * @userdata2[32:63] In state change + * @devdesc Unable to inform PHYP of NVDIMM protection + * @custdesc Internal firmware error + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NOTIFY_NVDIMM_PROTECTION_CHG, + NVDIMM_NULL_FIRMWARE_REQUEST_PTR, + get_huid(l_proc), + TWO_UINT32_TO_UINT64( + l_combined_status, + i_state) + ); + + l_err->addProcedureCallout(HWAS::EPUB_PRC_PHYP_CODE, + HWAS::SRCI_PRIORITY_HIGH); + + break; + } + + TRACFCOMP( g_trac_nvdimm, + "notifyNvdimmProtectionChange: 0x%.8X " + "NV_STATUS to HYP: 0x%02X", + get_huid(l_proc), + l_combined_status ); + + // Create the firmware_request request struct to send data + hostInterfaces::hbrt_fw_msg l_req_fw_msg; + memset(&l_req_fw_msg, 0, sizeof(l_req_fw_msg)); // clear it all + + // actual msg size (one type of hbrt_fw_msg) + uint64_t l_req_fw_msg_size = hostInterfaces::HBRT_FW_MSG_BASE_SIZE + + sizeof(l_req_fw_msg.nvdimm_protection_state); + + // Populate the firmware_request request struct with given data + l_req_fw_msg.io_type = + hostInterfaces::HBRT_FW_MSG_TYPE_NVDIMM_PROTECTION; + l_req_fw_msg.nvdimm_protection_state.i_procId = l_chipId; + l_req_fw_msg.nvdimm_protection_state.i_state = l_combined_status; + + // Create the firmware_request response struct to receive data + hostInterfaces::hbrt_fw_msg l_resp_fw_msg; + uint64_t l_resp_fw_msg_size = sizeof(l_resp_fw_msg); + memset(&l_resp_fw_msg, 0, l_resp_fw_msg_size); + + // Make the firmware_request call + l_err = firmware_request_helper(l_req_fw_msg_size, + &l_req_fw_msg, + &l_resp_fw_msg_size, + &l_resp_fw_msg); +#endif + + } while (0); + + TRACFCOMP( g_trac_nvdimm, + EXIT_MRK "notifyNvdimmProtectionChange(%.8X, %d) - ERRL %.8X:%.4X", + get_huid(i_target), i_state, + ERRL_GETEID_SAFE(l_err), ERRL_GETRC_SAFE(l_err) ); + + return l_err; +} + + +/* + * @brief Get operational unit operation timeout + */ +errlHndl_t getOperOpsTimeout(TARGETING::Target* i_nvdimm, + uint16_t& o_timeout) +{ + errlHndl_t l_err = nullptr; + + do + { + // Get timeout lsb + uint8_t l_lsb = 0; + l_err = nvdimmReadReg(i_nvdimm, + OPERATIONAL_UNIT_OPS_TIMEOUT0, + l_lsb); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getOperOpsTimeout() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_OPS_TIMEOUT0); + break; + } + + // Get timeout msb + uint8_t l_msb = 0; + l_err = nvdimmReadReg(i_nvdimm, + OPERATIONAL_UNIT_OPS_TIMEOUT1, + l_msb); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getOperOpsTimeout() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_OPS_TIMEOUT1); + break; + } + + // Bit 7 of the MSB indicates whether the time should + // be interpreted in seconds or milliseconds + // 0 = millisecond + // 1 = second + if (l_msb < MSBIT_SET_MASK) + { + o_timeout = l_msb; + o_timeout <<= 8; + o_timeout += l_lsb; + o_timeout = o_timeout / MS_PER_SEC; + } + else + { + l_msb = l_msb & MSBIT_CLR_MASK; + o_timeout = l_msb; + o_timeout <<= 8; + o_timeout += l_lsb; + } + + } while(0); + + return l_err; +} + + +/* + * @brief Wait for operational unit operation to complete + */ +errlHndl_t waitOperOpsComplete(TARGETING::Target* i_nvdimm, uint8_t i_cmd) +{ + errlHndl_t l_err = nullptr; + bool l_complete = false; + uint16_t l_timeout = 0; + uint8_t l_status = 0; + + // Get the timeout + l_err = getOperOpsTimeout(i_nvdimm, l_timeout); + + do + { + // Exit if l_timeout invalid + if (l_err) + { + break; + } + + // Delay before reading status + nanosleep( OPERATION_SLEEP_SECONDS, 0 ); + if (OPERATION_SLEEP_SECONDS > l_timeout) + { + l_timeout = 0; + } + else + { + l_timeout = l_timeout - OPERATION_SLEEP_SECONDS; + } + + // Get timeout cmd status 1 + l_err = nvdimmReadReg(i_nvdimm, + NVDIMM_CMD_STATUS1, + l_status); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "waitOperOpsComplete() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), NVDIMM_CMD_STATUS1); + break; + } + + if (l_status >= 0x01) + { + // If bit 1 is set that means the command is in progress + // Wait for it to become 0 + } + else + { + l_complete = true; + break; + } + + } while(l_timeout > 0); + + // Timed out + if (!l_err && (l_complete == false) ) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "waitOperOpsComplete() nvdimm[%X] " + "Timeout waiting for operation 0x%X to complete, " + "NVDIMM_CMD_STATUS1 0x%X", + get_huid(i_nvdimm), i_cmd, l_status); + + // Get the timeout value again + getOperOpsTimeout(i_nvdimm, l_timeout); + + /*@ + *@errortype + *@reasoncode NVDIMM_VENDOR_LOG_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_WAIT_OPER_OPS_COMPLETE + *@userdata1[0:31] NVDIMM HUID + *@userdata1[32:63] OPERATIONAL_UNIT_OPS_CMD + *@userdata2[0:31] NVDIMM_CMD_STATUS1 + *@userdata2[32:63] OPERATIONAL_UNIT_OPS_TIMEOUT + *@devdesc NVDIMM timeout reading vendor log + *@custdesc NVDIMM logging error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_WAIT_OPER_OPS_COMPLETE, + NVDIMM_VENDOR_LOG_TIMEOUT, + TWO_UINT32_TO_UINT64( + get_huid(i_nvdimm), + i_cmd + ), + TWO_UINT32_TO_UINT64( + l_status, + l_timeout + ), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + } + + return l_err; +} + + +/* + * @brief Get the vendor log unit + */ +errlHndl_t getLogPerUnit(TARGETING::Target* i_nvdimm, + uint16_t i_unitId, + std::vector<uint8_t>& o_unitData) +{ + // 3a) write OPERATIONAL_UNIT_ID0 and OPERATIONAL_UNIT_ID1 with unit_id + // 3b) set OPERATIONAL_UNIT_OPS_CMD to GET_OPERATIONAL_UNIT + // 3c) wait for NVDIMM_CMD_STATUS1 to return 0 + // 3d) for (block_id = 0; + // block_id < VENDOR_LOG_UNIT_SIZE/BLOCKSIZE; + // block_id++) + // 3da) Write block_id to BLOCK_ID + // 3db) Read TYPED_BLOCK_DATA_BYTE0 to TYPED_BLOCK_DATA_BYTE31 + // 3dc) Save data to buffer + + errlHndl_t l_err = nullptr; + + do + { + // 3a) + // Write the unit LSB + l_err = nvdimmWriteReg(i_nvdimm, + OPERATIONAL_UNIT_ID0, + i_unitId & 0x00FF); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getLogPerUnit() nvdimm[%X] error writing reg 0x%X to 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_ID0, (i_unitId & 0x00FF)); + break; + } + + // Write the unit MSB + l_err = nvdimmWriteReg(i_nvdimm, + OPERATIONAL_UNIT_ID1, + i_unitId >> 8); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getLogPerUnit() nvdimm[%X] error writing reg 0x%X to 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_ID0, (i_unitId >> 8) ); + break; + } + + // 3b) + // Write the cmd + l_err = nvdimmWriteReg(i_nvdimm, + OPERATIONAL_UNIT_OPS_CMD, + GET_OPERATIONAL_UNIT); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getLogPerUnit() nvdimm[%X] error writing reg 0x%X to 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_OPS_CMD, + GET_OPERATIONAL_UNIT ); + break; + } + + // 3c + l_err = waitOperOpsComplete(i_nvdimm, GET_OPERATIONAL_UNIT); + if (l_err) + { + break; + } + + // 3d + for (uint8_t l_blockId = 0; + l_blockId < (VENDOR_LOG_UNIT_SIZE / VENDOR_LOG_BLOCK_SIZE); + l_blockId++) + { + // 3da + // Write the block id + l_err = nvdimmWriteReg(i_nvdimm, + BLOCK_ID, + l_blockId); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getLogPerUnit() nvdimm[%X] error writing reg 0x%X to 0x%X", + get_huid(i_nvdimm), BLOCK_ID, l_blockId ); + break; + } + + // 3db + // Read all the block data + for (uint16_t l_byteId = TYPED_BLOCK_DATA_BYTE0; + l_byteId < (TYPED_BLOCK_DATA_BYTE0 + VENDOR_BLOCK_DATA_BYTES); + l_byteId++) + { + uint8_t l_data = 0; + l_err = nvdimmReadReg(i_nvdimm, + l_byteId, + l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getLogPerUnit() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), l_byteId); + break; + } + + // 3dc + o_unitData.push_back(l_data); + } // for byteId + + if (l_err) + { + break; + } + } // for blockId + + } while(0); + + return l_err; +} + + +/* + * @brief Calculate CRC + */ +uint16_t crc16(const uint8_t * i_data, int i_size) +{ + // From JEDEC JESD245B.01 document + // https://www.jedec.org/standards-documents/docs/jesd245a + int i, crc; + crc = 0; + while (--i_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); +} + + +/* + * @brief Get operational unit crc + */ +errlHndl_t getOperUnitCrc(TARGETING::Target* i_nvdimm, uint16_t& o_crc) +{ + errlHndl_t l_err = nullptr; + + do + { + // Get crc lsb + uint8_t l_lsb = 0; + l_err = nvdimmReadReg(i_nvdimm, + OPERATIONAL_UNIT_CRC0, + l_lsb); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getOperUnitCrc() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_CRC0); + break; + } + + // Get crc msb + uint8_t l_msb = 0; + l_err = nvdimmReadReg(i_nvdimm, + OPERATIONAL_UNIT_CRC1, + l_msb); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "getOperUnitCrc() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_CRC1); + break; + } + + o_crc = l_msb; + o_crc <<= 8; + o_crc += l_lsb; + + } while(0); + + return l_err; +} + + +/* + * @brief Compare host and nvdimm checksum + */ +errlHndl_t compareCksum(TARGETING::Target* i_nvdimm, + std::vector<uint8_t>& i_unitData) +{ + // 3e) Compare checksum for unit retrieved + // 3ea) Write GENERATE_OPERATIONAL_UNIT_CKSUM + // to OPERATIONAL_UNIT_OPS_CMD + // 3eb) wait for NVDIMM_CMD_STATUS1 to return 0 + // 3ec) Read OPERATIONAL_UNIT_CRC1(MSB) and OPERATIONAL_UNIT_CRC0(LSB) + // 3ed) Calculate host checksum + // 3ee) return true if 3ec) == 3ed) + + errlHndl_t l_err = nullptr; + + do + { + // 3ea) + // Command the nvdimm to calculate the CRC on the unit + l_err = nvdimmWriteReg(i_nvdimm, + OPERATIONAL_UNIT_OPS_CMD, + GENERATE_OPERATIONAL_UNIT_CKSUM); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "compareCksum() nvdimm[%X] error writing reg 0x%X to 0x%X", + get_huid(i_nvdimm), OPERATIONAL_UNIT_OPS_CMD, + GENERATE_OPERATIONAL_UNIT_CKSUM ); + break; + } + + // 3eb) + // Wait for the command to finish + l_err = waitOperOpsComplete(i_nvdimm, + GENERATE_OPERATIONAL_UNIT_CKSUM); + if (l_err) + { + break; + } + + // 3ec) + // Read the HW CRC MSB + LSB + uint16_t l_nvdimmCrc = 0; + l_err = getOperUnitCrc(i_nvdimm, l_nvdimmCrc); + if (l_err) + { + break; + } + + // 3ed) + // Calculate the host checksum + uint8_t* l_hostData = reinterpret_cast<uint8_t*>(i_unitData.data()); + uint16_t l_hostCrc = crc16(l_hostData, i_unitData.size()); + + // 3ee) + if (l_hostCrc != l_nvdimmCrc) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "compareCksum() nvdimm[%X] compare cksum failed " + "hostCrc 0x%X nvdimmCrc 0x%X", + get_huid(i_nvdimm), l_hostCrc, l_nvdimmCrc); + /*@ + *@errortype + *@reasoncode NVDIMM_VENDOR_LOG_CKSUM_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_COMPARE_CKSUM + *@userdata1 NVDIMM HUID + *@userdata2[0:31] HOST CRC + *@userdata2[32:63] NVDIMM CRC + *@devdesc NVDIMM vendor log checksum failed + *@custdesc NVDIMM logging error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_COMPARE_CKSUM, + NVDIMM_VENDOR_LOG_CKSUM_FAILED, + get_huid(i_nvdimm), + TWO_UINT32_TO_UINT64( + l_hostCrc, + l_nvdimmCrc), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME); + l_err->addPartCallout( i_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + } + + } while(0); + + return l_err; +} + + +/* + * @brief Add vendor log data to FFDC + * Added to all NVDIMM HW errors + */ +void nvdimmAddVendorLog( TARGETING::Target* i_nvdimm, errlHndl_t& io_err ) +{ + TRACFCOMP( g_trac_nvdimm, ENTER_MRK + "nvdimmAddVendorLog: Target huid 0x%.8X", + get_huid(i_nvdimm)); + + /* + 1) Read VENDOR_LOG_PAGE_SIZE. Multiply the return value with BLOCKSIZE + to get the total page size (LOG_PAGE_SIZE) + 2) Set TYPED_BLOCK_DATA to VENDOR_DATA_TYPE + 3) for (unit_id = 0; + unit_id < LOG_PAGE_LENGTH/VENDOR_LOG_UNIT_SIZE; + unit_id++) + 3a) write OPERATIONAL_UNIT_ID0 and OPERATIONAL_UNIT_ID1 with unit_id + 3b) set OPERATIONAL_UNIT_OPS_CMD to GET_OPERATIONAL_UNIT + 3c) wait for NVDIMM_CMD_STATUS1 to return 0 + 3d) for (block_id = 0; + block_id < VENDOR_LOG_UNIT_SIZE/BLOCKSIZE; + block_id++) + 3da) Write block_id to BLOCK_ID + 3db) Read TYPED_BLOCK_DATA_BYTE0 to TYPED_BLOCK_DATA_BYTE31 + 3dc) Save data to buffer + 3e) Compare checksum for unit retrieved + 3ea) Write GENERATE_OPERATIONAL_UNIT_CKSUM + to OPERATIONAL_UNIT_OPS_CMD + 3eb) wait for NVDIMM_CMD_STATUS1 to return 0 + 3ec) Read OPERATIONAL_UNIT_CRC1(MSB) and OPERATIONAL_UNIT_CRC0(LSB) + 3ed) Calculate host checksum + 3ee) return true if 3ec) == 3ed) + */ + + errlHndl_t l_err = nullptr; + + // Get the vendor log attribute + auto l_vendorLog = i_nvdimm->getAttr<ATTR_NVDIMM_READING_VENDOR_LOG>(); + + do + { + // If attr is set we are already in the process of + // reading the vendor log, exit + if (l_vendorLog) + { + break; + } + + if (io_err == nullptr) + { + // A nullptr was given when it should not have been. Emit a trace + // and break out of this function. + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddVendorLog() io_err was nullptr!! Skip adding additional FFDC."); + break; + } + + + // Set the vendor log attribute so we don't recursively + // execute the nvdimmAddVendorLog function + l_vendorLog = 0x1; + i_nvdimm->setAttr<ATTR_NVDIMM_READING_VENDOR_LOG>(l_vendorLog); + + uint8_t l_readData = 0; + std::vector<uint8_t> l_fullData; + + // Step 1 + l_err = nvdimmReadReg(i_nvdimm, + VENDOR_LOG_PAGE_SIZE, + l_readData); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddVendorLog() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), VENDOR_LOG_PAGE_SIZE); + break; + } + + size_t l_logPgeLength = l_readData * VENDOR_LOG_BLOCK_SIZE; + + // Step 2 + // Some weird bug here - switching directly to VENDOR_DATA_TYPE + // would not work. Need to switch to something else first + l_err = nvdimmWriteReg(i_nvdimm, + TYPED_BLOCK_DATA, + FIRMWARE_IMAGE_DATA); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddVendorLog() nvdimm[%X] error writing 0x%X to 0x%X", + get_huid(i_nvdimm),TYPED_BLOCK_DATA, FIRMWARE_IMAGE_DATA ); + break; + } + + l_err = nvdimmWriteReg(i_nvdimm, + TYPED_BLOCK_DATA, + VENDOR_DATA_TYPE); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddVendorLog() nvdimm[%X] error writing 0x%X to 0x%X", + get_huid(i_nvdimm),TYPED_BLOCK_DATA, VENDOR_DATA_TYPE ); + break; + } + + // Step 3 + // Loop through all the log units. + for (uint16_t l_unitId = 0; + l_unitId < (l_logPgeLength / VENDOR_LOG_UNIT_SIZE); + l_unitId++) + { + // Step 3a) - 3dc) + // Get one log unit + std::vector<uint8_t> l_unitData; + l_err = getLogPerUnit(i_nvdimm, l_unitId, l_unitData); + if (l_err) + { + break; + } + + // Step 3e) - 3ee) + // Check the checksum for the entire log unit + l_err = compareCksum(i_nvdimm, l_unitData); + if (l_err) + { + break; + } + + // Append to full data + l_fullData.insert(l_fullData.end(), + l_unitData.begin(), + l_unitData.end()); + } + + if (l_err) + { + break; + } + + // Find first NUL char in the vendor log data + bool l_foundNull = false; + uint32_t l_idx = 0; + for (l_idx = 0; l_idx < l_fullData.size(); l_idx++) + { + if (l_fullData[l_idx] == 0x00) + { + l_foundNull = true; + break; + } + } + + // If NULL char not found + // then this is the old log format + if (l_foundNull == false) + { + // Add NUL terminator to ascii data + l_fullData.push_back(0x00); + } + // Else new log format + else + { + // If the next char is not NULL + // then the log has wrapped + // Re-arrange the data in chronological order + if (l_fullData[l_idx + 1] != 0x00) + { + // Save the data after the NULL char + // This is the start of the log + std::vector<uint8_t> l_tmpData; + l_tmpData.insert(l_tmpData.begin(), + l_fullData.begin() + l_idx + 1, + l_fullData.end()); + + // Erase this data from the vector + l_fullData.erase(l_fullData.begin() + l_idx + 1, + l_fullData.end()); + + // Place the saved data at the front + l_fullData.insert(l_fullData.begin(), + l_tmpData.begin(), + l_tmpData.end()); + } + // Else log has not wrapped + else + { + // Erase the data at the end of the vector + l_fullData.erase(l_fullData.begin() + l_idx + 1, + l_fullData.end()); + } + } + + // Add vendor data to error log as string + const char* l_fullChar = reinterpret_cast<char*>(l_fullData.data()); + ERRORLOG::ErrlUserDetailsStringSet l_stringSet; + l_stringSet.add("Vendor Log", l_fullChar); + l_stringSet.addToLog(io_err); + + // Change back to default + l_err = nvdimmWriteReg(i_nvdimm, + TYPED_BLOCK_DATA, + VENDOR_DEFAULT); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddVendorLog() nvdimm[%X] error writing 0x%X to 0x%X", + get_huid(i_nvdimm),TYPED_BLOCK_DATA, VENDOR_DEFAULT ); + break; + } + + } while(0); + + if (l_err) + { + // FFDC error, set as informational + l_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // Clear the vendor log attribute before exiting + l_vendorLog = 0x0; + i_nvdimm->setAttr<ATTR_NVDIMM_READING_VENDOR_LOG>(l_vendorLog); + + TRACFCOMP( g_trac_nvdimm, EXIT_MRK + "nvdimmAddVendorLog: Target huid 0x%.8X", + get_huid(i_nvdimm)); +} + + +/* + * @brief Add NVDIMM Update regs to FFDC for errors encountered + * during NVDIMM update process + */ +void nvdimmAddUpdateRegs( TARGETING::Target* i_nvdimm, errlHndl_t& io_err ) +{ + errlHndl_t l_err = nullptr; + + do { + + if (io_err == nullptr) + { + // A nullptr was given when it should not have been. Emit a trace + // and break out of this function. + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddUpdateRegs() io_err was nullptr!! Skip adding additional FFDC."); + break; + } + + ERRORLOG::ErrlUserDetailsLogRegister l_regUD(i_nvdimm); + const uint32_t l_regList[] = { + NVDIMM_READY, + FIRMWARE_OPS_STATUS, + NVDIMM_CMD_STATUS0, + FIRMWARE_OPS_TIMEOUT0, + FIRMWARE_OPS_TIMEOUT1, + FW_REGION_CRC0, + FW_REGION_CRC1, + MODULE_HEALTH, + MODULE_HEALTH_STATUS0, + MODULE_HEALTH_STATUS1, + ERROR_THRESHOLD_STATUS, + ENCRYPTION_CONFIG_STATUS, + FW_SLOT_INFO, + SLOT0_ES_FWREV0, + SLOT0_ES_FWREV1, + SLOT1_ES_FWREV0, + SLOT1_ES_FWREV1, + SLOT1_SUBFWREV, + CSAVE_INFO, + CSAVE_FAIL_INFO1, + RESTORE_STATUS, + RESTORE_FAIL_INFO, + }; + uint8_t l_readData = 0; + + for (auto l_reg : l_regList) + { + l_err = nvdimmReadReg(i_nvdimm, + l_reg, + l_readData); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddUpdateRegs() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), l_reg); + + // Don't commit, just delete the error and continue + delete l_err; + l_err = nullptr; + continue; + } + + l_regUD.addDataBuffer(&l_readData, + sizeof(l_readData), + DEVICE_NVDIMM_ADDRESS(l_reg)); + } + + l_regUD.addToLog(io_err); + + } while(0); +} + + +/* + * @brief Add Page 4 regs to FFDC + * Added to all NVDIMM HW errors + */ +void nvdimmAddPage4Regs( TARGETING::Target* i_nvdimm, errlHndl_t& io_err ) +{ + errlHndl_t l_err = nullptr; + + do + { + if (io_err == nullptr) + { + // A nullptr was given when it should not have been. Emit a trace + // and break out of this function. + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddPage4Regs() io_err was nullptr!! Skip adding additional FFDC."); + break; + } + + + // Get the page4 attribute, if set we are already + // reading the page4 regs, exit + auto l_page4 = i_nvdimm->getAttr<ATTR_NVDIMM_READING_PAGE4>(); + if (l_page4) + { + break; + } + + // Set the page4 attribute so we don't recursively + // execute the nvdimmAddPage4Regs function + l_page4 = 0x1; + i_nvdimm->setAttr<ATTR_NVDIMM_READING_PAGE4>(l_page4); + + ERRORLOG::ErrlUserDetailsLogRegister l_regUD(i_nvdimm); + uint32_t l_regList[] = { + PANIC_CNT, + PARITY_ERROR_COUNT, + FLASH_ERROR_COUNT0, + FLASH_ERROR_COUNT1, + FLASH_ERROR_COUNT2, + FLASH_BAD_BLOCK_COUNT0, + FLASH_BAD_BLOCK_COUNT1, + SCAP_STATUS, + STATUS_EVENT_INT_INFO1, + STATUS_EVENT_INT_INFO2 + }; + uint8_t l_readData = 0; + + for (auto l_reg : l_regList) + { + l_err = nvdimmReadReg(i_nvdimm, + l_reg, + l_readData); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmAddPage4Regs() nvdimm[%X] error reading 0x%X", + get_huid(i_nvdimm), l_reg); + + // Don't commit, just delete the error and continue + delete l_err; + l_err = nullptr; + continue; + } + + l_regUD.addDataBuffer(&l_readData, + sizeof(l_readData), + DEVICE_NVDIMM_ADDRESS(l_reg)); + } + + l_regUD.addToLog(io_err); + + // Clear the page4 attribute before exiting + l_page4 = 0x0; + i_nvdimm->setAttr<ATTR_NVDIMM_READING_PAGE4>(l_page4); + + } while(0); +} + +/* + * @brief Utility function to send the value of + * ATTR_NVDIMM_ARMED to the FSP + */ +void send_ATTR_NVDIMM_ARMED( Target* i_nvdimm, + ATTR_NVDIMM_ARMED_type& i_val ) +{ +#ifdef __HOSTBOOT_RUNTIME + errlHndl_t l_err = nullptr; + + // Send attr to HWSV if at runtime + AttributeTank::Attribute l_attr = {}; + if( !makeAttribute<ATTR_NVDIMM_ARMED> + (i_nvdimm, l_attr) ) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"send_ATTR_NVDIMM_ARMED() Could not create Attribute"); + /*@ + *@errortype + *@reasoncode NVDIMM_CANNOT_MAKE_ATTRIBUTE + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid SEND_ATTR_NVDIMM_ARMED + *@devdesc Couldn't create an Attribute to send the data + * to the FSP + *@custdesc NVDIMM encryption error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + SEND_ATTR_NVDIMM_ARMED, + NVDIMM_CANNOT_MAKE_ATTRIBUTE, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + else + { + std::vector<TARGETING::AttributeTank::Attribute> l_attrList; + l_attrList.push_back(l_attr); + l_err = sendAttributes( l_attrList ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"send_ATTR_NVDIMM_ARMED() Error sending ATTR_NVDIMM_ARMED down to FSP"); + l_err->setSev(ERRORLOG::ERRL_SEV_PREDICTIVE); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + } +#endif //__HOSTBOOT_RUNTIME +} + +/** + * @brief Grab the current slot that NVDIMM code is running + */ +errlHndl_t nvdimmGetRunningSlot(TARGETING::Target *i_nvdimm, uint8_t & o_slot) +{ + errlHndl_t l_err = nullptr; + uint8_t l_data = 0; + o_slot = 0; //default to slot 0 + + // Check if the firmware slot is 0 + l_err = nvdimmReadReg ( i_nvdimm, FW_SLOT_INFO, l_data); + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmGetRunningSlot() nvdimm[%X], failed to read slot info", + get_huid(i_nvdimm)); + } + else + { + // Bits 7-4 = RUNNING_FW_SLOT - slot number of running firmware + o_slot = (l_data & RUNNING_FW_SLOT) >> 4; + } + return l_err; +} + +/** + * @brief This function polls the command status register for arm completion + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollArmDone(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollArmDone() nvdimm[%X]", get_huid(i_nvdimm) ); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, ARM, o_poll); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollArmDone() nvdimm[%X]", + get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function checks the arm status register to make sure + * the trigger has been armed to ddr_reset_n + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * @param[in] i_arm_timeout - nvdimm local timeout status + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmCheckArmSuccess(Target *i_nvdimm, bool i_arm_timeout) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmCheckArmSuccess() nvdimm[%X]", + get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data = 0; + + l_err = nvdimmReadReg(i_nvdimm, ARM_STATUS, l_data); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckArmSuccess() nvdimm[%X]" + "failed to read arm status reg!",get_huid(i_nvdimm)); + } + else if (((l_data & ARM_ERROR) == ARM_ERROR) || ((l_data & RESET_N_ARMED) != RESET_N_ARMED) || i_arm_timeout) + { + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckArmSuccess() nvdimm[%X]" + "failed to arm! ARM status 0x%X ARM timeout %d" + ,get_huid(i_nvdimm),l_data,i_arm_timeout); + /*@ + *@errortype + *@reasoncode NVDIMM_ARM_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SET_ARM + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2[0:31] ARM Status + *@userdata2[32:63] ARM Timeout + *@devdesc Encountered error arming the catastrophic save + * trigger on NVDIMM. Make sure an energy source + * is connected to the NVDIMM and the ES policy + * is set properly + *@custdesc NVDIMM encountered error arming save trigger + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_ARM, + NVDIMM_ARM_FAILED, + TWO_UINT32_TO_UINT64(ARM, get_huid(i_nvdimm)), + TWO_UINT32_TO_UINT64(l_data, i_arm_timeout), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Failure to arm could mean internal NV controller error or + // even error on the battery pack. NVDIMM will lose persistency + // if failed to arm trigger + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_Fatal); + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmCheckArmSuccess() nvdimm[%X] ret[%X]", + get_huid(i_nvdimm), l_data); + + return l_err; +} + +/** + * @brief This function performs arm precheck. + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmArmPreCheck(Target* i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmArmPreCheck() nvdimm[%X]", + get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_ready = 0; + uint8_t l_fwupdate = 0; + uint8_t l_module_health = 0; + uint8_t l_continue = true; + auto l_RegInfo = nvdimm_reg_t(); + + do + { + // Read out the Module Health status register + l_err = nvdimmReadReg(i_nvdimm, MODULE_HEALTH_STATUS0, l_module_health); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArmPreCheck() nvdimm[%X] - failed to read Module Health Status", + get_huid(i_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_continue = false; + break; + } + + // Read out the NVDimm Ready register + l_err = nvdimmReadReg(i_nvdimm, NVDIMM_READY, l_ready); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArmPreCheck() nvdimm[%X] - failed to read NVDimm Ready register", + get_huid(i_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_continue = false; + break; + } + + // Read out the FW OPs Status register + l_err = nvdimmReadReg(i_nvdimm, FIRMWARE_OPS_STATUS, l_fwupdate); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArmPreCheck() nvdimm[%X] - failed to read Firmware OPs Status register", + get_huid(i_nvdimm)); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_continue = false; + } + + }while(0); + + // Check ARM pre-requisites + // All nvdimms in i_nvdimmTargetList must pass the pre-req checks + // before continuing with arm. + if ((!l_continue) || (l_module_health & NVM_LIFETIME_ERROR) + || (l_ready != NV_READY) + || (l_fwupdate & FW_OPS_UPDATE)) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArmPreCheck() nvdimm[%X] - failed NVDimm Arm prechecks", + get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ARM_PRE_CHECK_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_ARM_PRE_CHECK + *@userdata1[0:31] Target Huid + *@userdata1[32:39] l_continue + *@userdata1[40:47] l_module_health + *@userdata1[48:56] l_ready + *@userdata1[57:63] l_fwupdate + *@userdata2 <UNUSED> + *@devdesc NVDIMM failed arm precheck. Refer to FFDC for exact reason + *@custdesc NVDIMM failed the arm precheck and is unable to arm + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_ARM_PRE_CHECK, + NVDIMM_ARM_PRE_CHECK_FAILED, + NVDIMM_SET_USER_DATA_1(TARGETING::get_huid(i_nvdimm), + FOUR_UINT8_TO_UINT32(l_continue, l_module_health, l_ready, l_fwupdate)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Callout the dimm + l_err->addHwCallout( i_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + // Read relevant regs for trace data + nvdimmTraceRegs(i_nvdimm, l_RegInfo); + nvdimmAddPage4Regs(i_nvdimm,l_err); + nvdimmAddVendorLog(i_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmArmPreCheck() nvdimm[%X]", + get_huid(i_nvdimm)); + + return l_err; +} + + +bool nvdimmArm(TargetHandleList &i_nvdimmTargetList) +{ + bool o_arm_successful = true; + bool l_continue = true; + bool l_arm_timeout = false; + uint8_t l_data; + auto l_RegInfo = nvdimm_reg_t(); + uint64_t l_writeData; + uint32_t l_writeAddress; + size_t l_writeSize = sizeof(l_writeData); + + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmArm() numNvdimm[%d]", + i_nvdimmTargetList.size()); + + errlHndl_t l_err = nullptr; + errlHndl_t l_err_t = nullptr; + + // Prerequisite Arm Checks + for (auto const l_nvdimm : i_nvdimmTargetList) + { + l_err = nvdimmArmPreCheck(l_nvdimm); + + // If we are failing the precheck, commit the error then exit + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArm() failed arm precheck, exiting"); + errlCommit(l_err, NVDIMM_COMP_ID); + return false; + } + } + + // Encryption unlocked check + // Check one nvdimm at a time + for (auto const l_nvdimm : i_nvdimmTargetList) + { + // Unlock function will create an error log + // Create another here to make it clear that the arm failed + TargetHandleList l_nvdimmTargetList; + l_nvdimmTargetList.push_back(l_nvdimm); + if (!nvdimm_encrypt_unlock(l_nvdimmTargetList)) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArm() nvdimm[%X] - failed NVDimm Arm encryption unlock", + get_huid(l_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ARM_ENCRYPTION_UNLOCK_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_ARM + *@userdata1 Target Huid + *@userdata2 <UNUSED> + *@devdesc NVDIMM failed to unlock encryption during arming + *@custdesc NVDIMM failed to ARM + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_ARM, + NVDIMM_ARM_ENCRYPTION_UNLOCK_FAILED, + get_huid(l_nvdimm), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Callout the dimm + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_MED, + HWAS::DELAYED_DECONFIG, + HWAS::GARD_NULL); + + // Read relevant regs for trace data + nvdimmTraceRegs(l_nvdimm, l_RegInfo); + nvdimmAddPage4Regs(l_nvdimm,l_err); + nvdimmAddVendorLog(l_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + + // Commit the error then exit + errlCommit(l_err, NVDIMM_COMP_ID); + return false; + } + } + + // Mask MBACALFIR EventN to separate ARM handling + for (TargetHandleList::iterator it = i_nvdimmTargetList.begin(); + it != i_nvdimmTargetList.end();) + { + TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, CLASS_UNIT, TYPE_MCA); + assert(l_mcaList.size(), "nvdimmArm() failed to find parent MCA."); + + l_writeAddress = MBACALFIR_OR_MASK_REG; + l_writeData = MBACALFIR_EVENTN_OR_BIT; + l_err = deviceWrite(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + it++; + } + + for (auto const l_nvdimm : i_nvdimmTargetList) + { + l_arm_timeout = false; + + // skip if the nvdimm is already armed + ATTR_NVDIMM_ARMED_type l_armed_state = {}; + l_armed_state = l_nvdimm->getAttr<ATTR_NVDIMM_ARMED>(); + if (l_armed_state.armed) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] called when already armed", get_huid(l_nvdimm)); + continue; + } + + // Set ES Policy, contains all of its status checks + l_err = nvdimmSetESPolicy(l_nvdimm); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed to set ES Policy", get_huid(l_nvdimm)); + o_arm_successful = false; + + nvdimmDisarm(i_nvdimmTargetList); + + // 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); + + // Callout the nvdimm on high and gard + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_Fatal); + + errlCommit( l_err, NVDIMM_COMP_ID ); + + break; + } + + // Clear all status registers in case of leftover bits + l_err = nvdimmWriteReg(l_nvdimm, NVDIMM_MGT_CMD0, CLEAR_ALL_STATUS); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArm() nvdimm[%X] - error clearing all status registers", + get_huid(l_nvdimm)); + o_arm_successful = false; + break; + } + + bool l_is_retryable = true; + //continue flag set by the retry loop to continue on the outer loop + bool l_continue_arm = false; + //break flag set by the retry loop to break on the outer loop + bool l_break = false; + errlHndl_t l_err_retry = nullptr; + + // Attempt arm multiple times in case of glitches + for (size_t l_retry = 0; l_retry <= ARM_MAX_RETRY_COUNT; l_retry++) + { + + 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 + // system continue to boot and let the user + // salvage the data + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed to trigger arm", get_huid(l_nvdimm)); + + nvdimmDisarm(i_nvdimmTargetList); + + // 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); + errlCommit( l_err, NVDIMM_COMP_ID ); + o_arm_successful = false; + + // Cause the main loop to skip the rest of the arm procedure + // and move to the next target + l_continue_arm = true; + break; + } + + // Arm happens one module at a time. No need to set any offset on the counter + uint32_t l_poll = 0; + l_err = nvdimmPollArmDone(l_nvdimm, l_poll); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] arm command timed out", get_huid(l_nvdimm)); + l_arm_timeout = true; + + l_err_t = notifyNvdimmProtectionChange(l_nvdimm, NVDIMM_DISARMED); + if (l_err_t) + { + errlCommit( l_err_t, NVDIMM_COMP_ID ); + } + + // 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); + + errlCommit( l_err, NVDIMM_COMP_ID ); + o_arm_successful = false; + } + + // Pass l_arm_timeout value in for health status check + l_continue = l_arm_timeout; + + // Sleep for 1 second before checking the health status + // to let the glitches settle in case there were any + nanosleep(1, 0); + + // Check health status registers and exit if required + l_err = nvdimmHealthStatusCheck( l_nvdimm, HEALTH_PRE_ARM, l_continue ); + + // Check for health status failure + // Any fail picked up by the health check is a legit fail + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed first health status check", get_huid(l_nvdimm)); + + // The arm timeout variable is used here as the continue variable for the + // health status check. This was done to include the timeout for use in the check + // If true either the arm timed out with a health status fail or the + // health status check failed with another disarm and exit condition + if (!l_continue) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + + // Disarming all dimms due to error + nvdimmDisarm(i_nvdimmTargetList); + o_arm_successful = false; + + // Cause the main loop to exit out of the main arm procedure + l_break = true; + break; + } + else + { + errlCommit( l_err, NVDIMM_COMP_ID ); + + // Cause the main loop to skip the rest of the arm procedure + // and move to the next target + l_continue_arm = true; + break; + } + } + + l_err = nvdimmCheckArmSuccess(l_nvdimm, l_arm_timeout); + + // At this point we have passed the health check. If the arm were + // to fail now, it is likely it was due to some glitch. Let's retry + // the arm again as long as the fail is not due to timeout. + // A timeout would mean a charging issue, it would have been caught + // by the health check. + l_is_retryable = !l_arm_timeout && l_retry < ARM_MAX_RETRY_COUNT; + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed to succesfully arm. %s retryable.", + get_huid(l_nvdimm), l_is_retryable? "IS" : "NOT"); + + if (l_is_retryable) + { + // Save the original error + // If a previous error was saved then delete it + if (l_err_retry) + { + delete l_err_retry; + } + l_err_retry = l_err; + + /*@ + *@errortype + *@reasoncode NVDIMM_ARM_RETRY + *@severity ERRORLOG_SEV_INFORMATIONAL + *@moduleid NVDIMM_ARM_ERASE + *@userdata1[0:31] Target Huid + *@userdata1[32:39] l_is_retryable + *@userdata1[40:47] MAX arm retry count + *@userdata2[0:31] Original errlog plid + *@userdata2[32:63] Original errlog reason code + *@devdesc NVDIMM encountered a glitch causing the initial + * arm to fail. System firmware will retry the arm + *@custdesc NVDIMM requires an arm retry + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_INFORMATIONAL, + NVDIMM_ARM_ERASE, + NVDIMM_ARM_RETRY, + NVDIMM_SET_USER_DATA_1(TARGETING::get_huid(l_nvdimm), + FOUR_UINT8_TO_UINT32(l_is_retryable, ARM_MAX_RETRY_COUNT,0,0)), + TWO_UINT32_TO_UINT64(l_err_retry->plid(), l_err_retry->reasonCode()), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Callout the dimm + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + errlCommit( l_err, NVDIMM_COMP_ID ); + } + else + { + // Handle retryable error + if (l_err_retry) + { + ERRORLOG::ErrlUserDetailsString("Arm RETRY failed").addToLog(l_err_retry); + + // Delete the current errlog and use the original errlog for callout + delete l_err; + l_err = l_err_retry; + l_err_retry = nullptr; + } + + // Disarming all dimms due to error + nvdimmDisarm(i_nvdimmTargetList); + + l_err_t = notifyNvdimmProtectionChange(l_nvdimm, NVDIMM_DISARMED); + if (l_err_t) + { + errlCommit( l_err_t, NVDIMM_COMP_ID ); + } + + // 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); + + // Dump Traces for error logs + nvdimmTraceRegs( l_nvdimm, l_RegInfo ); + nvdimmAddPage4Regs(l_nvdimm,l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + + errlCommit(l_err, NVDIMM_COMP_ID); + o_arm_successful = false; + + // Cause the main loop to exit out of the main arm procedure + l_break = true; + break; + } + } + else + { + // Arm worked. Exit the retry loop + break; + } // close nvdimmCheckArmSuccess check + } // close arm retry loop + + if (l_continue_arm) + { + continue; + } + else if (l_break) + { + break; + } + + // After arming the trigger, erase the image to prevent the possible + // stale image getting the restored on the next boot in case of failed + // save. + l_err = nvdimmEraseNF(l_nvdimm); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed to erase post arm", get_huid(l_nvdimm)); + + // Disarming all dimms due to error + nvdimmDisarm(i_nvdimmTargetList); + + // 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); + errlCommit( l_err, NVDIMM_COMP_ID ); + o_arm_successful = false; + break; + } + + // Arm successful, update armed status + l_err = NVDIMM::notifyNvdimmProtectionChange(l_nvdimm, + NVDIMM::NVDIMM_ARMED); + if (l_err) + { + l_err->setSev(ERRORLOG::ERRL_SEV_PREDICTIVE); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit(l_err, NVDIMM_COMP_ID); + } + + // Enable Persistency and Warning Threshold notifications + l_err = nvdimmWriteReg(l_nvdimm, SET_EVENT_NOTIFICATION_CMD, ENABLE_NOTIFICATIONS); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] setting persistency notification", + TARGETING::get_huid(l_nvdimm)); + break; + } + + // Check notification status and errors + l_err = nvdimmReadReg(l_nvdimm, SET_EVENT_NOTIFICATION_STATUS, l_data); + if (l_err) + { + break; + } + else if (((l_data & SET_EVENT_NOTIFICATION_ERROR) == SET_EVENT_NOTIFICATION_ERROR) + || ((l_data & NOTIFICATIONS_ENABLED) != NOTIFICATIONS_ENABLED)) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed to set event notification", + get_huid(l_nvdimm)); + + // Set NVDIMM Status flag to partial working, as error detected but data might persist + notifyNvdimmProtectionChange(l_nvdimm, NVDIMM_RISKY_HW_ERROR); + + /*@ + *@errortype + *@reasoncode NVDIMM_SET_EVENT_NOTIFICATION_ERROR + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SET_EVENT_NOTIFICATION + *@userdata1[0:31] Target Huid + *@userdata2 <UNUSED> + *@devdesc NVDIMM threw an error or failed to set event + * notifications during arming + *@custdesc NVDIMM failed to enable event notificaitons + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_EVENT_NOTIFICATION, + NVDIMM_SET_EVENT_NOTIFICATION_ERROR, + TARGETING::get_huid(l_nvdimm), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace( NVDIMM_COMP_NAME ); + + // Callout the dimm + l_err->addHwCallout( l_nvdimm, + HWAS::SRCI_PRIORITY_LOW, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL); + + // Read relevant regs for trace data + nvdimmTraceRegs(l_nvdimm, l_RegInfo); + nvdimmAddPage4Regs(l_nvdimm,l_err); + nvdimmAddVendorLog(l_nvdimm, l_err); + + // Add reg traces to the error log + NVDIMM::UdNvdimmOPParms( l_RegInfo ).addToLog(l_err); + + errlCommit( l_err, NVDIMM_COMP_ID ); + + // We are after the arm step now, so on any error cases let's log it + // then move to the next nvdimm + continue; + } + + // Re-check health status registers + l_err = nvdimmHealthStatusCheck( l_nvdimm, HEALTH_POST_ARM, l_continue ); + + // Check for health status failure + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() nvdimm[%X] failed final health status check", get_huid(l_nvdimm)); + + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + + } + + // Check for uncommited i2c fail error logs + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, "nvdimmArm() failed an i2c read/write"); + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmDisarm(i_nvdimmTargetList); + return false; + } + + // Unmask firs if the arm completed successfully + if (o_arm_successful) + { + // Unmask MBACALFIR EventN and set to recoverable + for (TargetHandleList::iterator it = i_nvdimmTargetList.begin(); + it != i_nvdimmTargetList.end();) + { + TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, CLASS_UNIT, TYPE_MCA); + assert(l_mcaList.size(), "nvdimmArm() failed to find parent MCA."); + + // Set MBACALFIR_ACTION0 to recoverable + l_writeAddress = MBACALFIR_ACTION0_REG; + l_writeData = 0; + l_err = deviceRead(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + + l_writeData &= MBACALFIR_EVENTN_AND_BIT; + l_err = deviceWrite(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // Set MBACALFIR_ACTION1 to recoverable + l_writeAddress = MBACALFIR_ACTION1_REG; + l_writeData = 0; + l_err = deviceRead(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + l_writeData |= MBACALFIR_EVENTN_OR_BIT; + l_err = deviceWrite(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + // Unmask MBACALFIR[8] + l_writeAddress = MBACALFIR_AND_MASK_REG; + l_writeData = MBACALFIR_UNMASK_BIT; + l_err = deviceWrite(l_mcaList[0], &l_writeData, l_writeSize, + DEVICE_SCOM_ADDRESS(l_writeAddress)); + if(l_err) + { + TRACFCOMP(g_trac_nvdimm, "SCOM to address 0x%08x failed", + l_writeAddress); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + + it++; + } + + } + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmArm() returning %d", + o_arm_successful); + return o_arm_successful; +} + +bool nvdimmDisarm(TargetHandleList &i_nvdimmTargetList) +{ + bool o_disarm_successful = true; + + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmDisarm() %d", + i_nvdimmTargetList.size()); + + errlHndl_t l_err = nullptr; + + for (auto const l_nvdimm : i_nvdimmTargetList) + { + l_err = NVDIMM::nvdimmChangeArmState(l_nvdimm, DISARM_TRIGGER); + // If we run into any error here we will just + // commit the error log and move on. Let the + // system continue to boot and let the user + // salvage the data + if (l_err) + { + // 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); + errlCommit( l_err, NVDIMM_COMP_ID ); + o_disarm_successful = false; + continue; + } + + // Disarm successful, update armed status + l_err = NVDIMM::notifyNvdimmProtectionChange(l_nvdimm, + NVDIMM::NVDIMM_DISARMED); + if (l_err) + { + l_err->setSev(ERRORLOG::ERRL_SEV_PREDICTIVE); + l_err->collectTrace(NVDIMM_COMP_NAME); + errlCommit(l_err, NVDIMM_COMP_ID); + } + } + + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmDisarm() returning %d", + o_disarm_successful); + + return o_disarm_successful; + +} + + +/* + * @brief Wrapper function to return NVDIMMs to factory default + */ +bool nvdimmFactoryDefault(TargetHandleList &i_nvdimmList) +{ + errlHndl_t l_err = nullptr; + bool l_success = true; + + // Factory default for all nvdimms in the list + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimm_factory_reset(l_nvdimm); + if (l_err) + { + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + + // Update nvdimm status + l_err = notifyNvdimmProtectionChange(l_nvdimm, NVDIMM_DISARMED); + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + } + } + + return l_success; +} + + +/* + * @brief Function to start secure erase verify of NVDIMMs + */ +bool nvdimmSecureEraseVerifyStart(TargetHandleList &i_nvdimmList) +{ + errlHndl_t l_err = nullptr; + bool l_success = true; + + // Secure erase verify for all nvdimms in the list + for (const auto & l_nvdimm : i_nvdimmList) + { + // Clear the erase_verify_status reg + l_err = nvdimmWriteReg(l_nvdimm, + ERASE_VERIFY_STATUS, + ERASE_VERIFY_CLEAR); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStart() HUID 0x%X" + "Failed to write ERASE_VERIFY_STATUS register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + + // Start the erase verify operation + l_err = nvdimmWriteReg(l_nvdimm, + ERASE_VERIFY_CONTROL, + ERASE_VERIFY_START); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStart() HUID 0x%X" + "Failed to write ERASE_VERIFY_CONTROL register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + + // Call notify to clear NV_STATUS bit + l_err = notifyNvdimmProtectionChange(l_nvdimm, + ERASE_VERIFY_STARTED); + if (l_err) + { + l_success = false; + errlCommit(l_err, NVDIMM_COMP_ID); + continue; + } + } + + return l_success; +} + + +/* + * @brief Function to check status of secure erase verify of NVDIMMs + */ +bool nvdimmSecureEraseVerifyStatus(TargetHandleList &i_nvdimmList) +{ + errlHndl_t l_err = nullptr; + bool l_success = true; + uint8_t l_data = 0; + + // Check secure erase verify status for all nvdimms in the list + for (const auto & l_nvdimm : i_nvdimmList) + { + // Check if secure-erase-verify is already complete for this nvdimm + ATTR_NV_STATUS_FLAG_type l_nv_status = + l_nvdimm->getAttr<ATTR_NV_STATUS_FLAG>(); + if (l_nv_status & NV_STATUS_ERASE_VERIFY_SET) + { + continue; + } + + l_err = nvdimmReadReg(l_nvdimm, ERASE_VERIFY_CONTROL, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStatus() HUID 0x%X" + "Failed to read ERASE_VERIFY_CONTROL register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; // Continue to next nvdimm + } + + // If trigger is set the operation is not yet complete + if (l_data & ERASE_VERIFY_TRIGGER) + { + continue; // Continue to next nvdimm + } + + // Secure erase verify on this nvdimm is complete + // Call notify to set NV_STATUS bit + l_err = notifyNvdimmProtectionChange(l_nvdimm, + ERASE_VERIFY_COMPLETED); + if (l_err) + { + l_success = false; + errlCommit(l_err, NVDIMM_COMP_ID); + } + + + // Check the status register + l_err = nvdimmReadReg(l_nvdimm, ERASE_VERIFY_STATUS, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStatus() HUID 0x%X" + "Failed to read ERASE_VERIFY_STATUS register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; // Continue to next nvdimm + } + + // Non-zero status is an error + if (l_data) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSecureEraseVerifyStatus() " + "HUID 0x%X ERASE_VERIFY_STATUS returned non-zero status", + get_huid(l_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ERASE_VERIFY_STATUS_NONZERO + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SECURE_ERASE_VERIFY_STATUS + *@userdata1 NVDIMM HUID + *@userdata2 ERASE_VERIFY_STATUS + *@devdesc Error detected during secure erase verify + *@custdesc NVDIMM erase error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SECURE_ERASE_VERIFY_STATUS, + NVDIMM_ERASE_VERIFY_STATUS_NONZERO, + get_huid(l_nvdimm), + l_data, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + nvdimmAddVendorLog(l_nvdimm, l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_success = false; + continue; // Continue to next nvdimm + } + + + // Check the result registers + uint16_t l_result = 0; + l_err = nvdimmReadReg(l_nvdimm, ERASE_VERIFY_RESULT_MSB, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStatus() HUID 0x%X" + "Failed to read ERASE_VERIFY_RESULT_MSB register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; // Continue to next nvdimm + } + + // Save result + l_result = l_data << 8; + + l_err = nvdimmReadReg(l_nvdimm, ERASE_VERIFY_RESULT_LSB, l_data); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK + "nvdimmSecureEraseVerifyStatus() HUID 0x%X" + "Failed to read ERASE_VERIFY_RESULT_LSB register", + get_huid(l_nvdimm)); + l_success = false; + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; // Continue to next nvdimm + } + + // Save result + l_result |= l_data; + + // Non-zero result is an error + if (l_result) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSecureEraseVerifyStatus() " + "HUID 0x%X ERASE_VERIFY_RESULT returned non-zero data", + get_huid(l_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ERASE_VERIFY_RESULT_NONZERO + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SECURE_ERASE_VERIFY_STATUS + *@userdata1 NVDIMM HUID + *@userdata2 ERASE_VERIFY_RESULT + *@devdesc Error detected during secure erase verify + *@custdesc NVDIMM erase error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SECURE_ERASE_VERIFY_STATUS, + NVDIMM_ERASE_VERIFY_RESULT_NONZERO, + get_huid(l_nvdimm), + l_result, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME); + l_err->addPartCallout( l_nvdimm, + HWAS::NV_CONTROLLER_PART_TYPE, + HWAS::SRCI_PRIORITY_HIGH); + nvdimmAddVendorLog(l_nvdimm, l_err); + errlCommit( l_err, NVDIMM_COMP_ID ); + l_success = false; + continue; // Continue to next nvdimm + } + + } + + return l_success; +} + + } // end NVDIMM namespace |