summaryrefslogtreecommitdiffstats
path: root/src/usr/diag/attn/ipl/attnsvc.C
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr/diag/attn/ipl/attnsvc.C')
-rw-r--r--src/usr/diag/attn/ipl/attnsvc.C724
1 files changed, 724 insertions, 0 deletions
diff --git a/src/usr/diag/attn/ipl/attnsvc.C b/src/usr/diag/attn/ipl/attnsvc.C
new file mode 100644
index 000000000..4b6b75faa
--- /dev/null
+++ b/src/usr/diag/attn/ipl/attnsvc.C
@@ -0,0 +1,724 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/diag/attn/hostboot/attnsvc.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014 */
+/* [+] International Business Machines Corp. */
+/* */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/* */
+/* IBM_PROLOG_END_TAG */
+/**
+ * @file attnsvc.C
+ *
+ * @brief HBATTN background service class function definitions.
+ */
+
+#include <sys/msg.h>
+#include <errl/errlmanager.H>
+#include "ipl/attnsvc.H"
+#include "common/attntrace.H"
+#include "common/attnprd.H"
+#include "common/attnproc.H"
+#include "common/attnmem.H"
+#include "common/attntarget.H"
+
+using namespace std;
+using namespace PRDF;
+using namespace TARGETING;
+using namespace ERRORLOG;
+
+namespace ATTN
+{
+
+void getPbGp2Mask(uint64_t i_pos, void * i_data)
+{
+ uint64_t & mask = *static_cast<uint64_t *>(i_data);
+
+ uint64_t tmp = 0;
+ GP1::getCheckbits(i_pos, tmp);
+
+ mask |= tmp;
+}
+
+/**
+ * @brief calculated mask cache for ipoll
+ */
+class HostMask
+{
+ uint64_t iv_hostMask;
+ uint64_t iv_nonHostMask;
+
+ HostMask() : iv_hostMask(0), iv_nonHostMask(0)
+ {
+ IPOLL::getCheckbits(HOST, iv_hostMask);
+ IPOLL::forEach(~0, &iv_nonHostMask, &getIpollMask);
+
+ iv_nonHostMask = iv_nonHostMask & ~iv_hostMask;
+ }
+
+ static void getIpollMask(uint64_t i_type, void * i_data)
+ {
+ uint64_t & mask = *static_cast<uint64_t *>(i_data);
+
+ uint64_t tmp = 0;
+ IPOLL::getCheckbits(i_type, tmp);
+
+ mask |= tmp;
+ }
+
+ static HostMask & get()
+ {
+ static HostMask hm;
+
+ return hm;
+ }
+
+ public:
+
+ static uint64_t host()
+ {
+ return get().iv_hostMask;
+ }
+
+ static uint64_t nonHost()
+ {
+ return get().iv_nonHostMask;
+ }
+};
+
+errlHndl_t Service::configureInterrupts(
+ msg_q_t i_q,
+ ConfigureMode i_mode)
+{
+ errlHndl_t err = NULL;
+
+ // First register for Q
+ // This will set up the psi host bridge logic for
+ // lcl_err interrupt on all chips
+
+ if(i_mode == UP)
+ {
+ err = INTR::registerMsgQ(i_q,
+ ATTENTION,
+ INTR::ISN_LCL_ERR);
+ }
+
+ // setup the ITR macro for GPIO type host attentions,
+ // on all procs
+
+ if(!err)
+ {
+ TargetHandleList procs;
+ getTargetService().getAllChips(procs, TYPE_PROC);
+ TargetHandleList::iterator it = procs.begin();
+
+ while(it != procs.end())
+ {
+ uint64_t mask = 0;
+
+ // clear GPIO interrupt type status register
+
+ if(i_mode == UP)
+ {
+ err = putScom(*it, INTR_TYPE_LCL_ERR_STATUS_AND_REG,
+ 0);
+ }
+
+ if(err)
+ {
+ break;
+ }
+
+ // unmask GPIO interrupt type
+
+ mask = 0x8000000000000000ull;
+
+ err = putScom(
+ *it,
+ (i_mode == UP
+ ? INTR_TYPE_MASK_AND_REG
+ : INTR_TYPE_MASK_OR_REG),
+ i_mode == UP ? ~mask : mask);
+
+ if(err)
+ {
+ break;
+ }
+
+ // set GPIO interrupt type mode - or
+
+ if(i_mode == UP)
+ {
+ err = putScom(*it, INTR_TYPE_CONFIG_AND_REG,
+ ~mask);
+ }
+
+ if(err)
+ {
+ break;
+ }
+
+ // enable/disable MCSes
+
+ mask = 0;
+
+ GP1::forEach(~0, &mask, &getPbGp2Mask);
+
+ err = modifyScom(
+ *it,
+ GP2_REG,
+ i_mode == UP ? mask : ~mask,
+ i_mode == UP ? SCOM_OR : SCOM_AND);
+
+ if(err)
+ {
+ break;
+ }
+
+ // enable attentions in ipoll mask
+
+ mask = HostMask::nonHost();
+ mask |= HostMask::host();
+
+ // this doesn't have an and/or reg for some reason...
+
+ err = modifyScom(
+ *it,
+ IPOLL::address,
+ i_mode == UP ? ~mask : mask,
+ i_mode == UP ? SCOM_AND : SCOM_OR);
+
+ if(err)
+ {
+ break;
+ }
+
+ ++it;
+ }
+
+ if(!err && i_mode == DOWN)
+ {
+ if(NULL == INTR::unRegisterMsgQ(INTR::ISN_LCL_ERR))
+ {
+ ATTN_ERR("INTR did not find isn: 0x%07x",
+ INTR::ISN_LCL_ERR);
+ }
+ }
+ }
+ return err;
+}
+
+void * Service::intrTask(void * i_svc)
+{
+ // interrupt task loop
+
+ Service & svc = *static_cast<Service *>(i_svc);
+ bool shutdown = false;
+ msg_t * msg = NULL;
+
+ while(true)
+ {
+ // wait for a shutdown message or interrupt
+
+ shutdown = svc.intrTaskWait(msg);
+
+ if(shutdown)
+ {
+ break;
+ }
+
+ // got an interrupt. process it
+
+ svc.processIntrQMsg(*msg);
+ }
+ return NULL;
+}
+
+bool Service::intrTaskWait(msg_t * & o_msg)
+{
+ // wait for a shutdown message
+ // or an interrupt
+
+ bool shutdown = false;
+ msg_q_t q = iv_intrTaskQ;
+
+ o_msg = msg_wait(q);
+
+ ATTN_FAST("...intr task woke up");
+
+ shutdown = o_msg->type == SHUTDOWN;
+
+ if(shutdown)
+ {
+ // this was a shutdown message.
+ // ack the message and tell the
+ // task loop to exit
+
+ iv_intrTaskQ = 0;
+ iv_shutdownPrdTask = true;
+
+ msg_respond(q, o_msg);
+
+ ATTN_FAST("interrupt task shutting down");
+ }
+
+ return shutdown;
+}
+
+
+void Service::processIntrQMsgPreAck(const msg_t & i_msg)
+{
+ // this function should do as little as possible
+ // since the hw can't generate additional interrupts
+ // until the msg is acknowledged
+
+ TargetHandle_t proc = NULL;
+
+ INTR::XISR_t xisr;
+
+ xisr.u32 = i_msg.data[0];
+
+ TargetHandleList procs;
+ getTargetService().getAllChips(procs, TYPE_PROC);
+
+ TargetHandleList::iterator it = procs.begin();
+
+ // resolve the xisr to a proc target
+
+ while(it != procs.end())
+ {
+ uint64_t node = 0, chip = 0;
+
+ getTargetService().getAttribute(ATTR_FABRIC_NODE_ID, *it, node);
+ getTargetService().getAttribute(ATTR_FABRIC_CHIP_ID, *it, chip);
+
+ if(node == xisr.node
+ && chip == xisr.chip)
+ {
+ proc = *it;
+ break;
+ }
+
+ ++it;
+ }
+
+ uint64_t hostMask = HostMask::host();
+ uint64_t nonHostMask = HostMask::nonHost();
+ uint64_t data = 0;
+
+ // do the minimum that is required
+ // for sending EOI without getting
+ // another interrupt. for host attentions
+ // this is clearing the gpio interrupt
+ // type status register
+ // and for xstp,rec,spcl this is
+ // masking the appropriate bit in
+ // ipoll mask
+
+ // read the ipoll status register
+ // to determine the interrupt was
+ // caused by host attn or something
+ // else (xstp,rec,spcl)
+
+ errlHndl_t err = getScom(proc, IPOLL_STATUS_REG, data);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+
+ // assume everything is on
+
+ data = hostMask | nonHostMask;
+ }
+
+ if(data & hostMask)
+ {
+ // if host attention, clear the ITR macro gpio interrupt
+ // type status register.
+
+ err = putScom(proc, INTR_TYPE_LCL_ERR_STATUS_AND_REG, 0);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+ }
+
+ if(data & nonHostMask)
+ {
+ // mask local proc xstp,rec and/or special attns if on.
+
+ // the other thread might be trying to unmask
+ // on the same target. The mutex ensures
+ // neither thread corrupts the register.
+
+ mutex_lock(&iv_mutex);
+
+ err = modifyScom(proc, IPOLL::address, data & nonHostMask, SCOM_OR);
+
+ mutex_unlock(&iv_mutex);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+ }
+}
+
+void Service::processIntrQMsg(msg_t & i_msg)
+{
+ // processIntrQMsgPreAck function should do as little as possible
+ // since the hw can't generate additional interrupts until the
+ // msg is acknowledged
+
+ processIntrQMsgPreAck(i_msg);
+
+ // respond to the interrupt service so hw
+ // can generate additional interrupts
+
+ INTR::sendEOI(iv_intrTaskQ, &i_msg);
+
+ // wake up the prd task
+
+ mutex_lock(&iv_mutex);
+
+ iv_interrupt = true;
+
+ mutex_unlock(&iv_mutex);
+
+ sync_cond_signal(&iv_cond);
+}
+
+void* Service::prdTask(void * i_svc)
+{
+ // prd task loop
+
+ Service & svc = *static_cast<Service *>(i_svc);
+ bool shutdown = false;
+
+ TargetHandleList procs;
+ getTargetService().getAllChips(procs, TYPE_PROC);
+
+ // wait for a wakeup
+
+ while(true)
+ {
+ shutdown = svc.prdTaskWait();
+
+ if(shutdown)
+ {
+ // this was a shutdown wakeup.
+ // tell the prd task loop to exit
+
+ break;
+ }
+
+ // new attentions for prd to handle
+
+ svc.processAttentions(procs);
+ }
+
+ return NULL;
+}
+
+bool Service::prdTaskWait()
+{
+ // wait for a wakeup
+
+ bool shutdown = false;
+
+ mutex_lock(&iv_mutex);
+
+ while(!iv_interrupt && !iv_shutdownPrdTask)
+ {
+ sync_cond_wait(&iv_cond, &iv_mutex);
+ }
+
+ ATTN_FAST("...prd task woke up, shutdown: %d, pending: %d",
+ iv_shutdownPrdTask,
+ iv_interrupt);
+
+ if(iv_shutdownPrdTask)
+ {
+ swap(shutdown, iv_shutdownPrdTask);
+ }
+
+ iv_interrupt = false;
+
+ mutex_unlock(&iv_mutex);
+
+ if(shutdown)
+ {
+ ATTN_FAST("prd task shutting down");
+ }
+
+ return shutdown;
+}
+
+void Service::processAttentions(const TargetHandleList & i_procs)
+{
+ errlHndl_t err = NULL;
+ AttentionList attentions;
+
+ MemOps & memOps = getMemOps();
+ ProcOps & procOps = getProcOps();
+
+ do {
+
+ attentions.clear();
+
+ // enumerate the highest priority pending attention
+ // on every chip and then give the entire set to PRD
+
+ TargetHandleList::const_iterator pit = i_procs.end();
+
+ while(pit-- != i_procs.begin())
+ {
+ // enumerate proc local attentions (xstp,spcl,rec).
+
+ err = procOps.resolveIpoll(*pit, attentions);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+
+ // enumerate host attentions and convert
+ // to centaur targets
+
+ err = memOps.resolve(*pit, attentions);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+ }
+
+ err = getPrdWrapper().callPrd(attentions);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+
+ // unmask proc local attentions
+ // (xstp,rec,special) in ipoll mask
+
+ // any pending attentions will be found
+ // on the next pass
+
+ pit = i_procs.end();
+
+ while(pit-- != i_procs.begin())
+ {
+ mutex_lock(&iv_mutex);
+
+ // the other thread might be trying to mask
+ // on the same target. The mutex ensures
+ // neither thread corrupts the register.
+
+ err = modifyScom(
+ *pit,
+ IPOLL::address,
+ ~HostMask::nonHost(),
+ SCOM_AND);
+
+ mutex_unlock(&iv_mutex);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+ }
+
+ // if on a given Centaur with a pending attention
+ // on an MBA, an attention comes on in the other MBA
+ // we don't get an interrupt for that. So make another
+ // pass and check for that.
+
+ } while(!attentions.empty());
+}
+
+errlHndl_t Service::stop()
+{
+ ATTN_SLOW("shutting down...");
+
+ mutex_lock(&iv_mutex);
+
+ tid_t intrTask = iv_intrTask;
+ tid_t prdTask = iv_prdTask;
+
+ msg_q_t q = iv_intrTaskQ;
+
+ iv_intrTask = 0;
+ iv_prdTask = 0;
+
+ mutex_unlock(&iv_mutex);
+
+ if(intrTask)
+ {
+ errlHndl_t err = configureInterrupts(q, DOWN);
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+
+ msg_t * shutdownMsg = msg_allocate();
+ shutdownMsg->type = SHUTDOWN;
+
+ msg_sendrecv(q, shutdownMsg);
+ msg_free(shutdownMsg);
+
+ task_wait_tid(intrTask, 0, 0);
+
+ msg_q_destroy(q);
+ }
+
+ if(prdTask)
+ {
+ sync_cond_signal(&iv_cond);
+ task_wait_tid(prdTask, 0, 0);
+ }
+
+ ATTN_SLOW("..shutdown complete");
+
+ return 0;
+}
+
+bool Service::startIntrTask()
+{
+ if(!iv_intrTask)
+ {
+ iv_intrTask = task_create(&intrTask, this);
+ }
+
+ return iv_intrTask != 0;
+}
+
+bool Service::startPrdTask()
+{
+ if(!iv_prdTask)
+ {
+ iv_prdTask = task_create(&prdTask, this);
+ }
+
+ return iv_prdTask != 0;
+}
+
+errlHndl_t Service::start()
+{
+ errlHndl_t err = NULL;
+ bool cleanStartup = false;
+
+ ATTN_SLOW("starting...");
+
+ mutex_lock(&iv_mutex);
+
+ do {
+
+ if(!iv_intrTaskQ) {
+
+ // register msg q with interrupt
+ // service for attention type interrupts
+
+ msg_q_t q = msg_q_create();
+
+ err = configureInterrupts(q, UP);
+
+ if(err)
+ {
+ msg_q_destroy(q);
+ break;
+ }
+
+ iv_intrTaskQ = q;
+ }
+
+ if(!startIntrTask())
+ {
+ break;
+ }
+
+ if(!startPrdTask())
+ {
+ break;
+ }
+
+ cleanStartup = true;
+
+ } while(0);
+
+ tid_t prd = iv_prdTask, intr = iv_intrTask;
+
+ mutex_unlock(&iv_mutex);
+
+ if(!cleanStartup)
+ {
+ errlHndl_t err2 = stop();
+
+ if(err2)
+ {
+ errlCommit(err2, ATTN_COMP_ID);
+ }
+ }
+ else
+ {
+ ATTN_SLOW("..startup complete, intr: %d, prd: %d", intr, prd);
+ }
+
+ return err;
+}
+
+Service::Service() :
+ iv_interrupt(false),
+ iv_intrTaskQ(0),
+ iv_shutdownPrdTask(false),
+ iv_prdTask(0),
+ iv_intrTask(0)
+{
+ mutex_init(&iv_mutex);
+ sync_cond_init(&iv_cond);
+}
+
+Service::~Service()
+{
+ errlHndl_t err = stop();
+
+ if(err)
+ {
+ errlCommit(err, ATTN_COMP_ID);
+ }
+
+ sync_cond_destroy(&iv_cond);
+ mutex_destroy(&iv_mutex);
+}
+
+bool Service::running()
+{
+ bool running;
+
+ mutex_lock(&iv_mutex);
+
+ running = 0 != iv_intrTaskQ;
+
+ mutex_unlock(&iv_mutex);
+
+ return running;
+}
+}
OpenPOWER on IntegriCloud