diff options
author | Dan Crowell <dcrowell@us.ibm.com> | 2013-09-12 13:46:07 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2013-10-11 11:03:38 -0500 |
commit | d5adce60c0cc910171c2938e581f187a2083cba7 (patch) | |
tree | 4f7e0470c1d8fd3808f1e47746c8732184b221f3 /src | |
parent | 6d19bd7f6660d4a0f739e883e2f5d0434419a135 (diff) | |
download | talos-hostboot-d5adce60c0cc910171c2938e581f187a2083cba7.tar.gz talos-hostboot-d5adce60c0cc910171c2938e581f187a2083cba7.zip |
PNOR ECC Support
Adding ECC support to the PNOR Resource Provider as well as the
makefiles that create the images.
Also fixed a bug in the PNOR DD for writes across erase blocks.
Change-Id: I31ff6817cd35728badcd23a48fa73e51727142b9
RTC: 66213
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/6203
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Reviewed-by: Michael Baiocchi <baiocchi@us.ibm.com>
Tested-by: Jenkins Server
Reviewed-by: ADAM R. MUHLE <armuhle@us.ibm.com>
Diffstat (limited to 'src')
-rwxr-xr-x | src/build/buildpnor/buildpnor.pl | 29 | ||||
-rw-r--r-- | src/build/buildpnor/defaultPnorLayout.xml | 1 | ||||
-rwxr-xr-x | src/build/citest/autocitest | 16 | ||||
-rwxr-xr-x | src/build/mkrules/hbfw/img/makefile | 24 | ||||
-rw-r--r-- | src/include/usr/pnor/pnor_reasoncodes.H | 2 | ||||
-rw-r--r-- | src/usr/pnor/common/ffs_hb.H | 10 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.C | 5 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.C | 180 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.H | 54 | ||||
-rw-r--r-- | src/usr/pnor/test/pnorrptest.H | 196 |
10 files changed, 398 insertions, 119 deletions
diff --git a/src/build/buildpnor/buildpnor.pl b/src/build/buildpnor/buildpnor.pl index 3c7ded545..6b4e4a1dd 100755 --- a/src/build/buildpnor/buildpnor.pl +++ b/src/build/buildpnor/buildpnor.pl @@ -213,6 +213,8 @@ sub loadPnorLayout my $physicalOffset = $sectionEl->{physicalOffset}[0]; my $physicalRegionSize = $sectionEl->{physicalRegionSize}[0]; my $side = $sectionEl->{side}[0]; + my $testonly = $sectionEl->{testonly}[0]; + my $ecc = (exists $sectionEl->{ecc} ? "yes" : "no"); if (($emitTestSections == 0) && ($sectionEl->{testonly}[0] eq "yes")) { @@ -231,6 +233,7 @@ sub loadPnorLayout $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalOffset} = $physicalOffset; $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalRegionSize} = $physicalRegionSize; $$i_pnorLayoutRef{sections}{$physicalOffset}{side} = $side; + $$i_pnorLayoutRef{sections}{$physicalOffset}{ecc} = $ecc; } @@ -319,6 +322,7 @@ sub createPnorImage #Add Partition #f{fs,part} --add --target tuleta.pnor --partition-offset 0 --offset 0x1000 --size 0x280000 --name HBI --flags 0x0 if ($g_ffsCmd eq "") { + trace(1, "$g_fpartCmd --target $i_pnorBinName --partition-offset $sideAOffset --add --offset $physicalOffset --size $physicalRegionSize --name $eyeCatch --flags 0x0"); my $Out = `$g_fpartCmd --target $i_pnorBinName --partition-offset $sideAOffset --add --offset $physicalOffset --size $physicalRegionSize --name $eyeCatch --flags 0x0`; } else { my $Out = `$g_ffsCmd --target $i_pnorBinName --partition-offset $sideAOffset --add --offset $physicalOffset --size $physicalRegionSize --name $eyeCatch --flags 0x0`; @@ -330,6 +334,31 @@ sub createPnorImage last; } + #ECC flag + my $chip = 0; + my $compress = 0; + my $ecc = 0; + if( ($sectionHash{$key}{ecc} eq "yes") ) + { + $ecc = 0x8000; + }; + #[1:chip][1:compression][2:ecc] + my $userflags0 = ($chip << 16) + | ($compress << 8) + | $ecc; + trace(0,"userflags0 = $userflags0"); + if ($g_ffsCmd eq "") { + trace(1, "$g_fpartCmd --target $i_pnorBinName --partition-offset $sideAOffset --user 0 --name $eyeCatch --value $userflags0"); + my $Out = `$g_fpartCmd --target $i_pnorBinName --partition-offset $sideAOffset --user 0 --name $eyeCatch --value $userflags0`; + } + $rc = $?; + if($rc) + { + trace(0, "$this_func: Call to add userdata to $eyeCatch failed. rc=$rc. Aborting!"); + last; + } + + #Trunc Partition #f{fs,part} --target tuleta.pnor --partition-offset 0 --name HBI --trunc if ($g_ffsCmd eq "") { diff --git a/src/build/buildpnor/defaultPnorLayout.xml b/src/build/buildpnor/defaultPnorLayout.xml index 102b0fad3..58eafe112 100644 --- a/src/build/buildpnor/defaultPnorLayout.xml +++ b/src/build/buildpnor/defaultPnorLayout.xml @@ -108,6 +108,7 @@ Layout Description <physicalRegionSize>0x8000</physicalRegionSize> <side>A</side> <!-- Choices: A, B --> <testonly/> + <ecc/> </section> <section> <description>Hostboot Data (1M)</description> diff --git a/src/build/citest/autocitest b/src/build/citest/autocitest index a4f083893..d79737637 100755 --- a/src/build/citest/autocitest +++ b/src/build/citest/autocitest @@ -319,6 +319,8 @@ mods_started_addr=`grep "CxxTest::g_ModulesStarted" \ ${SBXHOME}/${HBICORE_TEST_SYMS} | awk '{split($0,a,","); print a[2]}'` mods_completed_addr=`grep "CxxTest::g_ModulesCompleted" \ ${SBXHOME}/${HBICORE_TEST_SYMS} | awk '{split($0,a,","); print a[2]}'` +shutdown_status=`grep "CpuManager::cv_shutdown_status" \ + ${SBXHOME}/${HBICORE_TEST_SYMS} | awk '{split($0,a,","); print a[2]}'` #Adjust addresses for HRMOR HRMOR=`autosim $NOWIN --simcmd "python \"getHRMOR()\""| tr -d '\n\r'` @@ -344,6 +346,9 @@ mods_started_addr=`echo "obase=16; $temp" | bc` temp=$(($HRMOR + 0x$mods_completed_addr)) mods_completed_addr=`echo "obase=16; $temp" | bc` +temp=$(($HRMOR + 0x$shutdown_status)) +shutdown_status=`echo "obase=16; $temp" | bc` + ## note, don't use $VERBOSE here or you get all sorts of extra junk in the output file. echo "Wait for unit test completion." declare -i timeout=0 @@ -368,6 +373,17 @@ while [ $(($modsstarted)) -lt 1 -o $(($modsstarted)) -ne $(($modscompleted)) ]; fi done +echo "====> waiting for shutdown..." +loopcount=0 +while [ "$loopcount" -lt 6 ]; do + autosim $NOWIN --simcmd "print ((system_cmp0.phys_mem).read 0x$shutdown_status 0x08)" 1> $SBXHOME/shutdown_status.log 2> /dev/null + shutdown_yet=`cat $SBXHOME/shutdown_status.log | awk '/0x/ {print strtonum($1)}'` + if [ "$shutdown_yet" -ne 0 ]; then + break + fi + sleep 5 + ((loopcount++)) # increment loopcount +done echo "====> dump totaltests..." autosim $NOWIN --simcmd "print ((system_cmp0.phys_mem).read 0x$totaltests_addr 0x08)" 1> $SBXHOME/totaltests.log 2> /dev/null diff --git a/src/build/mkrules/hbfw/img/makefile b/src/build/mkrules/hbfw/img/makefile index e6f4b39a0..dcaf0ea25 100755 --- a/src/build/mkrules/hbfw/img/makefile +++ b/src/build/mkrules/hbfw/img/makefile @@ -52,7 +52,16 @@ BASE_IMAGES = ${BASE_IMAGE} ${EXT_IMAGE} ${HBRT_IMAGE} BASE_W_HEADER_IMAGE = hostboot.header.bin BASE_W_HEADER_ECC_IMAGE = hostboot.header.bin.ecc BASE_ECC_IMAGE = hostboot.bin.ecc - +EXT_PAD_IMAGE = hostboot_extended.bin.pad +EXT_ECC_IMAGE = hostboot_extended.bin.ecc +TESTDATA = hbtestdata.bin +TESTDATA_ECC = hbtestdata.bin.ecc +ALL_HB_IMAGES = ${BASE_IMAGES} \ + ${BASE_W_HEADER_IMAGE} \ + ${BASE_ECC_IMAGE} ${EXT_ECC_IMAGE} \ + ${BASE_W_HEADER_ECC_IMAGE} \ + ${EXT_PAD_IMAGE} \ + ${TESTDATA} ${TESTDATA_ECC} cp_hbfiles: .SPECTARG ${BASE_IMAGES:@image@cp -f -u ${SRCPATH:F${image}} ${image};@} @@ -64,9 +73,15 @@ cp_hbfiles: .SPECTARG currentsb -chain ecc --inject ${BASE_IMAGE} --output ${BASE_ECC_IMAGE} --p8 ecc --inject ${BASE_W_HEADER_IMAGE} --output ${BASE_W_HEADER_ECC_IMAGE} --p8 + # dd command will pad image up to the next 4K page + dd if=${EXT_IMAGE} of=${EXT_PAD_IMAGE} ibs=4k count=1280 conv=sync + ecc --inject ${EXT_PAD_IMAGE} --output ${EXT_ECC_IMAGE} --p8 + # create data for a test partition in pnor + dd if=/dev/urandom of=${TESTDATA} bs=1 count=28K + ecc --inject ${TESTDATA} --output ${TESTDATA_ECC} --p8 clobber_cp_hbfiles: - rm -f ${BASE_IMAGES} ${BASE_W_HEADER_IMAGE} {BASE_W_HEADER_ECC_IMAGE} \ + rm -f ${ALL_HB_IMAGES} \ sbe.header secureboot.header hb.footer hostboot.stage.bin @@ -89,7 +104,10 @@ PNOR_BUILD_SCRIPT = ${buildpnor.pl:P} #so need to use tryinclude for now. .tryinclude <${.PATH:Ffips_pnor.mk}> -HOSTBOOT_DEFAULT_SECTIONS = HBB=${BASE_W_HEADER_ECC_IMAGE},HBI=${EXT_IMAGE},HBRT=${HBRT_IMAGE} +#uncomment the below line to enable ECC in the extended image +#HOSTBOOT_DEFAULT_SECTIONS = HBB=${BASE_W_HEADER_ECC_IMAGE},HBI=${EXT_ECC_IMAGE},HBRT=${HBRT_IMAGE},TEST=${TESTDATA_ECC} +HOSTBOOT_DEFAULT_SECTIONS = HBB=${BASE_W_HEADER_ECC_IMAGE},HBI=${EXT_IMAGE},HBRT=${HBRT_IMAGE},TEST=${TESTDATA_ECC} + HBFW_OBJPATH = ${.PATH:M*obj*} ENGD_OBJPATH = ${HBFW_OBJPATH:S/hbfw\/img/engd\/href/g} diff --git a/src/include/usr/pnor/pnor_reasoncodes.H b/src/include/usr/pnor/pnor_reasoncodes.H index 6e66a89e8..8b4225cb0 100644 --- a/src/include/usr/pnor/pnor_reasoncodes.H +++ b/src/include/usr/pnor/pnor_reasoncodes.H @@ -77,6 +77,8 @@ namespace PNOR //RC_UNSUPPORTED_SFCRANGE can only be used in // shutdown calls. RC_UNSUPPORTED_SFCRANGE = PNOR_COMP_ID | 0x0E, + // Cannot change value of RC_ECC_UE, used by FSP + RC_ECC_UE = PNOR_COMP_ID | 0x0F, }; }; diff --git a/src/usr/pnor/common/ffs_hb.H b/src/usr/pnor/common/ffs_hb.H index 6174c24ee..8ad50e90e 100644 --- a/src/usr/pnor/common/ffs_hb.H +++ b/src/usr/pnor/common/ffs_hb.H @@ -42,21 +42,27 @@ */ /** - * FFS Misc information flags + * FFS User Data flags */ enum { + /* Chip Select : 1 byte */ FFS_CHIPSEL_UNUSED = 0xFF, /**< Chip select not used */ + + /* Compression : 1 byte */ FFS_COMPRESS_UNUSED = 0xFF, /**< Compression not used */ + /* Data Integrity : 2 bytes */ FFS_INTEG_ECC_PROTECT = 0x8000, /**< Data Integrity: ECC protected */ FFS_INTEG_UNUSED = 0x1FFF, /**< Unused Data Integrity Bits */ + /* Version Checking : 1 byte */ FFS_VERS_SHA512 = 0x80, /**< SHA512 used for Version */ FFS_VERS_SHA512_PER_EC = 0x40, /**< SHA512 version per EC */ FFS_VERS_UNUSED = 0x3F, /**< Unused Version bits */ - FFS_MISC_PRESERVED = 0x80, /**< Preserved across code updates */ + /* Miscellaneous Bits : 1 byte */ + FFS_MISC_PRESERVED = 0x80, /**< Preserved across code updates */ FFS_MISC_UNUSED = 0x1F, /**< Unused MISC Flags */ }; diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C index ef4bdf68a..69ca4baf9 100644 --- a/src/usr/pnor/pnordd.C +++ b/src/usr/pnor/pnordd.C @@ -91,7 +91,7 @@ errlHndl_t ddRead(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); do{ - //@todo (RTC:34763) - add support for unaligned data + //@todo (RTC:36951) - add support for unaligned data // Ensure we are operating on a 32-bit (4-byte) boundary assert( reinterpret_cast<uint64_t>(io_buffer) % 4 == 0 ); assert( io_buflen % 4 == 0 ); @@ -142,7 +142,7 @@ errlHndl_t ddWrite(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); do{ - //@todo (RTC:34763) - add support for unaligned data + //@todo (RTC:36951) - add support for unaligned data // Ensure we are operating on a 32-bit (4-byte) boundary assert( reinterpret_cast<uint64_t>(io_buffer) % 4 == 0 ); assert( io_buflen % 4 == 0 ); @@ -1488,7 +1488,6 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, return l_err; } - /** * @brief Compare the existing data in 1 erase block of the flash with * the incoming data and write or erase as needed diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C index debc1bf7e..c13f4a306 100644 --- a/src/usr/pnor/pnorrp.C +++ b/src/usr/pnor/pnorrp.C @@ -36,10 +36,12 @@ #include "pnordd.H" #include "ffs.h" //Common header file with BuildingBlock. #include "common/ffs_hb.H" //Hostboot definition of user data in ffs_entry struct. +#include <pnor/ecc.H> +#include <kernel/console.H> // Trace definition trace_desc_t* g_trac_pnor = NULL; -TRAC_INIT(&g_trac_pnor, "PNOR", 2*KILOBYTE, TRACE::BUFFER_SLOW); //2K +TRAC_INIT(&g_trac_pnor, PNOR_COMP_NAME, 2*KILOBYTE, TRACE::BUFFER_SLOW); //2K // Easy macro replace for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) @@ -48,7 +50,7 @@ TRAC_INIT(&g_trac_pnor, "PNOR", 2*KILOBYTE, TRACE::BUFFER_SLOW); //2K /** * Eyecatcher strings for PNOR TOC entries */ -const char* cv_EYECATCHER[] = { //@todo - convert there to uint64_t +const char* cv_EYECATCHER[] = { "part", /**< PNOR::TOC : Table of Contents */ "HBI", /**< PNOR::HB_EXT_CODE : Hostboot Extended Image */ "GLOBAL", /**< PNOR::GLOBAL_DATA : Global Data */ @@ -104,7 +106,7 @@ void PnorRP::init( errlHndl_t &io_rtaskRetErrl ) if( Singleton<PnorRP>::instance().didStartupFail(rc) ) { - /*@ errorlog tag + /*@ * @errortype ERRL_SEV_CRITICAL_SYS_TERM * @moduleid PNOR::MOD_PNORRP_DIDSTARTUPFAIL * @reasoncode PNOR::RC_BAD_STARTUP_RC @@ -119,6 +121,7 @@ void PnorRP::init( errlHndl_t &io_rtaskRetErrl ) PNOR::RC_BAD_STARTUP_RC, rc, 0 ); + l_errl->collectTrace(PNOR_COMP_NAME); } io_rtaskRetErrl=l_errl; @@ -139,6 +142,18 @@ void* wait_for_message( void* unused ) return NULL; } +/** + * @brief Static function wrapper to call doShutdown + * to avoid deadlock in main task + */ +void* pnor_shutdown( void* unused ) +{ + TRACFCOMP(g_trac_pnor, "pnor_shutdown> " ); + printk( "PNOR errors causing shutdown\n" ); + INITSERVICE::doShutdown( PNOR::RC_ECC_UE ); + return NULL; +} + /******************** Private/Protected Methods @@ -150,6 +165,7 @@ void* wait_for_message( void* unused ) PnorRP::PnorRP() : iv_msgQ(NULL) ,iv_startupRC(0) +,iv_shutdownUE(false) { TRACFCOMP(g_trac_pnor, "PnorRP::PnorRP> " ); @@ -219,6 +235,7 @@ void PnorRP::initDaemon() PNOR::RC_INVALID_SECTION, TO_UINT64(BASE_VADDR), TO_UINT64(rc)); + l_errhdl->collectTrace(PNOR_COMP_NAME); break; } @@ -284,6 +301,7 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, PNOR::RC_STARTUP_FAIL, TO_UINT64(i_section), rc); + l_errhdl->collectTrace(PNOR_COMP_NAME); // set the return section to our invalid data id = PNOR::INVALID_SECTION; @@ -308,6 +326,7 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, PNOR::RC_INVALID_SECTION, TO_UINT64(i_section), TO_UINT64(side)); + l_errhdl->collectTrace(PNOR_COMP_NAME); // set the return section to our invalid data id = PNOR::INVALID_SECTION; @@ -325,7 +344,7 @@ errlHndl_t PnorRP::getSectionInfo( PNOR::SectionId i_section, o_info.name = cv_EYECATCHER[id]; o_info.vaddr = iv_TOC[side][id].virtAddr; o_info.size = iv_TOC[side][id].size; - o_info.eccProtected = (bool)(iv_TOC[side][id].miscFlags & + o_info.eccProtected = (bool)(iv_TOC[side][id].integrity & FFS_INTEG_ECC_PROTECT); } @@ -343,6 +362,7 @@ errlHndl_t PnorRP::readTOC() uint8_t* tocBuffer = NULL; #define SIDELESS_VADDR_INDEX 2 uint64_t nextVAddr[] = {SIDEA_VADDR, SIDEB_VADDR, SIDELESS_VADDR}; + bool fatal_error = false; do{ // Zero out my table @@ -354,11 +374,7 @@ errlHndl_t PnorRP::readTOC() { iv_TOC[side][id].id = (PNOR::SectionId)id; iv_TOC[side][id].side = (PNOR::SideSelect)side; - iv_TOC[side][id].chip = 0; - iv_TOC[side][id].flashAddr = 0; - iv_TOC[side][id].virtAddr = 0; - iv_TOC[side][id].size = 0; - iv_TOC[side][id].miscFlags = 0; + //everything else should default to zero } } @@ -369,7 +385,8 @@ errlHndl_t PnorRP::readTOC() // TOC starts at offset zero tocBuffer = new uint8_t[PAGESIZE]; - l_errhdl = readFromDevice( FFS_TABLE_BASE_ADDR, 0, false, tocBuffer ); + l_errhdl = readFromDevice( FFS_TABLE_BASE_ADDR, 0, false, + tocBuffer, fatal_error ); if( l_errhdl ) { break; } ffs_hdr* l_ffs_hdr = (ffs_hdr*) tocBuffer; @@ -461,7 +478,7 @@ errlHndl_t PnorRP::readTOC() continue; } - ffsUserData = (ffs_hb_user_t*)&cur_entry->user; + ffsUserData = (ffs_hb_user_t*)&(cur_entry->user); //size iv_TOC[cur_side][secId].size = ((uint64_t)cur_entry->size)*PAGESIZE; @@ -480,8 +497,10 @@ errlHndl_t PnorRP::readTOC() //chipSelect iv_TOC[cur_side][secId].chip = ffsUserData->chip; - //mics flags - iv_TOC[cur_side][secId].miscFlags = ffsUserData->miscFlags; + //user data + iv_TOC[cur_side][secId].integrity = ffsUserData->dataInteg; + iv_TOC[cur_side][secId].version = ffsUserData->verCheck; + iv_TOC[cur_side][secId].misc = ffsUserData->miscFlags; if((iv_TOC[cur_side][secId].flashAddr + iv_TOC[cur_side][secId].size) > (l_ffs_hdr->block_count*PAGESIZE)) { @@ -506,7 +525,7 @@ errlHndl_t PnorRP::readTOC() if(tocBuffer != NULL) { TRACUCOMP(g_trac_pnor, "Deleting tocBuffer"); - delete tocBuffer; + delete[] tocBuffer; } TRACUCOMP(g_trac_pnor, "< PnorRP::readTOC" ); @@ -531,6 +550,7 @@ void PnorRP::waitForMessage() bool needs_ecc = false; int rc = 0; uint64_t status_rc = 0; + bool fatal_error = false; while(1) { @@ -556,8 +576,18 @@ void PnorRP::waitForMessage() switch(message->type) { case( MSG_MM_RP_READ ): - l_errhdl = readFromDevice( dev_offset, chip_select, needs_ecc, user_addr ); - if( l_errhdl ) + // do not allow reads in the shutdown path, only writes + if( iv_shutdownUE ) + { + status_rc = -EIO; + break; + } + l_errhdl = readFromDevice( dev_offset, + chip_select, + needs_ecc, + user_addr, + fatal_error ); + if( l_errhdl || fatal_error ) { status_rc = -EIO; /* I/O error */ } @@ -584,6 +614,7 @@ void PnorRP::waitForMessage() PNOR::RC_INVALID_MESSAGE_TYPE, TO_UINT64(message->type), (uint64_t)eff_addr); + l_errhdl->collectTrace(PNOR_COMP_NAME); status_rc = -EINVAL; /* Invalid argument */ } } @@ -604,6 +635,7 @@ void PnorRP::waitForMessage() PNOR::RC_INVALID_ASYNC_MESSAGE, TO_UINT64(message->type), (uint64_t)eff_addr); + l_errhdl->collectTrace(PNOR_COMP_NAME); status_rc = -EINVAL; /* Invalid argument */ } @@ -638,11 +670,13 @@ void PnorRP::waitForMessage() errlHndl_t PnorRP::readFromDevice( uint64_t i_offset, uint64_t i_chip, bool i_ecc, - void* o_dest ) + void* o_dest, + bool& o_fatalError ) { TRACUCOMP(g_trac_pnor, "PnorRP::readFromDevice> i_offset=0x%X, i_chip=%d", i_offset, i_chip ); errlHndl_t l_errhdl = NULL; uint8_t* ecc_buffer = NULL; + o_fatalError = false; do { @@ -655,7 +689,7 @@ errlHndl_t PnorRP::readFromDevice( uint64_t i_offset, // if we need to handle ECC we need to read more than 1 page if( i_ecc ) { - ecc_buffer = new uint8_t[PAGESIZE_PLUS_ECC]; + ecc_buffer = new uint8_t[PAGESIZE_PLUS_ECC](); data_to_read = ecc_buffer; read_size = PAGESIZE_PLUS_ECC; } @@ -674,10 +708,49 @@ errlHndl_t PnorRP::readFromDevice( uint64_t i_offset, // remove the ECC data if( i_ecc ) { - l_errhdl = stripECC( data_to_read, o_dest ); - if( l_errhdl ) + // remove the ECC and fix the original data if it is broken + PNOR::ECC::eccStatus ecc_stat = + PNOR::ECC::removeECC( reinterpret_cast<uint8_t*>(data_to_read), + reinterpret_cast<uint8_t*>(o_dest), + PAGESIZE ); + + // create an error if we couldn't correct things + if( ecc_stat == PNOR::ECC::UNCORRECTABLE ) { - break; + TRACFCOMP( g_trac_pnor, "PnorRP::readFromDevice> Uncorrectable ECC error : chip=%d, offset=0x%.X", i_chip, i_offset ); + + // Need to shutdown here instead of creating an error log + // because the bad page could be critical to the regular + // error handling path and cause an infinite loop. + // Also need to spawn a separate task to do the shutdown + // so that the regular PNOR task can service the writes + // that happen during shutdown. + iv_shutdownUE = true; + o_fatalError = true; + task_create( pnor_shutdown, NULL ); + } + // found an error so we need to fix something + else if( ecc_stat != PNOR::ECC::CLEAN ) + { + TRACFCOMP( g_trac_pnor, "PnorRP::readFromDevice> Correctable ECC error : chip=%d, offset=0x%.X", i_chip, i_offset ); + + // need to write good data back to PNOR + l_errhdl = DeviceFW::deviceWrite(pnor_target, + data_to_read,//corrected data + read_size, + DEVICE_PNOR_ADDRESS(i_chip,i_offset) ); + if( l_errhdl ) + { + TRACFCOMP(g_trac_pnor, "PnorRP::readFromDevice> Error writing corrected data back to device : RC=%X", l_errhdl->reasonCode() ); + // we don't need to fail here since we can correct + // it the next time we read it again, instead just + // commit the log here + errlCommit(l_errhdl,PNOR_COMP_ID); + } + + // keep some stats here in case we want them someday + //no need for mutex since only ever 1 thread accessing this + iv_stats[i_offset/PAGESIZE].numCEs++; } } } while(0); @@ -714,17 +787,22 @@ errlHndl_t PnorRP::writeToDevice( uint64_t i_offset, // apply ECC to data if needed if( i_ecc ) { - ecc_buffer = new uint8_t[PAGESIZE]; - applyECC( i_src, ecc_buffer ); - data_to_write = (void*)ecc_buffer; + ecc_buffer = new uint8_t[PAGESIZE_PLUS_ECC]; + PNOR::ECC::injectECC( reinterpret_cast<uint8_t*>(i_src), + PAGESIZE, + reinterpret_cast<uint8_t*>(ecc_buffer) ); + data_to_write = reinterpret_cast<void*>(ecc_buffer); write_size = PAGESIZE_PLUS_ECC; } + //no need for mutex since only ever a singleton object + iv_stats[i_offset/PAGESIZE].numWrites++; + // write the data out to the PNOR DD - errlHndl_t l_errhdl = DeviceFW::deviceWrite(pnor_target, - data_to_write, - write_size, - DEVICE_PNOR_ADDRESS(i_chip,i_offset) ); + errlHndl_t l_errhdl = DeviceFW::deviceWrite( pnor_target, + data_to_write, + write_size, + DEVICE_PNOR_ADDRESS(i_chip,i_offset) ); if( l_errhdl ) { TRACFCOMP(g_trac_pnor, "PnorRP::writeToDevice> Error from device : RC=%X", l_errhdl->reasonCode() ); @@ -773,6 +851,7 @@ errlHndl_t PnorRP::computeDeviceAddr( void* i_vaddr, PNOR::RC_INVALID_ADDRESS, l_vaddr, BASE_VADDR); + l_errhdl->collectTrace(PNOR_COMP_NAME); return l_errhdl; } @@ -787,44 +866,22 @@ errlHndl_t PnorRP::computeDeviceAddr( void* i_vaddr, // pull out the information we need to return from our global copy o_chip = iv_TOC[side][id].chip; - o_ecc = (bool)(iv_TOC[side][id].miscFlags & FFS_INTEG_ECC_PROTECT); - o_offset = l_vaddr - iv_TOC[side][id].virtAddr; //offset into pnor + o_ecc = (bool)(iv_TOC[side][id].integrity & FFS_INTEG_ECC_PROTECT); + o_offset = l_vaddr - iv_TOC[side][id].virtAddr; //offset into section + + // for ECC we need to figure out where the ECC-enhanced offset is + // before tacking on the offset to the section + if( o_ecc ) + { + o_offset = (o_offset * 9) / 8; + } + // add on the offset of the section itself o_offset += iv_TOC[side][id].flashAddr; - TRACUCOMP( g_trac_pnor, "< PnorRP::computeDeviceAddr: o_offset=0x%X, o_chip=%d", o_offset, o_chip ); + TRACUCOMP( g_trac_pnor, "< PnorRP::computeDeviceAddr: i_vaddr=%X, o_offset=0x%X, o_chip=%d", l_vaddr, o_offset, o_chip ); return l_errhdl; } - -/** - * @brief Apply ECC algorithm to data, assumes size of 1 page - */ -void PnorRP::applyECC( void* i_orig, - void* o_ecc ) -{ - TRACFCOMP(g_trac_pnor, "> PnorRP::applyECC" ); - - //@todo - fill this in (Story 34763) - memcpy( o_ecc, i_orig, PAGESIZE ); - - TRACFCOMP(g_trac_pnor, "< PnorRP::applyECC" ); -} - -/** - * @brief Apply ECC algorithm to data, assumes logical size of 1 page - */ -errlHndl_t PnorRP::stripECC( void* i_orig, - void* o_data ) -{ - TRACFCOMP(g_trac_pnor, "> PnorRP::stripECC" ); - - //@todo - fill this in (Story 34763) - memcpy( o_data, i_orig, PAGESIZE ); - - TRACFCOMP(g_trac_pnor, "< PnorRP::stripECC" ); - return NULL; -} - /** * @brief Static instance function for testcase only */ @@ -898,6 +955,7 @@ errlHndl_t PnorRP::computeSection( uint64_t i_vaddr, PNOR::RC_INVALID_ADDRESS, i_vaddr, 0); + errhdl->collectTrace(PNOR_COMP_NAME); return errhdl; } diff --git a/src/usr/pnor/pnorrp.H b/src/usr/pnor/pnorrp.H index bc15249d6..d246360b0 100644 --- a/src/usr/pnor/pnorrp.H +++ b/src/usr/pnor/pnorrp.H @@ -5,7 +5,7 @@ /* */ /* IBM CONFIDENTIAL */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2012 */ +/* COPYRIGHT International Business Machines Corp. 2011,2013 */ /* */ /* p1 */ /* */ @@ -29,6 +29,7 @@ #include <builtins.h> #include <errl/errlentry.H> #include <vmmconst.h> +#include <map> /** * PNOR Resource Provider @@ -90,7 +91,7 @@ class PnorRP 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), // 8 bytes of data + 1 byte of ECC + 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 */ @@ -106,10 +107,20 @@ class PnorRP 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 miscFlags; /**<chip select, ECC Protect, sideless flags */ + uint8_t version; /**< Version Checking */ + uint16_t integrity; /**< Data Integrity */ + uint8_t misc; /**< Misc Flags */ } PACKED; /** + * Flash statistics + */ + struct FlashStats_t { + uint8_t numWrites; /**< Number of writes to this page */ + uint8_t numCEs; /**< Number of ECC corrections made */ + }; + + /** * Cached copy of section data */ SectionData_t iv_TOC[NUM_SIDES][PNOR::NUM_SECTIONS+1]; @@ -132,6 +143,18 @@ class PnorRP uint64_t iv_startupRC; /** + * Track some information related to flash wear and health + * indexed by physical page number inside PNOR + */ + std::map<uint64_t,FlashStats_t> iv_stats; + + /** + * Remember that we got a UE and we are shutting down + */ + bool iv_shutdownUE; + + + /** * @brief Initialize the daemon, called by constructor */ void initDaemon(); @@ -155,13 +178,16 @@ class PnorRP * @param[in] i_chip Which PNOR chip * @param[in] i_ecc true=apply ECC after reading * @param[out] o_dest Buffer to copy data into + * @param[out] o_fatalError true=fatal error encountered, but no + * log could be created * * @return Error from device */ errlHndl_t readFromDevice( uint64_t i_offset, uint64_t i_chip, bool i_ecc, - void* o_dest ); + void* o_dest, + bool& o_fatalError ); /** * @brief Write 1 logical page of data to the PNOR device @@ -207,26 +233,6 @@ class PnorRP PNOR::SectionId& o_id ); /** - * @brief Apply ECC algorithm to data - * - * @param[in] i_orig Original data to write - * @param[in] o_ecc Data after applying ECC - */ - void applyECC( void* i_orig, - void* o_ecc ); - - /** - * @brief Apply ECC algorithm to data, assumes size of 1 page - * - * @param[in] i_orig Original data that was read - * @param[in] o_data Data after removing ECC - * - * @return Error if ECC is incorrect - */ - errlHndl_t stripECC( void* i_orig, - void* o_data ); - - /** * @brief Returns true if the initial startup failed for some reason * @param[out] Return code * @return true if startup failed diff --git a/src/usr/pnor/test/pnorrptest.H b/src/usr/pnor/test/pnorrptest.H index 00f7bc2eb..d48ec491c 100644 --- a/src/usr/pnor/test/pnorrptest.H +++ b/src/usr/pnor/test/pnorrptest.H @@ -1,26 +1,25 @@ -/* IBM_PROLOG_BEGIN_TAG - * This is an automatically generated prolog. - * - * $Source: src/usr/pnor/test/pnorrptest.H $ - * - * IBM CONFIDENTIAL - * - * COPYRIGHT International Business Machines Corp. 2011-2012 - * - * p1 - * - * Object Code Only (OCO) source materials - * Licensed Internal Code Source Materials - * IBM HostBoot Licensed Internal Code - * - * The source code for this program is not published or other- - * wise divested of its trade secrets, irrespective of what has - * been deposited with the U.S. Copyright Office. - * - * Origin: 30 - * - * IBM_PROLOG_END_TAG - */ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/test/pnorrptest.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2011,2013 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ #ifndef __PNORRPTEST_H #define __PNORRPTEST_H @@ -36,6 +35,10 @@ #include <pnor/pnorif.H> #include <sys/msg.h> #include <limits.h> +#include <sys/mm.h> +#include <targeting/common/targetservice.H> +#include <devicefw/userif.H> +#include <pnor/ecc.H> #include "../pnorrp.H" extern trace_desc_t* g_trac_pnor; @@ -79,7 +82,7 @@ class PnorRpTest : public CxxTest::TestSuite testSections[idx], errhdl->reasonCode() ); TS_FAIL( "PnorRpTest::test_getSectionInfo> ERROR : Unexpected error log" ); fails++; - errlCommit(errhdl,PNOR_COMP_ID); + ERRORLOG::errlCommit(errhdl,PNOR_COMP_ID); } // Look for non-zero size @@ -108,6 +111,147 @@ class PnorRpTest : public CxxTest::TestSuite }; /** + * @brief PNOR RP test - ECC + * Verify ECC errors are handled + */ + void test_ECC(void) + { + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_ECC> Start" ); + uint64_t fails = 0; + uint64_t total = 0; + errlHndl_t errhdl = NULL; + + const uint64_t FIRST_VAL = 0x1122334455667788; + const size_t ECC_PAGESIZE = (PAGESIZE*9)/8; + const uint64_t LOG_OFFSET = PAGESIZE/4; //force a page crossing + const uint64_t PART_OFFSET = PAGESIZE*2 + LOG_OFFSET; + const uint64_t TEST_PHYS_OFFSET = 0x3590000 //see defaultPnorLayout.xml + + (ECC_PAGESIZE*2) //matches PART_OFFSET + + (LOG_OFFSET*9)/8; + + // use the TEST partition as scratch space + PNOR::SectionInfo_t info; + errhdl = PNOR::getSectionInfo( PNOR::TEST, PNOR::SIDE_A, info ); + if( errhdl ) + { + TRACFCOMP( g_trac_pnor, "PnorRpTest::test_ECC> ERROR : getSectionInfo returned error for PNOR::TEST : RC=%X", errhdl->reasonCode() ); + TS_FAIL( "PnorRpTest::test_ECC> ERROR : Unexpected error log" ); + ERRORLOG::errlCommit(errhdl,PNOR_COMP_ID); + return; + } + + // Use the 3rd page of data + uint64_t* dataptr = reinterpret_cast<uint64_t*> + (info.vaddr+PART_OFFSET); + + // read some data + uint64_t* tmp1 = new uint64_t[PAGESIZE/sizeof(uint64_t)+1]; + for( size_t i = 0; i < PAGESIZE/sizeof(uint64_t); i++ ) + { + tmp1[i] = dataptr[i]; + } + + // write some data + for( size_t i = 0; i < PAGESIZE/sizeof(uint64_t); i++ ) + { + dataptr[i] = FIRST_VAL+i; + } + + // flush the page to make sure it gets out to the device + int rc = mm_remove_pages( RELEASE, dataptr, PAGESIZE ); + total++; + if( rc ) + { + TRACFCOMP( g_trac_pnor, "PnorRpTest::test_ECC> ERROR : error on RELEASE : rc=%X", rc ); + TS_FAIL( "PnorRpTest::test_ECC> ERROR : error on RELEASE" ); + fails++; + } + + // manually read the data from the PNOR device + uint8_t* chip_data = new uint8_t[ECC_PAGESIZE]; + size_t l_size = ECC_PAGESIZE; + errhdl = deviceRead(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + chip_data, + l_size, + DEVICE_PNOR_ADDRESS(0, TEST_PHYS_OFFSET)); + total++; + if( errhdl ) + { + TS_FAIL("PnorRpTest::test_ECC: PNORDD deviceRead() failed! Error committed."); + ERRORLOG::errlCommit(errhdl,PNOR_COMP_ID); + fails++; + } + + // compare the ECC-stripped data from the driver with the + // data we wrote + uint64_t data_ecc = 0; + uint64_t data_noecc = 0; + uint8_t* chip_data_ptr = chip_data; + PNOR::ECC::eccStatus ecc_rc = PNOR::ECC::CLEAN; + for( size_t i = FIRST_VAL; i < PAGESIZE/sizeof(uint64_t); i++ ) + { + memcpy( &data_ecc, chip_data_ptr, sizeof(uint64_t) ); + uint8_t ecc_byte = chip_data_ptr[8]; + ecc_rc = PNOR::ECC::removeECC( chip_data_ptr, + reinterpret_cast<uint8_t*>(&data_noecc), + sizeof(uint64_t) ); + total++; + if( ecc_rc != PNOR::ECC::CLEAN ) + { + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_ECC: Error removing ECC from word %d : status=%d, orig=%.16X:%.2X", i, ecc_rc, data_ecc, ecc_byte) + TS_FAIL("PnorRpTest::test_ECC: Data mismatch."); + fails++; + } + + total++; + if( data_noecc != FIRST_VAL + i ) + { + TRACFCOMP( g_trac_pnor, "PnorRpTest::test_ECC: Data mismatch on word %d : exp=%.16X, act=%.16X, orig=%.16X:%.2X", i, FIRST_VAL+i, data_noecc, data_ecc, ecc_byte); + TS_FAIL("PnorRpTest::test_ECC: Data mismatch."); + fails++; + } + chip_data_ptr += 9; + } + + // generate data with CEs + chip_data_ptr = chip_data; + for (int i = 0; i < 9; i++) + { + memcpy( &data_ecc, chip_data_ptr, sizeof(uint64_t) ); + uint64_t bad_data = data_ecc ^ (1ul << (63 - i*5)); + memcpy( chip_data_ptr, &bad_data, sizeof(uint64_t) ); + chip_data_ptr += 9; + } + + // write the bad data to the chip directly + errhdl = deviceWrite(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + chip_data, + l_size, + DEVICE_PNOR_ADDRESS(0, TEST_PHYS_OFFSET)); + total++; + if( errhdl ) + { + TS_FAIL("PnorRpTest::test_ECC: PNORDD deviceWrite() failed! Error committed."); + ERRORLOG::errlCommit(errhdl,PNOR_COMP_ID); + fails++; + } + + // read the same data through the RP, ECC errors should be corrected + for( size_t i = i; i < PAGESIZE/sizeof(uint64_t); i++ ) + { + total++; + if( dataptr[i] != FIRST_VAL+i ) + { + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_ECC: Mismatch on readback from RP on word %d : exp=%.16X, act=%.16X", i, FIRST_VAL+i, dataptr[i] ); + TS_FAIL("PnorRpTest::test_ECC: Mismatch on readback from RP."); + fails++; + } + } + + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_ECC> %d/%d fails", fails, total ); + }; + + /** * @brief PNOR RP test - Read/Write Page * Use message interface to read and write individual pages */ @@ -127,9 +271,9 @@ class PnorRpTest : public CxxTest::TestSuite uint64_t data2_r[PAGESIZE/sizeof(uint64_t)]; uint64_t data_tmp[PAGESIZE/sizeof(uint64_t)]; - // use the HB_DATA as scratch space + // use the TEST partition as scratch space PNOR::SectionInfo_t info; - PNOR::getSectionInfo( PNOR::HB_DATA, PNOR::SIDE_A, info ); + PNOR::getSectionInfo( PNOR::TEST, PNOR::SIDE_A, info ); msg_t* msg = msg_allocate(); |