diff options
author | Matt Derksen <mderkse1@us.ibm.com> | 2018-12-10 16:38:11 -0600 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2019-02-28 10:24:21 -0600 |
commit | 37e67698be56632c6505990dc3375136494d2a67 (patch) | |
tree | a23967962c1e31f9a2a8b7430fff1b10232241b2 /src/usr | |
parent | 80cea86add7ba742181cd272b16e10185b5e9a4d (diff) | |
download | talos-hostboot-37e67698be56632c6505990dc3375136494d2a67.tar.gz talos-hostboot-37e67698be56632c6505990dc3375136494d2a67.zip |
Framework for NVDIMM update
This includes framework to update the firmware
running on the NV controller. The controller requires
12V power to update, so this function in inside hostboot.
Change-Id: I0733d83ff6ba2fc3f026d49c72784b1295bd3eed
RTC:201197
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/69879
Reviewed-by: Roland Veloz <rveloz@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/isteps/istep20/call_host_load_payload.C | 13 | ||||
-rw-r--r-- | src/usr/isteps/istep20/call_nvdimm_update.C | 76 | ||||
-rw-r--r-- | src/usr/isteps/istep20/call_nvdimm_update.H | 38 | ||||
-rw-r--r-- | src/usr/isteps/istep20/makefile | 1 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.mk | 10 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.C | 544 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.H | 278 | ||||
-rw-r--r-- | src/usr/targeting/common/util.C | 24 | ||||
-rwxr-xr-x | src/usr/vpd/spdDDR4.H | 4 |
9 files changed, 978 insertions, 10 deletions
diff --git a/src/usr/isteps/istep20/call_host_load_payload.C b/src/usr/isteps/istep20/call_host_load_payload.C index 6251eaf80..26dab532b 100644 --- a/src/usr/isteps/istep20/call_host_load_payload.C +++ b/src/usr/isteps/istep20/call_host_load_payload.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -40,6 +40,11 @@ #include <xz/xz.h> #include <config.h> +#ifdef CONFIG_NVDIMM +#include "call_nvdimm_update.H" +#endif + + using namespace ERRORLOG; using namespace ISTEP; using namespace ISTEP_ERROR; @@ -129,6 +134,12 @@ void* call_host_load_payload (void *io_pArgs) } } +#ifdef CONFIG_NVDIMM + // Update the NVDIMM controller code, if necessary + // Need to do this after LIDs are accessible + NVDIMM_UPDATE::call_nvdimm_update(); +#endif + }while(0); return l_err; diff --git a/src/usr/isteps/istep20/call_nvdimm_update.C b/src/usr/isteps/istep20/call_nvdimm_update.C new file mode 100644 index 000000000..7a1817faf --- /dev/null +++ b/src/usr/isteps/istep20/call_nvdimm_update.C @@ -0,0 +1,76 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/istep20/call_nvdimm_update.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <initservice/isteps_trace.H> +#include <targeting/common/commontargeting.H> +#include <targeting/common/utilFilter.H> + +// NVDIMM support +#include <isteps/nvdimm/nvdimm.H> + +#include "call_nvdimm_update.H" + +namespace NVDIMM_UPDATE +{ + +/** + * @brief This function updates the NVDIMM firmware code + */ +void call_nvdimm_update() +{ + TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,ENTER_MRK"call_nvdimm_update()"); + + TARGETING::TargetHandleList l_nvdimmTargetList; + TARGETING::TargetHandleList l_procList; + TARGETING::getAllChips(l_procList, TARGETING::TYPE_PROC, false); + + // grab the NVDIMMs under each processor and add to overall list + for (auto l_proc : l_procList) + { + TARGETING::TargetHandleList tmpList = + TARGETING::getProcNVDIMMs(l_proc); + l_nvdimmTargetList.insert(l_nvdimmTargetList.end(), + tmpList.begin(), tmpList.end()); + } + + // Run the nvdimm update function if the list is not empty + if ( !l_nvdimmTargetList.empty() ) + { + TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, + "call_nvdimm_update(): found %d nvdimms to check for update", + l_nvdimmTargetList.size()); + bool updateWorked = NVDIMM::nvdimm_update(l_nvdimmTargetList); + if (!updateWorked) + { + TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, + "call_nvdimm_update(): nvdimm update failed"); + } + } + + TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,EXIT_MRK"call_nvdimm_update()"); +} + +}; diff --git a/src/usr/isteps/istep20/call_nvdimm_update.H b/src/usr/isteps/istep20/call_nvdimm_update.H new file mode 100644 index 000000000..70922f3d8 --- /dev/null +++ b/src/usr/isteps/istep20/call_nvdimm_update.H @@ -0,0 +1,38 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/istep20/call_nvdimm_update.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __CALL_NVDIMM_UPDATE_H +#define __CALL_NVDIMM_UPDATE_H + +namespace NVDIMM_UPDATE +{ + /** + * @brief This function updates the NVDIMM firmware code + * Will locate functional NVDIMMs and update their + * nv controller code if necessary. + */ + void call_nvdimm_update(); +} + +#endif diff --git a/src/usr/isteps/istep20/makefile b/src/usr/isteps/istep20/makefile index f98bb7e9b..5a656bd3b 100644 --- a/src/usr/isteps/istep20/makefile +++ b/src/usr/isteps/istep20/makefile @@ -26,5 +26,6 @@ ROOTPATH = ../../../.. MODULE = istep20 OBJS += call_host_load_payload.o +OBJS += $(if $(CONFIG_NVDIMM),call_nvdimm_update.o,) include ${ROOTPATH}/config.mk diff --git a/src/usr/isteps/nvdimm/nvdimm.mk b/src/usr/isteps/nvdimm/nvdimm.mk index cf5ec4fe6..397b27814 100644 --- a/src/usr/isteps/nvdimm/nvdimm.mk +++ b/src/usr/isteps/nvdimm/nvdimm.mk @@ -22,6 +22,9 @@ # permissions and limitations under the License. # # IBM_PROLOG_END_TAG +# nvdimmm.mk should only be called when CONFIG_NVDIMM is set +# Called by src/makefile with the condition that CONFIG_NVDIMM is defined + PROCEDURE_PATH = ${ROOTPATH}/src/import/chips/p9/procedures #Add all the extra include paths @@ -45,4 +48,11 @@ OBJS += nvdimm.o OBJS += nvdimmdd.o OBJS += errlud_nvdimm.o +ifneq (${HOSTBOOT_RUNTIME},1) + +# code update path for NVDIMMs (not at RUNTIME) +OBJS += nvdimm_update.o + +endif + VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ddr4/ diff --git a/src/usr/isteps/nvdimm/nvdimm_update.C b/src/usr/isteps/nvdimm/nvdimm_update.C new file mode 100644 index 000000000..a3191cb53 --- /dev/null +++ b/src/usr/isteps/nvdimm/nvdimm_update.C @@ -0,0 +1,544 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/nvdimm/nvdimm_update.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "nvdimm_update.H" +#include "nvdimm.H" +#include <isteps/nvdimm/nvdimm.H> +#include <isteps/nvdimm/nvdimmreasoncodes.H> + +#include <errl/errlmanager.H> +#include <devicefw/userif.H> +#include <vpd/spdenums.H> + +// Easy macro replace for unit testing +// #define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + +namespace NVDIMM +{ +////////////////////////////////////////////////////////////////////////////// +// Helper Inline functions +////////////////////////////////////////////////////////////////////////////// +/** + * @brief Inline function to collect NVDIMM traces and + * make sure error is logged at least as PREDICTIVE + */ +inline void commitPredictiveNvdimmError(errlHndl_t & io_err) +{ + io_err->collectTrace(NVDIMM_COMP_NAME, 256); + if ( io_err->sev() < ERRORLOG::ERRL_SEV_PREDICTIVE ) + { + io_err->setSev( ERRORLOG::ERRL_SEV_PREDICTIVE ); + } + ERRORLOG::errlCommit(io_err, NVDIMM_COMP_ID); +} + + +/////////////////////////////////////////////////////////////////////////////// +// NVDIMM LID Image +/////////////////////////////////////////////////////////////////////////////// +NvdimmLidImage::NvdimmLidImage(Util::LidId i_lidId, errlHndl_t& io_errHdl) +{ + iv_lidImage = nullptr; + iv_lidImageSize = 0; + iv_ImageLoaded = false; + + iv_lidMgr = new UtilLidMgr(i_lidId); + io_errHdl = loadImage(); +} + +NvdimmLidImage::~NvdimmLidImage() +{ + errlHndl_t l_err = unloadImage(); + if (l_err) + { + ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); + } +} + +uint32_t NvdimmLidImage::getType() +{ + uint32_t o_type = INVALID_TYPE; + + if (iv_lidImageSize >= sizeof(nvdimm_image_header_t)) + { + nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> + (iv_lidImage); + o_type = (uint32_t)pLid->module_mnfg_id_code << 16; + o_type |= (uint32_t)pLid->module_product_id; + } + return o_type; +} + +uint16_t NvdimmLidImage::getVersion() +{ + uint16_t o_version = INVALID_VERSION; + + if (iv_lidImageSize >= sizeof(nvdimm_image_header_t)) + { + nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> + (iv_lidImage); + o_version = pLid->controller_firmware_revision; + } + return o_version; +} + +void * NvdimmLidImage::getFlashImage() +{ + void * o_image_ptr = nullptr; + if (iv_lidImageSize > sizeof(nvdimm_image_header_t)) + { + nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> + (iv_lidImage); + + // make sure we don't point outside of lid memory + // nvdimm flash image starts after the header and digital signature + if ((sizeof(nvdimm_image_header_t) + + le16toh(pLid->SMART_digital_signature_size)) < iv_lidImageSize) + { + o_image_ptr = reinterpret_cast<uint8_t*>(iv_lidImage) + + sizeof(nvdimm_image_header_t) + + le16toh(pLid->SMART_digital_signature_size); + } + } + return o_image_ptr; +} + +size_t NvdimmLidImage::getFlashImageSize() +{ + uint32_t o_flash_size = 0; + if (iv_lidImageSize > sizeof(nvdimm_image_header_t)) + { + nvdimm_image_header_t * pLid = reinterpret_cast<nvdimm_image_header_t*> + (iv_lidImage); + o_flash_size = le32toh(pLid->firmware_image_size); + o_flash_size -= sizeof(nvdimm_image_header_t); + + // safety check so we don't access past lid's memory size + if ((o_flash_size + sizeof(nvdimm_image_header_t) + + le16toh(pLid->SMART_digital_signature_size)) > iv_lidImageSize) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"getFlashImageSize(): %ld flash size + %ld header + " + "%ld digital signature is greater than %ld overall lid size", + o_flash_size, sizeof(nvdimm_image_header_t), + le16toh(pLid->SMART_digital_signature_size), + iv_lidImageSize); + // flash image size is outside of lid memory bounds so don't return + // a valid flash size + o_flash_size = 0; + } + } + return o_flash_size; +} + +bool NvdimmLidImage::isImageLoaded() +{ + return iv_ImageLoaded; +} +/////////////////////////////////////////////////// +// Private member functions for NvdimmLidImage +/////////////////////////////////////////////////// +errlHndl_t NvdimmLidImage::loadImage() +{ + errlHndl_t l_err = nullptr; + if (!iv_ImageLoaded && (iv_lidMgr != nullptr)) + { + // @todo RTC 205015 -- need to use secure load + // iv_lidImage will point to memory allocated + // and controlled by iv_lidMgr + l_err = iv_lidMgr->getStoredLidImage(iv_lidImage, iv_lidImageSize); + if (l_err == nullptr) + { + // image successfully loaded into memory + iv_ImageLoaded = true; + } + } + return l_err; +} + +errlHndl_t NvdimmLidImage::unloadImage() +{ + errlHndl_t l_err = nullptr; + if (iv_ImageLoaded && (iv_lidMgr != nullptr)) + { + // use lidMgr to delete allocated memory for this lid + l_err = iv_lidMgr->releaseLidImage(); + iv_lidImage = nullptr; + iv_lidImageSize = 0; + iv_ImageLoaded = false; + } + return l_err; +} + +/////////////////////////////////////////////////////////////////////////////// +// NVDIMM Installed Image +/////////////////////////////////////////////////////////////////////////////// +NvdimmInstalledImage::NvdimmInstalledImage(TARGETING::Target * i_nvDimm) : + iv_dimm(i_nvDimm), iv_version(INVALID_VERSION), + iv_manufacturer_id(INVALID_ID), iv_product_id(INVALID_ID) +{ + // initialize to invalid values +} + +errlHndl_t NvdimmInstalledImage::getType(uint32_t & o_type) +{ + errlHndl_t l_err = nullptr; + do { + size_t l_id_size = 0; // size of id + if ( iv_manufacturer_id == INVALID_ID) + { + // grab values for the installed NVDIMM via SPD + l_id_size = sizeof(iv_manufacturer_id); + l_err = deviceRead(iv_dimm, &iv_manufacturer_id, l_id_size, + DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_MANUFACTURER_ID)); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::getType()" + " nvdimm[%X] failed to read manufacturer ID", + TARGETING::get_huid(iv_dimm)); + iv_manufacturer_id = INVALID_ID; + break; + } + } + + if (iv_product_id == INVALID_ID) + { + // grab values for the installed NVDIMM via SPD + l_id_size = sizeof(iv_product_id); + l_err = deviceRead(iv_dimm, &iv_product_id, l_id_size, + DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_PRODUCT_ID)); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::getType()" + " nvdimm[%X] failed to read product ID", + TARGETING::get_huid(iv_dimm)); + iv_product_id = INVALID_ID; + break; + } + } + } while (0); + + // return the concatenated Type (this may include INVALID_IDs) + o_type = ((uint32_t)iv_manufacturer_id << 16) | (uint32_t)iv_product_id; + return l_err; +} + +errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version) +{ + errlHndl_t l_err = nullptr; + + do { + if ((iv_version == INVALID_VERSION)) + { + // Return version in little-endian format + uint8_t l_rev1 = 0xFF; + uint8_t l_rev0 = 0xFF; + + l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV1, l_rev1 ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::" + "getVersion() nvdimm[%X] failed to read SLOT1_FWREV1", + TARGETING::get_huid(iv_dimm)); + iv_version = INVALID_VERSION; + break; + } + iv_version = (uint16_t)l_rev1 << 8; + + l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV0, l_rev0 ); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NvdimmInstalledImage::" + "getVersion() nvdimm[%X] failed to read SLOT1_FWREV0", + TARGETING::get_huid(iv_dimm)); + iv_version = INVALID_VERSION; + break; + } + iv_version |= (uint16_t)l_rev0; + } + } while (0); + o_version = iv_version; + return l_err; +} + + +/////////////////////////////////////////////////////////////////////////////// +// NVDIMMS Update Functions +/////////////////////////////////////////////////////////////////////////////// +NvdimmsUpdate::NvdimmsUpdate(TARGETING::TargetHandleList i_nvdimmList) +{ + iv_nvdimmList = i_nvdimmList; +} + +bool NvdimmsUpdate::runUpdate(void) +{ + bool o_no_error_found = true; // true if no error was found during update + + errlHndl_t l_err = nullptr; + + uint32_t l_installed_type = INVALID_TYPE; // current LID type installed + NvdimmLidImage * pCurLid = nullptr; // current LID being used for update + + // These are kept to minimize lid memory loading/unloading and + // reported errors + NvdimmLidImage * pSmallLid = nullptr; + NvdimmLidImage * pLargeLid = nullptr; + + for (auto l_nvdimm : iv_nvdimmList) + { + NvdimmInstalledImage l_installed_image(l_nvdimm); + l_err = l_installed_image.getType(l_installed_type); + if (l_err) + { + // Continue updating other dimms + TRACFCOMP(g_trac_nvdimm, ERR_MRK "NvdimmsUpdate::runUpdate() - " + "Unable to get nvdimm[0x%.8X] installed image type. " + "RC=0x%X, PLID=0x%.8X", + get_huid(l_nvdimm), ERRL_GETRC_SAFE(l_err), + ERRL_GETPLID_SAFE(l_err)); + commitPredictiveNvdimmError(l_err); + o_no_error_found = false; + continue; + } + + if (l_installed_type == JEDEC_NVDIMM_16GB_TYPE) + { + if (pSmallLid == nullptr) + { + pSmallLid = new NvdimmLidImage(Util::NVDIMM_16GB_LIDID, l_err); + if (l_err) + { + // Continue to try updating other dimms + TRACFCOMP(g_trac_nvdimm, + ERR_MRK "NvdimmsUpdate::runUpdate() - Unable to load " + "NVDIMM_16GB_LIDID(0x%X). RC=0x%X, PLID=0x%.8X", + Util::NVDIMM_16GB_LIDID, + ERRL_GETRC_SAFE(l_err), + ERRL_GETPLID_SAFE(l_err)); + + // @todo RTC 201197 - enable when valid LIDs are present + //commitPredictiveNvdimmError(l_err); + delete l_err; + l_err = nullptr; + // -------------------------------- // + + o_no_error_found = false; + // leaving pSmallLid object so don't continuously post the + // same error for each NVDIMM + continue; + } + } + pCurLid = pSmallLid; + } + else if (l_installed_type == JEDEC_NVDIMM_32GB_TYPE) + { + if (pLargeLid == nullptr) + { + pLargeLid = new NvdimmLidImage(Util::NVDIMM_32GB_LIDID, l_err); + if (l_err) + { + // Continue to try updating other dimms + TRACFCOMP(g_trac_nvdimm, + ERR_MRK "NvdimmsUpdate::runUpdate() - Unable to load " + "NVDIMM_32GB_LIDID(0x%X). RC=0x%X, PLID=0x%.8X", + Util::NVDIMM_32GB_LIDID, + ERRL_GETRC_SAFE(l_err), + ERRL_GETPLID_SAFE(l_err)); + + // @todo RTC 201197 - enable when valid LIDs are present + //commitPredictiveNvdimmError(l_err); + delete l_err; + l_err = nullptr; + // -------------------------------- // + o_no_error_found = false; + // leaving pLargeLid object so don't continuously post the + // same error for each NVDIMM + continue; + } + } + pCurLid = pLargeLid; + } + else + { + // unknown/unsupported Type + TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - unknown " + "nvdimm[%X] installed type 0x%04X, skipping update", + TARGETING::get_huid(l_nvdimm), l_installed_type); + /* + *@errortype + *@reasoncode NVDIMM_UNSUPPORTED_NVDIMM_TYPE + *@moduleid NVDIMM_RUN_UPDATE + *@userdata1[0:31] Unsupported Type + *@userdata1[32:63] NVDIMM Target Huid + *@userdata2[0:31] Supported nvdimm type + *@userdata2[32:63] Other supported nvdimm type + *@devdesc Unable to update an unsupported NVDIMM type + *@custdesc NVDIMM not updated + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_INFORMATIONAL, + NVDIMM_RUN_UPDATE, + NVDIMM_UNSUPPORTED_NVDIMM_TYPE, + TWO_UINT32_TO_UINT64( + l_installed_type, + TARGETING::get_huid(l_nvdimm)), + TWO_UINT32_TO_UINT64( + JEDEC_NVDIMM_16GB_TYPE, + JEDEC_NVDIMM_32GB_TYPE), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + l_err->addHwCallout( l_nvdimm, HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, HWAS::GARD_NULL ); + l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_LOW ); + ERRORLOG::errlCommit(l_err, NVDIMM_COMP_ID); + pCurLid = nullptr; + continue; + } + + // Verify a valid LID was loaded and ready to read + if ((pCurLid == nullptr) || (!pCurLid->isImageLoaded())) + { + // Errors already logged, just continue to the next NVDIMM + continue; + } + + bool updateNeeded = false; + l_err = isUpdateNeeded(updateNeeded, pCurLid, &l_installed_image); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK "NvdimmsUpdate::runUpdate() - " + "Unable to determine if nvdimm[%X] needs NV controller update." + " RC=0x%X, PLID=0x%.8X", + TARGETING::get_huid(l_nvdimm), ERRL_GETRC_SAFE(l_err), + ERRL_GETPLID_SAFE(l_err)); + commitPredictiveNvdimmError(l_err); + o_no_error_found = false; + } + else if (updateNeeded) + { + // perform update for this DIMM with the current LID image + TRACFCOMP(g_trac_nvdimm, "NvdimmsUpdate::runUpdate() - " + "now update nvdimm[0x%.8X]", TARGETING::get_huid(l_nvdimm)); + + TRACFCOMP(g_trac_nvdimm,"Updating with flash size: 0x%08X", + pCurLid->getFlashImageSize()); + + // @todo RTC 202536 : Add update calls + } + } + + if (pLargeLid) + { + delete pLargeLid; + } + if (pSmallLid) + { + delete pSmallLid; + } + + return o_no_error_found; +} + +errlHndl_t NvdimmsUpdate::isUpdateNeeded(bool & o_update_needed, + NvdimmLidImage * i_lid_image, + NvdimmInstalledImage * i_cur_image) +{ + o_update_needed = false; // initialize to false + + errlHndl_t l_err = nullptr; + uint32_t lidType = INVALID_TYPE; + uint32_t curType = INVALID_TYPE; + + do { + const TARGETING::Target * l_dimm = i_cur_image->getNvdimmTarget(); + + // check Types match (same manufacturer and product) + lidType = i_lid_image->getType(); + l_err = i_cur_image->getType(curType); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + "isUpdateNeeded(): failed to find type of NVDIMM[%X]", + TARGETING::get_huid(l_dimm)); + break; + } + + if ((lidType == curType) && (lidType != INVALID_TYPE)) + { + // check that versions do NOT match + uint16_t curVersion = INVALID_VERSION; + uint16_t lidVersion = i_lid_image->getVersion(); + l_err = i_cur_image->getVersion(curVersion); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, + "isUpdateNeeded(): failed to find version of NVDIMM[%X]", + TARGETING::get_huid(l_dimm)); + break; + } + + + if (curVersion != lidVersion) + { + // verify we are updating with a good version + if (lidVersion != INVALID_VERSION) + { + // Found mismatched version and a valid lid update version + // so an update can proceed + o_update_needed = true; + + TRACFCOMP(g_trac_nvdimm, + "NVDIMM[%X] code level - current: 0x%04X, new: 0x%04X", + TARGETING::get_huid(l_dimm), + le16toh(curVersion), le16toh(lidVersion)); + } + else + { + TRACFCOMP(g_trac_nvdimm, "NVDIMM[%X] has invalid version", + TARGETING::get_huid(l_dimm)); + } + } + else + { + TRACUCOMP(g_trac_nvdimm, + "Keeping current NVDIMM[%X] level: 0x%04X", + TARGETING::get_huid(l_dimm), le16toh(curVersion)); + } + } + } while (0); + + return l_err; +} + +//////////////////////////////////////////////////////////////////////////////// +// External function to update the NVDIMMs +//////////////////////////////////////////////////////////////////////////////// +bool nvdimm_update(TARGETING::TargetHandleList &i_nvdimmList) +{ + NvdimmsUpdate l_nvdimmsUpdate(i_nvdimmList); + return l_nvdimmsUpdate.runUpdate(); +} + +}; // end namespace NVDIMM diff --git a/src/usr/isteps/nvdimm/nvdimm_update.H b/src/usr/isteps/nvdimm/nvdimm_update.H new file mode 100644 index 000000000..6e2a137fc --- /dev/null +++ b/src/usr/isteps/nvdimm/nvdimm_update.H @@ -0,0 +1,278 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/nvdimm/nvdimm_update.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef NVDIMM_UPDATE_H +#define NVDIMM_UPDATE_H + +/** + * @file nvdimm/nvdimm_update.H + * + * @brief Interface to support updating NVDIMM controller code + */ + +#include <cstdint> +#include <errl/errlentry.H> +#include <util/utillidmgr.H> + +namespace NVDIMM +{ +// Some invalid constants +const uint16_t INVALID_ID = 0xFFFF; +const uint16_t INVALID_VERSION = 0xFFFF; +const uint32_t INVALID_TYPE = 0xFFFFFFFF; + +// Type is combination of manufacturer id and product id +const uint32_t JEDEC_NVDIMM_16GB_TYPE = 0x01945377; +const uint32_t JEDEC_NVDIMM_32GB_TYPE = 0x01945378; + +class NvdimmLidImage +{ + public: + /** + * @brief Constructor that sets access to LID information + * @param i_lidId - NVDIMM_16GB_LIDID or NVDIMM_32GB_LIDID + * @param io_errHndl - error filled in if lid not loadable + */ + explicit NvdimmLidImage(Util::LidId i_lidId, errlHndl_t& io_errHdl); + + /** + * @brief Cleanup any allocated memory holding the lid image + */ + ~NvdimmLidImage(); + + /** + * @brief Grab the type of the image + * (The type will be in raw little-endian format) + * @return concated manufacturer id and product id + * module_mnfg_id_code (0-1) and module_product_id (2-3) + */ + uint32_t getType(); + + /** + * @brief Get the firmware version of image + * @return o_version - version of the image in raw little-endian format + */ + uint16_t getVersion(); + + /** + * @brief Get the actual NVDIMM flash image to load on the NVDIMM + * @return Pointer to the start of the flash image + */ + void * getFlashImage(); + + /** + * @brief Get the size of the actual flash image + * @return Image size + */ + size_t getFlashImageSize(); + + /** + * @brief Check if the image was loaded into memory + * @return true, if image was loaded into memory + */ + bool isImageLoaded(); + + + //-----------------------------------------------------------// + // Layout of NVDIMM lid image + // + // Keeping as struct so it can be overlayed on lid image + // Note: the multiple byte variables will be in little-endian + //-----------------------------------------------------------// + // Header of NVDIMM lid image + typedef struct nvdimm_image_header + { + // Byte 0-1 + uint16_t module_mnfg_id_code; + // Byte 2-3 + uint16_t module_product_id; + // Byte 4-5 + uint16_t nv_memory_subsys_cntrlr_mnfg_id_code; + // Byte 6-7 + uint16_t nv_memory_subsys_cntrlr_product_id; + // Byte 8 + uint8_t module_revision_code; + // Byte 9 + uint8_t nv_memory_subsys_cntrlr_revision_code; + // Byte 10-11 + uint16_t controller_firmware_revision; + // Byte 12-13 + uint16_t energy_source_firmware_revision; + // Byte 14 + uint8_t subcomponent_firmware_revision; + // Byte 15 + uint8_t rsvd_0; + // Byte 16-17 + uint16_t SMART_digital_signature_size; + // Byte 18 + uint8_t SMART_digital_signature; + // Byte 19 + union + { + uint8_t _valid; + struct + { + uint8_t _subcomponent_firmware_revision:1; // bit7 in spec + uint8_t _energy_source_firmware_revision:1; // bit6 + uint8_t _nv_memory_subsys_cntrlr_revision_code:1; // bit5 + uint8_t _module_revision_code:1; // bit4 + uint8_t _nv_memory_subsys_cntrlr_product_id:1; // bit3 + uint8_t _nv_memory_subsys_cntrlr_mnfg_id_code:1; // bit2 + uint8_t _module_mnfg_product_id:1; // bit1 + uint8_t _module_mnfg_id:1; // bit0 in spec + } PACKED; + }; + + // Byte 20-23 + uint32_t firmware_image_size; // Includes 32-byte header + actual fw image + // Byte 20 LSB, Byte 23 MSB + // Byte 24-25 + uint16_t firmware_image_checksum; + // Byte 26-30 + uint8_t rsvd_1[5]; + // Byte 31 + uint8_t firmware_image_format; + } nvdimm_image_header_t; + // After header, these two follow: + // Digital signature (size: SMART_digital_signature_size bytes) + // Actual flash firmware image + // (size: firmware_image_size bytes - 32-byte header) + //-----------------------------------------------------------// + + private: + /** + * @brief Load the lid image into memory + */ + errlHndl_t loadImage(); + + /** + * @brief Unload the lid image from memory + */ + errlHndl_t unloadImage(); + + // force user to supply lid_id + NvdimmLidImage(); + + // status of whether or not the image was loaded into memory + bool iv_ImageLoaded; + + // pointer to lid image + // note: memory allocated/deallocated by iv_lidMgr + void * iv_lidImage; + + // size of lid image + size_t iv_lidImageSize; + + // lid manager for nvdimm lid image + UtilLidMgr* iv_lidMgr; +}; + + +class NvdimmInstalledImage +{ + public: + /** + * @brief Constructor to associate a target DIMM to grab info from + * @param i_nvDimm - NVDIMM target + */ + explicit NvdimmInstalledImage(TARGETING::Target * i_nvDimm); + + /** + * @brief Grab the type of the installed nvdimm + * (The type will be in raw little-endian format) + * @param o_type - concated manufacturer id and product id + * @return error if read operation fails + */ + errlHndl_t getType(uint32_t & o_type); + + /** + * @brief Grab the installed NVDIMM's version + * @param o_version - version of installed NVDIMM image + * @return error if read operation fails + */ + errlHndl_t getVersion(uint16_t & o_version); + + /** + * @brief Accessor to grab the current NVDIMM target + * @return NVDIMM target + */ + const TARGETING::Target * getNvdimmTarget(void) + { + return iv_dimm; + } + + private: + // nvdimm target + TARGETING::Target * iv_dimm; + + // little-endian version of installed nvdimm + uint16_t iv_version; + + // Type contains these two concatentated little-endian IDs + uint16_t iv_manufacturer_id; + uint16_t iv_product_id; +}; + + +class NvdimmsUpdate +{ + public: + /** + * @brief Constructor that uses passed in nvdimm list + * @parm[in] i_nvdimmList List of NVDIMMs to update + */ + explicit NvdimmsUpdate(TARGETING::TargetHandleList i_nvdimmList); + + /** + * @brief Main function that tries to update all NVDIMMs (if needed) + * This function runs SPD and lid loading/unloading, so should + * not be called multiple times + * @return true if no errors reported, else false + */ + bool runUpdate(void); + + protected: + /** + * @brief Checks if an update is needed for an individual nvdimm + * @param[out] o_update_needed - true if lid image is good to + * update current image, else false + * @param[in] i_lid_image - lid image that is appropriate for nvdimm update + * @param[in] i_cur_image - current installed image on the nvdimm + + */ + errlHndl_t isUpdateNeeded(bool & o_update_needed, + NvdimmLidImage * i_lid_image, + NvdimmInstalledImage * i_cur_image); + + private: + // Force user to supply NVDIMM list + NvdimmsUpdate(); + + // List of NVDIMMs installed in system + TARGETING::TargetHandleList iv_nvdimmList; + +}; + +} // Namespace NVDIMM +#endif diff --git a/src/usr/targeting/common/util.C b/src/usr/targeting/common/util.C index 71811366f..286c37b6c 100644 --- a/src/usr/targeting/common/util.C +++ b/src/usr/targeting/common/util.C @@ -329,19 +329,27 @@ TARGETING::TargetHandleList getProcNVDIMMs( TARGETING::Target * i_proc ) { TargetHandleList o_nvdimmList; - TargetHandleList l_dimmTargetList; - getChildAffinityTargets( l_dimmTargetList, i_proc, CLASS_NA, TYPE_DIMM ); + TARGETING::ATTR_MODEL_type l_chipModel = + i_proc->getAttr<TARGETING::ATTR_MODEL>(); - for (TargetHandleList::iterator it = l_dimmTargetList.begin(); - it != l_dimmTargetList.end(); ++it) + // NVDIMM only present on NIMBUS systems + if (l_chipModel == TARGETING::MODEL_NIMBUS) { - TARGETING::Target* l_dimm = *it; - if (TARGETING::isNVDIMM(l_dimm)) + TargetHandleList l_dimmTargetList; + getChildAffinityTargets(l_dimmTargetList, i_proc, CLASS_NA, TYPE_DIMM); + + for (TargetHandleList::iterator it = l_dimmTargetList.begin(); + it != l_dimmTargetList.end(); ++it) { - // Found a valid NVDIMM - o_nvdimmList.push_back(l_dimm); + TARGETING::Target* l_dimm = *it; + if (TARGETING::isNVDIMM(l_dimm)) + { + // Found a valid NVDIMM + o_nvdimmList.push_back(l_dimm); + } } } + return o_nvdimmList; } diff --git a/src/usr/vpd/spdDDR4.H b/src/usr/vpd/spdDDR4.H index bf4d0dfc8..8e03a0f0d 100755 --- a/src/usr/vpd/spdDDR4.H +++ b/src/usr/vpd/spdDDR4.H @@ -47,7 +47,7 @@ namespace SPD const KeywordData ddr4Data[] = { // ---------------------------------------------------------------------------------- - // NOTE: This list must remain an ordered list! The Keyword must be in numerical + // NOTE: This list must remain an ordered list! The Keyword must be in numerical // order (values defined in spdenums.H) to allow efficient searching, a unit // test enforces this. // ---------------------------------------------------------------------------------- @@ -136,6 +136,8 @@ const KeywordData ddr4Data[] = { DRAM_STEPPING, 0x160, 0x01, 0x00, 0x00, false, false, NA }, { MANUFACTURING_SECTION_CRC, 0x17f, 0x02, 0x00, 0x00, true, false, NA }, { NVM_INIT_TIME, 0xCB, 0x01, 0x00, 0x00, false, false, NA }, + { RAW_MODULE_PRODUCT_ID, 0xc0, 0x02, 0x00, 0x00, false, false, NA }, + { RAW_MODULE_MANUFACTURER_ID, 0x140, 0x02, 0x00, 0x00, false, false, NA }, // Module Specific fields supported on both DDR3 and DDR4 { MODSPEC_COM_NOM_HEIGHT_MAX, 0x80, 0x01, 0x1f, 0x00, false, false, ALL }, { MODSPEC_COM_MAX_THICK_BACK, 0x81, 0x01, 0xf0, 0x04, false, false, ALL }, |