summaryrefslogtreecommitdiffstats
path: root/src/usr
diff options
context:
space:
mode:
authorDoug Gilbert <dgilbert@us.ibm.com>2011-11-04 12:12:01 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2012-01-05 11:06:04 -0600
commit7de0708eac63bb81786c2a5e794c5d6fbef069c4 (patch)
treeb47aeb4c9827851d61b44d5cb922704f73257693 /src/usr
parent048789fdce6b406de3b7149f8171afd63eea1829 (diff)
downloadtalos-hostboot-7de0708eac63bb81786c2a5e794c5d6fbef069c4.tar.gz
talos-hostboot-7de0708eac63bb81786c2a5e794c5d6fbef069c4.zip
Interrupt presenter implementation
Change-Id: If6b499d819b71298b8a64e096e1eb83c639ad645 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/517 Tested-by: Jenkins Server Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com> Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r--src/usr/initservice/extinitsvc/extinitsvctasks.H13
-rw-r--r--src/usr/intr/intrrp.C600
-rw-r--r--src/usr/intr/intrrp.H191
-rw-r--r--src/usr/intr/makefile30
-rw-r--r--src/usr/intr/test/intrtest.H221
-rw-r--r--src/usr/intr/test/makefile29
-rw-r--r--src/usr/makefile3
-rw-r--r--src/usr/pnor/pnorrp.C2
-rw-r--r--src/usr/pnor/test/pnorrptest.H6
9 files changed, 1090 insertions, 5 deletions
diff --git a/src/usr/initservice/extinitsvc/extinitsvctasks.H b/src/usr/initservice/extinitsvc/extinitsvctasks.H
index fb8dfa1a6..03bc232c8 100644
--- a/src/usr/initservice/extinitsvc/extinitsvctasks.H
+++ b/src/usr/initservice/extinitsvc/extinitsvctasks.H
@@ -170,6 +170,19 @@ const TaskInfo g_exttaskinfolist[] = {
// NOTE: libistepdisp.so needs to always be last in this list!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/**
+ * @brief External interrupt resource provider
+ */
+ {
+ "libintr.so", // tasknmae
+ NULL, // no ptr to fnct
+ {
+ START_TASK, // task type
+ EXT_IMAGE, // Extended Module
+ START_INTR_ERRL_ID, // module id
+ }
+ },
+
+ /**
* @brief IStep Dispatcher task, runs ISteps
*
*
diff --git a/src/usr/intr/intrrp.C b/src/usr/intr/intrrp.C
new file mode 100644
index 000000000..d328c75ae
--- /dev/null
+++ b/src/usr/intr/intrrp.C
@@ -0,0 +1,600 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/usr/intr/intrrp.C $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2011
+//
+// 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
+/**
+ * @file intrrp.C
+ * @brief Interrupt Resource Provider
+ */
+
+#include "intrrp.H"
+#include <trace/interface.H>
+#include <errno.h>
+#include <initservice/taskargs.H>
+#include <util/singleton.H>
+#include <intr/intr_reasoncodes.H>
+#include <sys/mmio.h>
+#include <sys/misc.h>
+#include <kernel/console.H>
+#include <sys/task.h>
+#include <targeting/targetservice.H>
+#include <vmmconst.h>
+
+using namespace INTR;
+
+trace_desc_t * g_trac_intr = NULL;
+TRAC_INIT(&g_trac_intr, INTR_COMP_NAME, 2 * 1024);
+
+
+/**
+ * setup _start and handle barrier
+ */
+TASK_ENTRY_MACRO( IntrRp::init );
+
+
+void IntrRp::init( void * i_taskArgs )
+{
+ errlHndl_t err = NULL;
+ err = Singleton<IntrRp>::instance()._init();
+ INITSERVICE::TaskArgs* args =
+ static_cast<INITSERVICE::TaskArgs*>(i_taskArgs);
+ if(err)
+ {
+ args->postErrorLog(err);
+ }
+}
+
+
+// ICPBAR = INTP.ICP_BAR[0:25] in P7 = 0x3FBFF90 + (8*node) + procPos
+// P7 Scom address 0x02011C09 P8 = 0x020109c9
+// BaseAddress:
+// BA[18:43] = ICPBAR (P8 says [14:43] (30 bits))
+// BA[47:49] = COREid (0-7)
+// BA[50:51] = cpu thread (0-3)
+//
+// BA+0 = XIRR (poll - Read/Write has no side effects))
+// BA+4 = XIRR (Read locks HW, Write -> EOI to HW))
+// BA+12 = MFRR (1 byte)
+// BA+16 = LINKA (4 bytes)
+// BA+20 = LINKB (4 bytes)
+// BA+24 = LINKC (4 bytes)
+errlHndl_t IntrRp::_init()
+{
+ errlHndl_t err = NULL;
+
+ // TODO Temporaritly DISABLE in VBU until P8 support is added
+ TARGETING::EntityPath syspath(TARGETING::EntityPath::PATH_PHYSICAL);
+ syspath.addLast(TARGETING::TYPE_SYS,0);
+ TARGETING::Target* sys = TARGETING::targetService().toTarget(syspath);
+ uint8_t vpo_mode = 0;
+ if( sys
+ && sys->tryGetAttr<TARGETING::ATTR_IS_SIMULATION>(vpo_mode)
+ && (vpo_mode == 1) )
+ {
+ iv_isVBU = true;
+ }
+
+ // get the PIR
+ // Which ever cpu core this is running on is the MASTER cpu
+ // Make master thread 0
+ iv_masterCpu.word = task_getcpuid();
+ iv_masterCpu.threadId = 0;
+
+ uint64_t realAddr = 0;
+
+ // TODO Does the BAR scom reg need to be read here or set here??
+ // Who sets the BAR?
+ //uint64_t size = sizeof(realAddr);
+ //TARGETING::TargetService& targetService = TARGETING::targetService();
+ //TARGETING::Target* procTarget = NULL;
+ //targetService.masterProcChipTargetHandle( procTarget );
+ //
+ // TODO What if this does not jive with with the PIR?
+
+ // TODO Does this need to be read here?
+ //err = deviceRead(procTarget,
+ // &realAddr,
+ // size,
+ // DEVICE_SCOM_ADDRESS(ICPBAR_SCOM_ADDR));
+ //if(err) return err;
+
+ if(realAddr == 0)
+ {
+ realAddr = (static_cast<uint64_t>(ICPBAR_VAL)) << 34;
+ }
+
+ // TODO Does this need to be set here?
+ // err = deviceWrite(procTarget,
+ // &realAddr,
+ // size,
+ // DEVICE_SCOM_ADDRESS(ICPBAR_SCOM_ADDR));
+
+ realAddr &= 0xFFFFFFFC00000000ul; //[0:29] is ICP_BAR
+ realAddr >>= 14; //[14:43] is BAR field in real address
+
+
+ // The realAddr is the base address for the whole system.
+ // Therefore the realAddr must be based on the processor with
+ // lowest BAR value in the system. (usually n0p0)
+ // TODO Adjust the realAddr if the BAR came from a processor other
+ // than cpuid 0
+
+ TRACDCOMP(g_trac_intr,"INTR: realAddr = %lx",realAddr);
+
+ // VADDR_SIZE is 1MB per chip - max 32 -> 32MB
+ iv_baseAddr = reinterpret_cast<uint64_t>
+ (mmio_dev_map(reinterpret_cast<void*>(realAddr),THIRTYTWO_MB));
+
+ TRACDCOMP(g_trac_intr,"INTR: vAddr = %lx",iv_baseAddr);
+
+ err = checkAddress(iv_baseAddr);
+ if(!err)
+ {
+
+ // Set up the interrupt provider registers
+ // NOTE: Simics only supports 4 threads, and two cores per proc chip.
+ //
+ // NOTE: P7 register address format only supports 4 threads per core.
+ // This will change for P8
+ //
+ // NOTE: It's only possible to set up the master core at this point.
+ //
+ // Set up link registers to forward all intrpts to master cpu.
+ //
+ // There is one register set per cpu thread or 1024 register sets max.
+ size_t threads = cpu_thread_count();
+
+ if(threads > 4) //TODO remove when true P8 support is added
+ {
+ TRACFCOMP(g_trac_intr,
+ "I>intrrp needs to be updated to handle > 4 threads");
+ threads = 4;
+ }
+
+ PIR_t pir = iv_masterCpu;
+ for(size_t thread = 0; thread < threads; ++thread)
+ {
+ pir.threadId = thread;
+ initInterruptPresenter(pir);
+ }
+
+ // Get the kernel msg queue for ext intr
+ // Create a task to handle the messages
+ iv_msgQ = msg_q_create();
+ msg_q_register(iv_msgQ, INTR_MSGQ);
+
+ task_create(IntrRp::msg_handler, NULL);
+ }
+
+ return err;
+}
+
+errlHndl_t IntrRp::enableInterrupts()
+{
+ errlHndl_t err = NULL;
+
+ // TODO Temporarily DISABLE in VBU until P8 support is added
+ if(iv_isVBU) return err;
+
+ // Enable the interrupt on master processor core, thread 0
+ uint64_t baseAddr = iv_baseAddr + cpuOffsetAddr(iv_masterCpu);
+
+ err = checkAddress(baseAddr);
+ if(!err)
+ {
+ uint8_t * cppr = reinterpret_cast<uint8_t*>(baseAddr+CPPR_OFFSET);
+ *cppr = 0xff;
+ }
+
+ return err;
+}
+
+errlHndl_t IntrRp::disableInterrupts()
+{
+ errlHndl_t err = NULL;
+
+ // Disable the interrupt on master processor core, thread 0
+ // TODO Temporarily DISABLE in VBU until P8 support is added
+ if(iv_isVBU) return err;
+
+ uint64_t baseAddr = iv_baseAddr + cpuOffsetAddr(iv_masterCpu);
+
+ err = checkAddress(baseAddr);
+ if(!err)
+ {
+ uint8_t * cppr = reinterpret_cast<uint8_t*>(baseAddr+CPPR_OFFSET);
+ *cppr = 0;
+ }
+
+ return err;
+}
+
+/**
+ * Helper function to start the messge handler
+ */
+void IntrRp::msg_handler(void * unused)
+{
+ Singleton<IntrRp>::instance().msgHandler();
+}
+
+
+void IntrRp::msgHandler()
+{
+ while(1)
+ {
+ msg_t* msg = msg_wait(iv_msgQ); // wait for interrupt msg
+
+ switch(msg->type)
+ {
+ case MSG_INTR_EXTERN:
+ {
+ // Acknowlege msg
+ msg->data[1] = 0;
+ msg_respond(iv_msgQ, msg);
+
+ ext_intr_t type = NO_INTERRUPT;
+
+ // type = XISR = XIRR[8:31]
+ // priority = XIRR[0:7]
+ uint32_t * xirrAddress =
+ reinterpret_cast<uint32_t *>(iv_baseAddr+ XIRR_OFFSET+
+ cpuOffsetAddr(iv_masterCpu));
+
+ // xirr was read by interrupt message handler.
+ // Passed in as data[0]
+ uint32_t xirr = static_cast<uint32_t>(msg->data[0]);
+ type = static_cast<ext_intr_t>(xirr & 0x00FFFFFF);
+
+ TRACDCOMP(g_trac_intr,"External Interrupt recieved, Type=%x",type);
+
+ Registry_t::iterator r = iv_registry.find(type);
+ if(r != iv_registry.end())
+ {
+ msg_q_t msgQ = r->second;
+ msg_t * rmsg = msg_allocate();
+ rmsg->type = type;
+ int rc = msg_sendrecv(msgQ,rmsg);
+ if(rc)
+ {
+ TRACFCOMP(g_trac_intr,ERR_MRK
+ "External Interrupt recieved type = %d, "
+ "but could not send message to registered"
+ " handler. Ignorming it. rc = %d",
+ (uint32_t) type, rc);
+ }
+ }
+ else // no queue registered for this interrupt type
+ {
+ // Throw it away for now.
+ TRACFCOMP(g_trac_intr,ERR_MRK
+ "External Interrupt recieved type = %d, but "
+ "nothing registered to handle it. "
+ "Ignoreing it.",
+ (uint32_t)type);
+ }
+
+ // Writing the XIRR with the same value read earlier
+ // tells the interrupt presenter hardware to signal an EOI.
+ *xirrAddress = xirr;
+ }
+ break;
+
+ case MSG_INTR_REGISTER_MSGQ:
+ {
+ msg_q_t l_msgQ= reinterpret_cast<msg_q_t>(msg->data[0]);
+ ext_intr_t l_t= static_cast<ext_intr_t>(msg->data[1]);
+ errlHndl_t err = registerInterrupt(l_msgQ,l_t);
+
+ msg->data[1] = reinterpret_cast<uint64_t>(err);
+ msg_respond(iv_msgQ,msg);
+ }
+ break;
+
+ case MSG_INTR_ENABLE:
+ {
+ errlHndl_t err = enableInterrupts();
+ msg->data[1] = reinterpret_cast<uint64_t>(err);
+ msg_respond(iv_msgQ,msg);
+ }
+ break;
+
+ case MSG_INTR_DISABLE:
+ {
+ errlHndl_t err =disableInterrupts();
+ msg->data[1] = reinterpret_cast<uint64_t>(err);
+ msg_respond(iv_msgQ,msg);
+ }
+ break;
+
+ // Called when a new cpu becomes active other than the master
+ // Expect a call for each new core
+ case MSG_INTR_ADD_CPU_USR:
+ case MSG_INTR_ADD_CPU:
+ {
+ PIR_t pir = msg->data[0];
+
+ size_t threads = cpu_thread_count();
+
+ for(size_t thread = 0; thread < threads; ++thread)
+ {
+ pir.threadId = thread;
+ initInterruptPresenter(pir);
+ }
+
+ msg->data[1] = 0;
+ msg_respond(iv_msgQ, msg);
+ }
+ break;
+
+ default:
+ msg->data[1] = -EINVAL;
+ msg_respond(iv_msgQ, msg);
+ }
+ }
+}
+
+
+errlHndl_t IntrRp::registerInterrupt(msg_q_t i_msgQ, ext_intr_t i_type)
+{
+ errlHndl_t err = NULL;
+
+ Registry_t::iterator r = iv_registry.find(i_type);
+ if(r == iv_registry.end())
+ {
+ iv_registry[i_type] = i_msgQ;
+ }
+ else
+ {
+ if(r->second != i_msgQ)
+ {
+ /*@ errorlog tag
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid INTR_MODULE_ID
+ * @reasoncode INTR_ALREADY_REGISTERED
+ * @userdata1 Interrupt type
+ * @userdata2 0
+ *
+ * @defdesc Interrupt type already registered
+ *
+ */
+ err = new ERRORLOG::ErrlEntry
+ (
+ ERRORLOG::ERRL_SEV_INFORMATIONAL, // severity
+ INTR::INTR_MODULE_ID, // moduleid
+ INTR::INTR_ALREADY_REGISTERED, // reason code
+ i_type,
+ 0
+ );
+ }
+
+ }
+ return err;
+}
+
+void IntrRp::initInterruptPresenter(const PIR_t i_pir) const
+{
+ // TODO Temporaritly DISABLE in VBU until P8 support is added
+ if(iv_isVBU) return;
+
+ TARGETING::EntityPath syspath(TARGETING::EntityPath::PATH_PHYSICAL);
+ syspath.addLast(TARGETING::TYPE_SYS,0);
+ TARGETING::Target* sys = TARGETING::targetService().toTarget(syspath);
+ uint8_t vpo_mode = 0;
+ if( sys
+ && sys->tryGetAttr<TARGETING::ATTR_IS_SIMULATION>(vpo_mode)
+ && (vpo_mode == 1) )
+ {
+ return;
+ }
+
+ uint64_t baseAddr = iv_baseAddr + cpuOffsetAddr(i_pir);
+ uint8_t * cppr =
+ reinterpret_cast<uint8_t*>(baseAddr + CPPR_OFFSET);
+ uint32_t * plinkReg =
+ reinterpret_cast<uint32_t *>(baseAddr + LINKA_OFFSET);
+
+ TRACDCOMP(g_trac_intr,"PIR 0x%x offset: 0x%lx",
+ i_pir.word,
+ cpuOffsetAddr(i_pir));
+ if(i_pir.word == iv_masterCpu.word)
+ {
+ *cppr = 0xff;
+ }
+ else
+ {
+ *cppr = 0; // no interrupts allowed except on master
+ }
+
+ // Links are intended to be set up in rings. If an interrupt ends up
+ // where it started, it gets rejected by hardware.
+ //
+ // According to BOOK IV, The links regs are setup by firmware.
+ //
+ // Should be possible to link all interrupt forwarding directly to
+ // the master core and either make them direct (lspec = 0) or by setting
+ // the LOOPTRIP bit to stop the forwarding at the masterProc.
+ //
+ LinkReg_t linkReg;
+ linkReg.word = 0;
+ linkReg.loopTrip = 1; // needed?
+ linkReg.pchip= (iv_masterCpu.nodeId*8)+iv_masterCpu.chipId;
+ linkReg.pcore= iv_masterCpu.coreId;
+ linkReg.tspec= iv_masterCpu.threadId;
+
+ *(plinkReg) = linkReg.word;
+ *(plinkReg + 1) = linkReg.word;
+ linkReg.last = 1;
+ *(plinkReg + 2) = linkReg.word;
+}
+
+
+errlHndl_t IntrRp::checkAddress(uint64_t i_addr)
+{
+ errlHndl_t err = NULL;
+
+ if(i_addr < VMM_VADDR_DEVICE_SEGMENT)
+ {
+ /*@ errorlog tag
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid INTR_MODULE_ID
+ * @reasoncode INTR_BAD_VIRTUAL_IO_ADDRESS
+ * @userdata1 The bad virtual address
+ * @userdata2 0
+ *
+ * @defdesc The virutal address is not a valid IO address
+ *
+ */
+ err = new ERRORLOG::ErrlEntry
+ (
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ INTR::INTR_MODULE_ID,
+ INTR::INTR_BAD_VIRTUAL_IO_ADDRESS,
+ i_addr,
+ 0
+ );
+ }
+
+ return err;
+}
+
+
+// Register a message queue with a particular intr type
+errlHndl_t INTR::registerMsgQ(msg_q_t i_msgQ, ext_intr_t i_type)
+{
+ errlHndl_t err = NULL;
+ // Can't add while handling an interrupt, so
+ // send msg instead of direct call
+ msg_q_t intr_msgQ = msg_q_resolve(INTR_MSGQ);
+ if(intr_msgQ)
+ {
+ msg_t * msg = msg_allocate();
+ msg->type = MSG_INTR_REGISTER_MSGQ;
+ msg->data[0] = reinterpret_cast<uint64_t>(i_msgQ);
+ msg->data[1] = static_cast<uint64_t>(i_type);
+
+ msg_sendrecv(intr_msgQ, msg);
+ err = reinterpret_cast<errlHndl_t>(msg->data[1]);
+ }
+ else
+ {
+ /*@ errorlog tag
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid INTR_MODULE_ID
+ * @reasoncode INTR_REGISTRY_NOT_READY
+ * @userdata1 Interrupt type to register
+ * @userdata2 0
+ *
+ * @defdesc Interrupt resource provider not initialized yet.
+ *
+ */
+ err = new ERRORLOG::ErrlEntry
+ (
+ ERRORLOG::ERRL_SEV_INFORMATIONAL, // severity
+ INTR::INTR_MODULE_ID, // moduleid
+ INTR::INTR_REGISTRY_NOT_READY, // reason code
+ static_cast<uint64_t>(i_type),
+ 0
+ );
+ }
+ return err;
+}
+
+/*
+ * Enable hardware to report external interrupts
+ */
+errlHndl_t INTR::enableExternalInterrupts()
+{
+ errlHndl_t err = NULL;
+ msg_q_t intr_msgQ = msg_q_resolve(INTR_MSGQ);
+ if(intr_msgQ)
+ {
+ msg_t * msg = msg_allocate();
+ msg->type = MSG_INTR_ENABLE;
+
+ msg_sendrecv(intr_msgQ, msg);
+
+ err = reinterpret_cast<errlHndl_t>(msg->data[1]);
+ }
+ else
+ {
+ /*@ errorlog tag
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid INTR_MODULE_ID
+ * @reasoncode INTR_RP_NOT_INITIALIZED
+ * @userdata1 MSG_INTR_ENABLE
+ * @userdata2 0
+ *
+ * @defdesc Interrupt resource provider not initialized yet.
+ *
+ */
+ err = new ERRORLOG::ErrlEntry
+ (
+ ERRORLOG::ERRL_SEV_INFORMATIONAL, // severity
+ INTR::INTR_MODULE_ID, // moduleid
+ INTR::INTR_RP_NOT_INITIALIZED, // reason code
+ static_cast<uint64_t>(MSG_INTR_ENABLE),
+ 0
+ );
+ }
+ return err;
+}
+
+/*
+ * Disable hardware from reporting external interrupts
+ */
+errlHndl_t INTR::disableExternalInterrupts()
+{
+ errlHndl_t err = NULL;
+ // Can't disable while handling interrupt, so send msg to serialize
+ msg_q_t intr_msgQ = msg_q_resolve(INTR_MSGQ);
+ if(intr_msgQ)
+ {
+ msg_t * msg = msg_allocate();
+ msg->type = MSG_INTR_DISABLE;
+
+ msg_sendrecv(intr_msgQ, msg);
+
+ err = reinterpret_cast<errlHndl_t>(msg->data[1]);
+ }
+ else
+ {
+ /*@ errorlog tag
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid INTR_MODULE_ID
+ * @reasoncode INTR_RP_NOT_INITIALIZED
+ * @userdata1 MSG_INTR_DISABLE
+ * @userdata2 0
+ *
+ * @defdesc Interrupt resource provider not initialized yet.
+ *
+ */
+ err = new ERRORLOG::ErrlEntry
+ (
+ ERRORLOG::ERRL_SEV_INFORMATIONAL, // severity
+ INTR::INTR_MODULE_ID, // moduleid
+ INTR::INTR_RP_NOT_INITIALIZED, // reason code
+ static_cast<uint64_t>(MSG_INTR_DISABLE),
+ 0
+ );
+ }
+ return err;
+}
+
diff --git a/src/usr/intr/intrrp.H b/src/usr/intr/intrrp.H
new file mode 100644
index 000000000..6c54cf993
--- /dev/null
+++ b/src/usr/intr/intrrp.H
@@ -0,0 +1,191 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/usr/intr/intrrp.H $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2011
+//
+// 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
+#ifndef INTRRP_H
+#define INTRRP_H
+
+#include <stdint.h>
+#include <builtins.h>
+#include <limits.h>
+#include <errl/errltypes.H>
+#include <sys/msg.h>
+#include <intr/interrupt.H>
+#include <map>
+
+struct msg_t;
+
+namespace INTR
+{
+ class IntrRp
+ {
+ public:
+
+ /**
+ * Prepare HW and system to recieve external interrupts
+ * @param[in] task args
+ */
+ static void init(void * i_taskArgs);
+
+ protected:
+
+ /**
+ * Constructor
+ */
+ IntrRp() :
+ iv_msgQ(NULL),
+ iv_baseAddr(0),
+ iv_masterCpu(0),
+ iv_isVBU(false) {}
+
+ /**
+ * Destructor
+ */
+ ~IntrRp() {}
+
+ /**
+ * Start message handler
+ */
+ static void msg_handler(void * unused);
+
+ private: //Data
+
+ enum
+ {
+ CPPR_OFFSET = 4, //!< offset to CPPR (1 byte)
+ XIRR_OFFSET = 4, //!< offset to XIRR (4 bytes)
+ LINKA_OFFSET = 16, //!< offset to LINKA register
+ LINKB_OFFSET = 20, //!< offset to LINKB register
+ LINKC_OFFSET = 24, //!< offset to LINKC register
+ };
+
+ // If the interrupt can't be handled by the current chip there are
+ // three link registers used provide targets to forward the
+ // interrupt to.
+ struct LinkReg_t
+ {
+ union
+ {
+ uint32_t word;
+ struct
+ {
+ uint32_t last:1; //!< RO, 0 means last reg
+ uint32_t loopTrip:1; //!< Stop forwarding
+ uint32_t reserved:17;//!< Not implemented
+ uint32_t pchip:6; //!< Target chip
+ uint32_t pcore:3; //!< Target core
+ uint32_t tspec:2; //!< Target tread
+ uint32_t lspec:2; //!< Target link register
+ } PACKED;
+ };
+ };
+
+ /**
+ * cpu PIR register
+ * @note TODO P7 bits - thread 2, core 3, chip 2, node 3,
+ * P8 will be different. Need P8 book IV
+ */
+ struct PIR_t
+ {
+ union
+ {
+ uint32_t word;
+ struct
+ {
+ uint32_t reserved:22; //!< zeros
+ uint32_t nodeId:3; //!< node (4)
+ uint32_t chipId:2; //!< chip pos on node (8)
+ uint32_t coreId:3; //!< Core number (12)
+ uint32_t threadId:2; //!< thread number (8)
+ } PACKED;
+ };
+ PIR_t(uint32_t i_word) : word(i_word) {}
+ };
+
+
+ typedef std::map<ext_intr_t,msg_q_t> Registry_t;
+
+
+ msg_q_t iv_msgQ; //!< Kernel Interrupt message queue
+ Registry_t iv_registry; //!< registered interrupt type
+ uint64_t iv_baseAddr; //!< Base address of hw INTR regs
+ PIR_t iv_masterCpu; //!< Master cpu PIR
+ bool iv_isVBU; //!< TODO Don't run on VBU for now
+
+
+ private: //functions
+
+ errlHndl_t _init();
+
+ /**
+ * Message handler
+ */
+ void msgHandler();
+
+ /**
+ * Register a message queue for an interrupt type
+ * @param[in] i_msgQ The message queue
+ * @param[in] i_type the interrupt type
+ */
+ errlHndl_t registerInterrupt(msg_q_t i_msgQ, ext_intr_t i_type);
+
+ /**
+ * Enable hardware to reporting external interrupts
+ */
+ errlHndl_t enableInterrupts();
+
+ /**
+ * Disable hardware from reporting external interupts
+ */
+ errlHndl_t disableInterrupts();
+
+ /**
+ * Initialize the interrupt presenter registers
+ * @param[in] i_pir PIR value for the presenter
+ */
+ void initInterruptPresenter(const PIR_t i_pir) const;
+
+ /**
+ * Calculate the adress offset for the given cpu
+ * @param[in] i_pir PIR value for the presenter
+ * @return the offset
+ */
+ ALWAYS_INLINE
+ uint64_t cpuOffsetAddr(const PIR_t i_pir) const
+ {
+ uint64_t offset = (i_pir.nodeId * 8) + i_pir.chipId;
+ offset <<= 20;
+ offset |= static_cast<uint64_t>(i_pir.coreId) << 14;
+ offset |= static_cast<uint64_t>(i_pir.threadId) << 12;
+ return offset;
+ }
+
+ /**
+ * Validity check for an I/O address
+ * @param[in] i_addr the address
+ * @return error log if not valid
+ */
+ static errlHndl_t checkAddress(uint64_t i_addr);
+
+ };
+}; // INTR namespace
+
+#endif
diff --git a/src/usr/intr/makefile b/src/usr/intr/makefile
new file mode 100644
index 000000000..e9b520654
--- /dev/null
+++ b/src/usr/intr/makefile
@@ -0,0 +1,30 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/intr/makefile $
+#
+# IBM CONFIDENTIAL
+#
+# COPYRIGHT International Business Machines Corp. 2011
+#
+# 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
+ROOTPATH = ../../..
+MODULE = intr
+
+OBJS = intrrp.o
+
+SUBDIRS = test.d
+
+include ${ROOTPATH}/config.mk
diff --git a/src/usr/intr/test/intrtest.H b/src/usr/intr/test/intrtest.H
new file mode 100644
index 000000000..9c0d4e412
--- /dev/null
+++ b/src/usr/intr/test/intrtest.H
@@ -0,0 +1,221 @@
+// IBM_PROLOG_BEGIN_TAG
+// This is an automatically generated prolog.
+//
+// $Source: src/usr/intr/test/intrtest.H $
+//
+// IBM CONFIDENTIAL
+//
+// COPYRIGHT International Business Machines Corp. 2011
+//
+// 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
+#ifndef __INTRTEST_H
+#define __INTRTEST_H
+
+#include <cxxtest/TestSuite.H>
+#include <intr/interrupt.H>
+#include <errl/errlentry.H>
+#include <errl/errlmanager.H>
+#include <kernel/console.H>
+#include <targeting/targetservice.H>
+
+
+class IntrTest: public CxxTest::TestSuite
+{
+ public:
+ /**
+ * @brief INTR test setup values
+ */
+ void test_verifyState( void )
+ {
+
+ // TODO Temporaritly DISABLE in VBU until P8 support is added
+ TARGETING::EntityPath syspath(TARGETING::EntityPath::PATH_PHYSICAL);
+ syspath.addLast(TARGETING::TYPE_SYS,0);
+ TARGETING::Target* sys = TARGETING::targetService().toTarget(syspath);
+ uint8_t vpo_mode = 0;
+ if( sys
+ && sys->tryGetAttr<TARGETING::ATTR_IS_SIMULATION>(vpo_mode)
+ && (vpo_mode == 1) )
+ {
+ return;
+ }
+
+ // Add support for second chip (dummy)
+ uint32_t fake_pir = 0x00000001 << 5; // P7 chip 1 TODO P8=?
+ msg_q_t intr_msgQ = msg_q_resolve(INTR_MSGQ);
+ msg_t * msg = msg_allocate();
+ msg->type = INTR::MSG_INTR_ADD_CPU_USR;
+ msg->data[0] = fake_pir;
+
+ msg_sendrecv(intr_msgQ, msg);
+
+ // all the simics registers
+ for(uint64_t chip = 0; chip < 4; ++chip)
+ {
+ // simics P7 only supports 4 threads per core
+ // and 2 cores - let the thread field overflow
+ // into the core field -> 2 cores x 4 threads = 8 threads
+ for(uint64_t thread = 0; thread < 8; ++thread)
+ {
+ uint64_t offset = (chip << 20) | (thread << 12);
+
+ uint32_t * addr =
+ reinterpret_cast<uint32_t *>(cv_baseAddr + offset);
+
+ if(offset == 0) // Master cpu
+ {
+ if(*addr != 0xFF000000)
+ {
+ TS_FAIL
+ ("INTR:Master cpu not initialized-XIRR=0x%08x",
+ *addr);
+ }
+ }
+
+ if (chip < 2 && thread < 4) // TODO Change when all threads supported
+ {
+ if(offset != 0 && *addr != 0)
+ {
+ TS_FAIL("INTR:Chip %ld Thread %ld bad XIRR=0x%08x",
+ chip,thread,*addr);
+ }
+
+
+
+ if(*(addr+4) != 0x40000000 ||
+ *(addr+5) != 0x40000000 ||
+ *(addr+6) != 0xC0000000)
+ {
+ TS_FAIL("INTR:Chip %ld Thread %ld bad LINKS"
+ " 0x%08x 0x%08x 0x%08x",
+ chip,thread,
+ *(addr+4),*(addr+5),*(addr+6));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @brief Disable then Enable interrupt handling
+ */
+ void test_enableDisable( void )
+ {
+ // TODO Temporaritly DISABLE in VBU until P8 support is added
+ TARGETING::EntityPath syspath(TARGETING::EntityPath::PATH_PHYSICAL);
+ syspath.addLast(TARGETING::TYPE_SYS,0);
+ TARGETING::Target* sys = TARGETING::targetService().toTarget(syspath);
+ uint8_t vpo_mode = 0;
+ if( sys
+ && sys->tryGetAttr<TARGETING::ATTR_IS_SIMULATION>(vpo_mode)
+ && (vpo_mode == 1) )
+ {
+ return;
+ }
+
+ uint32_t * addr = reinterpret_cast<uint32_t *>(cv_baseAddr);
+
+ errlHndl_t err = INTR::disableExternalInterrupts();
+
+ if(err)
+ {
+ TS_FAIL("INTR::disableExternalInterrupts returned error log");
+ delete err;
+ err = NULL;
+ }
+
+ if((*addr & 0xFF000000) != 0)
+ {
+ TS_FAIL("INTR not disabled");
+ }
+
+ err = INTR::enableExternalInterrupts();
+
+ if(err)
+ {
+ TS_FAIL("INTR::enableExternalInterrupts returned error log");
+ delete err;
+ err = NULL;
+ }
+
+
+ if((*addr & 0xFF000000) != 0xFF000000)
+ {
+ TS_FAIL("INTR not enabled");
+ }
+ }
+
+ /**
+ * @brief Register an interrupt message queue, force an interrupt,
+ * then handle the interrupt.
+ */
+ void test_intr( void )
+ {
+ // Injecting interproc interrupt seems to work sometimes and not others
+ // TODO need to investigate.
+#ifdef __NOT_NOW__
+ // TODO Temporaritly DISABLE in VBU until P8 support is added
+ TARGETING::EntityPath syspath(TARGETING::EntityPath::PATH_PHYSICAL);
+ syspath.addLast(TARGETING::TYPE_SYS,0);
+ TARGETING::Target* sys = TARGETING::targetService().toTarget(syspath);
+ uint8_t vpo_mode = 0;
+ if( sys
+ && sys->tryGetAttr<TARGETING::ATTR_IS_SIMULATION>(vpo_mode)
+ && (vpo_mode == 1) )
+ {
+ return;
+ }
+
+ extern trace_desc_t * g_trac_intr;
+
+ errlHndl_t err = NULL;
+
+ // Need to register a msgq
+ msg_q_t msgQ = msg_q_create();
+ err = INTR::registerMsgQ(msgQ,INTR::INTERPROC);
+ if(err)
+ {
+ TS_FAIL("Errl from INTR::registerMsgQ()");
+ delete err;
+ err = NULL;
+ }
+
+ // Force an interrupt by writing to the MFFR on master
+ volatile uint8_t * mfrr =
+ reinterpret_cast<uint8_t *>(cv_baseAddr+12);
+ *(mfrr) = 0x55;
+ *(mfrr) = 0xff;
+
+ msg_t* msg = msg_wait(msgQ); // wait for interrupt msg
+ TRACFCOMP(g_trac_intr,"Interrupt handled! Type=%lx",msg->type);
+ if(msg->type != INTR::INTERPROC)
+ {
+ TS_FAIL("INTR::unexpected interrupt type %lx",msg->type);
+ }
+ msg_respond(msgQ,msg);
+#endif
+ }
+
+
+ private:
+
+ static uint64_t cv_baseAddr;
+};
+
+uint64_t IntrTest::cv_baseAddr = 0x20000000000ul;
+
+
+#endif
+
diff --git a/src/usr/intr/test/makefile b/src/usr/intr/test/makefile
new file mode 100644
index 000000000..8923d526a
--- /dev/null
+++ b/src/usr/intr/test/makefile
@@ -0,0 +1,29 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/intr/test/makefile $
+#
+# IBM CONFIDENTIAL
+#
+# COPYRIGHT International Business Machines Corp. 2011
+#
+# 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
+ROOTPATH = ../../../..
+
+MODULE = testintr
+TESTS = *.H
+
+include ${ROOTPATH}/config.mk
+
diff --git a/src/usr/makefile b/src/usr/makefile
index 10c88e94f..d9089f4d6 100644
--- a/src/usr/makefile
+++ b/src/usr/makefile
@@ -26,6 +26,7 @@ OBJS = module_init.o
SUBDIRS = example.d trace.d cxxtest.d testcore.d errl.d devicefw.d \
scom.d xscom.d targeting.d initservice.d hwpf.d \
- ecmddatabuffer.d pnor.d i2c.d vfs.d fsi.d hwas.d fsiscom.d
+ ecmddatabuffer.d pnor.d i2c.d vfs.d fsi.d hwas.d fsiscom.d \
+ intr.d
include ${ROOTPATH}/config.mk
diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C
index e39f49758..3b2e5cc0e 100644
--- a/src/usr/pnor/pnorrp.C
+++ b/src/usr/pnor/pnorrp.C
@@ -329,7 +329,7 @@ errlHndl_t PnorRP::readTOC()
// put some random sizes in here
iv_TOC[PNOR::SIDE_A][PNOR::TOC].size = 8 + 8 + PNOR::NUM_SECTIONS*sizeof(TOCEntry_t);
- iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].size = 500*1024; //500K
+ iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].size = 700*KILOBYTE; //700K
iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].size = PAGESIZE; //4K
iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].size = 128*KILOBYTE; //128K
diff --git a/src/usr/pnor/test/pnorrptest.H b/src/usr/pnor/test/pnorrptest.H
index 9f562a590..3bd139238 100644
--- a/src/usr/pnor/test/pnorrptest.H
+++ b/src/usr/pnor/test/pnorrptest.H
@@ -64,12 +64,12 @@ class PnorRpTest : public CxxTest::TestSuite
};
const ExpVals_t exp_data[] = {
/* TOC */ { 0x690, 0x80000000 },
- /* GLOBAL_DATA */ { PAGESIZE, 0x8007D690 },
+ /* GLOBAL_DATA */ { PAGESIZE, 0x800AF690 },
/* SBE_IPL */ { 0, 0 },
/* HB_BASE_CODE */ { 0, 0 },
- /* HB_DATA */ { 128*KILOBYTE, 0x8007E690 },
+ /* HB_DATA */ { 128*KILOBYTE, 0x800B0690 },
/* HB_ERRLOGS */ { 0, 0 },
- /* HB_EXT_CODE */ { 0x7D000, 0x80000690 },
+ /* HB_EXT_CODE */ { 0xAF000, 0x80000690 },
/* HB_RUNTIME */ { 0, 0 },
/* PAYLOAD */ { 0, 0 },
/* PFW_LITE_CODE */ { 0, 0 },
OpenPOWER on IntegriCloud