diff options
author | Patrick Williams <iawillia@us.ibm.com> | 2013-05-20 12:03:29 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2013-06-06 11:47:27 -0500 |
commit | 5623531a2f9efa10450a5e1e3b1eb6bd4a998357 (patch) | |
tree | 23225e612af65ec60cf3a324df22111bb5c13eef | |
parent | 66474f4f93fd10ad3b35ed89cecc3d6b11e99eed (diff) | |
download | talos-hostboot-5623531a2f9efa10450a5e1e3b1eb6bd4a998357.tar.gz talos-hostboot-5623531a2f9efa10450a5e1e3b1eb6bd4a998357.zip |
Tolerate memory UEs during dump extraction.
Change-Id: I0dc57ec047beb47b557b816162d619a5b2a54108
RTC: 64619
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/4600
Tested-by: Jenkins Server
Reviewed-by: ADAM R. MUHLE <armuhle@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
-rw-r--r-- | src/include/kernel/machchk.H | 42 | ||||
-rw-r--r-- | src/include/kernel/task.H | 48 | ||||
-rw-r--r-- | src/include/sys/mm.h | 22 | ||||
-rw-r--r-- | src/kernel/exception.C | 82 | ||||
-rw-r--r-- | src/kernel/machchk.C | 118 | ||||
-rw-r--r-- | src/kernel/makefile | 2 | ||||
-rw-r--r-- | src/lib/syscall_mm.C | 26 | ||||
-rw-r--r-- | src/makefile | 2 | ||||
-rw-r--r-- | src/usr/dump/dumpCollect.C | 42 |
9 files changed, 269 insertions, 115 deletions
diff --git a/src/include/kernel/machchk.H b/src/include/kernel/machchk.H new file mode 100644 index 000000000..51cd14acc --- /dev/null +++ b/src/include/kernel/machchk.H @@ -0,0 +1,42 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/kernel/machchk.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 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 __KERNEL_MACHCHK_H +#define __KERNEL_MACHCHK_H + +#include <stdint.h> +#include <kernel/task.H> + +namespace Kernel +{ + namespace MachineCheck + { + /** Handles memory UE machine checks. + * + * @param[in] t - Task which took the machine check. + * @return bool - True if MC successfully handled, false otherwise. + */ + bool handleLoadUE(task_t* t); + } +} + +#endif diff --git a/src/include/kernel/task.H b/src/include/kernel/task.H index d41172bd9..a4b9128e5 100644 --- a/src/include/kernel/task.H +++ b/src/include/kernel/task.H @@ -1,26 +1,25 @@ -/* IBM_PROLOG_BEGIN_TAG - * This is an automatically generated prolog. - * - * $Source: src/include/kernel/task.H $ - * - * IBM CONFIDENTIAL - * - * COPYRIGHT International Business Machines Corp. 2010-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/include/kernel/task.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2010,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 */ /** @file task.H * @brief Defines kernel information about tasks. */ @@ -115,6 +114,9 @@ struct task_t /** Detached state of the task. */ bool detached; + /** Determine if the task should tolerate memory UEs. */ + bool tolerate_ue; + // Pointers for queue containers. task_t* prev; task_t* next; diff --git a/src/include/sys/mm.h b/src/include/sys/mm.h index f3488b3d8..d71d5a1b3 100644 --- a/src/include/sys/mm.h +++ b/src/include/sys/mm.h @@ -5,7 +5,7 @@ /* */ /* IBM CONFIDENTIAL */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2012 */ +/* COPYRIGHT International Business Machines Corp. 2011,2013 */ /* */ /* p1 */ /* */ @@ -141,6 +141,26 @@ void mm_icache_invalidate(void * i_addr, size_t i_cpu_word_count); */ uint64_t mm_virt_to_phys( void* i_vaddr ); +/** @fn mm_tolerate_ue + * @brief Allows a task to tolerate a memory UE without being killed. + * + * This should be used when copying content from memory of unknown quality, + * such as during a memory preserved IPL. When a memory UE is consumed by + * the task, instead of killing the task, the kernel will replace the + * memory access by a special value of '0xdeadd47a'. + * + * @param[in] i_state - Should the task tolerate memory UEs: 0 = no, 1 = yes. + * + * Example usage: + * mm_tolerate_ue(1); + * memcpy(src, dest, size); + * mm_tolerate_ue(0); + */ +void mm_tolerate_ue(uint64_t i_state); +/** Value inserted into process when it encounters a UE, if mm_tolerate_ue is + * enabled. */ +enum { MM_UE_MAGIC_VALUE = 0xdeadd47adeadd47aull }; + #ifdef __cplusplus } #endif diff --git a/src/kernel/exception.C b/src/kernel/exception.C index ba2d803b5..47d8a3c01 100644 --- a/src/kernel/exception.C +++ b/src/kernel/exception.C @@ -31,6 +31,7 @@ #include <kernel/intmsghandler.H> #include <errno.h> #include <kernel/vmmmgr.H> +#include <kernel/machchk.H> namespace ExceptionHandles { @@ -235,12 +236,7 @@ void kernel_execute_softpatch() const uint64_t EXCEPTION_MSR_PR_BIT_MASK = 0x0000000000004000; const uint64_t EXCEPTION_SRR1_LOADSTORE_ERR = 0x0000000000200000; -const uint64_t EXCEPTION_DSISR_LDX_UE_INTERRUPT = 0x0000000000008000; -const uint32_t EXCEPTION_LDX_INSTR_MASK = 0xFC0007FE; -const uint32_t EXCEPTION_LDX_INSTR = 0x7C00002A; -const uint32_t EXCEPTION_RA_MASK = 0x001F0000; -const uint32_t EXCEPTION_RB_MASK = 0x0000F800; -const uint32_t EXCEPTION_RT_MASK = 0x03E00000; +const uint64_t EXCEPTION_DSISR_LD_UE_INTERRUPT = 0x0000000000008000; extern "C" void kernel_execute_machine_check() @@ -260,75 +256,27 @@ void kernel_execute_machine_check() kassert(false); } - //User Space MC - printk("User Space Machine check in %d on %ld:\n" - "\tSRR0 = %lx, SRR1 = %lx\n" - "\tDSISR = %lx, DAR = %lx\n", - t->tid, getPIR(), - getSRR0(), getSRR1(), getDSISR(), getDAR()); - - - //Determine if this Machine Check was triggered by a SUE on - //CI (Cache Inhibited) Load. - if(!((getSRR1() & EXCEPTION_SRR1_LOADSTORE_ERR) && - (getDSISR() & EXCEPTION_DSISR_LDX_UE_INTERRUPT))) - { - //SUE not caused by CI Load, unhandled exception. - TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED); - } - - //Get instruction that caused the SUE - uint64_t phys_addr = VmmManager::findKernelAddress( - reinterpret_cast<uint64_t>(t->context.nip)); - - sync(); - uint32_t* instruction = reinterpret_cast<uint32_t*>(phys_addr); - - if(!((*instruction & EXCEPTION_LDX_INSTR_MASK) == - EXCEPTION_LDX_INSTR)) - { - //Not an LDX instruction, unhandled exception - printk("kernel_execute_machine_check: Instruction 0x%.8x not an LDX instruction. Ending task\n", *instruction); - TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED); - - } - - //Compute the accessed address - uint32_t rA = (*instruction & EXCEPTION_RA_MASK) >> 16; - uint32_t rB = (*instruction & EXCEPTION_RB_MASK) >> 11; + bool handled = false; - uint64_t vaddr = 0; - if(rA != 0) + // SUE on load instruction. + if ((getSRR1() & EXCEPTION_SRR1_LOADSTORE_ERR) && + (getDSISR() & EXCEPTION_DSISR_LD_UE_INTERRUPT)) { - vaddr = t->context.gprs[rA] + - t->context.gprs[rB]; + handled = Kernel::MachineCheck::handleLoadUE(t); } else { - vaddr = t->context.gprs[rB]; - } - uint64_t phys = VmmManager::findPhysicalAddress(vaddr); - - //Check if address is in IBSCOM MMIO Range. - if( (phys >= MMIO_IBSCOM_START) && - (phys <= MMIO_IBSCOM_END) ) - { - //Read occured during IBSCOM read. Indicate error to caller - //by setting known pattern (ascii SCOMFAIL) in rT - uint32_t rT = (*instruction & EXCEPTION_RT_MASK) >> 21; - - //set rT = MMIO_IBSCOM_UE_DETECTED - t->context.gprs[rT] = MMIO_IBSCOM_UE_DETECTED; - //Advance to next instruction - uint32_t* nextInst = reinterpret_cast<uint32_t*>(t->context.nip); - nextInst++; - t->context.nip = reinterpret_cast<void*>(nextInst); } - else + + if (!handled) { - printk("kernel_execute_machine_check: Unrecognized memory address(%lx) - ending Task\n", - phys); + //User Space MC + printk("User Space Machine check in %d on %ld:\n" + "\tSRR0 = %lx, SRR1 = %lx\n" + "\tDSISR = %lx, DAR = %lx\n", + t->tid, getPIR(), + getSRR0(), getSRR1(), getDSISR(), getDAR()); TaskManager::endTask(t, NULL, TASK_STATUS_CRASHED); } } diff --git a/src/kernel/machchk.C b/src/kernel/machchk.C new file mode 100644 index 000000000..746c77c83 --- /dev/null +++ b/src/kernel/machchk.C @@ -0,0 +1,118 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/kernel/machchk.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 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 */ +#include <kernel/machchk.H> +#include <kernel/console.H> +#include <kernel/vmmmgr.H> +#include <sys/mmio.h> + +namespace Kernel +{ +namespace MachineCheck +{ + +bool handleLoadUE(task_t* t) +{ + bool handled = false; + + static const uint32_t LDX_INSTR_MASK = 0xFC0007FE; + static const uint32_t LDX_INSTR = 0x7C00002A; + static const uint32_t LDX_RA_MASK = 0x001F0000; + static const uint32_t LDX_RB_MASK = 0x0000F800; + static const uint32_t LDX_RT_MASK = 0x03E00000; + + do + { + //Get instruction that caused the SUE + uint64_t physInstAddr = VmmManager::findKernelAddress( + reinterpret_cast<uint64_t>(t->context.nip)); + uint32_t* instruction = reinterpret_cast<uint32_t*>(physInstAddr); + + if((*instruction & LDX_INSTR_MASK) != LDX_INSTR) + { + //Not an LDX instruction, unhandled exception + printk("MachineCheck::handleUE: Instruction 0x%.8x not handled.\n", + *instruction); + break; + } + + uint64_t ueMagicValue = 0x0; + + // If task is tolerating UEs, replace with "Dead Data". + if (t->tolerate_ue) + { + ueMagicValue = MM_UE_MAGIC_VALUE; + } + // Otherwise, check for specific MMIO addresses. + else + { + //Compute the accessed address + uint32_t rA = (*instruction & LDX_RA_MASK) >> 16; + uint32_t rB = (*instruction & LDX_RB_MASK) >> 11; + + uint64_t vaddr = 0; + if(rA != 0) + { + vaddr = t->context.gprs[rA] + + t->context.gprs[rB]; + } + else + { + vaddr = t->context.gprs[rB]; + } + + uint64_t phys = VmmManager::findPhysicalAddress(vaddr); + + //Check if address is in IBSCOM MMIO Range. + if((phys >= MMIO_IBSCOM_START) && + (phys <= MMIO_IBSCOM_END)) + { + ueMagicValue = MMIO_IBSCOM_UE_DETECTED; + } + else + { + printk("MachineCheck::handleUE: Unrecognized address %lx\n", + phys); + break; + } + } + + // Write pattern into result register. + uint32_t rT = (*instruction & LDX_RT_MASK) >> 21; + t->context.gprs[rT] = ueMagicValue; + + // Advance to next instruction. + uint32_t* nextInst = reinterpret_cast<uint32_t*>(t->context.nip); + nextInst++; + t->context.nip = reinterpret_cast<void*>(nextInst); + + // Successfully handled. + handled = true; + + } while (false); + + return handled; +} + + +} +} diff --git a/src/kernel/makefile b/src/kernel/makefile index 3627798a6..fd1f8ae51 100644 --- a/src/kernel/makefile +++ b/src/kernel/makefile @@ -27,7 +27,7 @@ OBJS += syscall.o scheduler.o spinlock.o exception.o vmmmgr.o timemgr.o OBJS += futexmgr.o ptmgr.o segmentmgr.o devicesegment.o basesegment.o OBJS += block.o cpuid.o misc.o msghandler.o blockmsghdlr.o stacksegment.o OBJS += softpatch_p8.o barrier.o idebug.o intmsghandler.o deferred.o -OBJS += shutdown.o forceattn_p8.o terminate.o ipc.o +OBJS += shutdown.o forceattn_p8.o terminate.o ipc.o machchk.o include ${ROOTPATH}/config.mk diff --git a/src/lib/syscall_mm.C b/src/lib/syscall_mm.C index 5bb85a4fb..98a4aa0a6 100644 --- a/src/lib/syscall_mm.C +++ b/src/lib/syscall_mm.C @@ -5,7 +5,7 @@ /* */ /* IBM CONFIDENTIAL */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2012 */ +/* COPYRIGHT International Business Machines Corp. 2011,2013 */ /* */ /* p1 */ /* */ @@ -24,7 +24,7 @@ #include <sys/mm.h> #include <arch/ppc.H> #include <kernel/vmmmgr.H> - +#include <kernel/task.H> using namespace Systemcalls; @@ -108,3 +108,25 @@ int mm_linear_map(void *i_paddr, uint64_t i_size) { return (int64_t)_syscall2(MM_LINEAR_MAP, i_paddr, (void*)i_size); } + +/** + * mm_tolerate_ue. Update task state and do appropriate synchronization. + */ +void mm_tolerate_ue(uint64_t i_state) +{ + // Get task structure. + register task_t* task; + asm volatile("mr %0, 13" : "=r"(task)); + + // Update task state. + task->tolerate_ue = (i_state != 0); + + // Note: We do not need any sort of synchronization instructions here + // because the state is only used by the local HW thread which + // might be handling the machine check due to memory UE. Any + // exception is a context synchronizing event which ensures + // that all preceeding instructions have completed, so there + // are no visible effects of instruction reordering with respect + // to this state change. + +} diff --git a/src/makefile b/src/makefile index 258f2fb11..7b99eb510 100644 --- a/src/makefile +++ b/src/makefile @@ -43,7 +43,7 @@ DIRECT_BOOT_OBJECTS = start.o kernel.o taskmgr.o cpumgr.o syscall.o \ ptmgr.o segmentmgr.o basesegment.o devicesegment.o \ block.o cxxtest_data.o cpuid.o misc.o msghandler.o \ blockmsghdlr.o stacksegment.o softpatch_p8.o \ - shutdown.o forceattn_p8.o terminate.o ipc.o + shutdown.o forceattn_p8.o terminate.o ipc.o machchk.o BASE_MODULES = trace errl devicefw scom xscom initservice \ pnor vfs diff --git a/src/usr/dump/dumpCollect.C b/src/usr/dump/dumpCollect.C index 4af04ec71..7f704faef 100644 --- a/src/usr/dump/dumpCollect.C +++ b/src/usr/dump/dumpCollect.C @@ -37,7 +37,7 @@ #include <dump/dumpif.H> #include <sys/msg.h> // message Q's -#include <mbox/mbox_queues.H> // +#include <mbox/mbox_queues.H> // // Trace definition trace_desc_t* g_trac_dump = NULL; @@ -116,14 +116,14 @@ namespace DUMP // local src table info uint64_t *vaSrcTableAddr = 0; uint64_t *vaMapSrcTableAddr = 0; - uint64_t curSrcTableAddr = 0; + uint64_t curSrcTableAddr = 0; // destination table info uint64_t *vaDestTableAddr = 0; uint64_t *vaMapDestTableAddr = 0; uint64_t curDestTableAddr = 0; - // Data sizes + // Data sizes uint64_t sizeToCopy = 0; uint64_t bytesLeftInSrc = 0; uint64_t bytesLeftInDest = 0; @@ -170,14 +170,14 @@ namespace DUMP curDestTableAddr = destTableEntry[curDestIndex].dataAddr; bytesLeftInDest = destTableEntry[curDestIndex].dataSize; - // Determine the src and destination offset. + // Determine the src and destination offset. uint64_t destOffset = curDestTableAddr - ALIGN_PAGE_DOWN(curDestTableAddr); uint64_t srcOffset = curSrcTableAddr - ALIGN_PAGE_DOWN(curSrcTableAddr); // If the data size is greater then 32GB after page alignment // create an error. Current limitation on DevMap is 32GB in size. // Not sure yet if we will ever see a table entry > 3GB. - if (bytesLeftInSrc + srcOffset > THIRTYTWO_GB) + if (bytesLeftInSrc + srcOffset > THIRTYTWO_GB) { invalidSrcSize = true; break; @@ -205,10 +205,10 @@ namespace DUMP vaDestTableAddr += (destOffset/(sizeof (uint64_t))); - // Current Source physical and Va address + // Current Source physical and Va address TRACFCOMP(g_trac_dump, "copySrcToDest SrcTableIndex = %d, srcTableAddr = %.16X, VA = %.16X", curSourceIndex, curSrcTableAddr, vaSrcTableAddr); - // Current Destination physical and Va address + // Current Destination physical and Va address TRACFCOMP(g_trac_dump, "HBDumpCopySrcToDest DestTableIndex = %d, DestTableAddr = %.16X, VA = %.16X", curDestIndex, curDestTableAddr, vaDestTableAddr); @@ -257,14 +257,14 @@ namespace DUMP bytesLeftInSrc = srcTableEntry[curSourceIndex].dataSize; // If the current Src table Address is 0 we are done - if (curSrcTableAddr == 0) + if (curSrcTableAddr == 0) { break; } srcOffset = curSrcTableAddr - ALIGN_PAGE_DOWN(curSrcTableAddr); - // If the data size is less then 32GB after page alignment + // If the data size is less then 32GB after page alignment if (bytesLeftInSrc + srcOffset > THIRTYTWO_GB) { invalidSrcSize = true; @@ -281,7 +281,7 @@ namespace DUMP vaSrcTableAddr += (srcOffset/(sizeof (uint64_t))); - // Current Source physical and Va address + // Current Source physical and Va address TRACFCOMP(g_trac_dump, "copySrcToDest SrcTableIndex = %d, srcTableAddr = %.16X, VA = %.16X", curSourceIndex, curSrcTableAddr, vaSrcTableAddr); } @@ -392,7 +392,7 @@ namespace DUMP // condition is such that we have a destination // entry that either has a zero address or zero // size.. Perhaps put the bad destination entry - // there as well + // there as well } break; @@ -400,7 +400,7 @@ namespace DUMP destOffset = curDestTableAddr - ALIGN_PAGE_DOWN(curDestTableAddr); - // If the data size is less then 32GB after page alignment + // If the data size is less then 32GB after page alignment if (bytesLeftInDest + destOffset > THIRTYTWO_GB) { invalidDestSize = true; @@ -416,17 +416,19 @@ namespace DUMP vaDestTableAddr += (destOffset/(sizeof(uint64_t))); - // Current Destination physical and Va address + // Current Destination physical and Va address TRACFCOMP(g_trac_dump, "HBDumpCopySrcToDest DestTableIndex = %d, DestTableAddr = %.16X, VA = %.16X", curDestIndex, curDestTableAddr, vaDestTableAddr); } - // Determine how much to copy.. + // Determine how much to copy.. sizeToCopy = std::min(bytesLeftInSrc, bytesLeftInDest); // Do the copy of the data from the source to the destination + mm_tolerate_ue(1); memcpy( vaDestTableAddr,vaSrcTableAddr, sizeToCopy); + mm_tolerate_ue(0); if (curResultIndex < maxResultEntries) { @@ -455,7 +457,7 @@ namespace DUMP curResultIndex, maxResultEntries); - // commit the error and continue. + // commit the error and continue. errlCommit(l_err,DUMP_COMP_ID); l_err = NULL; @@ -470,7 +472,7 @@ namespace DUMP // increment the current src and dest addresses in both the // physical and virtual addresses. curSrcTableAddr += sizeToCopy; - curDestTableAddr += sizeToCopy; + curDestTableAddr += sizeToCopy; vaSrcTableAddr += addrOffset; vaDestTableAddr += addrOffset; @@ -693,7 +695,7 @@ namespace DUMP do { - + //Create a mailbox message to send to FSP msg = msg_allocate(); msg->type = i_type; @@ -747,14 +749,14 @@ namespace DUMP } - // if no error then collect as expected. + // if no error then collect as expected. if (!l_err) { // TODO: Issue RTC 67082 - fix sending the MDRT // to FSP once we have a design that can send the // entire table.. Max size is 512K - truncating to - // 32K for the time being. + // 32K for the time being. uint64_t l_resultsTableSize = resultsTableSize; // If the results table is greater than 32K truncate it. @@ -805,7 +807,7 @@ namespace DUMP msg->data[1] = l_err->plid(); // plid // just commit the log from failure on Read.. and - // send an error msg to FSP. + // send an error msg to FSP. errlCommit( l_err, DUMP_COMP_ID ); l_err = NULL; |