summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Crowell <dcrowell@us.ibm.com>2013-09-12 13:46:07 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2013-10-11 11:03:38 -0500
commitd5adce60c0cc910171c2938e581f187a2083cba7 (patch)
tree4f7e0470c1d8fd3808f1e47746c8732184b221f3 /src
parent6d19bd7f6660d4a0f739e883e2f5d0434419a135 (diff)
downloadtalos-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-xsrc/build/buildpnor/buildpnor.pl29
-rw-r--r--src/build/buildpnor/defaultPnorLayout.xml1
-rwxr-xr-xsrc/build/citest/autocitest16
-rwxr-xr-xsrc/build/mkrules/hbfw/img/makefile24
-rw-r--r--src/include/usr/pnor/pnor_reasoncodes.H2
-rw-r--r--src/usr/pnor/common/ffs_hb.H10
-rw-r--r--src/usr/pnor/pnordd.C5
-rw-r--r--src/usr/pnor/pnorrp.C180
-rw-r--r--src/usr/pnor/pnorrp.H54
-rw-r--r--src/usr/pnor/test/pnorrptest.H196
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();
OpenPOWER on IntegriCloud