summaryrefslogtreecommitdiffstats
path: root/src/usr/ipmibase
diff options
context:
space:
mode:
authorAndrew Jeffery <andrewrj@au1.ibm.com>2018-09-28 14:48:58 +0930
committerDaniel M. Crowell <dcrowell@us.ibm.com>2018-10-10 13:39:41 -0500
commit5fc457309f2c6bad2bf1464a4cc7756692ede3b8 (patch)
treef7a1190a397d8fe77cd232548f5e8615c0dedab4 /src/usr/ipmibase
parent1b481183921d9877a5693219249bb9c4cd8ccf69 (diff)
downloadtalos-hostboot-5fc457309f2c6bad2bf1464a4cc7756692ede3b8.tar.gz
talos-hostboot-5fc457309f2c6bad2bf1464a4cc7756692ede3b8.zip
ipmi: Split into ipmibase and ipmiext modules
Split the IPMI module into base and ext portions, with the BT interface, device driver and resource provider in the base portion, and all remaining IPMI functionality in the ext portion. The split is in preparation for moving the base functionality in the hostboot base image. Change-Id: Iec864f96240d79f4fadd5519d2ef46437d07c1fd Signed-off-by: Andrew Jeffery <andrewrj@au1.ibm.com> Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/66792 Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com> Reviewed-by: Corey V. Swenson <cswenson@us.ibm.com> Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com> Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr/ipmibase')
-rw-r--r--src/usr/ipmibase/HBconfig10
-rw-r--r--src/usr/ipmibase/ipmibt.C399
-rw-r--r--src/usr/ipmibase/ipmibt.H184
-rw-r--r--src/usr/ipmibase/ipmiconfig.C45
-rw-r--r--src/usr/ipmibase/ipmiconfig.H55
-rw-r--r--src/usr/ipmibase/ipmidd.C436
-rw-r--r--src/usr/ipmibase/ipmidd.H174
-rw-r--r--src/usr/ipmibase/ipmimsg.C69
-rw-r--r--src/usr/ipmibase/ipmimsg.H133
-rw-r--r--src/usr/ipmibase/ipmirp.C1140
-rw-r--r--src/usr/ipmibase/ipmirp.H284
-rw-r--r--src/usr/ipmibase/ipmiselrecord.C54
-rw-r--r--src/usr/ipmibase/makefile34
13 files changed, 3017 insertions, 0 deletions
diff --git a/src/usr/ipmibase/HBconfig b/src/usr/ipmibase/HBconfig
new file mode 100644
index 000000000..0980a3eb2
--- /dev/null
+++ b/src/usr/ipmibase/HBconfig
@@ -0,0 +1,10 @@
+config BMC_IPMI
+ default y if BMC_BT_LPC_IPMI
+ help
+ Determines if Hostboot communicates to the BMC using IPMI
+
+config BMC_BT_LPC_IPMI
+ default n
+ depends on BMC_IPMI
+ help
+ Determines if the BMC uses the LPC bus for block-transfer IPMI traffic
diff --git a/src/usr/ipmibase/ipmibt.C b/src/usr/ipmibase/ipmibt.C
new file mode 100644
index 000000000..1f99a3384
--- /dev/null
+++ b/src/usr/ipmibase/ipmibt.C
@@ -0,0 +1,399 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmibt.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 ipmibt.C
+ * @brief code for the IPMI BT message class
+ */
+
+#include <devicefw/driverif.H>
+#include <devicefw/userif.H>
+#include <ipmi/ipmi_reasoncodes.H>
+#include <errl/errlmanager.H>
+
+#include <util/lockfree/counter.H>
+
+#include "ipmibt.H"
+#include "ipmirp.H"
+#include <ipmi/ipmiif.H>
+#include <errno.h>
+#include <config.h>
+
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"bt: " printf_string,##args)
+
+namespace IPMI
+{
+ ///
+ /// @brief msg ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTMessage::BTMessage(const command_t& i_cmd, const uint8_t i_len,
+ uint8_t* i_data):
+ Message(i_cmd, i_len, i_data)
+ {
+ // Sometimes we need to get back to this IPMI msg from the msg_t,
+ // and sometimes we need to get the msg_t from the IPMI msg. So they
+ // know about each other.
+ iv_msg->extra_data = static_cast<void*>(this);
+ iv_msg->type = MSG_STATE_SEND;
+ }
+
+ ///
+ /// @brief Transimit - send the data out the device interface
+ ///
+ errlHndl_t BTMessage::phy_xmit(void)
+ {
+ // When a uint8_t is constructed, it's initialied to 0. So,
+ // this initializes the sequence counter to 0.
+ static Util::Lockfree::Counter<uint8_t> seq;
+
+ // Assign a "unique" sequence number. Note that we don't
+ // leverage the network function to create a sequence
+ // number, we just keep an 8 bit counter. This *should*
+ // be ok - it means we will get back the response to any
+ // particular message before we send another 254 messages.
+ // This seems safe.
+ iv_seq = seq.next();
+
+ // Initialize the error state of the message
+ iv_state = 0;
+
+ size_t unused_size;
+ errlHndl_t err = deviceOp(DeviceFW::WRITE,
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
+ static_cast<void*>(this),
+ unused_size,
+ DeviceFW::IPMIBT);
+
+ // If we're not going to remain on the i_sendq, we need to delete
+ // the data.
+ if ((err) || (iv_state != EAGAIN))
+ {
+ delete[] iv_data;
+ iv_data = NULL;
+ }
+
+ if (!err)
+ {
+ // If there wasn't an error, and we don't see EAGAIN, we need to
+ // queue up for a response. Note we queue up both synchronus and
+ // asynchronous messages, and let the subclasses handle what
+ // happens when the response arrives (because it will.)
+ if (iv_state != EAGAIN)
+ {
+ Singleton<IpmiRP>::instance().queueForResponse(*this);
+ }
+
+ // Otherwise we had no error, but were told EAGAIN, which means the
+ // interface was busy.
+ else
+ {
+ IPMI_TRAC(INFO_MRK "busy, queue head %x:%x", iv_netfun, iv_cmd);
+ }
+ }
+
+ return err;
+ }
+
+ ///
+ /// @brief Receive - get bits off the block-transfer interface
+ ///
+ errlHndl_t BTMessage::recv(void)
+ {
+ // Check to make sure we are in the right state (coding error)
+ assert(iv_data == NULL);
+
+ // Go down to the device and read. Note the driver is BT specific
+ // and we're BT specific so we can send down a BTMessage object.
+ size_t unused_length;
+ errlHndl_t err = deviceOp(DeviceFW::READ,
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
+ static_cast<void*>(this),
+ unused_length,
+ DeviceFW::IPMIBT);
+ if (err)
+ {
+ delete[] iv_data;
+
+ // If the reading the response fails, the caller may still call
+ // delete[] on the pointer we return to them. This makes sure that
+ // for this case, they're just deleting NULL.
+ iv_data = NULL;
+ iv_len = 0;
+ }
+
+ // For BT messages, the sequence number is the key - for other xports
+ // it might be different. Note the sequence number is a reference to
+ // our base's iv_key, so we're done.
+ return err;
+ }
+
+ ///
+ /// @brief BTSyncMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTSyncMessage::BTSyncMessage(const command_t& i_cmd,
+ const uint8_t i_len,
+ uint8_t* i_data):
+ BTMessage(i_cmd, i_len, i_data)
+ {
+ }
+
+ ///
+ /// @brief BTAsyncMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTAsyncMessage::BTAsyncMessage(const command_t& i_cmd,
+ const uint8_t i_len,
+ uint8_t* i_data):
+ BTMessage(i_cmd, i_len, i_data)
+ {
+ }
+
+ ///
+ /// @brief BTAsyncReadEventMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTAsyncReadEventMessage::BTAsyncReadEventMessage(const command_t& i_cmd,
+ const uint8_t i_len,
+ uint8_t* i_data):
+ BTAsyncMessage(i_cmd, i_len, i_data)
+ {
+ }
+
+ ///
+ /// @brief sync msg transmit
+ ///
+ bool BTSyncMessage::xmit(void)
+ {
+ errlHndl_t err = BTMessage::phy_xmit();
+
+ if (err)
+ {
+ // Something went wrong, so we need to respond back with the error
+ iv_errl = err;
+
+ msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue();
+ int rc = msg_respond(mq, iv_msg);
+ if (rc)
+ {
+ // Yuk. We can't respond back to our caller with that error. So,
+ // we'll commit it. I don't see much sense in creating another
+ // error log, so we'll just trace the msg_respond() failure.
+ IPMI_TRAC(ERR_MRK "msg_respond() i/o error (transmit) %d", rc);
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ iv_errl = NULL;
+ }
+ }
+
+ // If we had an i/o error we want the idle loop to stop
+ // If we got EAGAIN we want the idle loop to stop as we just
+ // put a message on the queue which couldn't be sent.
+ return (iv_state != 0);
+ }
+
+ ///
+ /// @brief async msg transmit
+ ///
+ bool BTAsyncMessage::xmit(void)
+ {
+ errlHndl_t err = BTMessage::phy_xmit();
+
+ if (err)
+ {
+ // Not much we're going to do here, so just commit the error.
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+
+ // If we had an i/o error we want the idle loop to stop.
+ // If we got EAGAIN we want the idle loop to stop as we just
+ // put a message on the queue which couldn't be sent.
+ return (iv_state != 0);
+ }
+
+ ///
+ /// @brief sync handle response
+ ///
+ void BTSyncMessage::response(msg_q_t i_msgQ)
+ {
+ // Send the response to the original caller of sendrecv()
+ int rc = msg_respond(i_msgQ, iv_msg);
+ if (rc)
+ {
+ // Not much we're going to do here, so lets commit an error and
+ // the original request will timeout.
+ IPMI_TRAC(ERR_MRK "msg_respond() i/o error (response) %d", rc);
+
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISRV_REPLY
+ * @reasoncode IPMI::RC_INVALID_QRESPONSE
+ * @userdata1 rc from msg_respond()
+ * @devdesc msg_respond() failed
+ * @custdesc Firmware error during system boot
+ */
+ errlHndl_t err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISRV_REPLY,
+ IPMI::RC_INVALID_QRESPONSE,
+ rc,
+ 0,
+ true);
+
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+
+ // Frotz the response data
+ delete[] iv_data;
+ iv_data = NULL;
+ }
+ }
+
+ ///
+ /// @brief async msg response
+ ///
+ void BTAsyncMessage::response(msg_q_t)
+ {
+ // If our completion code isn't CC_OK, lets log that fact. There's
+ // not much we can do, but at least this might give a hint that
+ // something is awry. Note the caller doesn't care, or this would
+ // be synchronous.
+ if (iv_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "async message (%x:%x seq %d) completion code %x",
+ iv_netfun, iv_cmd, iv_seq, iv_cc);
+ }
+
+ // Yes, this is OK - there is no further reference to this object.
+ delete this;
+ }
+
+ ///
+ /// @brief handle the read of an event (OEM SEL)
+ ///
+ void BTAsyncReadEventMessage::response(msg_q_t)
+ {
+ do {
+ // If our completion code isn't CC_OK, lets log that fact. There's
+ // not much we can do, but at least this might give a hint that
+ // something is awry.
+ if (iv_cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC(ERR_MRK "read event message (%x:%x seq %d) "
+ "completion code %x",
+ iv_netfun, iv_cmd, iv_seq, iv_cc);
+
+ if (iv_cc == IPMI::CC_CMDSPC1)
+ {
+ // We got a completion code with 0x80, which is no data
+ // Let's trace the event, but not log an error.
+ IPMI_TRAC(ERR_MRK "SEL returned with no data, not logging "
+ "an error");
+ break;
+ }
+
+ /*@
+ * @errortype ERRL_SEV_INFORMATIONAL
+ * @moduleid IPMI::MOD_IPMISRV_REPLY
+ * @reasoncode IPMI::RC_READ_EVENT_FAILURE
+ * @userdata1 command of message
+ * @userdata2 completion code
+ * @devdesc an async completion code was not CC_OK
+ * @custdesc Unexpected IPMI completion code from the BMC
+ */
+ errlHndl_t err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_INFORMATIONAL,
+ IPMI::MOD_IPMISRV_REPLY,
+ IPMI::RC_READ_EVENT_FAILURE,
+ iv_cmd,
+ iv_cc,
+ true);
+
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ break;
+ }
+
+ // Before we self destruct, we need to turn the data collected in to
+ // a record we can pass to the waiting event handler.
+ Singleton<IpmiRP>::instance().postEvent(new IPMI::oemSEL(iv_data));
+
+ } while(false);
+
+ // Yes, this is OK - there is no further reference to this object.
+ delete this;
+ }
+
+ ///
+ /// @brief static factory
+ ///
+ /// Implement the factory member in terms of the BT interface. This is done
+ /// in each of the back-end implementations, only one of which should be
+ /// configured in at build time.
+ ///
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (allocated space)
+ /// @param[in] i_type, synchronous or async
+ ///
+ /// @return a pointer to a new'd Message object
+ ///
+ Message* Message::factory(const command_t& i_cmd, const uint8_t i_len,
+ uint8_t* i_data, const message_type i_type)
+ {
+ Message* new_message = NULL;
+
+ switch(i_type)
+ {
+ case TYPE_SYNC:
+ new_message = new BTSyncMessage(i_cmd, i_len, i_data);
+ break;
+ case TYPE_ASYNC:
+ new_message = new BTAsyncMessage(i_cmd, i_len, i_data);
+ break;
+ case TYPE_EVENT:
+ new_message = new BTAsyncReadEventMessage(i_cmd, i_len, i_data);
+ break;
+ default:
+ // We have ourselves a bug
+ assert(false, "ipmi message factory: unk type %d\n", i_type);
+ break;
+ }
+
+ return new_message;
+ }
+};
diff --git a/src/usr/ipmibase/ipmibt.H b/src/usr/ipmibase/ipmibt.H
new file mode 100644
index 000000000..5fae5dc5e
--- /dev/null
+++ b/src/usr/ipmibase/ipmibt.H
@@ -0,0 +1,184 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmibt.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 */
+#ifndef __IPMI_IPMIBT_H
+#define __IPMI_IPMIBT_H
+
+#include "ipmimsg.H"
+
+/// So, IPMI_BT_MAX_DATA == BMC receive size - IPMI_BT_HEADER_SIZE;
+#define IPMI_BT_HEADER_SIZE 3
+
+namespace IPMI
+{
+ // IPMI block-transfer message base class
+ class BTMessage : public Message
+ {
+ public:
+ ///
+ /// @brief msg ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_data, the data for the command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTMessage(const command_t& i_cmd = no_command(),
+ const uint8_t i_len = 0,
+ uint8_t* i_data = NULL);
+
+ virtual ~BTMessage(void)
+ {}
+
+ ///
+ /// @brief transmit a message.
+ /// @return Error from operation
+ ///
+ errlHndl_t phy_xmit(void);
+
+ ///
+ /// @brief transmit a message.
+ /// @return true iff there was no transmission error
+ ///
+ /// @note we're not pure abstract as we want to be able to
+ /// instantiate an object of BTMessage for reading.
+ ///
+ virtual bool xmit(void)
+ {return true;}
+
+ ///
+ /// @brief receive a message.
+ ///
+ errlHndl_t recv(void);
+
+ ///
+ /// @brief complete the processing when a response arrives
+ /// @param[in] i_msgQ, the resource providers message queue
+ /// @return void
+ /// @note we're not pure abstract as we want to be able to
+ /// instantiate an object of BTMessage for reading.
+ ///
+ virtual void response(msg_q_t i_msgQ)
+ {return;}
+
+ ///
+ /// @brief the size of the BT header
+ /// @param void
+ /// @return size_t, the header size
+ /// @note IPMI_BT_MAX_DATA == BMC receive size - IPMI_BT_HEADER_SIZE;
+ ///
+ size_t header_size(void)
+ { return IPMI_BT_HEADER_SIZE; }
+ };
+
+ // IPMI BT synchronous message
+ class BTSyncMessage : public BTMessage
+ {
+ public:
+ ///
+ /// @brief BTSyncMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTSyncMessage(const command_t& i_cmd,
+ const uint8_t i_len, uint8_t* i_data);
+
+ ///
+ /// @brief transmit a message.
+ ///
+ /// @return true iff there was no transmission error
+ ///
+ bool xmit(void);
+
+ ///
+ /// @brief complete the processing when a response arrives
+ /// @param[in] i_msgQ, the resource providers message queue
+ /// @return void
+ ///
+ void response(msg_q_t i_msgQ);
+
+ };
+
+ // IPMI BT asynchronous message
+ class BTAsyncMessage : public BTMessage
+ {
+ public:
+ ///
+ /// @brief BTSyncMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTAsyncMessage(const command_t& i_cmd,
+ const uint8_t i_len, uint8_t* i_data);
+
+ ///
+ /// @brief BTSyncMessage dtor
+ ///
+ virtual ~BTAsyncMessage(void)
+ { delete[] iv_data; }
+
+ ///
+ /// @brief transmit a message.
+ ///
+ /// @return true iff there was no transmission error
+ ///
+ bool xmit(void);
+
+ ///
+ /// @brief complete the processing when a response arrives
+ /// @param[in] i_msgQ, the resource providers message queue
+ /// @return void
+ ///
+ void response(msg_q_t i_msgQ);
+ };
+
+ // IPMI BT asynchronous read event message. Reading events is sort
+ // of an asynch message; we fire the request from the resource
+ // provider thread (so we don't want to wait.) The response is
+ // special in that we don't just delete ourselves, but rather turn
+ // the response (OEM SEL) into a message for an event handler.
+ class BTAsyncReadEventMessage : public BTAsyncMessage
+ {
+ public:
+ ///
+ /// @brief BTASyncReadEventMessage ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ BTAsyncReadEventMessage(const command_t& i_cmd,
+ const uint8_t i_len, uint8_t* i_data);
+
+ ///
+ /// @brief complete the processing when a response arrives
+ /// @param[in] i_msgQ, the resource providers message queue
+ /// @return void
+ ///
+ void response(msg_q_t i_msgQ);
+ };
+
+}; // end namespace IPMI
+
+#endif
diff --git a/src/usr/ipmibase/ipmiconfig.C b/src/usr/ipmibase/ipmiconfig.C
new file mode 100644
index 000000000..269ae00b2
--- /dev/null
+++ b/src/usr/ipmibase/ipmiconfig.C
@@ -0,0 +1,45 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmiconfig.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 */
+
+#include <stdint.h>
+#include "ipmiconfig.H"
+
+ //
+ // Information contained in the Get Interface Capabilities command
+ //
+ // Request to response time default, in seconds
+const uint8_t IPMI::g_bmc_timeout = 5;
+
+ // Number of allowed outstanding requests default
+const uint8_t IPMI::g_outstanding_req = 0x01;
+
+ // The size of the BMC input buffer default (our write)
+const uint8_t IPMI::g_xmit_buffer_size = 0x40;
+
+ // The size of the BMC transmit buffer default (our read)
+const uint8_t IPMI::g_recv_buffer_size = 0x40;
+
+ // How many times we should retry a message if the BMC timesout default
+const uint8_t IPMI::g_retries = 0x00;
diff --git a/src/usr/ipmibase/ipmiconfig.H b/src/usr/ipmibase/ipmiconfig.H
new file mode 100644
index 000000000..07e027f21
--- /dev/null
+++ b/src/usr/ipmibase/ipmiconfig.H
@@ -0,0 +1,55 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmiconfig.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2014,2018 */
+/* [+] 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 */
+
+#ifndef __IPMI_IPMICONFIG_H
+#define __IPMI_IMPICONFIG_H
+
+/**
+ * @file ipmiconfig.H
+ * @brief IPMI service provider defaults and constants
+ */
+
+namespace IPMI
+{
+ //
+ // Information contained in the Get Interface Capabilities command
+ //
+ // Request to response time default, in seconds
+ extern const uint8_t g_bmc_timeout;
+
+ // Number of allowed outstanding requests default
+ extern const uint8_t g_outstanding_req;
+
+ // The size of the BMC input buffer default (our write)
+ extern const uint8_t g_xmit_buffer_size;
+
+ // The size of the BMC transmit buffer default (our read)
+ extern const uint8_t g_recv_buffer_size;
+
+ // How many times we should retry a message if the BMC timesout default
+ extern const uint8_t g_retries;
+};
+
+#endif
diff --git a/src/usr/ipmibase/ipmidd.C b/src/usr/ipmibase/ipmidd.C
new file mode 100644
index 000000000..0e155a808
--- /dev/null
+++ b/src/usr/ipmibase/ipmidd.C
@@ -0,0 +1,436 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmidd.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2011,2018 */
+/* [+] 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 ipmidd.C
+ *
+ * @brief Implementation of the IPMI Device Driver
+ */
+
+/*****************************************************************************/
+// I n c l u d e s
+/*****************************************************************************/
+#include <devicefw/driverif.H>
+#include <trace/interface.H>
+#include <errl/errlentry.H>
+#include <errl/errlmanager.H>
+#include <errl/errludlogregister.H>
+#include <errl/errludstring.H>
+#include "ipmidd.H"
+#include <ipmi/ipmiif.H>
+#include <initservice/initserviceif.H>
+#include <util/align.H>
+#include <lpc/lpcif.H>
+#include <config.h>
+
+#include <sys/msg.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/task.h>
+
+/*****************************************************************************/
+// D e f i n e s
+/*****************************************************************************/
+
+trace_desc_t* g_trac_ipmi;
+TRAC_INIT(&g_trac_ipmi, IPMI_COMP_NAME, 6*KILOBYTE, TRACE::BUFFER_SLOW);
+
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"dd: " printf_string,##args)
+
+/**
+ * @brief Performs an IPMI Message Read Operation
+ * This function performs a IPMI Message Read operation. It follows a
+ * pre-defined prototype functions in order to be registered with the
+ * device-driver framework.
+ *
+ * @param[in] i_opType Operation type READ
+ * @param[in] i_target IPMI target, ignored use the master sentinel
+ * @param[out] o_buffer Pointer to a BT message we're going to fill in.
+ * @param[out] o_buflen Always sizeof(uint8_t)
+ * @param[in] i_accessType DeviceFW::IPMIBT
+ * @param[in] i_args This is an argument list for DD framework.
+ * In this function, it is unused
+ * @return errlHndl_t
+ */
+errlHndl_t ddRead(DeviceFW::OperationType i_opType,
+ TARGETING::Target* i_target,
+ void* o_buffer,
+ size_t& o_buflen,
+ int64_t i_accessType,
+ va_list i_args)
+{
+ // If someone passed in the wrong target, we have a coding error
+ assert(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target,
+ "ipmi read expects master processor target");
+
+ // We are the BT driver, so it's safe to assume they sent us a BTMessage.
+ return Singleton<IpmiDD>::instance().receive(
+ static_cast<IPMI::BTMessage*>(o_buffer));
+}
+
+/**
+ * @brief Performs an IPMI Message Write Operation
+ * This function performs a IPMI Message Write operation. It follows a
+ * pre-defined prototype functions in order to be registered with the
+ * device-driver framework.
+ *
+ * @param[in] i_opType Operation type WRITE
+ * @param[in] i_target IPMI target, ignored use the master sentinel
+ * @param[in] i_buffer Pointer to a BT message we're going to transmit
+ * @param[in] i_buflen Always sizeof(uint8_t)
+ * @param[in] i_accessType DeviceFW::IPMIBT
+ * @param[in] i_args This is an argument list for DD framework.
+ * In this function, it is unused
+ * @return errlHndl_t
+ */
+errlHndl_t ddWrite(DeviceFW::OperationType i_opType,
+ TARGETING::Target* i_target,
+ void* i_buffer,
+ size_t& i_buflen,
+ int64_t i_accessType,
+ va_list i_args)
+{
+ // If someone passed in the wrong target, we have a coding error
+ assert(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target,
+ "ipmi write expects master processor target");
+
+ // We are the BT driver, so it's safe to assume they sent us a BTMessage
+ return Singleton<IpmiDD>::instance().send(
+ static_cast<IPMI::BTMessage*>(i_buffer));
+}
+
+// Register IPMIDD access functions to DD framework
+DEVICE_REGISTER_ROUTE(DeviceFW::READ,
+ DeviceFW::IPMIBT,
+ TARGETING::TYPE_PROC,
+ ddRead);
+
+DEVICE_REGISTER_ROUTE(DeviceFW::WRITE,
+ DeviceFW::IPMIBT,
+ TARGETING::TYPE_PROC,
+ ddWrite);
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Read an address from LPC space
+ */
+errlHndl_t IpmiDD::readLPC(const uint32_t i_addr, uint8_t& o_data)
+{
+ static size_t size = sizeof(uint8_t);
+ errlHndl_t err = deviceOp( DeviceFW::READ,
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
+ static_cast<void*>(&o_data),
+ size,
+ DEVICE_LPC_ADDRESS(LPC::TRANS_IO, i_addr) );
+ return err;
+}
+
+/**
+ * @brief Write an address from LPC space
+ */
+errlHndl_t IpmiDD::writeLPC(const uint32_t i_addr,
+ uint8_t i_data)
+{
+ static size_t size = sizeof(uint8_t);
+ errlHndl_t err = deviceOp(DeviceFW::WRITE,
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
+ static_cast<void*>(&i_data),
+ size,
+ DEVICE_LPC_ADDRESS(LPC::TRANS_IO, i_addr) );
+ return err;
+}
+
+/**
+ * @brief Static function wrapper to pass into task_create
+ */
+static void* poll_control_register( void* /* unused */ )
+{
+ IPMI_TRAC(ENTER_MRK "poll_control_register" );
+ Singleton<IpmiDD>::instance().pollCtrl();
+ return NULL;
+}
+
+/**
+ * @brief Poll the control register
+ */
+void IpmiDD::pollCtrl(void)
+{
+ IPMI_TRAC(ENTER_MRK "poll_control_register" );
+
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ // We send a message to this message queue when the ipmi state changes.
+ // Don't free these messages - these messages are sent async so the
+ // consumer will free the message.
+ msg_t* msg = NULL;
+
+ uint8_t ctrl = 0;
+
+ while(1)
+ {
+ mutex_lock(&iv_mutex);
+
+ errlHndl_t err = readLPC(REG_CONTROL, ctrl);
+
+ // Not sure there's much we can do here but commit the log
+ // and let this thread fail.
+ if (err)
+ {
+ mutex_unlock(&iv_mutex);
+ IPMI_TRAC(ERR_MRK "polling loop encountered an error, exiting");
+ errlCommit(err, IPMI_COMP_ID);
+ break;
+ }
+ else
+ {
+ // If we're idle, tell the resoure provider to check for any
+ // pending messages which were delayed due to contention. But don't
+ // send a message every time we see idle, only if there we suspect
+ // we sent EAGAINs.
+ if ((ctrl & IDLE_STATE) == 0)
+ {
+ if (iv_eagains)
+ {
+ msg = msg_allocate();
+ msg->type = IPMI::MSG_STATE_IDLE;
+ msg_send(iv_eventQ, msg);
+ iv_eagains = false;
+ }
+ }
+ // If we see the B2H_ATN, there's a response waiting
+ else if (ctrl & CTRL_B2H_ATN)
+ {
+ msg = msg_allocate();
+ msg->type = IPMI::MSG_STATE_RESP;
+ msg_send(iv_eventQ, msg);
+ }
+
+ // If we see the SMS_ATN, there's an event waiting
+ else if (ctrl & CTRL_SMS_ATN)
+ {
+ IPMI_TRAC(INFO_MRK "sending state sms/event");
+ msg = msg_allocate();
+ msg->type = IPMI::MSG_STATE_EVNT;
+ msg_send(iv_eventQ, msg);
+
+ // Clear the SMS bit.
+ errlHndl_t err = writeLPC(REG_CONTROL, CTRL_SMS_ATN);
+
+ // Commit this error. There's no one to tell ...
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+ }
+ }
+ mutex_unlock(&iv_mutex);
+
+ nanosleep(0, WAIT_TIME);
+ }
+ IPMI_TRAC(EXIT_MRK "poll_control_register" );
+}
+
+/**
+ * @brief Performs a reset of the BT hardware
+ */
+inline errlHndl_t IpmiDD::reset(void)
+{
+ IPMI_TRAC("resetting the IPMI BT interface");
+ return writeLPC(REG_INTMASK, INT_BMC_HWRST);
+}
+
+/**
+ * @brief Performs an IPMI Message Write Operation
+ */
+errlHndl_t IpmiDD::send(IPMI::BTMessage* i_msg)
+{
+ errlHndl_t err = NULL;
+ uint8_t ctrl = 0;
+
+ mutex_lock(&iv_mutex);
+ do
+ {
+ err = readLPC(REG_CONTROL, ctrl);
+ if (err) { break; }
+
+ // If the interface isn't idle, tell the caller to come back
+ if ((ctrl & IDLE_STATE) != 0)
+ {
+ i_msg->iv_state = EAGAIN;
+ iv_eagains = true;
+ continue;
+ }
+
+ // Tell the interface we're writing. Per p. 135 of the
+ // spec we *do not* set H_BUSY.
+ err = writeLPC(REG_CONTROL, CTRL_CLR_WR_PTR);
+ if (err) { break; }
+
+ // Add the header size on as req_len is only the length of the data
+ err = writeLPC(REG_HOSTBMC, i_msg->iv_len + IPMI_BT_HEADER_SIZE);
+ if (err) { break; }
+
+ err = writeLPC(REG_HOSTBMC, i_msg->iv_netfun);
+ if (err) { break; }
+
+ err = writeLPC(REG_HOSTBMC, i_msg->iv_seq);
+ if (err) { break; }
+
+ err = writeLPC(REG_HOSTBMC, i_msg->iv_cmd);
+ if (err) { break; }
+
+ for( size_t i = 0; (i < i_msg->iv_len) && (err == NULL); ++i)
+ {
+ err = writeLPC(REG_HOSTBMC, i_msg->iv_data[i]);
+ }
+ if (err) { break; }
+
+ // If all is well, alert the host we sent bits.
+ err = writeLPC(REG_CONTROL, CTRL_H2B_ATN);
+ if (err) {break;}
+
+ } while(false);
+
+ mutex_unlock(&iv_mutex);
+
+ // If we have an error, try to reset the interface.
+ if (err)
+ {
+ reset();
+ }
+
+ // Don't bother reporting a write if we returned EAGAIN, the
+ // upper layers will report the re-queue or whatever.
+ if (i_msg->iv_state != EAGAIN)
+ {
+ IPMI_TRAC(INFO_MRK "write %s %x:%x seq %x len %x",
+ err ? "err" : "ok",
+ i_msg->iv_netfun, i_msg->iv_cmd, i_msg->iv_seq,
+ i_msg->iv_len);
+ }
+
+ return err;
+}
+
+/**
+ * @brief Read a response to an issued command, or an sms
+ */
+errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg)
+{
+ errlHndl_t err = NULL;
+ uint8_t ctrl = 0;
+ bool marked_busy = false;
+
+ mutex_lock(&iv_mutex);
+
+ do
+ {
+ err = readLPC(REG_CONTROL, ctrl);
+ if (err) { break; }
+
+ // Tell the interface we're busy.
+ err = writeLPC(REG_CONTROL, CTRL_H_BUSY);
+ if (err) {break;}
+
+ marked_busy = true;
+
+ // Clear the pending state from the control register.
+ // Note the spec distinctly says "after setting H_BUSY,
+ // the host should clear this bit" - not at the same time.
+ // This is the hand-shake; H_BUSY gates the BMC which allows
+ // us to clear the ATN bits. Don't get fancy.
+ err = writeLPC(REG_CONTROL, CTRL_B2H_ATN);
+ if (err) {break;}
+
+ // Tell the interface we're reading
+ err = writeLPC(REG_CONTROL, CTRL_CLR_RD_PTR);
+ if (err) {break;}
+
+ // The first byte is the length, grab it so we can allocate a buffer.
+ err = readLPC(REG_HOSTBMC, o_msg->iv_len);
+ if (err) { break; }
+
+ o_msg->iv_len -= IPMI_BT_HEADER_SIZE + 1;
+
+ err = readLPC(REG_HOSTBMC, o_msg->iv_netfun);
+ if (err) { break; }
+
+ err = readLPC(REG_HOSTBMC, o_msg->iv_seq);
+ if (err) { break; }
+
+ err = readLPC(REG_HOSTBMC, o_msg->iv_cmd);
+ if (err) { break; }
+
+ err = readLPC(REG_HOSTBMC, o_msg->iv_cc);
+ if (err) { break; }
+
+ o_msg->iv_data = new uint8_t[o_msg->iv_len];
+
+ for( size_t i = 0; (i < o_msg->iv_len) && (err == NULL); ++i)
+ {
+ err = readLPC(REG_HOSTBMC, o_msg->iv_data[i]);
+ }
+ if (err) { break; }
+
+ } while(0);
+
+ if (marked_busy)
+ {
+ // Clear the busy state (write 1 to toggle). Note if we get
+ // an error from the writeLPC, we toss it and return the first
+ // error as it likely has better information in it.
+ delete writeLPC(REG_CONTROL, CTRL_H_BUSY);
+ }
+
+ mutex_unlock(&iv_mutex);
+
+ IPMI_TRAC(INFO_MRK "read b2h %s %x:%x seq %x len %x cc %x",
+ err ? "err" : "ok",
+ o_msg->iv_netfun, o_msg->iv_cmd, o_msg->iv_seq,
+ o_msg->iv_len, o_msg->iv_cc);
+
+ return err;
+}
+
+/**
+ * @brief Constructor
+ */
+IpmiDD::IpmiDD(void):
+ iv_eagains(false),
+ iv_eventQ(msg_q_create())
+{
+ mutex_init(&iv_mutex);
+
+ // Start task to poll the control register
+ // This is a singleton so this will only be called once, right?
+ task_create( poll_control_register, NULL );
+
+ return;
+}
diff --git a/src/usr/ipmibase/ipmidd.H b/src/usr/ipmibase/ipmidd.H
new file mode 100644
index 000000000..9e95f466e
--- /dev/null
+++ b/src/usr/ipmibase/ipmidd.H
@@ -0,0 +1,174 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmidd.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2011,2018 */
+/* [+] 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 */
+#ifndef __IPMI_IPMIDD_H
+#define __IPMI_IMPIDD_H
+
+#include <sys/msg.h>
+#include <ipmi/ipmiif.H>
+#include "ipmibt.H"
+
+/** @file ipmidd.H
+ * @brief Provides the interfaces to the IPMI Device Driver
+ */
+
+enum {
+ // Registers. These are fixed for LPC/BT so we can hard-wire them
+ REG_CONTROL = 0xE4,
+ REG_HOSTBMC = 0xE5,
+ REG_INTMASK = 0xE6,
+
+ // Control register bits. The control register is interesting in that
+ // writing 0's never does anything; all registers are either set to 1
+ // when written with a 1 or toggled (1/0) when written with a one. So,
+ // we don't ever need to read-modify-write, we can just write an or'd
+ // mask of bits.
+ CTRL_B_BUSY = (1 << 7),
+ CTRL_H_BUSY = (1 << 6),
+ CTRL_OEM0 = (1 << 5),
+ CTRL_SMS_ATN = (1 << 4),
+ CTRL_B2H_ATN = (1 << 3),
+ CTRL_H2B_ATN = (1 << 2),
+ CTRL_CLR_RD_PTR = (1 << 1),
+ CTRL_CLR_WR_PTR = (1 << 0),
+
+ IDLE_STATE = (CTRL_B_BUSY | CTRL_B2H_ATN |
+ CTRL_SMS_ATN | CTRL_H2B_ATN),
+
+ // Bit in the INMASK register which signals to the BMC
+ // to reset it's end of things.
+ INT_BMC_HWRST = (1 << 7),
+
+ // How long to sychronously wait for the device to change state (in ns)
+ WAIT_TIME = 100000000,
+};
+
+/**
+ * @brief IPMI Device Driver Class
+ * Provides read/write message capabilities.
+ */
+class IpmiDD
+{
+ public:
+ /**
+ * @brief Poll the control register
+ *
+ * @parm void
+ */
+ void pollCtrl(void);
+
+ /**
+ * @brief Performs an IPMI message read operation
+ *
+ * @param[out] o_msg - Destination buffer for data
+ *
+ * @return errlHndl_t NULL on success
+ */
+
+ errlHndl_t receive(IPMI::BTMessage* o_msg);
+
+ /**
+ * @brief Performs an IPMI message write operation
+ *
+ * @param[in] i_msg - Location of data to be written
+ *
+ * @return errlHndl_t NULL on success
+ */
+ errlHndl_t send(IPMI::BTMessage* i_msg);
+
+ /**
+ * @brief Performs a reset of the BT hardware
+ *
+ * @param void
+ *
+ * @return errlHndl_t NULL on success
+ */
+ errlHndl_t reset(void);
+
+ /**
+ * @brief Get the queue on which IpmiDD publishes hardware events
+ *
+ * The events on the queue are consumed by IpmiRP. This "publish" approach
+ * avoids circular dependencies between the two components.
+ */
+ msg_q_t eventQueue(void)
+ { return iv_eventQ; }
+
+ /**
+ * @brief Constructor
+ *
+ * @parm void
+ */
+ IpmiDD(void);
+
+ private:
+ /**
+ * @brief Read an address from LPC space
+ *
+ * @parm i_addr Absolute LPC Address
+ * @parm o_data Buffer to read data into
+ *
+ * @return Error from operation
+ */
+ errlHndl_t readLPC(const uint32_t i_addr, uint8_t& o_data);
+
+ /**
+ * @brief Write an address from LPC space
+ *
+ * @parm i_addr Absolute LPC Address
+ * @parm i_data Data to write
+ *
+ * @return Error from operation
+ */
+ errlHndl_t writeLPC(const uint32_t i_addr, uint8_t i_data);
+
+ private: // Variables
+
+ /**
+ * @brief Mutex used to protect internal state
+ */
+ mutex_t iv_mutex;
+
+ /**
+ * @brief True if we told the RP to try a write again
+ */
+ bool iv_eagains;
+
+ /**
+ * @brief Publishes changes to device state
+ *
+ * To clarify, IpmiDD does not consume any messages from this queue. The
+ * queue exists purely for the purpose of another component to consume IPMI
+ * hardware events (currently, IpmiRP). IpmiDD performs an asynchronous
+ * msg_send() to deliver the events to the listening task.
+ */
+ msg_q_t iv_eventQ;
+
+ // Disallow copying this class.
+ IpmiDD& operator=(const IpmiDD&);
+ IpmiDD(const IpmiDD&);
+};
+
+
+#endif
diff --git a/src/usr/ipmibase/ipmimsg.C b/src/usr/ipmibase/ipmimsg.C
new file mode 100644
index 000000000..d86b68229
--- /dev/null
+++ b/src/usr/ipmibase/ipmimsg.C
@@ -0,0 +1,69 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmimsg.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 ipmimsg.C
+ * @brief code for the IPMI message class
+ */
+
+#include <errl/errlmanager.H>
+
+#include "ipmimsg.H"
+
+#include <kernel/console.H>
+#include <config.h>
+
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"msg: " printf_string,##args)
+
+namespace IPMI
+{
+
+ ///
+ /// @brief msg ctor
+ /// @param[in] i_cmd, the network function & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (new'd space)
+ ///
+ Message::Message(const command_t& i_cmd,
+ const uint8_t i_len,
+ uint8_t* i_data):
+ iv_msg(msg_allocate()),
+ iv_key(0),
+ iv_len(i_len),
+ iv_netfun(i_cmd.first),
+ iv_seq(iv_key),
+ iv_cmd(i_cmd.second),
+ iv_cc(0),
+ iv_state(0),
+ iv_errl(NULL),
+ iv_data(i_data)
+ {
+ iv_timeout.tv_sec = 0;
+ iv_timeout.tv_nsec = 0;
+ }
+
+};
diff --git a/src/usr/ipmibase/ipmimsg.H b/src/usr/ipmibase/ipmimsg.H
new file mode 100644
index 000000000..e91e01869
--- /dev/null
+++ b/src/usr/ipmibase/ipmimsg.H
@@ -0,0 +1,133 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmimsg.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 */
+#ifndef __IPMI_IPMIMSG_H
+#define __IPMI_IPMIMSG_H
+
+#include <time.h>
+#include <map>
+#include <list>
+#include <sys/msg.h>
+#include <errl/errlentry.H>
+#include <ipmi/ipmiif.H>
+
+namespace IPMI
+{
+ typedef std::list<msg_t*> send_q_t;
+ typedef std::list<msg_t*> timeout_q_t;
+ typedef std::map<uint8_t, msg_t*> respond_q_t;
+ typedef std::map<uint8_t, msg_q_t> event_q_t;
+
+ // IPMI message base class. A thing which expects to be sent down a
+ // msg_q (so has a msg_t) and defines operations performed by a generic
+ // IPMI resource manager. Details are left to the subclasses.
+ class Message
+ {
+ public:
+ ///
+ /// @brief static factory
+ /// @param[in] i_cmd, the network functon & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (allocated space)
+ /// @param[in] i_type, synchronous or async
+ ///
+ /// @return a pointer to a new'd Message object
+ ///
+ static Message* factory(const command_t& i_cmd = no_command(),
+ const uint8_t i_len = 0,
+ uint8_t* i_data = NULL,
+ const message_type i_type = TYPE_SYNC);
+
+ ///
+ /// @brief Message ctor
+ /// @param[in] i_cmd, the network functon & command
+ /// @param[in] i_len, the length of the data
+ /// @param[in] i_data, the data (allocated space)
+ ///
+ Message(const command_t& i_cmd = no_command(),
+ const uint8_t i_len = 0,
+ uint8_t* i_data = NULL);
+
+ ///
+ /// @brief Message dtor
+ ///
+ virtual ~Message(void)
+ {
+ // Do NOT delete[] iv_data here. For synchronous messages
+ // the caller wants this data and expects to delete[] it
+ // itself. For async messages it is deleted in the dtor
+ msg_free(iv_msg);
+ }
+
+ ///
+ /// @brief the header size of a message for the underlying transport
+ /// @param void
+ /// @return size_t, the header size
+ ///
+ virtual size_t header_size(void) = 0;
+
+ ///
+ /// @brief transmit a message.
+ /// @return true iff there was no transmission error
+ ///
+ virtual bool xmit(void) = 0;
+
+ ///
+ /// @brief complete the processing when a response arrives
+ /// @return void
+ ///
+ virtual void response(msg_q_t i_msgQ) = 0;
+
+ ///
+ /// @brief receive a message.
+ /// @return Error from operation
+ /// @note fills our iv_key with the proper information
+ ///
+ virtual errlHndl_t recv(void) = 0;
+
+ msg_t* iv_msg; // Pointer back to our msg_q msg_t
+ uint8_t iv_key; // key used by the respond queue
+
+ // Note: Some of these might turn out to be transport specific
+ // if so, we'll just move them down in to the subclasses
+ uint8_t iv_len; // Length
+ uint8_t iv_netfun; // Network Function
+ uint8_t& iv_seq; // Sequence number, reference to iv_key
+ uint8_t iv_cmd; // Command
+ uint8_t iv_cc; // Completion Code
+ uint8_t iv_state; // Driver things, like EAGAIN
+ errlHndl_t iv_errl; // Pointer to the errlHandl_t if needed
+ uint8_t* iv_data; // Pointer to the message data
+ timespec_t iv_timeout; // Absolute time of when I timeout
+
+ private:
+ // Disallow copying this class. Should suffice for disabling copy for
+ // all subclasses too.
+ Message& operator=(const Message&);
+ Message(const Message&);
+ };
+
+}; // end namespace IPMI
+
+#endif
diff --git a/src/usr/ipmibase/ipmirp.C b/src/usr/ipmibase/ipmirp.C
new file mode 100644
index 000000000..c8272274c
--- /dev/null
+++ b/src/usr/ipmibase/ipmirp.C
@@ -0,0 +1,1140 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmirp.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 ipmirp.C
+ * @brief IPMI resource provider definition
+ */
+
+#include "ipmirp.H"
+#include "ipmiconfig.H"
+#include "ipmidd.H"
+#include <ipmi/ipmi_reasoncodes.H>
+#include <ipmi/ipmiif.H>
+#include <ipmi/ipmisel.H>
+#include <devicefw/driverif.H>
+#include <devicefw/userif.H>
+
+#include <config.h>
+#include <sys/task.h>
+#include <initservice/taskargs.H>
+#include <initservice/initserviceif.H>
+#include <initservice/istepdispatcherif.H>
+#include <sys/vfs.h>
+
+#include <targeting/common/commontargeting.H>
+#include <kernel/ipc.H>
+#include <arch/ppc.H>
+#include <errl/errlmanager.H>
+#include <sys/time.h>
+#include <sys/misc.h>
+#include <errno.h>
+
+#include <console/consoleif.H>
+// Defined in ipmidd.C
+extern trace_desc_t * g_trac_ipmi;
+#define IPMI_TRAC(printf_string,args...) \
+ TRACFCOMP(g_trac_ipmi,"rp: " printf_string,##args)
+
+/**
+ * setup _start and handle barrier
+ */
+TASK_ENTRY_MACRO( IpmiRP::daemonProcess );
+
+/**
+ * @brief Constructor
+ */
+IpmiRP::IpmiRP(void):
+ iv_msgQ(msg_q_create()),
+ iv_sendq(),
+ iv_timeoutq(),
+ iv_respondq(),
+ iv_eventq(),
+ iv_last_chanceq(msg_q_create()),
+ iv_bmc_timeout(IPMI::g_bmc_timeout),
+ iv_outstanding_req(IPMI::g_outstanding_req),
+ iv_xmit_buffer_size(IPMI::g_xmit_buffer_size),
+ iv_recv_buffer_size(IPMI::g_recv_buffer_size),
+ iv_retries(IPMI::g_retries),
+ iv_shutdown_msg(NULL),
+ iv_graceful_shutdown_pending(false),
+ iv_chassis_power_mod(IPMI::CHASSIS_POWER_OFF)
+{
+ mutex_init(&iv_mutex);
+ sync_cond_init(&iv_cv);
+}
+
+/**
+ * @brief Destructor
+ */
+IpmiRP::~IpmiRP(void)
+{
+ msg_q_destroy(iv_msgQ);
+}
+
+void* IpmiRP::start(void* unused)
+{
+ Singleton<IpmiRP>::instance().execute();
+ return NULL;
+}
+
+void* IpmiRP::timeout_thread(void* unused)
+{
+ Singleton<IpmiRP>::instance().timeoutThread();
+ return NULL;
+}
+
+void* IpmiRP::get_capabilities(void* unused)
+{
+ Singleton<IpmiRP>::instance().getInterfaceCapabilities();
+ return NULL;
+}
+
+void* IpmiRP::last_chance_event_handler(void* unused)
+{
+ Singleton<IpmiRP>::instance().lastChanceEventHandler();
+ return NULL;
+}
+
+void IpmiRP::daemonProcess(errlHndl_t& o_errl)
+{
+ task_create(&IpmiRP::attach, NULL);
+ task_create(&IpmiRP::start, NULL);
+}
+
+/**
+ * @brief Return the transport header size
+ */
+size_t getXportHeaderSize(void)
+{
+ // Get the header size from the physical transport.
+ IPMI::Message* msg = IPMI::Message::factory();
+ size_t xport_size = msg->header_size();
+ delete msg;
+ return xport_size;
+}
+
+/**
+ * @brief Return the maximum data size to allocate
+ */
+inline size_t IpmiRP::maxBuffer(void)
+{
+ // If the interface isn't up (say we're sending the
+ // get-capabilities command, or some such) we use the
+ // defaults setup in the ctor.
+
+ static size_t xport_header_size = getXportHeaderSize();
+
+ mutex_lock(&iv_mutex);
+
+ // Given the max buffer information from the get-capabilities
+ // command, subtract off the physical layers header size.
+
+ // iv_xmit_buffer_size can change - it'll be one thing for
+ // the default when the RP is created, and possibly another
+ // when the get-capabilities command returns.
+ // an additional 1 is subtracted based on issues seen with AMI
+ size_t mbs = iv_xmit_buffer_size - xport_header_size - 1;
+
+ mutex_unlock(&iv_mutex);
+
+ return mbs;
+}
+
+/**
+ * @brief Start routine of the time-out handler
+ */
+void IpmiRP::timeoutThread(void)
+{
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ IPMI_TRAC(ENTER_MRK "time out thread");
+
+ // If there's something on the queue we want to grab it's timeout time
+ // and wait. Note the response queue is "sorted" as we send messages in
+ // order. So, the first message on the queue is the one who's timeout
+ // is going to come first.
+ while (true)
+ {
+ mutex_lock(&iv_mutex);
+ while ((iv_timeoutq.size() == 0))
+ {
+ sync_cond_wait(&iv_cv, &iv_mutex);
+ }
+
+ msg_t*& msq_msg = iv_timeoutq.front();
+ IPMI::Message* msg = static_cast<IPMI::Message*>(msq_msg->extra_data);
+
+ // The difference between the timeout of the first message in the
+ // queue and the current time is the time we wait for a timeout
+ timespec_t tmp_time;
+ clock_gettime(CLOCK_MONOTONIC, &tmp_time);
+
+ uint64_t now = (NS_PER_SEC * tmp_time.tv_sec) + tmp_time.tv_nsec;
+ uint64_t timeout = (NS_PER_SEC * msg->iv_timeout.tv_sec) +
+ msg->iv_timeout.tv_nsec;
+
+ if (now >= timeout)
+ {
+ IPMI_TRAC("timeout: %x:%x", msg->iv_netfun, msg->iv_cmd);
+
+ // This little bugger timed out. Get him off the timeoutq
+ iv_timeoutq.pop_front();
+
+ // Get him off the responseq, and reply back to the waiter that
+ // there was a timeout
+ response(msg, IPMI::CC_TIMEOUT);
+
+ // Tell the resource provider to check for any pending messages
+ msg_t* msg_idleMsg = msg_allocate();
+ msg_idleMsg->type = IPMI::MSG_STATE_IDLE;
+ msg_send(iv_msgQ, msg_idleMsg);
+
+ mutex_unlock(&iv_mutex);
+ }
+ else
+ {
+ mutex_unlock(&iv_mutex);
+ nanosleep( 0, timeout - now );
+ }
+ }
+ IPMI_TRAC(EXIT_MRK "time out thread");
+
+ return;
+}
+
+/**
+ * @brief Get the BMC interface capabilities
+ */
+void IpmiRP::getInterfaceCapabilities(void)
+{
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ // Queue up a get-capabilities message. Anything that queues up behind us
+ // (I guess it could queue up in front of us too ...) will use the defaults.
+
+ IPMI::completion_code cc = IPMI::CC_UNKBAD;
+ size_t len = 0;
+ uint8_t* data = NULL;
+ errlHndl_t err = IPMI::sendrecv(IPMI::get_capabilities(), cc, len, data);
+
+ do {
+ // If we have a problem, we can't "turn on" the IPMI stack.
+ if (err)
+ {
+ IPMI_TRAC("get_capabilities returned an error, using defaults");
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ break;
+ }
+
+ // If we get back a funky completion code, we'll use the defaults.
+ if (cc != IPMI::CC_OK)
+ {
+ IPMI_TRAC("get_capabilities not ok %d, using defaults", cc);
+ break;
+ }
+
+ // If we didn't get back what we expected, use the defaults
+ if (len != 5)
+ {
+ IPMI_TRAC("get_capabilities length %d; using defaults", len);
+ break;
+ }
+
+ // Protect the members as we're on another thread.
+ mutex_lock(&iv_mutex);
+ // @TODO RTC:123041 - In theory the number of outstanding requests is
+ // set via the response data below, but currently the response
+ // value isn't correct so the default will be used.
+ //iv_outstanding_req = data[0];
+ iv_xmit_buffer_size = data[1];
+ iv_recv_buffer_size = data[2];
+ // @TODO RTC:123041 - In theory the BMC timeout is set via the response
+ // data below, but currently the response value isn't correct so
+ // the default will be used.
+ //iv_bmc_timeout = data[3];
+ iv_retries = data[4];
+
+ IPMI_TRAC("get_capabilities: requests %d, in buf %d, "
+ "out buf %d, timeout %d, retries %d",
+ iv_outstanding_req, iv_xmit_buffer_size,
+ iv_recv_buffer_size, iv_bmc_timeout, iv_retries);
+
+ mutex_unlock(&iv_mutex);
+ } while(false);
+
+ // store this size for hbrt's usage
+ TARGETING::Target * sys = NULL;
+ TARGETING::targetService().getTopLevelTarget( sys );
+ if (sys)
+ {
+ sys->setAttr<TARGETING::ATTR_IPMI_MAX_BUFFER_SIZE>
+ (iv_xmit_buffer_size - getXportHeaderSize() - 1);
+ IPMI_TRAC("setAttr(IPMI_MAX_BUFFER_SIZE) = %d",
+ (iv_xmit_buffer_size - getXportHeaderSize() - 1));
+ }
+
+ delete[] data;
+
+ return;
+}
+
+/**
+ * @brief Tell the resource provider which queue to use for events
+ * @param[in] i_cmd, the command we're looking for
+ * @param[in] i_msgq, the queue we should be notified on
+ */
+void IpmiRP::registerForEvent(const IPMI::command_t& i_cmd,
+ const msg_q_t& i_msgq)
+{
+ mutex_lock(&iv_mutex);
+
+ // We only need the command internally, but we create the entire
+ // command_t as it's really the true representation of the event type.
+ iv_eventq[i_cmd.second] = i_msgq;
+ mutex_unlock(&iv_mutex);
+ IPMI_TRAC("event registration for %x:%x", i_cmd.first, i_cmd.second);
+}
+
+void IPMI::register_for_event(const IPMI::command_t& i_cmd,
+ const msg_q_t& i_msgq)
+{
+ Singleton<IpmiRP>::instance().registerForEvent(i_cmd, i_msgq);
+}
+
+/**
+ * @brief Give the resource provider a message to put in the eventq
+ * @param[in] i_event, pointer to the new'd event (OEM SEL)
+ */
+void IpmiRP::postEvent(IPMI::oemSEL* i_event)
+{
+ // Called in the context of the RP message loop, mutex locked
+
+ do {
+ // Check to see if event is valid. AMI recomends we check the netfun,
+ // the SEL id, and the record id. If they don't match, we're allowed
+ // to consider this an unhandled message.
+ if ( (i_event->iv_netfun != IPMI::OEM_VALID_NETFUN) ||
+ (i_event->iv_record != IPMI::OEM_VALID_SEL_ID) ||
+ (i_event->iv_record_type != IPMI::OEM_VALID_RECORD_TYPE) )
+ {
+ IPMI_TRAC("rejecting event netfun: 0x%x record: 0x%x record type: 0x%x cmd: 0x%x",
+ i_event->iv_netfun,
+ i_event->iv_record,
+ i_event->iv_record_type,
+ i_event->iv_cmd[0]);
+
+ // ... and clean up the memory for the caller
+ delete i_event;
+ break;
+ }
+
+ // Check to see if this event has a queue registered
+ IPMI::event_q_t::iterator it = iv_eventq.find(i_event->iv_cmd[0]);
+ msg_q_t outq = (it == iv_eventq.end()) ? iv_last_chanceq : it->second;
+
+ // Create a message to send asynchronously to the event handler queue
+ // Assign the event to the message, the caller will delete the message
+ // and the event.
+ msg_t* msg = msg_allocate();
+ msg->type = IPMI::TYPE_EVENT;
+ msg->extra_data = i_event;
+
+ IPMI_TRAC("queuing event %x:%x for handler",
+ i_event->iv_netfun, i_event->iv_cmd[0])
+ int rc = msg_send(outq, msg);
+
+ if (rc)
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISRV_POSTEVENT
+ * @reasoncode IPMI::RC_INVALID_SEND
+ * @userdata1 rc from msq_send()
+ * @devdesc msg_send() failed
+ * @custdesc Firmware error during IPMI event handling
+ */
+ errlHndl_t err =
+ new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISRV_POSTEVENT,
+ IPMI::RC_INVALID_SEND,
+ rc,
+ 0,
+ true);
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+
+ // ... and clean up the memory for the caller
+ delete i_event;
+ msg_free(msg);
+ }
+
+ } while(0);
+
+ return;
+}
+
+/**
+ * @brief Send a message indicating we're rejecting the pnor handshake request.
+ */
+static void rejectPnorRequest(void)
+{
+ // Per AMI email, send a 0 to reject the pnor request.
+ static const uint8_t reject_request = 0x0;
+
+ uint8_t* data = new uint8_t(reject_request);
+ IPMI_TRAC("rejecting pnor access request %x", *data);
+
+ uint8_t len = 0;
+ errlHndl_t err = send(IPMI::pnor_response(), len, data);
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+}
+
+/**
+ * @brief Handle various IPMI Power Messages
+ */
+void IpmiRP::handlePowerMessage( IPMI::oemSEL* i_event )
+{
+
+ do {
+ // if the event type is "soft off" then update the modifier to send
+ // a power off to the BMC
+ if( i_event->iv_cmd[1] == IPMI::CHASSIS_POWER_OFF )
+ {
+ iv_chassis_power_mod = IPMI::CHASSIS_POWER_OFF;
+
+ // handle the graceful shutdown message
+ IPMI_TRAC("Graceful shutdown request received");
+
+#ifdef CONFIG_CONSOLE
+ CONSOLE::displayf(NULL, "IPMI: shutdown requested");
+ CONSOLE::flush();
+#endif
+
+ }
+ // If the event type is a power soft reset, send a chasis reset
+ // update the modifier to send to the BMC
+ else if( i_event->iv_cmd[1] == IPMI::CHASSIS_POWER_SOFT_RESET )
+ {
+ // handle the message as a power reset request
+ IPMI_TRAC("IPMI power reset request received");
+ iv_chassis_power_mod = IPMI::CHASSIS_POWER_RESET;
+#ifdef CONFIG_CONSOLE
+ CONSOLE::displayf(NULL, "IPMI: power cycle requested");
+ CONSOLE::flush();
+#endif
+ }
+ else
+ {
+ //Ignore this message - It is an undefined/unsupported command
+ IPMI_TRAC("Ignoring command with unknown Power Control Action: %x",
+ i_event->iv_cmd[1]);
+ break;
+ }
+
+ // tell the istep dispacher to stop executing isteps
+ INITSERVICE::stopIpl();
+
+ // register for the post memory flush callback
+ INITSERVICE::registerShutdownEvent(IPMI_COMP_ID, iv_msgQ,
+ IPMI::MSG_STATE_GRACEFUL_SHUTDOWN,
+ INITSERVICE::POST_MEM_FLUSH_NOTIFY_LAST);
+
+ iv_graceful_shutdown_pending = true;
+ lwsync();
+
+ // initiate the shutdown processing in the background
+ INITSERVICE::doShutdown(SHUTDOWN_STATUS_GOOD,true);
+
+
+ } while (0);
+
+}
+/**
+ * @brief Wait for events and read them
+ */
+void IpmiRP::lastChanceEventHandler(void)
+{
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ // To create a event handler, all you need to do is create a message
+ // queue (or use one you have) and register for events.
+
+ // register with the resource provider, use the existing queue
+ registerForEvent(IPMI::power_off(), iv_last_chanceq);
+
+
+ // We'll handle the pnor request in this context as we just send
+ // an async message which says "no."
+ registerForEvent(IPMI::pnor_request(), iv_last_chanceq);
+
+ do {
+
+ msg_t* msg = msg_wait(iv_last_chanceq);
+
+ IPMI::oemSEL* event = reinterpret_cast<IPMI::oemSEL*>(msg->extra_data);
+
+ if (event->iv_cmd[0] == IPMI::pnor_request().second)
+ {
+ // We'll handle the pnor request in this context as we just send
+ // an async message which says "no."
+ rejectPnorRequest();
+ }
+ else if ( event->iv_cmd[0] == IPMI::power_off().second )
+ {
+ handlePowerMessage(event);
+ }
+ else {
+ // TODO: RTC: 120128
+ // The last-chance handler should do more than this - it needs to
+ // respond back to the BMC and tell it whatever it needs to know. If
+ // this response isn't simple for a specific message, then a real
+ // handler should probably be written.
+
+ IPMI_TRAC("last chance handler for event: %x:%x (%x %x %x)",
+ event->iv_netfun, event->iv_cmd[0],
+ event->iv_record, event->iv_record_type,
+ event->iv_timestamp);
+ }
+ // There's no way anyone can post an event synchronously, so we're done.
+ delete event;
+ msg_free(msg);
+
+ } while(true);
+
+ return;
+}
+
+void* IpmiRP::attach(void *unused)
+{
+ Singleton<IpmiRP>::instance().attach();
+
+ return NULL;
+}
+
+void IpmiRP::attach(void)
+{
+ task_detach();
+
+ msg_q_t mq = Singleton<IpmiDD>::instance().eventQueue();
+
+ /* FIXME: Never shut down */
+ while (1)
+ {
+ /* Forward it into the internal message queue */
+ msg_send(iv_msgQ, msg_wait(mq));
+ lwsync();
+ }
+}
+
+/**
+ * @brief Entry point of the resource provider
+ */
+void IpmiRP::execute(void)
+{
+ bool l_shutdown_pending = false;
+
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ // Register shutdown events with init service.
+ // Done at the "end" of shutdown processing.
+ // This will flush out any IPMI messages which were sent as
+ // part of the shutdown processing. We chose MBOX priority
+ // as we don't want to accidentally get this message after
+ // interrupt processing has stopped in case we need intr to
+ // finish flushing the pipe.
+ INITSERVICE::registerShutdownEvent(IPMI_COMP_ID, iv_msgQ,
+ IPMI::MSG_STATE_SHUTDOWN,
+ INITSERVICE::MBOX_PRIORITY);
+
+ // Start the thread that waits for timeouts
+ task_create( &IpmiRP::timeout_thread, NULL );
+
+ // Queue and wait for a message for the interface capabilities
+ task_create( &IpmiRP::get_capabilities, NULL);
+
+ // Wait for an event message read it and handle it if no one else does
+ task_create( &IpmiRP::last_chance_event_handler, NULL);
+
+ // call ErrlManager function - tell him that IPMI is ready!
+ ERRORLOG::ErrlManager::errlResourceReady(ERRORLOG::IPMI);
+
+ while (true)
+ {
+
+ msg_t* msg = msg_wait(iv_msgQ);
+
+ const IPMI::msg_type msg_type =
+ static_cast<IPMI::msg_type>(msg->type);
+
+ // Invert the "default" by checking here. This allows the compiler
+ // to warn us if the enum has an un-handled case but still catch
+ // runtime errors where msg_type is set out of bounds.
+ assert(msg_type <= IPMI::MSG_LAST_TYPE,
+ "msg_type %d not handled", msg_type);
+
+ switch(msg_type)
+ {
+ // Messages we're told to send.
+ // Push the message on the queue, and the idle() at the
+ // bottom of this loop will start the transmit process.
+ // Be sure to push_back to ensure ordering of transmission.
+ case IPMI::MSG_STATE_SEND:
+ if (!l_shutdown_pending)
+ {
+ iv_sendq.push_back(msg);
+ }
+ // shutting down, do not accept new messages
+ else
+ {
+ IPMI_TRAC(WARN_MRK "IPMI shutdown pending. Message dropped");
+ IPMI::Message* ipmi_msg =
+ static_cast<IPMI::Message*>(msg->extra_data);
+ response(ipmi_msg, IPMI::CC_BADSTATE);
+ msg_free(msg);
+ }
+ break;
+
+ // State changes from the IPMI hardware. These are async
+ // messages so we get rid of them here.
+ case IPMI::MSG_STATE_IDLE:
+ msg_free(msg);
+ // No-op - we do it at the bottom of the loop.
+ break;
+
+ // Handle a response (B2H_ATN)
+ case IPMI::MSG_STATE_RESP:
+ msg_free(msg);
+ response();
+ break;
+
+ // Handle an event (SMS_ATN). The protocol states that when we see
+ // the sms attention bit, we issue a read_event message, which will
+ // come back with the OEM SEL of the event in its payload.
+ case IPMI::MSG_STATE_EVNT:
+ {
+ msg_free(msg);
+ uint8_t* data = NULL;
+ uint8_t len = 0;
+ errlHndl_t err = send(IPMI::read_event(), len, data,
+ IPMI::TYPE_EVENT);
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+ }
+ break;
+
+ // Accept no more messages. Anything in the sendq is sent and
+ // we wait for the reply from the BMC.
+ case IPMI::MSG_STATE_SHUTDOWN:
+ {
+ l_shutdown_pending = true; // Stop incoming new messages
+ iv_shutdown_msg = msg; // Reply to this message
+ break;
+ }
+ case IPMI::MSG_STATE_GRACEFUL_SHUTDOWN:
+ {
+ IPMI_TRAC(INFO_MRK "MSG_STATE_GRACEFUL_SHUTDOWN: send power"
+ " off command to BMC");
+ // clear the graceful shutdown flag so we will exit after
+ // sending the power off cmd
+ iv_graceful_shutdown_pending = false;
+ size_t len = 1;
+ uint8_t* data = new uint8_t[len];
+
+ // send the correct chassis power modifier.
+ data[0] = iv_chassis_power_mod;
+
+ IPMI::Message* ipmi_msg = IPMI::Message::factory(
+ IPMI::chassis_power_off(), len, data, IPMI::TYPE_ASYNC);
+
+ // queue up the power off message
+ iv_sendq.push_back(ipmi_msg->iv_msg);
+
+ iv_shutdown_msg = msg; // Reply to this message
+
+#ifdef CONFIG_CONSOLE
+ CONSOLE::displayf(NULL, "IPMI: shutdown complete\n");
+ CONSOLE::flush();
+#endif
+
+ }
+ break;
+
+ // begin a graceful reboot initiated by us
+ case IPMI::MSG_STATE_INITIATE_POWER_CYCLE:
+ {
+ msg_free(msg);
+
+#ifdef CONFIG_CONSOLE
+ CONSOLE::displayf(NULL, "IPMI: Initiate power cycle");
+ CONSOLE::flush();
+#endif
+ // setup the power cmd modifier to tell the bmc to
+ // do a power reset
+ iv_chassis_power_mod = IPMI::CHASSIS_POWER_RESET;
+
+ // register for the post memory flush callback
+ INITSERVICE::registerShutdownEvent(IPMI_COMP_ID, iv_msgQ,
+ IPMI::MSG_STATE_GRACEFUL_SHUTDOWN,
+ INITSERVICE::POST_MEM_FLUSH_NOTIFY_LAST);
+
+ iv_graceful_shutdown_pending = true;
+ lwsync();
+
+ // initiate the shutdown processing in the background
+ INITSERVICE::doShutdown(SHUTDOWN_STATUS_GOOD,true);
+
+ }
+ break;
+
+ };
+
+ // There's a good chance the interface will be idle right after
+ // the operation we just performed. Since we need to poll for the
+ // idle state, calling idle after processing a message may decrease
+ // the latency of waiting for idle. The worst case is that we find
+ // the interface busy and go back to waiting. Note: there's no need
+ // to keep calling idle if there are old elements on the sendq;
+ // we'll need to wait for the interface to indicate we're idle.
+ if ((IPMI::MSG_STATE_SEND != msg_type) || (iv_sendq.size() == 1))
+ {
+ idle();
+ }
+
+ // Once quiesced, reply to shutdown msg and exit.
+ // Shutdown simply puts us in a state we deny all further requests bar
+ // those from PnorIpmiDD. Access to the PNOR must be provided right up
+ // until we call the shutdown syscall, so there's no point at which we
+ // can deallocate the resources consumed by IpmiRP, IpmiDD or
+ // PnorIpmiDD.
+ if (l_shutdown_pending && iv_respondq.empty() && iv_sendq.empty())
+ {
+ IPMI_TRAC(INFO_MRK "reply to the MSG_STATE_SHUTDOWN message");
+ msg_respond(iv_msgQ, iv_shutdown_msg);
+ }
+ }
+
+ IPMI_TRAC(EXIT_MRK "message loop");
+ return;
+}
+
+///
+/// @brief Go in to the idle state
+///
+void IpmiRP::idle(void)
+{
+ // If the interface is idle, we can write anything we need to write.
+ for (IPMI::send_q_t::iterator i = iv_sendq.begin();
+ i != iv_sendq.end();)
+ {
+ // Check to see if we have many outstanding requests. If so, don't send
+ // any more messages. Note the eagain mechanism still works even though
+ // we're not sending messages as eventually we'll get enough responses
+ // to shorten the response queue and since the message loop calls us
+ // to transmit even for the reception of a message, the driver will
+ // eventually reset egagains. If responses timeout, we end up here as
+ // the response queue processing sends an idle message when anything is
+ // removed.
+ if (iv_outstanding_req <= iv_respondq.size())
+ {
+ break;
+ }
+
+ // If we have a problem transmitting a message, then we just stop
+ // here and wait for the next time the interface transitions to idle
+ // Note that there are two failure cases: the first is that there is
+ // a problem transmitting. In this case we told the other end of the
+ // message queue, and so the life of this message is over. The other
+ // case is that the interface turned out to be busy in which case
+ // this message can sit on the queue and it'll be next.
+
+ IPMI::Message* msg = static_cast<IPMI::Message*>((*i)->extra_data);
+
+ // If there was an i/o error, we do nothing - leave this message on
+ // the queue. Don't touch msg after xmit returns. If the message was
+ // sent, and it was async, msg has been destroyed.
+ if (msg->xmit())
+ {
+ break;
+ }
+ i = iv_sendq.erase(i);
+ }
+
+ return;
+}
+
+///
+/// @brief Handle a response to a message we sent
+///
+void IpmiRP::response(void)
+{
+ IPMI::Message* rcv_buf = IPMI::Message::factory();
+
+ do
+ {
+ // Go down to the device and read. Fills in iv_key.
+ errlHndl_t err = rcv_buf->recv();
+
+ if (err)
+ {
+ // Not much we're going to do here, so lets commit the error and
+ // the original request will timeout.
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ break;
+ }
+
+ mutex_lock(&iv_mutex);
+ response(rcv_buf);
+ mutex_unlock(&iv_mutex);
+
+ } while (false);
+
+ delete rcv_buf;
+ return;
+}
+
+///
+/// @brief Handle a response to a message we want to change
+///
+void IpmiRP::response(IPMI::Message* i_msg, IPMI::completion_code i_cc)
+{
+ // The third bit in the netfun indicates this is a reply.
+ static const uint8_t reply_bit = 0x04;
+
+ i_msg->iv_cc = i_cc;
+ i_msg->iv_netfun |= reply_bit; // update the netfun
+ i_msg->iv_len = 0; // sending no data
+ i_msg->iv_data = NULL;
+ response(i_msg);
+ return;
+}
+
+///
+/// @brief Handle a response to a message we have
+///
+void IpmiRP::response(IPMI::Message* i_msg)
+{
+ do {
+
+ // Look for a message with this seq number waiting for a
+ // response. If there isn't a message looking for this response,
+ // log and leave. Async messages should also be on this queue,
+ // even though the caller has long gone on to other things.
+ IPMI::respond_q_t::iterator itr = iv_respondq.find(i_msg->iv_key);
+ if (itr == iv_respondq.end())
+ {
+ IPMI_TRAC(ERR_MRK "message not found on the response queue: "
+ "%d %x:%x", i_msg->iv_key, i_msg->iv_netfun,
+ i_msg->iv_cmd);
+
+ delete[] i_msg->iv_data;
+ break;
+ }
+
+ msg_t* original_msg = itr->second;
+
+ // Get us off the response queue, and the timeout queue.
+ iv_respondq.erase(itr);
+ iv_timeoutq.remove(original_msg);
+
+ // Hand the allocated buffer over to the original message's
+ // ipmi_msg_t It will be responsible for de-allocating it
+ // when it's dtor is called.
+ IPMI::Message* ipmi_msg =
+ static_cast<IPMI::Message*>(original_msg->extra_data);
+
+ // Hand ownership of the data to the original requester
+ ipmi_msg->iv_data = i_msg->iv_data;
+ ipmi_msg->iv_len = i_msg->iv_len;
+ ipmi_msg->iv_cc = i_msg->iv_cc;
+ ipmi_msg->iv_netfun = i_msg->iv_netfun;
+
+ // The subclasses know how to handle the response from here.
+ // For example, sync messages will respond and async will delete
+ ipmi_msg->response(iv_msgQ);
+
+ } while(false);
+
+ return;
+}
+
+///
+/// @brief Queue a message on to the response queue
+///
+void IpmiRP::queueForResponse(IPMI::Message& i_msg)
+{
+ // Figure out when this fellow's timeout should occur. If we
+ // have a problem from clock_gettime we have a bug, not an error.
+ clock_gettime(CLOCK_MONOTONIC, &(i_msg.iv_timeout));
+
+ // Lock before accessing the timeout (and the queues, etc.)
+ mutex_lock(&iv_mutex);
+
+ // BMC request-to-response times are always seconds, 1 - 30.
+ // And I don't think we care about roll over here.
+ i_msg.iv_timeout.tv_sec += iv_bmc_timeout;
+
+ // Put this message on the response queue so we can find it later
+ // for a response and on the timeout queue so if it times out
+ // we can find it there. Note all message will all have the same
+ // timeout - mostly. Every message sent before the BMC tells us
+ // the timeout (at least one message) will have the shortest possible
+ // timeout. The BMC might lengthen the timeout, but can not shorten
+ // it. All messages after that will have the same timeout. So the
+ // timeout queue is "sorted."
+
+ iv_respondq[i_msg.iv_seq] = i_msg.iv_msg;
+ iv_timeoutq.push_back(i_msg.iv_msg);
+
+ // If we put a message in an empty timeout queue (we know this as
+ // there's only one message in the queue now) signal the timeout thread
+ if (iv_timeoutq.size() == 1)
+ {
+ sync_cond_signal(&iv_cv);
+ }
+
+ mutex_unlock(&iv_mutex);
+ return;
+}
+
+namespace IPMI
+{
+ ///
+ /// @brief Synchronus message send
+ ///
+ errlHndl_t sendrecv(const IPMI::command_t& i_cmd,
+ IPMI::completion_code& o_completion_code,
+ size_t& io_len, uint8_t*& io_data)
+ {
+ errlHndl_t err;
+ static msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue();
+
+ IPMI::Message* ipmi_msg = IPMI::Message::factory(i_cmd, io_len, io_data,
+ IPMI::TYPE_SYNC);
+
+ // I think if the buffer is too large this is a programming error.
+ assert(io_len <= max_buffer());
+
+ IPMI_TRAC("queuing sync %x:%x", ipmi_msg->iv_netfun, ipmi_msg->iv_cmd);
+ int rc = msg_sendrecv(mq, ipmi_msg->iv_msg);
+
+ // If the kernel didn't give a hassle about the message, check to see if
+ // there was an error reported back from the other end of the queue. If
+ // this message made it to the other end of the queue, then our memory
+ // (io_data) is in the proper state.
+ if (rc == 0) {
+ err = ipmi_msg->iv_errl;
+ }
+
+ // Otherwise, lets make an errl out of our return code
+ else
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISRV_SEND
+ * @reasoncode IPMI::RC_INVALID_SENDRECV
+ * @userdata1 rc from msq_sendrecv()
+ * @devdesc msg_sendrecv() failed
+ * @custdesc Firmware error during system boot
+ */
+ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISRV_SEND,
+ IPMI::RC_INVALID_SENDRECV,
+ rc,
+ 0,
+ true);
+ err->collectTrace(IPMI_COMP_NAME);
+
+ // ... and clean up the memory for the caller
+ delete[] io_data;
+ }
+
+ // The length and the data are the response, if there was one. All of
+ // there are pretty much invalid if there was an error.
+ io_len = ipmi_msg->iv_len;
+ io_data = ipmi_msg->iv_data;
+ o_completion_code = static_cast<IPMI::completion_code>(ipmi_msg->iv_cc);
+ delete ipmi_msg;
+
+ return err;
+ }
+
+ ///
+ /// @brief Asynchronous message send
+ ///
+ errlHndl_t send(const IPMI::command_t& i_cmd,
+ const size_t i_len, uint8_t* i_data,
+ IPMI::message_type i_type)
+ {
+ static msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue();
+ errlHndl_t err = NULL;
+
+ // We don't delete this message, the message will self destruct
+ // after it's been transmitted. Note it could be placed on the send
+ // queue and we are none the wiser - so we can't delete it.
+ IPMI::Message* ipmi_msg = IPMI::Message::factory(i_cmd, i_len,
+ i_data, i_type);
+
+ // I think if the buffer is too large this is a programming error.
+ assert(i_len <= max_buffer());
+
+ IPMI_TRAC("queuing async %x:%x", ipmi_msg->iv_netfun, ipmi_msg->iv_cmd);
+ int rc = msg_send(mq, ipmi_msg->iv_msg);
+
+ if (rc)
+ {
+ /*@
+ * @errortype ERRL_SEV_UNRECOVERABLE
+ * @moduleid IPMI::MOD_IPMISRV_SEND
+ * @reasoncode IPMI::RC_INVALID_SEND
+ * @userdata1 rc from msq_send()
+ * @devdesc msg_send() failed
+ * @custdesc Firmware error during system boot
+ */
+ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISRV_SEND,
+ IPMI::RC_INVALID_SEND,
+ rc,
+ 0,
+ true);
+ err->collectTrace(IPMI_COMP_NAME);
+
+ // ... and clean up the memory for the caller
+ delete[] i_data;
+ }
+
+ return err;
+ }
+
+ void initiateShutdownOrReboot(const IPMI::msg_type i_msgType)
+ {
+ const auto valid = IPMI::validShutdownRebootMsgType(i_msgType);
+ assert(valid,"BUG! IPMI message type of 0x%08X is not a valid shutdown "
+ "or reboot type",i_msgType);
+ static auto mq = Singleton<IpmiRP>::instance().msgQueue();
+ auto pMsg = msg_allocate();
+ assert(pMsg != nullptr,"BUG! msg_allocate returned nullptr.");
+ pMsg->type = i_msgType;
+ auto rc = msg_send(mq,pMsg);
+ assert(!rc,"BUG! msg_send failed with rc of %d",rc);
+ }
+
+ void initiateReboot()
+ {
+ (void)initiateShutdownOrReboot(MSG_STATE_INITIATE_POWER_CYCLE);
+ }
+
+ void initiatePowerOff()
+ {
+ (void)initiateShutdownOrReboot(MSG_STATE_GRACEFUL_SHUTDOWN);
+ }
+
+ ///
+ /// @brief Maximum buffer for data (max xport - header)
+ ///
+ size_t max_buffer(void)
+ {
+ return Singleton<IpmiRP>::instance().maxBuffer();
+ }
+
+ ///
+ /// @brief Synchronously send an event
+ ///
+ errlHndl_t send_event(const uint8_t i_sensor_type,
+ const uint8_t i_sensor_number,
+ const bool i_assertion,
+ const uint8_t i_type,
+ completion_code& o_completion_code,
+ const size_t i_len,
+ uint8_t* i_data)
+ {
+ static const size_t event_header = 5;
+
+ // Sanity check
+ assert((i_len > 0) && (i_len < 4),
+ "event request i_len incorrect %d", i_len);
+ assert(i_type < 0x80, "event request i_type out of range %x", i_type);
+
+ size_t len = event_header + i_len;
+ uint8_t* data = new uint8_t[len];
+ IPMI::completion_code cc = IPMI::CC_OK;
+
+ data[0] = 0x01; // More or less fixed, see table 5.4
+ data[1] = 0x04; // Fixed in the IPMI spec table 29.5
+ data[2] = i_sensor_type;
+ data[3] = i_sensor_number;
+ data[4] = (i_assertion ? 0x80 : 0x00) + i_type;
+ for (size_t i = 0; i < i_len; i++)
+ {
+ data[event_header + i] = i_data[i];
+ }
+
+ // We're done with i_data, but the caller deletes it. Note there's
+ // no response data to an event - so there's nothing to copy over,
+ // no reference to i_data, nothing.
+
+ errlHndl_t err = sendrecv(IPMI::platform_event(), cc, len, data);
+
+ o_completion_code = cc;
+ delete[] data;
+ return err;
+ }
+
+ /**
+ * Retrieve some information about the BMC and the connection
+ * we have to it.
+ */
+ BmcInfo_t getBmcInfo(void)
+ {
+ BmcInfo_t l_info;
+
+ l_info.bulkTransferLpcBaseAddr = REG_HOSTBMC;
+ static size_t size = sizeof(uint8_t);
+ l_info.bulkTransferSize = size;
+ l_info.chipVersion = cpu_dd_level();
+ l_info.smsAttnInterrupt = CTRL_SMS_ATN;
+ l_info.bmcToHostInterrupt = CTRL_B2H_ATN;
+
+ //TODO RTC:162537 Add in non-generic bmc vendors AMI, Aten and OpenBmc
+ char l_vendor[32] = "openpower,generic";
+ strncpy(l_info.bmcVendor,l_vendor,sizeof(l_info.bmcVendor));
+
+
+ return l_info;
+ }
+
+}; // End namespace IPMI
diff --git a/src/usr/ipmibase/ipmirp.H b/src/usr/ipmibase/ipmirp.H
new file mode 100644
index 000000000..ddf846afb
--- /dev/null
+++ b/src/usr/ipmibase/ipmirp.H
@@ -0,0 +1,284 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmirp.H $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 */
+
+#ifndef __IPMI_IPMIRP_H
+#define __IPMI_IMPIRP_H
+
+/**
+ * @file ipmirp.H
+ * @brief IPMI service provider declariation
+ */
+
+#include <stdint.h>
+#include <builtins.h>
+#include <ipmi/ipmiif.H>
+#include <errl/errlentry.H>
+#include <sys/msg.h>
+#include <sys/sync.h>
+#include <list>
+#include <map>
+#include <util/locked/list.H>
+#include <intr/interrupt.H>
+#include <limits.h>
+
+#include "ipmimsg.H"
+
+namespace TARGETING
+{
+ class Target;
+};
+
+class IpmiRP
+{
+ public:
+
+ /**
+ * Initialize the IPMI service
+ * @param[in] o_errl, NULL if OK
+ */
+ static void daemonProcess(errlHndl_t& o_errl);
+
+ /**
+ * IpmiDD data source
+ */
+ static void* attach(void *unused);
+
+ /**
+ * Thread start routine for the resource provider
+ * @param[in] void*, unused
+ */
+ static void* start(void* unused);
+
+ /**
+ * Thread start routine for the timeout thread
+ * @param[in] void*, unused
+ */
+ static void* timeout_thread(void* unused);
+
+ /**
+ * Thread start routine for a little task which
+ * waits for the BMC to give us the interface capabilties
+ * @param[in] void*, unused
+ */
+ static void* get_capabilities(void* unused);
+
+ /**
+ * Thread start routine for a little task which handles events
+ * which aren't registered by any other task
+ * @param[in] void*, unused
+ */
+ static void* last_chance_event_handler(void* unused);
+
+ /**
+ * Default constructor
+ */
+ IpmiRP(void);
+
+ /**
+ * Destructor
+ */
+ ~IpmiRP(void);
+
+ /**
+ * The mailbox service provider task
+ */
+ void msgHandler(void);
+
+ /**
+ * Return the max data buffer to allocate for the underlying transport.
+ */
+ inline size_t maxBuffer(void);
+
+ /**
+ * @brief Get the message queue associated with this RP
+ * @param[in] void
+ * @return, a msg_q_t which is the message queue
+ */
+ msg_q_t msgQueue(void)
+ { return iv_msgQ; }
+
+ /**
+ * @brief Queue a message on to the response queue
+ * @param[in] i_msg, the message to queue
+ */
+ void queueForResponse(IPMI::Message& i_msg);
+
+ /**
+ * @brief Tell the resource provider which queue to use for events
+ * @param[in] i_cmd, the command we're looking for
+ * @param[in] i_msgq, the queue we should be notified on
+ */
+ void registerForEvent(const IPMI::command_t& i_cmd, const msg_q_t& i_msgq);
+
+ /**
+ * @brief Give the resource provider a message to put in the eventq
+ * @param[in] i_event, pointer to the new'd event (OEM SEL)
+ */
+ void postEvent(IPMI::oemSEL* i_event);
+
+ /**
+ * @brief Give the resource provider a message to put in the eventq
+ * @param[in] i_event, pointer to the power event data to handle (OEM SEL)
+ */
+ void handlePowerMessage(IPMI::oemSEL* i_event);
+
+ private:
+ void attach(void);
+
+ /**
+ * Entry point for the resource provider
+ */
+ void execute(void);
+
+ /**
+ * Entry point for the timeout thread
+ */
+ void timeoutThread(void);
+
+ /**
+ * @brief Transmit a message over the IPMI interface
+ * @param[in] i_msg, ptr to the message_q message
+ * @note i_msg is not const because it contains a return code
+ */
+ int xmit(msg_t* i_msg);
+
+ /**
+ * @brief Transmit a synchronous message over the IPMI interface
+ * @param[in] i_msg, ptr to the message_q message
+ * @note this is a message used internally to send a message
+ * over the IPMI interface and wait for a response from the BMC
+ * @note i_msg is not const because it contains a return code
+ */
+ void xmit_sync(msg_t* i_msg);
+
+ /**
+ * @brief Transmit an asynchronous message over the IPMI interface
+ * @param[in] i_msg, ptr to the message_q message
+ * @note this is a message used internally to send a message
+ * over the IPMI interface and not wait for a response from the BMC
+ * @note i_msg is not const because it contains a return code
+ */
+ void xmit_async(msg_t* i_msg);
+
+ /**
+ * @brief Handle a message from the interface indicating the
+ * interface has gone idle (and can be written to.)
+ * @param[in] void
+ */
+ void idle(void);
+
+ /**
+ * @brief Handle an indication from the interface indicating the
+ * BMC interface has a response message ready to read
+ * @param[in] void
+ */
+ void response(void);
+
+ /**
+ * @brief Respond to an existing message.
+ * @note This is used as the "base" response handler
+ * @param[in] i_msg, the message to respond to
+ */
+ void response(IPMI::Message* i_msg);
+
+ /**
+ * @brief Respond to a message we're changing
+ * @note This is used when we have a timeout and need to
+ * respond to the caller.
+ * @param[in] i_msg, the message to respond to
+ * @param[in] i_cc, the complettion code
+ */
+ void response(IPMI::Message* i_msg, IPMI::completion_code i_cc);
+
+ /**
+ * @brief Query the BMC for interface capabilities
+ * @param[in] void
+ * @note this fills in iv_bmc_timeout, etc.
+ */
+ void getInterfaceCapabilities(void);
+
+ /**
+ * @brief Process incoming event messages if they're not processed by
+ * any other task.
+ * @param[in] void
+ */
+ void lastChanceEventHandler(void);
+
+ msg_q_t iv_msgQ; //!< ipmi mesage queue
+ IPMI::send_q_t iv_sendq; //!< msg to send queue
+ IPMI::timeout_q_t iv_timeoutq; //!< msgs waiting for a timeout
+ IPMI::respond_q_t iv_respondq; //!< msg respond pending list
+ IPMI::event_q_t iv_eventq; //!< map events to msg_t
+ msg_q_t iv_last_chanceq; //!< last chance event queue
+
+ // Protect the queues from the message loop and the timeout thread
+ mutex_t iv_mutex;
+ sync_cond_t iv_cv;
+
+ // The time, in seconds, the BMC told us is the max request/response
+ // time interval.
+ uint8_t iv_bmc_timeout;
+
+ // How many outstanding requests the BMC can handle
+ uint8_t iv_outstanding_req;
+
+ // Size of the xmit buffer (max we can send)
+ uint8_t iv_xmit_buffer_size;
+
+ // Size of the recv buffer (max BMC will send)
+ uint8_t iv_recv_buffer_size;
+
+ // Recommended number of retries
+ uint8_t iv_retries;
+
+ // Shutdown
+ msg_t * iv_shutdown_msg; //!< shutdown msg to respond to
+
+ //!< handle ipmi chassis power off request
+ bool iv_graceful_shutdown_pending;
+
+ //!< handle ipmi chassis power off request modifier
+ IPMI::power_request_type iv_chassis_power_mod;
+
+ // Disallow copying this class.
+ IpmiRP& operator=(const IpmiRP&);
+ IpmiRP(const IpmiRP&);
+};
+
+namespace IPMI
+{
+ /**
+ * @brief Initiate generic IPMI shutdown/reboot request via the IPMI
+ * resource provider
+ *
+ * @param[in] i_msgType IPMI message type indicating the shutdown or reboot
+ * to request. Asserts if not a valid shutdown or reboot message type as
+ * determined by IPMI::validShutdownRebootMsgType().
+ */
+ void initiateShutdownOrReboot(IPMI::msg_type i_msgType);
+
+} // End IPMI namespace
+
+#endif
diff --git a/src/usr/ipmibase/ipmiselrecord.C b/src/usr/ipmibase/ipmiselrecord.C
new file mode 100644
index 000000000..7fc74b967
--- /dev/null
+++ b/src/usr/ipmibase/ipmiselrecord.C
@@ -0,0 +1,54 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmibase/ipmiselrecord.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2018 */
+/* [+] 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 ipmiselrecord.C
+ * @brief code for the IPMI sel record class
+ */
+
+#include <ipmi/ipmiif.H>
+
+namespace IPMI
+{
+ ///
+ /// @brief populate an OEM SEL record from an event read
+ /// @param[in] i_raw_event_data, pointer to the read event data
+ ///
+ void oemSEL::populateFromEvent(uint8_t const* i_raw_event_data)
+ {
+ iv_record = i_raw_event_data[0] << 8 | i_raw_event_data[1];
+ iv_record_type = i_raw_event_data[2];
+ iv_timestamp = i_raw_event_data[6] << 24 |
+ i_raw_event_data[5] << 16 |
+ i_raw_event_data[4] << 8 |
+ i_raw_event_data[3];
+
+ memcpy(iv_manufacturer, &i_raw_event_data[7], MANUF_LENGTH);
+ iv_netfun = i_raw_event_data[10];
+ memcpy(iv_cmd, &i_raw_event_data[11], CMD_LENGTH);
+
+ return;
+ }
+
+};
diff --git a/src/usr/ipmibase/makefile b/src/usr/ipmibase/makefile
new file mode 100644
index 000000000..e08057254
--- /dev/null
+++ b/src/usr/ipmibase/makefile
@@ -0,0 +1,34 @@
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/usr/ipmibase/makefile $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2011,2018
+#
+#
+# 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
+ROOTPATH = ../../..
+MODULE = ipmibase
+
+OBJS += ipmimsg.o
+OBJS += $(if $(CONFIG_BMC_BT_LPC_IPMI),ipmibt.o)
+OBJS += ipmirp.o
+OBJS += $(if $(CONFIG_BMC_BT_LPC_IPMI),ipmidd.o)
+OBJS += ipmiconfig.o
+OBJS += ipmiselrecord.o
+
+include ${ROOTPATH}/config.mk
OpenPOWER on IntegriCloud