diff options
author | Prachi Gupta <pragupta@us.ibm.com> | 2014-10-28 15:41:49 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2015-01-16 12:34:43 -0600 |
commit | c068f50829ec46e4e5b056064dcbe9d786d549a4 (patch) | |
tree | 36c5fd8f2f6b5d420644ed83b877f6d0d8657f42 /src/usr | |
parent | 5412ba2270945edcfb23f60c34de01dccd44c098 (diff) | |
download | talos-hostboot-c068f50829ec46e4e5b056064dcbe9d786d549a4.tar.gz talos-hostboot-c068f50829ec46e4e5b056064dcbe9d786d549a4.zip |
hbrt interface for PNOR access
RTC:108836
Change-Id: I49e568e7f4fcad13fcd75dfdfa4aee8a263c5001
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/14307
Reviewed-by: STEPHEN M. CPREK <smcprek@us.ibm.com>
Tested-by: Jenkins Server
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/pnor/makefile | 4 | ||||
-rw-r--r-- | src/usr/pnor/pnor_common.C | 417 | ||||
-rw-r--r-- | src/usr/pnor/pnor_common.H | 85 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.C | 373 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.H | 55 | ||||
-rw-r--r-- | src/usr/pnor/pnorvalid.C | 6 | ||||
-rw-r--r-- | src/usr/pnor/runtime/makefile | 36 | ||||
-rw-r--r-- | src/usr/pnor/runtime/rt_pnor.C | 634 | ||||
-rw-r--r-- | src/usr/pnor/runtime/rt_pnor.H | 153 | ||||
-rw-r--r-- | src/usr/pnor/runtime/test/makefile | 31 | ||||
-rw-r--r-- | src/usr/pnor/runtime/test/testpnor_rt.H | 257 | ||||
-rw-r--r-- | src/usr/pnor/test/pnorrptest.H | 8 | ||||
-rw-r--r-- | src/usr/testcore/rtloader/loader.H | 161 |
13 files changed, 1856 insertions, 364 deletions
diff --git a/src/usr/pnor/makefile b/src/usr/pnor/makefile index e6585c123..31087a4b2 100644 --- a/src/usr/pnor/makefile +++ b/src/usr/pnor/makefile @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2011,2014 +# Contributors Listed Below - COPYRIGHT 2011,2015 # [+] Google Inc. # [+] International Business Machines Corp. # @@ -28,6 +28,7 @@ MODULE = pnor OBJS += pnorrp.o OBJS += pnordd.o +OBJS += pnor_common.o OBJS += pnorvalid.o OBJS += ecc.o OBJS += sfcdd.o @@ -42,5 +43,6 @@ OBJS += $(if $(CONFIG_ALLOW_MICRON_PNOR),nor_micron.o) SUBDIRS += test.d +SUBDIRS += runtime.d include ${ROOTPATH}/config.mk diff --git a/src/usr/pnor/pnor_common.C b/src/usr/pnor/pnor_common.C new file mode 100644 index 000000000..e92ce56c8 --- /dev/null +++ b/src/usr/pnor/pnor_common.C @@ -0,0 +1,417 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/pnor_common.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* [+] 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 "pnor_common.H" +#include <pnor/pnorif.H> +#include <pnor/pnor_reasoncodes.H> + +#include "ffs.h" //Common header file with BuildingBlock. +#include "common/ffs_hb.H" //Hostboot def of user data in ffs_entry struct + +#include <initservice/initserviceif.H> +#include <util/align.H> + +// Trace definition +trace_desc_t* g_trac_pnor = NULL; +TRAC_INIT(&g_trac_pnor, PNOR_COMP_NAME, 4*KILOBYTE, TRACE::BUFFER_SLOW); //4K + +// Easy macro replace for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + +/** + * Eyecatcher strings for PNOR TOC entries + */ +const char* cv_EYECATCHER[] = { + "part", /**< PNOR::TOC : Table of Contents */ + "HBI", /**< PNOR::HB_EXT_CODE : Hostboot Extended Image */ + "GLOBAL", /**< PNOR::GLOBAL_DATA : Global Data */ + "HBB", /**< PNOR::HB_BASE_CODE : Hostboot Base Image */ + "SBEC", /**< PNOR::CENTAUR_SBE : Centaur Self-Boot Engine image */ + "SBE", /**< PNOR::SBE_IPL : Self-Boot Enginer IPL image */ + "WINK", /**< PNOR::WINK : Sleep Winkle Reference image */ + "PAYLOAD", /**< PNOR::PAYLOAD : HAL/OPAL */ + "HBRT", /**< PNOR::HB_RUNTIME : Hostboot Runtime (for Sapphire) */ + "HBD", /**< PNOR::HB_DATA : Hostboot Data */ + "GUARD", /**< PNOR::GUARD_DATA : Hostboot Data */ + "HBEL", /**< PNOR::HB_ERRLOGS : Hostboot Error log Repository */ + "DJVPD", /**< PNOR::DIMM_JEDEC_VPD : Dimm JEDEC VPD */ + "MVPD", /**< PNOR::MODULE_VPD : Module VPD */ + "CVPD", /**< PNOR::CENTAUR_VPD : Centaur VPD */ + "NVRAM", /**< PNOR::NVRAM : OPAL Storage */ + "OCC", /**< PNOR::OCC : OCC LID */ + "FIRDATA", /**< PNOR::FIRDATA : FIRDATA */ + "ATTR_TMP", /**< PNOR::ATTR_TMP : Temporary Attribute Overrides */ + "ATTR_PERM", /**< PNOR::ATTR_PERM : Permanent Attribute Overrides */ + "TEST", /**< PNOR::TEST : Test space for PNOR*/ + //Not currently used +// "XXX", /**< NUM_SECTIONS : Used as invalid entry */ +}; + +/** + * @brief calculates the checksum on data(ffs header/entry) and will return + * 0 if the checksums match + */ +uint32_t PNOR::pnor_ffs_checksum(void* data, size_t size) +{ + uint32_t checksum = 0; + + for (size_t i = 0; i < (size/4); i++) + { + checksum ^= ((uint32_t*)data)[i]; + } + + checksum = htobe32(checksum); + return checksum; +} + +errlHndl_t PNOR::parseTOC(uint8_t* i_toc0Buffer, uint8_t* i_toc1Buffer, + uint32_t & o_TOC_used, SectionData_t * o_TOC, uint64_t i_baseVAddr) +{ + TRACUCOMP(g_trac_pnor,"PNOR::parseTOC>"); + errlHndl_t l_errhdl = NULL; + + bool TOC_0_failed = false; + + do{ + o_TOC_used = 0; + + for (uint32_t cur_TOC = 0; cur_TOC < NUM_TOCS; ++cur_TOC) + { + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC verifying TOC: %d",cur_TOC); + uint64_t nextVAddr = i_baseVAddr; + + // Zero out my table + for( size_t id = PNOR::FIRST_SECTION; + id <= PNOR::NUM_SECTIONS; //include extra entry for error paths + ++id ) + { + o_TOC[id].id = (PNOR::SectionId)id; + //everything else should default to zero + } + // Read TOC information from TOC 0 and then TOC 1 + ffs_hdr* l_ffs_hdr; + if (cur_TOC == 0) + { + l_ffs_hdr = (ffs_hdr*) i_toc0Buffer; + } + else if (cur_TOC == 1) + { + l_ffs_hdr = (ffs_hdr*) i_toc1Buffer; + } + + // ffs entry check, 0 if checksums match + if( PNOR::pnor_ffs_checksum(l_ffs_hdr, FFS_HDR_SIZE) != 0) + { + //@TODO - RTC:90780 - May need to handle this differently + // in SP-less config + TRACFCOMP(g_trac_pnor,"PNOR::parseTOC pnor_ffs_checksum header" + " checksums do not match."); + if (cur_TOC == 0) + { + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC TOC 0 failed header checksum"); + TOC_0_failed = true; + o_TOC_used = 1; + continue; + } + else if (cur_TOC == 1 && TOC_0_failed) + { + // Both TOC's failed + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC both TOCs are corrupted"); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORCOMMON_PARSETOC + * @reasoncode PNOR::RC_CORRUPTED_TOCS + * @devdesc Both TOCs are corruputed + */ + //@todo Add PNOR callout RTC:116145 + l_errhdl = new ERRORLOG::ErrlEntry + (ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORCOMMON_PARSETOC, + PNOR::RC_CORRUPTED_TOCS, + 0, 0, true); + break; + } + else + { + // TOC 1 failed + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC TOC 1 failed header checksum"); + break; + } + } + + // Only check header if on first TOC or the first TOC failed + if (cur_TOC == 0 || TOC_0_failed) + { + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC: FFS Block size=0x%.8X," + " Partition Table Size = 0x%.8x, entry_count=%d", + l_ffs_hdr->block_size,l_ffs_hdr->size,l_ffs_hdr->entry_count); + + uint64_t spaceUsed = (sizeof(ffs_entry))*l_ffs_hdr->entry_count; + + /* Checking FFS Header to make sure it looks valid */ + bool header_good = true; + if(l_ffs_hdr->magic != FFS_MAGIC) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC: Invalid magic" + " number in FFS header: 0x%.4X",l_ffs_hdr->magic); + header_good = false; + } + else if(l_ffs_hdr->version != SUPPORTED_FFS_VERSION) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC:Unsupported FFS" + " Header version: 0x%.4X", l_ffs_hdr->version); + header_good = false; + } + else if(l_ffs_hdr->entry_size != sizeof(ffs_entry)) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC: Unexpected" + " entry_size(0x%.8x) in FFS header: 0x%.4X", + l_ffs_hdr->entry_size); + header_good = false; + } + else if(l_ffs_hdr->entries == NULL) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC:" + " FFS Header pointer to entries is NULL."); + header_good = false; + } + else if(l_ffs_hdr->block_size != PAGESIZE) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC: Unsupported" + " Block Size(0x%.4X). PNOR Blocks must be 4k", + l_ffs_hdr->block_size); + header_good = false; + } + else if(l_ffs_hdr->block_count == 0) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC: Unsupported" + " Block COunt(0x%.4X). Device cannot be zero" + " blocks in length.",l_ffs_hdr->block_count); + header_good = false; + } + //Make sure all the entries fit in specified partition + //table size + else if(spaceUsed > + ((l_ffs_hdr->block_size*l_ffs_hdr->size)-sizeof(ffs_hdr))) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC: FFS Entries" + " (0x%.16X) go past end of FFS Table.",spaceUsed); + header_good = false; + } + + if(!header_good) + { + //Shutdown if we detected a partition table issue + //for any reason + if (TOC_0_failed) + { + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORCOMMON_PARSETOC + * @reasoncode PNOR::RC_BAD_TOC_HEADER + * @devdesc TOC 0 doesn't have a good header + */ + l_errhdl = new ERRORLOG::ErrlEntry + (ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORCOMMON_PARSETOC, + PNOR::RC_BAD_TOC_HEADER, + 0, 0, true); + break; + } + else + { + TOC_0_failed = true; + } + //Try TOC1 + continue; + } + } + + ffs_hb_user_t* ffsUserData = NULL; + + //Walk through all the entries in the table and parse the data. + for(uint32_t i=0; i<l_ffs_hdr->entry_count; i++) + { + ffs_entry* cur_entry = (&l_ffs_hdr->entries[i]); + + TRACUCOMP(g_trac_pnor, "PNOR::parseTOC: Entry %d, name=%s, " + "pointer=0x%X",i,cur_entry->name, (uint64_t)cur_entry); + + uint32_t secId = PNOR::INVALID_SECTION; + + // ffs entry check, 0 if checksums match + if( PNOR::pnor_ffs_checksum(cur_entry, FFS_ENTRY_SIZE) != 0) + { + //@TODO - RTC:90780 - May need to handle this differently + // in SP-less config + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC pnor_ffs_checksum" + " entry checksums do not match"); + if (cur_TOC == 0) + { + TRACFCOMP(g_trac_pnor,"PNOR::parseTOC TOC 0 entry" + " checksum failed"); + TOC_0_failed = true; + o_TOC_used = 1; + break; + } + else if (cur_TOC == 1 && TOC_0_failed) + { + // Both TOC's failed + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC both TOC's are" + " corrupted"); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORCOMMON_PARSETOC + * @reasoncode PNOR::RC_PARTITION_TABLE_INVALID + * @devdesc Both TOCs are corrupted + */ + l_errhdl = new ERRORLOG::ErrlEntry + (ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORCOMMON_PARSETOC, + PNOR::RC_PARTITION_TABLE_INVALID, + 0, 0, true); + break; + } + else + { + // TOC 1 failed + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC TOC 1 entry" + " checksum failed"); + break; + } + } + + // Only set data if on first TOC or the first TOC failed + if (cur_TOC == 0 || TOC_0_failed) + { + //Figure out section enum + for(uint32_t eyeIndex=PNOR::TOC;eyeIndex<PNOR::NUM_SECTIONS; + eyeIndex++) + { + if(strcmp(cv_EYECATCHER[eyeIndex],cur_entry->name) == 0) + { + secId = eyeIndex; + TRACUCOMP(g_trac_pnor, "PNOR::parseTOC: sectionId=%d", secId); + break; + } + } + + if(secId == PNOR::INVALID_SECTION) + { + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC: Unrecognized" + " Section name(%s), skipping",cur_entry->name); + continue; + } + + ffsUserData = (ffs_hb_user_t*)&(cur_entry->user); + + //size + o_TOC[secId].size = ((uint64_t)cur_entry->size)*PAGESIZE; + + //virtAddr + o_TOC[secId].virtAddr = nextVAddr; + nextVAddr += o_TOC[secId].size; + + //flashAddr + o_TOC[secId].flashAddr=((uint64_t)cur_entry->base)*PAGESIZE; + + //chipSelect + o_TOC[secId].chip = ffsUserData->chip; + + //user data + o_TOC[secId].integrity = ffsUserData->dataInteg; + o_TOC[secId].version = ffsUserData->verCheck; + o_TOC[secId].misc = ffsUserData->miscFlags; + + TRACFCOMP(g_trac_pnor,"PNOR::parseTOC: User Data %s", + cur_entry->name); + + if (o_TOC[secId].integrity == FFS_INTEG_ECC_PROTECT) + { + TRACFCOMP(g_trac_pnor, "PNOR::TOC: ECC enabled for %s", + cur_entry->name); + o_TOC[secId].size = ALIGN_PAGE_DOWN + ((o_TOC[secId].size * 8 ) / 9); + } + + // TODO RTC:96009 handle version header w/secureboot + if (o_TOC[secId].version == FFS_VERS_SHA512) + { + TRACFCOMP(g_trac_pnor, "PNOR::parseTOC: Incrementing" + " Flash Address for SHA Header"); + if (o_TOC[secId].integrity == FFS_INTEG_ECC_PROTECT) + { + o_TOC[secId].flashAddr += PAGESIZE_PLUS_ECC; + } + else + { + o_TOC[secId].flashAddr += PAGESIZE; + } + } + + if((o_TOC[secId].flashAddr + o_TOC[secId].size) > + (l_ffs_hdr->block_count*PAGESIZE)) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::parseTOC:Partition(%s)" + " at base address (0x%.8x) extends past end of" + " flash device",cur_entry->name, + o_TOC[secId].flashAddr); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORCOMMON_PARSETOC + * @reasoncode PNOR::RC_SECTION_SIZE_IS_BIG + * @devdesc Invalid partition table + */ + l_errhdl = new ERRORLOG::ErrlEntry + (ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORCOMMON_PARSETOC, + PNOR::RC_SECTION_SIZE_IS_BIG, + 0, 0, true); + break; + } + } + } + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"PNOR::parseTOC: error parsing"); + break; + } + + for(PNOR::SectionId tmpId = PNOR::FIRST_SECTION; + tmpId < PNOR::NUM_SECTIONS; + tmpId = (PNOR::SectionId) (tmpId + 1) ) + { + TRACFCOMP(g_trac_pnor, "%s: size=0x%.8X flash=0x%.8X " + "virt=0x%.16X", cv_EYECATCHER[tmpId], o_TOC[tmpId].size, + o_TOC[tmpId].flashAddr, o_TOC[tmpId].virtAddr ); + } + } + } while(0); + + TRACUCOMP(g_trac_pnor, "< PNOR::parseTOC" ); + return l_errhdl; +} + diff --git a/src/usr/pnor/pnor_common.H b/src/usr/pnor/pnor_common.H new file mode 100644 index 000000000..3944e27fa --- /dev/null +++ b/src/usr/pnor/pnor_common.H @@ -0,0 +1,85 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/pnor_common.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* [+] 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 PNOR_COMMON_H +#define PNOR_COMMON_H + +#include <pnor/pnorif.H> +namespace PNOR { + /** + * Internal information to deal with the sections of PNOR + */ + struct SectionData_t { + PNOR::SectionId id; /**< Identifier for this section */ + uint64_t virtAddr; /**< Virtual address for the start of the section */ + uint32_t flashAddr; /**< Address in flash */ + uint32_t size;/**< Actual size of content in bytes (not including ECC)*/ + uint8_t chip; /**< Chip Select */ + uint8_t version; /**< Version Checking */ + uint16_t integrity; /**< Data Integrity */ + uint8_t misc; /**< Misc Flags */ + } PACKED; + + enum + { + NUM_TOCS = 2, + TOC_0_OFFSET = 0, + TOC_1_OFFSET = 0x8000, + + /** Real number of bytes required to read 1 logical page */ + PAGESIZE_PLUS_ECC = ((PAGESIZE * 9)/8), // 8B data + 1B of ECC + + SUPPORTED_FFS_VERSION = 0x1, /**< Supported FFS Version */ + FFS_TABLE_BASE_ADDR = 0x0, /**< Currently only have FFS table */ + }; + + /** + * @brief Creates a 4-byte Cyclic Redundancy Check (CRC) on the data + * provided. The last iteration of the for-loop includes the ffs + * checksum itself. Therefore if the 4-byte CRC created matches + * the ffs checksum, the resulting CRC will be 0 + * + * @param[in] ptr Pointer to the data + * + * @param[in] size Size of the data + * + * @return uint32_t return 4-byte CRC, 0 if checksums match + */ + uint32_t pnor_ffs_checksum(void* data, size_t size); + + /** + * @brief parse the TOCs read from memory and store section + * information from one of the verified TOC's + * @param[in] i_toc0Buffer Pointer to toc0 + * @param[in] i_toc1Buffer Pointer to toc1 + * @param[out] o_TOC_used which TOC is used + * @param[out] o_TOC sectionInformation of the TOC used + * @param[in] i_baseVAddr base virtual address of the TOC + * @return Error + */ + errlHndl_t parseTOC(uint8_t* i_toc0Buffer, uint8_t* i_toc1Buffer, + uint32_t & o_TOC_used, SectionData_t * o_TOC, uint64_t i_baseVAddr); +} + +#endif diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C index a8ad0fb7a..3c1ce4a99 100644 --- a/src/usr/pnor/pnorrp.C +++ b/src/usr/pnor/pnorrp.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -45,43 +45,19 @@ #include <util/align.H> #include <config.h> -// Trace definition -trace_desc_t* g_trac_pnor = NULL; -TRAC_INIT(&g_trac_pnor, PNOR_COMP_NAME, 4*KILOBYTE, TRACE::BUFFER_SLOW); //2K + +extern trace_desc_t* g_trac_pnor; // Easy macro replace for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) #define TRACUCOMP(args...) +using namespace PNOR; + /** * Eyecatcher strings for PNOR TOC entries */ -const char* cv_EYECATCHER[] = { - "part", /**< PNOR::TOC : Table of Contents */ - "HBI", /**< PNOR::HB_EXT_CODE : Hostboot Extended Image */ - "GLOBAL", /**< PNOR::GLOBAL_DATA : Global Data */ - "HBB", /**< PNOR::HB_BASE_CODE : Hostboot Base Image */ - "SBEC", /**< PNOR::CENTAUR_SBE : Centaur Self-Boot Engine image */ - "SBE", /**< PNOR::SBE_IPL : Self-Boot Enginer IPL image */ - "WINK", /**< PNOR::WINK : Sleep Winkle Reference image */ - "PAYLOAD", /**< PNOR::PAYLOAD : HAL/OPAL */ - "HBRT", /**< PNOR::HB_RUNTIME : Hostboot Runtime (for Sapphire) */ - "HBD", /**< PNOR::HB_DATA : Hostboot Data */ - "GUARD", /**< PNOR::GUARD_DATA : Hostboot Data */ - "HBEL", /**< PNOR::HB_ERRLOGS : Hostboot Error log Repository */ - "DJVPD", /**< PNOR::DIMM_JEDEC_VPD : Dimm JEDEC VPD */ - "MVPD", /**< PNOR::MODULE_VPD : Module VPD */ - "CVPD", /**< PNOR::CENTAUR_VPD : Centaur VPD */ - "NVRAM", /**< PNOR::NVRAM : OPAL Storage */ - "OCC", /**< PNOR::OCC : OCC LID */ - "FIRDATA", /**< PNOR::FIRDATA : FIRDATA */ - "ATTR_TMP", /**< PNOR::ATTR_TMP : Temporary Attribute Overrides */ - "ATTR_PERM", /**< PNOR::ATTR_PERM : Permanent Attribute Overrides */ - "TEST", /**< PNOR::TEST : Test space for PNOR*/ - - //Not currently used -// "XXX", /**< NUM_SECTIONS : Used as invalid entry */ -}; +extern const char* cv_EYECATCHER[]; /** * @brief set up _start() task entry procedure for PNOR daemon @@ -102,28 +78,44 @@ errlHndl_t PNOR::getSectionInfo( PNOR::SectionId i_section, return Singleton<PnorRP>::instance().getSectionInfo(i_section,o_info); } -namespace PNOR -{ - /** - * @brief calculates the checksum on data(ffs header/entry) and will return - * 0 if the checksums match + * @brief Write the data for a given sectino into PNOR */ -uint32_t pnor_ffs_checksum(void* data, size_t size) +errlHndl_t PNOR::flush( PNOR::SectionId i_section) { - uint32_t checksum = 0; - - for (size_t i = 0; i < (size/4); i++) - { - checksum ^= ((uint32_t*)data)[i]; - } - - checksum = htobe32(checksum); - return checksum; + errlHndl_t l_err = NULL; + do { + PNOR::SectionInfo_t l_info; + l_err = getSectionInfo(i_section, l_info); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "PNOR::flush: getSectionInfo errored," + " secId: %d", (int)i_section); + break; + } + int l_rc = mm_remove_pages (RELEASE, + reinterpret_cast<void*>(l_info.vaddr), l_info.size); + if (l_rc) + { + TRACFCOMP(g_trac_pnor, "PNOR::flush: mm_remove_pages errored," + " secId: %d, rc: %d", (int)i_section, l_rc); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORRP_FLUSH + * @reasoncode PNOR::RC_MM_REMOVE_PAGES_FAILED + * @userdata1 section Id + * @userdata2 RC + * @devdesc mm_remove_pages failed + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORRP_FLUSH, + PNOR::RC_MM_REMOVE_PAGES_FAILED, + i_section, l_rc, true); + break; + } + } while (0); + return l_err; } - -}; - /** * STATIC * @brief Static Initializer @@ -147,7 +139,7 @@ void PnorRP::init( errlHndl_t &io_rtaskRetErrl ) * @custdesc A problem occurred while accessing the boot flash. */ l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, PNOR::MOD_PNORRP_DIDSTARTUPFAIL, PNOR::RC_BAD_STARTUP_RC, rc, @@ -363,6 +355,7 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, o_info.id = iv_TOC[id].id; o_info.name = cv_EYECATCHER[id]; o_info.vaddr = iv_TOC[id].virtAddr; + o_info.flashAddr = iv_TOC[id].flashAddr; o_info.size = iv_TOC[id].size; o_info.eccProtected = ((iv_TOC[id].integrity & FFS_INTEG_ECC_PROTECT) != 0) ? true : false; @@ -383,267 +376,45 @@ errlHndl_t PnorRP::readTOC() { TRACUCOMP(g_trac_pnor, "PnorRP::readTOC>" ); errlHndl_t l_errhdl = NULL; - uint8_t* tocBuffer = NULL; + uint8_t* toc0Buffer = new uint8_t[PAGESIZE]; + uint8_t* toc1Buffer = new uint8_t[PAGESIZE]; uint64_t fatal_error = 0; - bool TOC_0_failed = false; - - do{ - iv_TOC_used = 0; - - for (uint32_t cur_TOC = 0; cur_TOC < NUM_TOCS; ++cur_TOC) + do { + l_errhdl = readFromDevice( TOC_0_OFFSET, 0, false, + toc0Buffer, fatal_error ); + if (l_errhdl) { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC verifying TOC: %d",cur_TOC); - uint64_t nextVAddr = BASE_VADDR; - - // Zero out my table - for( size_t id = PNOR::FIRST_SECTION; - id <= PNOR::NUM_SECTIONS; //include extra entry for error paths - ++id ) - { - iv_TOC[id].id = (PNOR::SectionId)id; - //everything else should default to zero - } - - // Read TOC information from TOC 0 and then TOC 1 - tocBuffer = new uint8_t[PAGESIZE]; - if (cur_TOC == 0) - { - l_errhdl = readFromDevice( TOC_0_OFFSET, 0, false, - tocBuffer, fatal_error ); - } - else if (cur_TOC == 1) - { - l_errhdl = readFromDevice( TOC_1_OFFSET, 0, false, - tocBuffer, fatal_error ); - } - - if( l_errhdl ) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC readFromDevice Failed."); - break; - } - - ffs_hdr* l_ffs_hdr = (ffs_hdr*) tocBuffer; - - // ffs entry check, 0 if checksums match - if( PNOR::pnor_ffs_checksum(l_ffs_hdr, FFS_HDR_SIZE) != 0) - { - //@TODO - RTC:90780 - May need to handle this differently in SP-less config - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC pnor_ffs_checksum header checksums do not match."); - if (cur_TOC == 0) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC TOC 0 failed header checksum"); - TOC_0_failed = true; - iv_TOC_used = 1; - continue; - } - else if (cur_TOC == 1 && TOC_0_failed) - { - // Both TOC's failed - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC both TOC's are corrupted"); - INITSERVICE::doShutdown( PNOR::RC_PARTITION_TABLE_INVALID); - } - else - { - // TOC 1 failed - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC TOC 1 failed header checksum"); - break; - } - } - - // Only check header if on first TOC or the first TOC failed - if (cur_TOC == 0 || TOC_0_failed) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC: FFS Block size = 0x%.8X, Partition Table Size = 0x%.8x, entry_count=%d", - l_ffs_hdr->block_size, l_ffs_hdr->size, l_ffs_hdr->entry_count); - - uint64_t spaceUsed = (sizeof(ffs_entry))*l_ffs_hdr->entry_count; - - /* Checking FFS Header to make sure it looks valid */ - bool header_good = true; - if(l_ffs_hdr->magic != FFS_MAGIC) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Invalid magic number in FFS header: 0x%.4X", - l_ffs_hdr->magic); - header_good = false; - } - else if(l_ffs_hdr->version != SUPPORTED_FFS_VERSION) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Unsupported FFS Header version: 0x%.4X", - l_ffs_hdr->version); - header_good = false; - } - else if(l_ffs_hdr->entry_size != sizeof(ffs_entry)) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Unexpected entry_size(0x%.8x) in FFS header: 0x%.4X", l_ffs_hdr->entry_size); - header_good = false; - } - else if(l_ffs_hdr->entries == NULL) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: FFS Header pointer to entries is NULL."); - header_good = false; - } - else if(l_ffs_hdr->block_size != PAGESIZE) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Unsupported Block Size(0x%.4X). PNOR Blocks must be 4k", - l_ffs_hdr->block_size); - header_good = false; - } - else if(l_ffs_hdr->block_count == 0) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Unsupported BLock COunt(0x%.4X). Device cannot be zero blocks in length.", - l_ffs_hdr->block_count); - header_good = false; - } - //Make sure all the entries fit in specified partition table size. - else if(spaceUsed > - ((l_ffs_hdr->block_size * l_ffs_hdr->size) - sizeof(ffs_hdr))) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: FFS Entries (0x%.16X) go past end of FFS Table.", - spaceUsed); - header_good = false; - } - - if(!header_good) - { - //Shutdown if we detected a partition table issue for any reason - if (TOC_0_failed) - { - INITSERVICE::doShutdown( PNOR::RC_PARTITION_TABLE_INVALID); - } - else - { - TOC_0_failed = true; - } - //Try TOC1 - continue; - } - } - - ffs_hb_user_t* ffsUserData = NULL; - - //Walk through all the entries in the table and parse the data. - for(uint32_t i=0; i<l_ffs_hdr->entry_count; i++) - { - ffs_entry* cur_entry = (&l_ffs_hdr->entries[i]); - - TRACUCOMP(g_trac_pnor, "PnorRP::readTOC: Entry %d, name=%s, pointer=0x%X", i, cur_entry->name, (uint64_t)cur_entry); - - uint32_t secId = PNOR::INVALID_SECTION; - - // ffs entry check, 0 if checksums match - if( PNOR::pnor_ffs_checksum(cur_entry, FFS_ENTRY_SIZE) != 0) - { - //@TODO - RTC:90780 - May need to handle this differently in SP-less config - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC pnor_ffs_checksum entry checksums do not match."); - if (cur_TOC == 0) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC TOC 0 entry checksum failed"); - TOC_0_failed = true; - iv_TOC_used = 1; - break; - } - else if (cur_TOC == 1 && TOC_0_failed) - { - // Both TOC's failed - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC both TOC's are corrupted"); - INITSERVICE::doShutdown( PNOR::RC_PARTITION_TABLE_INVALID); - } - else - { - // TOC 1 failed - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC TOC 1 entry checksum failed"); - break; - } - } - - // Only set data if on first TOC or the first TOC failed - if (cur_TOC == 0 || TOC_0_failed) - { - //Figure out section enum - for(uint32_t eyeIndex=PNOR::TOC; eyeIndex < PNOR::NUM_SECTIONS; eyeIndex++) - { - if(strcmp(cv_EYECATCHER[eyeIndex], cur_entry->name) == 0) - { - secId = eyeIndex; - TRACUCOMP(g_trac_pnor, "PnorRP::readTOC: sectionId=%d", secId); - break; - } - } - - if(secId == PNOR::INVALID_SECTION) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC: Unrecognized Section name(%s), skipping", cur_entry->name); - continue; - } - - ffsUserData = (ffs_hb_user_t*)&(cur_entry->user); - - //size - iv_TOC[secId].size = ((uint64_t)cur_entry->size)*PAGESIZE; - - //virtAddr - iv_TOC[secId].virtAddr = nextVAddr; - nextVAddr += iv_TOC[secId].size; - - //flashAddr - iv_TOC[secId].flashAddr = ((uint64_t)cur_entry->base)*PAGESIZE; - - //chipSelect - iv_TOC[secId].chip = ffsUserData->chip; - - //user data - iv_TOC[secId].integrity = ffsUserData->dataInteg; - iv_TOC[secId].version = ffsUserData->verCheck; - iv_TOC[secId].misc = ffsUserData->miscFlags; - - TRACFCOMP(g_trac_pnor, "PnorRp::readTOC: User Data %s", cur_entry->name); - - if (iv_TOC[secId].integrity == FFS_INTEG_ECC_PROTECT) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC: ECC enabled for %s", cur_entry->name); - iv_TOC[secId].size = ALIGN_PAGE_DOWN((iv_TOC[secId].size * 8 ) / 9); - } + TRACFCOMP(g_trac_pnor, "readTOC: readFromDevice failed for TOC0"); + break; + } - // TODO RTC:96009 handle version header w/secureboot - if (iv_TOC[secId].version == FFS_VERS_SHA512) - { - TRACFCOMP(g_trac_pnor, "PnorRP::readTOC: Incrementing Flash Address for SHA Header"); - if (iv_TOC[secId].integrity == FFS_INTEG_ECC_PROTECT) - { - iv_TOC[secId].flashAddr += PAGESIZE_PLUS_ECC; - } - else - { - iv_TOC[secId].flashAddr += PAGESIZE; - } - } - - if((iv_TOC[secId].flashAddr + iv_TOC[secId].size) > (l_ffs_hdr->block_count*PAGESIZE)) - { - TRACFCOMP(g_trac_pnor, "E>PnorRP::readTOC: Partition(%s) at base address (0x%.8x) extends past end of flash device", - cur_entry->name, iv_TOC[secId].flashAddr); - INITSERVICE::doShutdown( PNOR::RC_PARTITION_TABLE_INVALID); - } - } - } + l_errhdl = readFromDevice( TOC_1_OFFSET, 0, false, + toc1Buffer, fatal_error ); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, "readTOC: readFromDevice failed for TOC1"); + break; + } - //keep these traces here until PNOR is rock-solid - for(PNOR::SectionId tmpId = PNOR::FIRST_SECTION; - tmpId < PNOR::NUM_SECTIONS; - tmpId = (PNOR::SectionId) (tmpId + 1) ) - { - TRACFCOMP(g_trac_pnor, "%s: size=0x%.8X flash=0x%.8X virt=0x%.16X", cv_EYECATCHER[tmpId], iv_TOC[tmpId].size, iv_TOC[tmpId].flashAddr, iv_TOC[tmpId].virtAddr ); - } + l_errhdl = PNOR::parseTOC(toc0Buffer, toc1Buffer, iv_TOC_used, iv_TOC, + BASE_VADDR); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, "readTOC: parseTOC failed"); + errlCommit(l_errhdl, PNOR_COMP_ID); + INITSERVICE::doShutdown(PNOR::RC_PARTITION_TABLE_INVALID); } - }while(0); + } while (0); - if(tocBuffer != NULL) + if(toc0Buffer != NULL) { - TRACUCOMP(g_trac_pnor, "Deleting tocBuffer"); - delete[] tocBuffer; + delete[] toc0Buffer; } + if(toc1Buffer != NULL) + { + delete[] toc1Buffer; + } TRACUCOMP(g_trac_pnor, "< PnorRP::readTOC" ); return l_errhdl; } diff --git a/src/usr/pnor/pnorrp.H b/src/usr/pnor/pnorrp.H index f11a2644d..54f3e364e 100644 --- a/src/usr/pnor/pnorrp.H +++ b/src/usr/pnor/pnorrp.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -25,7 +25,6 @@ /* IBM_PROLOG_END_TAG */ #ifndef __PNOR_PNORRP_H #define __PNOR_PNORRP_H - #include <pnor/pnorif.H> #include <sys/msg.h> #include <stdint.h> @@ -33,25 +32,7 @@ #include <errl/errlentry.H> #include <vmmconst.h> #include <map> - -namespace PNOR -{ - - /** - * @brief Creates a 4-byte Cyclic Redundancy Check (CRC) on the data - * provided. The last iteration of the for-loop includes the ffs - * checksum itself. Therefore if the 4-byte CRC created matches - * the ffs checksum, the resulting CRC will be 0 - * - * @param[in] ptr Pointer to the data - * - * @param[in] size Size of the data - * - * @return uint32_t return 4-byte CRC, 0 if checksums match - */ - uint32_t pnor_ffs_checksum(void* data, size_t size); - -}; //namespace PNOR +#include "pnor_common.H" /** * PNOR Resource Provider @@ -90,47 +71,18 @@ class PnorRP private: - /** - * PNOR Constants - */ - static const uint32_t NUM_TOCS = 2; - static const uint64_t TOC_0_OFFSET = 0; - static const uint64_t TOC_1_OFFSET = 0x8000; - enum { BASE_VADDR = VMM_VADDR_PNOR_RP, /**< 2GB = 0x80000000*/ TOTAL_SIZE = 64*MEGABYTE, /**< Allocate 64 MB (0x4000000)*/ - LAST_VADDR = BASE_VADDR + TOTAL_SIZE, /**< End of our VA range */ - - /** Real number of bytes required to read 1 logical page */ - PAGESIZE_PLUS_ECC = ((PAGESIZE * 9)/8), // 8B data + 1B of ECC - - SUPPORTED_FFS_VERSION = 0x1, /**< Supported FFS Version */ - FFS_TABLE_BASE_ADDR = 0x0, /**< Currently only have FFS table */ }; - /** * Which TOC (0 or 1) is used after verifying both. */ uint32_t iv_TOC_used; /** - * Internal information to deal with the sections of PNOR - */ - struct SectionData_t { - PNOR::SectionId id; /**< Identifier for this section */ - uint64_t virtAddr; /**< Virtual address for the start of the section */ - uint32_t flashAddr; /**< Address in flash */ - uint32_t size; /**< Actual size of content in bytes (not including ECC) */ - uint8_t chip; /**< Chip Select */ - uint8_t version; /**< Version Checking */ - uint16_t integrity; /**< Data Integrity */ - uint8_t misc; /**< Misc Flags */ - } PACKED; - - /** * Flash statistics */ struct FlashStats_t { @@ -141,7 +93,7 @@ class PnorRP /** * Cached copy of section data */ - SectionData_t iv_TOC[PNOR::NUM_SECTIONS+1]; + PNOR::SectionData_t iv_TOC[PNOR::NUM_SECTIONS+1]; /** * Pointer to the message queue where we receive messages @@ -263,7 +215,6 @@ class PnorRP friend class PnorDdTest; friend class SfcIBMTest; friend class SfcAST2400Test; - // allow this function to use constant(s) friend errlHndl_t PNOR::validateAltMaster( void ); diff --git a/src/usr/pnor/pnorvalid.C b/src/usr/pnor/pnorvalid.C index 048078c93..1bc09a2fa 100644 --- a/src/usr/pnor/pnorvalid.C +++ b/src/usr/pnor/pnorvalid.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2014 */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -102,7 +102,7 @@ errlHndl_t validateAltMaster( void ) // When reading PNOR TOC assume a single page and no ECC uint8_t* tocBuffer = new uint8_t[PAGESIZE]; size_t read_size = PAGESIZE; - const uint64_t toc0_offset = PnorRP::TOC_0_OFFSET; + const uint64_t toc0_offset = PNOR::TOC_0_OFFSET; do{ @@ -177,7 +177,7 @@ errlHndl_t validateAltMaster( void ) // Read Flash l_err = pnordd->readFlash(tocBuffer, read_size, - PnorRP::TOC_0_OFFSET); + PNOR::TOC_0_OFFSET); if ( l_err ) { // Commit Error Log, but continue the test diff --git a/src/usr/pnor/runtime/makefile b/src/usr/pnor/runtime/makefile new file mode 100644 index 000000000..0556af9e4 --- /dev/null +++ b/src/usr/pnor/runtime/makefile @@ -0,0 +1,36 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/pnor/runtime/makefile $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2014,2015 +# [+] 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 +HOSTBOOT_RUNTIME = 1 +ROOTPATH = ../../../.. +MODULE = pnor_rt + +OBJS += rt_pnor.o +OBJS += pnor_common.o +OBJS += ecc.o + +SUBDIRS += test.d + +VPATH += ../ +include $(ROOTPATH)/config.mk diff --git a/src/usr/pnor/runtime/rt_pnor.C b/src/usr/pnor/runtime/rt_pnor.C new file mode 100644 index 000000000..c43160f44 --- /dev/null +++ b/src/usr/pnor/runtime/rt_pnor.C @@ -0,0 +1,634 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/runtime/rt_pnor.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* [+] 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 <stdlib.h> +#include <targeting/common/targetservice.H> +#include <initservice/taskargs.H> + +#include <runtime/rt_targeting.H> +#include <runtime/interface.h> + +#include <pnor/pnorif.H> +#include <pnor/ecc.H> +#include <pnor/pnor_reasoncodes.H> +#include "rt_pnor.H" + +#include "../ffs.h" +#include "../common/ffs_hb.H" + +// Trace definition +extern trace_desc_t* g_trac_pnor; + +/** + * Eyecatcher strings for PNOR TOC entries + */ +extern const char* cv_EYECATCHER[]; + +/** + * @brief set up _start() task entry procedure for PNOR daemon + */ +TASK_ENTRY_MACRO( RtPnor::init ); + + +/** + * @brief Return the size and address of a given section of PNOR data + */ +errlHndl_t PNOR::getSectionInfo( PNOR::SectionId i_section, + PNOR::SectionInfo_t& o_info ) +{ + return Singleton<RtPnor>::instance().getSectionInfo(i_section,o_info); +} + +/** + * @brief Write the data for a given sectino into PNOR + */ +errlHndl_t PNOR::flush( PNOR::SectionId i_section) +{ + return Singleton<RtPnor>::instance().flush(i_section); +} + +/****************Public Methods***************************/ +/** + * STATIC + * @brief Static Initializer + */ +void RtPnor::init(errlHndl_t &io_taskRetErrl) +{ + TRACFCOMP(g_trac_pnor, "RtPnor::init> " ); + io_taskRetErrl = Singleton<RtPnor>::instance().readTOC(); + TRACFCOMP(g_trac_pnor, "<RtPnor::init" ); +} +/**************************************************************/ +errlHndl_t RtPnor::getSectionInfo(PNOR::SectionId i_section, + PNOR::SectionInfo_t& o_info) +{ + TRACFCOMP(g_trac_pnor, ENTER_MRK"RtPnor::getSectionInfo"); + errlHndl_t l_err = NULL; + do + { + if (i_section == PNOR::INVALID_SECTION) + { + TRACFCOMP(g_trac_pnor, "RtPnor::getSectionInfo: Invalid Section" + " %d", (int)i_section); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_GETSECTIONINFO + * @reasoncode PNOR::RC_RTPNOR_INVALID_SECTION + * @userdata1 PNOR::SectionId + * @devdesc invalid section passed to getSectionInfo + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_GETSECTIONINFO, + PNOR::RC_RTPNOR_INVALID_SECTION, + i_section, 0,true); + break; + } + + //size of the section + uint64_t l_sizeBytes = iv_TOC[i_section].size; + if (l_sizeBytes == 0) + { + TRACFCOMP(g_trac_pnor,"RtPnor::getSectionInfo: Section %d" + " size is 0", (int)i_section); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_GETSECTIONINFO + * @reasoncode PNOR::RC_SECTION_SIZE_IS_ZERO + * @userdata1 PNOR::SectionId + * @devdesc section size is zero + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_GETSECTIONINFO, + PNOR::RC_SECTION_SIZE_IS_ZERO, + i_section, 0,true); + break; + } + //find proc id + uint64_t l_procId; + TARGETING::Target* l_masterProc = NULL; + TARGETING::targetService().masterProcChipTargetHandle( l_masterProc ); + l_err = RT_TARG::getRtTarget (l_masterProc, l_procId); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::getSectionInfo: getRtTarget failed"); + break; + } + + //ecc + bool l_ecc = (iv_TOC[i_section].integrity&FFS_INTEG_ECC_PROTECT) ? + true : false; + + void* l_pWorking = NULL; + void* l_pClean = NULL; + + //find the section in the map first + if(iv_pnorMap.find(i_section) != iv_pnorMap.end()) + { + //get the addresses from the map + PnorAddrPair_t l_addrPair = iv_pnorMap[i_section]; + l_pWorking = l_addrPair.first; + l_pClean = l_addrPair.second; + } + else + { + //malloc twice -- one working copy and one clean copy + //So, we can diff and write only the dirty bytes + l_pWorking = malloc(l_sizeBytes); + l_pClean = malloc(l_sizeBytes); + + //offset = 0 : read the entire section + l_err = readFromDevice(l_procId, i_section, 0, l_sizeBytes, l_ecc, + l_pWorking); + if(l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::getSectionInfo:readFromDevice" + " failed"); + break; + } + + //copy data to another pointer to save a clean copy of data + memcpy(l_pClean, l_pWorking, l_sizeBytes); + + //save it in the map + iv_pnorMap [i_section] = PnorAddrPair_t(l_pWorking, l_pClean); + } + //return the data in the struct + o_info.id = i_section; + o_info.name = cv_EYECATCHER[i_section]; + o_info.vaddr = (uint64_t)l_pWorking; + o_info.flashAddr = iv_TOC[i_section].flashAddr; + o_info.size = l_sizeBytes; + o_info.eccProtected = l_ecc; + o_info.sha512Version= + (iv_TOC[i_section].version & FFS_VERS_SHA512) ? true : false; + o_info.sha512perEC = + (iv_TOC[i_section].version & FFS_VERS_SHA512_PER_EC) ? true : false; + } while (0); + + TRACFCOMP(g_trac_pnor, EXIT_MRK"RtPnor::getSectionInfo"); + return l_err; +} + +/**************************************************************/ +errlHndl_t RtPnor::flush( PNOR::SectionId i_section) +{ + TRACFCOMP(g_trac_pnor, ENTER_MRK"RtPnor::flush"); + errlHndl_t l_err = NULL; + do + { + if (i_section == PNOR::INVALID_SECTION) + { + TRACFCOMP(g_trac_pnor,"RtPnor::flush: Invalid Section: %d", + (int)i_section); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_FLUSH + * @reasoncode PNOR::RC_INVALID_SECTION + * @userdata1 PNOR::SectionId + * @devdesc invalid section passed to flush + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_FLUSH, + PNOR::RC_INVALID_SECTION, + i_section, 0,true); + break; + } + size_t l_sizeBytes = iv_TOC[i_section].size; + if (l_sizeBytes == 0) + { + TRACFCOMP(g_trac_pnor,"RtPnor::flush: Section %d" + " size is 0", (int)i_section); + + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_FLUSH + * @reasoncode PNOR::RC_SECTION_SIZE_IS_ZERO + * @userdata1 PNOR::SectionId + * @devdesc section size is zero + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_FLUSH, + PNOR::RC_SECTION_SIZE_IS_ZERO, + i_section, 0,true); + break; + } + + //get the saved pointers for the partitionName + PnorAddrMap_t::iterator l_it = iv_pnorMap.find(i_section); + if(l_it == iv_pnorMap.end()) + { + TRACFCOMP(g_trac_pnor,"RtPnor::flush: section %d has not been read before", + i_section); + break; + } + PnorAddrPair_t l_addrPair = l_it->second; + uint8_t* l_pWorking = reinterpret_cast<uint8_t*>(l_addrPair.first); + uint8_t* l_pClean = reinterpret_cast<uint8_t*>(l_addrPair.second); + + //ecc + bool l_ecc = (iv_TOC[i_section].integrity&FFS_INTEG_ECC_PROTECT) ? + true : false; + //find proc id + uint64_t l_procId; + TARGETING::Target* l_masterProc = NULL; + TARGETING::targetService().masterProcChipTargetHandle( l_masterProc ); + l_err = RT_TARG::getRtTarget (l_masterProc, l_procId); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::flush: getRtTarget failed"); + break; + } + + //find the diff between each pointer + //write back to pnor what doesn't match + TRACFCOMP(g_trac_pnor, "finding diff between working and clean copy..."); + for (uint64_t i = 0; i < (l_sizeBytes/PAGESIZE); i++) + { + if (0 != memcmp(l_pWorking, l_pClean, PAGESIZE)) + { + TRACFCOMP(g_trac_pnor, "RtPnor::flush: page %d is different," + " writing back to pnor", i); + l_err = writeToDevice(l_procId, i_section, i*PAGESIZE,PAGESIZE, + l_ecc,l_pWorking); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::flush: writeToDevice failed"); + break; + } + //update the clean copy + memcpy(l_pClean, l_pWorking, PAGESIZE); + } + l_pWorking += PAGESIZE; + l_pClean += PAGESIZE; + } + + if (l_err) + { + TRACFCOMP(g_trac_pnor,"RtPnor::flush: error writing section %d" + " back to pnor",(int)i_section); + break; + } + } while (0); + + TRACFCOMP(g_trac_pnor, EXIT_MRK"RtPnor::flush"); + return l_err; +} +/*******Protected Methods**************/ +RtPnor::RtPnor() +:iv_TOC_used(0) +{ + errlHndl_t l_err = readTOC(); + if (l_err) + { + errlCommit(l_err, PNOR_COMP_ID); + } +} + +/*************************/ +RtPnor::~RtPnor() +{ + +} + +/*******************Private Methods*********************/ +errlHndl_t RtPnor::readFromDevice (uint64_t i_procId, + PNOR::SectionId i_section, + uint64_t i_offset, + size_t i_size, + bool i_ecc, + void* o_data) +{ + TRACFCOMP(g_trac_pnor, ENTER_MRK"RtPnor::readFromDevice: i_offset=0x%X, " + "i_procId=%d sec=%d size=0x%X ecc=%d", i_offset, i_procId, i_section, + i_size, i_ecc); + errlHndl_t l_err = NULL; + uint8_t* l_eccBuffer = NULL; + do + { + + const char* l_partitionName = cv_EYECATCHER[i_section]; + void* l_dataToRead = o_data; + size_t l_readSize = i_size; + size_t l_readSizePlusECC = (i_size * 9)/8; + uint64_t l_offset = i_offset; + + // if we need to handle ECC, we need to read more + if( i_ecc ) + { + l_eccBuffer = new uint8_t[l_readSizePlusECC](); + l_dataToRead = l_eccBuffer; + l_readSize = l_readSizePlusECC; + l_offset = (i_offset * 9)/8; + } + + if (g_hostInterfaces && g_hostInterfaces->pnor_read) + { + // get the data from OPAL + int l_rc = g_hostInterfaces->pnor_read(i_procId, l_partitionName, + l_offset, l_dataToRead, l_readSize); + if (l_rc) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readFromDevice: pnor_read" + " failed proc:%d, part:%s, offset:0x%X, size:0x%X," + " dataPt:0x%X, rc:%d", i_procId, l_partitionName, + l_offset, l_readSize, l_dataToRead, l_rc); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_READFROMDEVICE + * @reasoncode PNOR::RC_PNOR_READ_FAILED + * @userdata1[00:31] rc returned from pnor_read + * @userdata1[32:63] section ID + * @userdata2[00:31] offset within the section + * @userdata2[32:63] size of data read in bytes + * @devdesc g_hostInterfaces->pnor_read failed + * @custdesc Error accessing system firmware flash + */ + //@todo Add PNOR callout RTC:116145 + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_READFROMDEVICE, + PNOR::RC_PNOR_READ_FAILED, + TWO_UINT32_TO_UINT64(l_rc, i_section), + TWO_UINT32_TO_UINT64(l_offset, l_readSize), + true); + break; + } + } + else + { + TRACFCOMP(g_trac_pnor,"RtPnor::readFromDevice: This version of" + " OPAL does not support pnor_read"); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_READFROMDEVICE + * @reasoncode PNOR::RC_PNOR_READ_NOT_SUPPORTED + * @devdesc g_hostInterfaces->pnor_read not supported + * @custdesc Error accessing system firmware flash + */ + //@todo Add PNOR callout RTC:116145 + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_READFROMDEVICE, + PNOR::RC_PNOR_READ_NOT_SUPPORTED, + 0,0,true); + break; + } + // remove the ECC data + if( i_ecc ) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readFromDevice: removing ECC..."); + // remove the ECC and fix the original data if it is broken + PNOR::ECC::eccStatus ecc_stat = + PNOR::ECC::removeECC(reinterpret_cast<uint8_t*>(l_dataToRead), + reinterpret_cast<uint8_t*>(o_data), + i_size); + + // create an error if we couldn't correct things + if( ecc_stat == PNOR::ECC::UNCORRECTABLE ) + { + TRACFCOMP(g_trac_pnor,"RtPnor::readFromDevice>" + " Uncorrectable ECC error : chip=%d,offset=0x%.X", + i_procId, i_offset ); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_READFROMDEVICE + * @reasoncode PNOR::RC_UNCORRECTABLE_ECC + * @devdesc UNCORRECTABLE ECC + */ + //@todo Add PNOR callout RTC:116145 + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_READFROMDEVICE, + PNOR::RC_UNCORRECTABLE_ECC, + 0, 0, true); + break; + } + + // found an error so we need to fix something + else if( ecc_stat != PNOR::ECC::CLEAN ) + { + TRACFCOMP(g_trac_pnor,"RtPnor::readFromDevice>" + "Correctable ECC error : chip=%d, offset=0x%.X", + i_procId, i_offset ); + if (g_hostInterfaces && g_hostInterfaces->pnor_write) + { + + //need to write good data back to PNOR + int l_rc = g_hostInterfaces->pnor_write(i_procId, + l_partitionName,l_offset, l_dataToRead,l_readSize); + if (l_rc) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readFromDevice> Error" + " writing corrected data back to device"); + + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_READFROMDEVICE + * @reasoncode PNOR::RC_PNOR_WRITE_FAILED + * @userdata1 rc returned from pnor_write + * @devdesc error writing corrected data back to PNOR + * @custdesc Error accessing system firmware flash + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_READFROMDEVICE, + PNOR::RC_PNOR_WRITE_FAILED, + l_rc, 0, true); + errlCommit(l_err, PNOR_COMP_ID); + } + } + } + } + } while(0); + + if( l_eccBuffer ) + { + delete[] l_eccBuffer; + } + + TRACFCOMP(g_trac_pnor, EXIT_MRK"RtPnor::readFromDevice" ); + return l_err; +} + +/*********************************************************************/ +errlHndl_t RtPnor::writeToDevice( uint64_t i_procId, + PNOR::SectionId i_section, + uint64_t i_offset, + size_t i_size, + bool i_ecc, + void* i_src ) +{ + TRACFCOMP(g_trac_pnor, ENTER_MRK"RtPnor::writeToDevice: i_offset=0x%X, " + "i_procId=%d sec=%d size=0x%X ecc=%d", i_offset, i_procId, i_section, + i_size, i_ecc); + errlHndl_t l_err = NULL; + uint8_t* l_eccBuffer = NULL; + + do + { + void* l_dataToWrite = i_src; + size_t l_writeSize = i_size; + size_t l_writeSizePlusECC = (i_size * 9)/8; + uint64_t l_offset = i_offset; + + // apply ECC to data if needed + if( i_ecc ) + { + l_eccBuffer = new uint8_t[l_writeSizePlusECC]; + PNOR::ECC::injectECC( reinterpret_cast<uint8_t*>(i_src), + l_writeSize, + reinterpret_cast<uint8_t*>(l_eccBuffer) ); + l_dataToWrite = reinterpret_cast<void*>(l_eccBuffer); + l_writeSize = l_writeSizePlusECC; + l_offset = (i_offset * 9)/8; + } + + const char* l_partitionName = cv_EYECATCHER[i_section]; + if (g_hostInterfaces && g_hostInterfaces->pnor_write) + { + //make call into opal to write the data + int l_rc = g_hostInterfaces->pnor_write(i_procId, + l_partitionName,l_offset,l_dataToWrite,l_writeSize); + if (l_rc) + { + TRACFCOMP(g_trac_pnor, "RtPnor::writeToDevice: pnor_write failed " + "proc:%d, part:%s, offset:0x%X, size:0x%X, dataPt:0x%X," + " rc:%d", i_procId, l_partitionName, l_offset, l_writeSize, + l_dataToWrite, l_rc); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_WRITETODEVICE + * @reasoncode PNOR::RC_PNOR_WRITE_FAILED + * @userdata1[00:31] rc returned from pnor_write + * @userdata1[32:63] section ID + * @userdata2[00:31] offset within the section + * @userdata2[32:63] size of data written in bytes + * @devdesc g_hostInterfaces->pnor_write failed + * @custdesc Error accessing system firmware flash + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_WRITETODEVICE, + PNOR::RC_PNOR_WRITE_FAILED, + TWO_UINT32_TO_UINT64(l_rc, i_section), + TWO_UINT32_TO_UINT64(l_offset, l_writeSize), + true); + break; + } + } + else + { + TRACFCOMP(g_trac_pnor,"RtPnor::writeToDevice: This version of" + " OPAL does not support pnor_write"); + /*@ + * @errortype + * @moduleid PNOR::MOD_RTPNOR_WRITETODEVICE + * @reasoncode PNOR::RC_PNOR_WRITE_NOT_SUPPORTED + * @devdesc g_hostInterfaces->pnor_write not supported + * @custdesc Error accessing system firmware flash + */ + //@todo Add PNOR callout RTC:116145 + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_RTPNOR_WRITETODEVICE, + PNOR::RC_PNOR_WRITE_NOT_SUPPORTED, + 0,0,true); + break; + + } + } while(0); + + if( l_eccBuffer ) + { + delete[] l_eccBuffer; + } + + TRACFCOMP(g_trac_pnor, EXIT_MRK"RtPnor::writeToDevice" ); + return l_err; +} + +/*****************************************************************/ +errlHndl_t RtPnor::readTOC () +{ + TRACFCOMP(g_trac_pnor, ENTER_MRK"RtPnor::readTOC" ); + errlHndl_t l_err = NULL; + uint8_t* toc0Buffer = new uint8_t[PAGESIZE]; + uint8_t* toc1Buffer = new uint8_t[PAGESIZE]; + do { + //find proc id + uint64_t l_procId; + TARGETING::Target* l_masterProc = NULL; + TARGETING::targetService().masterProcChipTargetHandle( l_masterProc ); + l_err = RT_TARG::getRtTarget (l_masterProc, l_procId); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readTOC: getRtTarget failed"); + break; + } + + l_err = readFromDevice (l_procId, PNOR::TOC, PNOR::TOC_0_OFFSET, + PAGESIZE,false,toc0Buffer); + if (l_err) + { + TRACFCOMP(g_trac_pnor,"RtPnor::readTOC:readFromDevice failed" + " for TOC0"); + break; + } + l_err = readFromDevice (l_procId, PNOR::TOC, PNOR::TOC_1_OFFSET, + PAGESIZE, false,toc1Buffer); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readTOC:readFromDevice failed" + " for TOC1"); + break; + } + + l_err = PNOR::parseTOC(toc0Buffer, toc1Buffer, iv_TOC_used, iv_TOC,0); + if (l_err) + { + TRACFCOMP(g_trac_pnor, "RtPnor::readTOC: parseTOC failed"); + break; + } + } while (0); + + if(toc0Buffer != NULL) + { + delete[] toc0Buffer; + } + + if(toc1Buffer != NULL) + { + delete[] toc1Buffer; + } + + TRACFCOMP(g_trac_pnor, EXIT_MRK"RtPnor::readTOC" ); + return l_err; +} + +/***********************************************************/ +RtPnor& RtPnor::getInstance() +{ + return Singleton<RtPnor>::instance(); +} diff --git a/src/usr/pnor/runtime/rt_pnor.H b/src/usr/pnor/runtime/rt_pnor.H new file mode 100644 index 000000000..43041fc3f --- /dev/null +++ b/src/usr/pnor/runtime/rt_pnor.H @@ -0,0 +1,153 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/runtime/rt_pnor.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* [+] 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 _RT_PNOR_H_ +#define _RT_PNOR_H_ + +#include <pnor/pnorif.H> +#include "../pnor_common.H" +#include <errl/errlentry.H> +#include <map> + +typedef std::pair<void*, void*> PnorAddrPair_t; +typedef std::map<PNOR::SectionId, PnorAddrPair_t> PnorAddrMap_t; + +class RtPnor +{ + public: + + /** + * @brief Static Initializer + * @param[in] ref to errlHndl_t + */ + static void init ( errlHndl_t &io_rtaskRetErrl ); + + /** + * @brief Return the size and address of a given section + * of PNOR data at runtime + * Called by external PNOR::getSectionInfo() + * + * @param[in] i_section PNOR section + * @param[out] o_info Location and size information + * + * @return errlHndl_t Error log if request was invalid + */ + errlHndl_t getSectionInfo ( PNOR::SectionId i_section, + PNOR::SectionInfo_t& o_info); + + /** + * @brief Writes the data back to PNOR of a given section + * of PNOR at runtime + * Called by external PNOR::flush() + * + * @param[in] i_section PNOR section + * @return errlHndl_t Error log if request was invalid + */ + errlHndl_t flush (PNOR::SectionId i_section); + + protected: + /** + * @brief Constructor + */ + RtPnor(); + + /** + * @brief Destructor + */ + ~RtPnor(); + + private: + /** + * Which TOC (0 or 1) is used after verifying both. + */ + uint32_t iv_TOC_used; + + /** + * Cached copy of section data + */ + PNOR::SectionData_t iv_TOC[PNOR::NUM_SECTIONS+1]; + + /** + * Keeps track of the data pointers for different sections in the PNOR + */ + PnorAddrMap_t iv_pnorMap; + + /** + * @brief Reads data from the PNOR device + * and removes ecc if necessary + * + * @param[in] i_procId processor id + * @param[in] i_section section of the pnor to write back + * @param[in] i_offset offset into the pnor + * @param[in] i_size size of data to read in bytes + * @param[in] i_ecc true=verify and strip ECC after reading + * @param[in] o_data Buffer to copy data into + * + * @return Error from device + */ + errlHndl_t readFromDevice (uint64_t i_procId, + PNOR::SectionId i_section, + uint64_t i_offset, + size_t i_size, + bool i_ecc, + void* o_data); + + /** + * @brief Write data back to the PNOR device + * and injects ecc if necessary + * + * @param[in] i_procId processor id + * @param[in] i_section section of the pnor to write back + * @param[in] i_offset offset into the pnor + * @param[in] i_size size of data to read in bytes + * @param[in] i_ecc true=apply ECC before writing + * @param[in] i_src Buffer to copy data from + * + * @return Error from device + */ + errlHndl_t writeToDevice( uint64_t i_procId, + PNOR::SectionId i_section, + uint64_t i_offset, + size_t i_size, + bool i_ecc, + void* i_src ); + + /** + * @brief Verify both TOC's and store section information from one of + * the verified TOC's + * + * @return Error from device + */ + errlHndl_t readTOC(); + + //allow testcases to see inside the class + friend class PnorRtTest; + + /** + * @brief Static instance function for testcase only + */ + static RtPnor& getInstance(); +}; + +#endif diff --git a/src/usr/pnor/runtime/test/makefile b/src/usr/pnor/runtime/test/makefile new file mode 100644 index 000000000..e53d87f15 --- /dev/null +++ b/src/usr/pnor/runtime/test/makefile @@ -0,0 +1,31 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/pnor/runtime/test/makefile $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2014,2015 +# [+] 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 +HOSTBOOT_RUNTIME = 1 +ROOTPATH = ../../../../.. + +MODULE = testpnor_rt +TESTS = *.H + +include ${ROOTPATH}/config.mk diff --git a/src/usr/pnor/runtime/test/testpnor_rt.H b/src/usr/pnor/runtime/test/testpnor_rt.H new file mode 100644 index 000000000..5a81ea9ac --- /dev/null +++ b/src/usr/pnor/runtime/test/testpnor_rt.H @@ -0,0 +1,257 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/runtime/test/testpnor_rt.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* [+] 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 <cxxtest/TestSuite.H> +#include <errl/errlmanager.H> +#include <errl/errlentry.H> +#include <devicefw/userif.H> + +#include <pnor/pnorif.H> +#include "../../pnor_common.H" +#include "../rt_pnor.H" + +#include <runtime/interface.h> +#include <trace/interface.H> + +#include "../../ffs.h" //Common header file with BuildingBlock. +#include "../../common/ffs_hb.H" //Hostboot def of user data in ffs_entry struct + +extern trace_desc_t* g_trac_pnor; +using namespace TARGETING; + +/* + * these taest cases are turned off as they have dependency on pnorddtest cases + */ +class PnorRtTest : public CxxTest::TestSuite +{ + public: + /** + * @brief: testTOC + * tests that the TOC is read/parsed properly during runtime + */ + void testTOC (void) + { +#if 1 + TRACFCOMP(g_trac_pnor, "PnorRtTest::testTOC - skipping - as it" + " adversly affects pnorddtests"); +#else + TRACFCOMP(g_trac_pnor, "PnorRtTest::testTOC Start" ); + errlHndl_t l_err = NULL; + uint32_t l_proc = 0; + uint64_t offset = 0; + uint8_t* tocHeader = new uint8_t[PAGESIZE]; + uint8_t* tocEntry = new uint8_t[PAGESIZE]; + uint8_t* corruptBuffer = new uint8_t[PAGESIZE]; + + // Corrupt both ffs header and first entry for each TOC + for (uint32_t cur_TOC = 0; cur_TOC < PNOR::NUM_TOCS; ++cur_TOC) + { + uint32_t TOC_used = cur_TOC; + offset = (cur_TOC == 0) ? PNOR::TOC_0_OFFSET : PNOR::TOC_1_OFFSET; + + // Read cur_TOC header data + l_err = RtPnor::getInstance().readFromDevice(l_proc,PNOR::TOC, + offset,PAGESIZE,false,tocHeader); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: readFromDevice failed"); + break; + } + + // Corrupt cur_TOC header data + memcpy(corruptBuffer, tocHeader, PAGESIZE); + corruptBuffer[0] = 0xFF; + corruptBuffer[1] = 0xFF; + + l_err = RtPnor::getInstance().writeToDevice(l_proc,PNOR::TOC, + offset,PAGESIZE,false,corruptBuffer); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: writeToDevice failed"); + } + + // Check if cur_TOC failed that other TOC is used + l_err = RtPnor::getInstance().readTOC(); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: readTOC failed"); + } + + TOC_used = RtPnor::getInstance().iv_TOC_used; + TRACFCOMP(g_trac_pnor, "PnorRtTest::testTOC : TOC %d Corrupt" + " Header, Toc_used = %d", cur_TOC, TOC_used); + + if (TOC_used == cur_TOC) + { + TS_FAIL("PnorRtTest::testTOC>ERROR:TOC %d header is corrupted," + " did not use other TOC"); + break; + } + // Fix cur_TOC header + l_err = RtPnor::getInstance().writeToDevice(l_proc,PNOR::TOC, + offset,PAGESIZE,false,tocHeader); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: writeToDevice failed"); + } + + // Read cur_TOC first entry data + l_err = RtPnor::getInstance().readFromDevice(l_proc,PNOR::TOC, + offset+FFS_HDR_SIZE,PAGESIZE,false,tocEntry); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: readTOC failed"); + break; + } + + // Corrupt cur_TOC header data + memcpy(corruptBuffer, tocEntry, PAGESIZE); + corruptBuffer[0] = 0xFF; + corruptBuffer[1] = 0xFF; + + l_err = RtPnor::getInstance().writeToDevice(l_proc,PNOR::TOC, + offset+FFS_HDR_SIZE,PAGESIZE,false,corruptBuffer); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: writeToDevice failed"); + } + + // Check if cur_TOC failed that other TOC is used + TOC_used = cur_TOC; + l_err = RtPnor::getInstance().readTOC(); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: readTOC failed"); + } + TOC_used = RtPnor::getInstance().iv_TOC_used; + TRACFCOMP(g_trac_pnor,"PnorRtTest::testTOC:TOC %d Corrupt" + " Entry, Toc_used = %d", cur_TOC, TOC_used); + + if (TOC_used == cur_TOC) + { + TS_FAIL("PnorRtTest::testTOC>ERROR: TOC %d entry is corrupted," + " did not use other TOC", cur_TOC); + } + + // Fix cur_TOC first entry + l_err = RtPnor::getInstance().writeToDevice(l_proc,PNOR::TOC, + offset+FFS_HDR_SIZE,PAGESIZE,false,tocEntry); + if (l_err) + { + TS_FAIL("PnorRtTest::testTOC: writeToDevice failed"); + } + } + + delete tocHeader; + delete tocEntry; + delete corruptBuffer; + + TRACFCOMP(g_trac_pnor, "PnorRtTest::testTOC End"); +#endif + } + + /** + * @brief RtPnor::testPnorReadWrite + * Compares the values read and written by runtime interfaces and + * IPL interfaces + */ + void testPnorReadWrite(void) + { + TRACFCOMP(g_trac_pnor, ENTER_MRK"testPnorReadWrite"); + do { + errlHndl_t l_err = NULL; + uint32_t l_proc = 0; + uint64_t l_offset = PNOR::pnorTestSec_rt_readwrite_offset; + uint64_t l_writeData = 0x0123456789ABCDEF; + PNOR::SectionId l_id = PNOR::TEST; + PNOR::SectionInfo_t l_info; + + size_t l_sizeBytes = (RtPnor::getInstance().iv_TOC[l_id].size) - + l_offset; + void* l_readData = malloc (l_sizeBytes); + + //read via hostInterfaces + l_err = RtPnor::getInstance().readFromDevice(l_proc,l_id,l_offset, + l_sizeBytes,true,l_readData); + if(l_err) + { + TS_FAIL("testPnorReadWrite: readFromDevice failed"); + break; + } + + //read using getSectionInfo + l_err = PNOR::getSectionInfo(l_id, l_info); + if (l_err) + { + TS_FAIL("testPnorReadWrite: getSectionInfo failed"); + break; + } + + void* l_gData = reinterpret_cast<void*> (l_info.vaddr); + + //verify that data is read correctly + if (0 != memcmp (l_readData, l_gData, l_sizeBytes)) + { + TS_FAIL("testPnorReadWrite: read failed"); + break; + } + TRACFCOMP(g_trac_pnor, "testPnorReadWrite: read successfull"); + + /***********************************************************/ + //make changes to the getSectionInfo pointer and call flush + uint8_t* l_vaddr = reinterpret_cast<uint8_t*>(l_info.vaddr); + + memcpy(l_vaddr, &l_writeData, 4); + memcpy(l_vaddr+PAGESIZE+20, &l_writeData, 4); + + l_err = PNOR::flush(l_id); + if (l_err) + { + TS_FAIL("testPnorReadWrite: flush failed"); + break; + } + + /***********************************************************/ + //calling read again to make sure data was written properly + //using pnor_wirte + l_err = RtPnor::getInstance().readFromDevice(l_proc,l_id,l_offset, + l_sizeBytes,true,l_readData); + if(l_err) + { + TS_FAIL("testPnorReadWrite: readFromDevice failed"); + break; + } + + if (0 != memcmp (l_readData, l_gData, l_sizeBytes)) + { + TS_FAIL("testPnorReadWrite: flush failed"); + break; + } + TRACFCOMP(g_trac_pnor, "testPnorReadWrite: flush passed"); + + } while (0); + TRACFCOMP(g_trac_pnor, EXIT_MRK"testPnorReadWrite"); + } + +}; diff --git a/src/usr/pnor/test/pnorrptest.H b/src/usr/pnor/test/pnorrptest.H index 6c43f9d4a..0a8bc4945 100644 --- a/src/usr/pnor/test/pnorrptest.H +++ b/src/usr/pnor/test/pnorrptest.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -518,17 +518,17 @@ class PnorRpTest : public CxxTest::TestSuite uint8_t* corruptBuffer = new uint8_t[PAGESIZE]; // Corrupt both ffs header and first entry for each TOC - for (uint32_t cur_TOC = 0; cur_TOC < PnorRP::NUM_TOCS; ++cur_TOC) + for (uint32_t cur_TOC = 0; cur_TOC < PNOR::NUM_TOCS; ++cur_TOC) { uint32_t TOC_used = cur_TOC; if (cur_TOC == 0) { - offset = PnorRP::TOC_0_OFFSET; + offset = PNOR::TOC_0_OFFSET; } else { - offset = PnorRP::TOC_1_OFFSET; + offset = PNOR::TOC_1_OFFSET; } // Read cur_TOC header data diff --git a/src/usr/testcore/rtloader/loader.H b/src/usr/testcore/rtloader/loader.H index b5d8aaa37..041337118 100644 --- a/src/usr/testcore/rtloader/loader.H +++ b/src/usr/testcore/rtloader/loader.H @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2013,2014 */ +/* Contributors Listed Below - COPYRIGHT 2013,2015 */ +/* [+] 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. */ @@ -23,7 +25,6 @@ #ifndef __TESTCORE_RTLOADER_LOADER_H #define __TESTCORE_RTLOADER_LOADER_H -#include <pnor/pnorif.H> #include <util/align.H> #include <sys/mm.h> #include <targeting/common/targetservice.H> @@ -36,10 +37,14 @@ #include <sys/time.h> #include <runtime/interface.h> #include <vpd/vpd_if.H> - +#include <pnor/pnorif.H> +#include <string.h> +#include <devicefw/userif.H> +#include <pnor/ecc.H> trace_desc_t* g_trac_hbrt = NULL; TRAC_INIT(&g_trac_hbrt, "HBRT_TEST", 2*KILOBYTE); +extern const char* cv_EYECATCHER[]; class RuntimeLoaderTest : public CxxTest::TestSuite { @@ -115,6 +120,8 @@ class RuntimeLoaderTest : public CxxTest::TestSuite intf->lid_load = rt_lid_load; intf->lid_unload = rt_lid_unload; intf->get_reserved_mem = rt_get_reserved_mem; + intf->pnor_read = rt_pnor_read; + intf->pnor_write= rt_pnor_write; // Call init. runtimeInterfaces_t* rtInterface = @@ -310,6 +317,154 @@ class RuntimeLoaderTest : public CxxTest::TestSuite return 0; } + + static PNOR::SectionId find_sectionId (const char* i_partitionName) + { + PNOR::SectionId l_id = PNOR::INVALID_SECTION; + for (size_t i=PNOR::FIRST_SECTION; i<=PNOR::NUM_SECTIONS; + ++i) + { + if (0 == strcmp(cv_EYECATCHER[i], i_partitionName)) + { + l_id = (PNOR::SectionId)i; + break; + } + } + return l_id; + } + + static int rt_pnor_read (uint32_t i_proc, const char* i_partitionName, + uint64_t i_offset, void* o_data, size_t i_sizeBytes) + { + TRACFCOMP(g_trac_hbrt, ENTER_MRK"rt_pnor_read: proc:%d, part:%s," + " offset:0x%X, dataPtr:0x%X, size:0x%X",i_proc, + i_partitionName, i_offset, o_data, i_sizeBytes); + + PNOR::SectionId l_id = PNOR::INVALID_SECTION; + PNOR::SectionInfo_t l_info; + errlHndl_t l_err = NULL; + uint32_t l_plid = 0; + + do + { + TARGETING::Target* pnor_target = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + + //search cv_EYECATHCER for partitionname + l_id = find_sectionId(i_partitionName); + if (l_id == PNOR::INVALID_SECTION) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_read: Invalid Section"); + break; + } + + //getSectionInfo -- this is PnorRP::getSectionInfo + l_err = PNOR::getSectionInfo(l_id, l_info); + if (l_err) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_read: getSectionInfo errored"); + break; + } + + // read far enough in the section so it doesn't collide + // with other test cases + if (l_id == PNOR::TEST) + { + //adjust the size of data if we are reading the entire sec + i_sizeBytes = (i_offset == 0)? (((l_info.size - + PNOR::pnorTestSec_rt_readwrite_offset)*9)/8) : + i_sizeBytes; + i_offset = ((PNOR::pnorTestSec_rt_readwrite_offset*9)/8); + } + + uint32_t l_flashAddr= l_info.flashAddr + i_offset; + + TRACFCOMP(g_trac_hbrt,"rt_pnor_read: calling" + " deviceRead: offset:0x%X, flashAddr:0x%X, size:0x%X", + i_offset, l_flashAddr, i_sizeBytes); + + l_err = DeviceFW::deviceRead (pnor_target, o_data, i_sizeBytes, + DEVICE_PNOR_ADDRESS(i_proc, l_flashAddr)); + if (l_err) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_read: deviceRead errored"); + break; + } + } while (0); + + //commit the error + if (l_err) + { + l_plid = l_err -> plid(); + errlCommit(l_err,CXXTEST_COMP_ID); + } + TRACFCOMP(g_trac_hbrt, EXIT_MRK"rt_pnor_read"); + return l_plid; + } + + + static int rt_pnor_write(uint32_t i_proc, const char* i_partitionName, + uint64_t i_offset, void* i_data, size_t i_sizeBytes) + { + TRACFCOMP(g_trac_hbrt, ENTER_MRK"rt_pnor_write: proc:%d, part:%s," + " offset:0x%X, dataPtr:0x%X, size:0x%X",i_proc, + i_partitionName, i_offset, i_data, i_sizeBytes); + + PNOR::SectionId l_id = PNOR::INVALID_SECTION; + PNOR::SectionInfo_t l_info; + errlHndl_t l_err = NULL; + uint32_t l_plid = 0; + do { + + TARGETING::Target* pnor_target = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + + //search cv_EYECATHCER for partitionname + l_id = find_sectionId(i_partitionName); + if (l_id == PNOR::INVALID_SECTION) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_write: Invalid section"); + break; + } + + //getSectionInfo - this is PnorRP::getSectionInfo + l_err = PNOR::getSectionInfo(l_id, l_info); + if (l_err) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_write: getSectionInfo errored"); + break; + } + + //fix the offset for the TEST section so that the testcases + //don't collide + i_offset = (PNOR::TEST) ? (i_offset+ + ((PNOR::pnorTestSec_rt_readwrite_offset*9)/8)):i_offset; + + uint32_t l_flashAddr = l_info.flashAddr + i_offset; + + TRACFCOMP(g_trac_hbrt,"rt_pnor_write: calling" + " deviceWrite: offset:0x%X, flashAddr:0x%X, size:0x%X", + i_offset, l_flashAddr, i_sizeBytes); + + l_err = DeviceFW::deviceWrite (pnor_target, i_data, i_sizeBytes, + DEVICE_PNOR_ADDRESS(i_proc, l_flashAddr)); + if (l_err) + { + TRACFCOMP(g_trac_hbrt, "rt_pnor_write: deviceWrite errored"); + break; + } + } while (0); + + //commit the error + if (l_err) + { + l_plid = l_err -> plid(); + errlCommit (l_err, CXXTEST_COMP_ID); + } + TRACFCOMP(g_trac_hbrt, EXIT_MRK"rt_pnor_write"); + return l_plid; + } + //-------------------------------------------------------------------- static uint64_t rt_get_vpd() { |