summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/usr/ipmi/ipmi_reasoncodes.H3
-rw-r--r--src/include/usr/ipmi/ipmiif.H77
-rw-r--r--src/usr/ipmi/ipmibt.C69
-rw-r--r--src/usr/ipmi/ipmibt.H32
-rw-r--r--src/usr/ipmi/ipmidd.C131
-rw-r--r--src/usr/ipmi/ipmimsg.C5
-rw-r--r--src/usr/ipmi/ipmimsg.H12
-rw-r--r--src/usr/ipmi/ipmirp.C185
-rw-r--r--src/usr/ipmi/ipmirp.H42
-rw-r--r--src/usr/ipmi/ipmiselrecord.C54
-rw-r--r--src/usr/ipmi/makefile1
11 files changed, 489 insertions, 122 deletions
diff --git a/src/include/usr/ipmi/ipmi_reasoncodes.H b/src/include/usr/ipmi/ipmi_reasoncodes.H
index 4ac310db3..2ad8a6d48 100644
--- a/src/include/usr/ipmi/ipmi_reasoncodes.H
+++ b/src/include/usr/ipmi/ipmi_reasoncodes.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2014 */
+/* Contributors Listed Below - COPYRIGHT 2014,2015 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -48,6 +48,7 @@ namespace IPMI
RC_EVENT_DATA_NOT_SETTABLE = IPMI_COMP_ID | 0x08,
RC_SENSOR_NOT_PRESENT = IPMI_COMP_ID | 0x09,
RC_SET_SENSOR_FAILURE = IPMI_COMP_ID | 0x0a,
+ RC_READ_EVENT_FAILURE = IPMI_COMP_ID | 0x0b,
};
};
diff --git a/src/include/usr/ipmi/ipmiif.H b/src/include/usr/ipmi/ipmiif.H
index a3f11aebb..57201918c 100644
--- a/src/include/usr/ipmi/ipmiif.H
+++ b/src/include/usr/ipmi/ipmiif.H
@@ -54,6 +54,14 @@ namespace IPMI
MSG_LAST_TYPE = MSG_STATE_SHUTDOWN,
};
+ // Used in the factory for creating the proper subclass.
+ enum message_type
+ {
+ TYPE_SYNC = 0,
+ TYPE_ASYNC = 1,
+ TYPE_EVENT = 2,
+ };
+
// IPMI network functions
enum network_function
{
@@ -69,6 +77,7 @@ namespace IPMI
NETFUN_FIRMWARE = (0x08 << 2),
NETFUN_STORAGE = (0x0a << 2),
NETFUN_TRANPORT = (0x0c << 2),
+ NETFUN_IBM = (0x3a << 2),
// Overload the OEM netfun for a "none" netfun. We use this as a
// default for objects which will have their netfun filled in
@@ -107,6 +116,61 @@ namespace IPMI
CC_UNKBAD = 0xff
};
+ // per IPMI Spec, section 32.2 OEM SEL Event Records
+ struct oemSEL {
+
+ enum Constants
+ {
+ MANUF_LENGTH = 3,
+ CMD_LENGTH = 5,
+ LENGTH = 16,
+ };
+
+ // ID used for SEL Record access. The Record ID values 0000h and FFFFh
+ // have special meaning in the Event Access commands and must not be
+ // used as Record ID values for stored SEL Event Records.
+ uint16_t iv_record;
+
+ // [7:0] - Record Type
+ // 02h = system event record
+ // C0h-DFh = OEM timestamped, bytes 8-16 OEM defined
+ // E0h-FFh = OEM non-timestamped, bytes 4-16 OEM defined
+ uint8_t iv_record_type;
+
+ // Time when event was logged. LS byte first.
+ uint32_t iv_timestamp;
+
+ // 3 bytes for the manuf id
+ uint8_t iv_manufacturer[MANUF_LENGTH];
+
+ // Presently 0x3A for IBM
+ uint8_t iv_netfun;
+
+ // Current command and arguments
+ uint8_t iv_cmd[CMD_LENGTH];
+
+ // @brief Populate a selRecord from the wire representation of an event
+ // @param[in] i_raw_event_data, pointer to the raw data
+ void populateFromEvent(uint8_t const* i_raw_event_data);
+
+ // ctor
+ oemSEL():
+ iv_record(0),
+ iv_record_type(0),
+ iv_timestamp(0),
+ iv_netfun(0)
+ {
+ memset(iv_manufacturer, 0, MANUF_LENGTH);
+ memset(iv_cmd, 0, CMD_LENGTH);
+ };
+
+ // @Brief Create a selRecord from the wire representation of an event
+ // @param[in] i_event_data, pointer to the event data
+ oemSEL( uint8_t const* i_event_data )
+ { populateFromEvent( i_event_data ); }
+
+ };
+
//
// Network function, command pairs.
//
@@ -126,6 +190,9 @@ namespace IPMI
inline const command_t reset_watchdog(void)
{ return std::make_pair(NETFUN_APP, 0x22); }
+ inline const command_t read_event(void)
+ { return std::make_pair(NETFUN_APP, 0x35); }
+
inline const command_t get_capabilities(void)
{ return std::make_pair(NETFUN_APP, 0x36); }
@@ -148,6 +215,12 @@ namespace IPMI
inline const command_t get_sensor_reading(void)
{ return std::make_pair(NETFUN_SENSOR, 0x2D); }
+ // OEM Messages
+ inline const command_t power_off(void)
+ { return std::make_pair(NETFUN_IBM, 0x04); }
+
+ inline const command_t pnor_request(void)
+ { return std::make_pair(NETFUN_IBM, 0x07); }
// Some helper messages
// Used to create an empty message for reception
@@ -164,6 +237,7 @@ namespace IPMI
* @param[in] i_cmd, the network function and command
* @param[in] i_len, the length of the buffer
* @param[in] i_data, the buffer - must be new'd
+ * @param[in] i_type, the message type EVENT/ASYNC- defaults to ASYNC
* @example uint8_t* data = new uint8_t[length];
* @warning do *not* delete data, the resource provider will do that.
*
@@ -171,7 +245,8 @@ namespace IPMI
*/
errlHndl_t send(const command_t& i_cmd,
const size_t i_len,
- uint8_t* i_data);
+ uint8_t* i_data,
+ message_type i_type = TYPE_ASYNC);
/**
* Send message synchronously
diff --git a/src/usr/ipmi/ipmibt.C b/src/usr/ipmi/ipmibt.C
index 57de5d8cc..f5cba4d1c 100644
--- a/src/usr/ipmi/ipmibt.C
+++ b/src/usr/ipmi/ipmibt.C
@@ -36,6 +36,7 @@
#include "ipmibt.H"
#include "ipmirp.H"
+#include <ipmi/ipmiif.H>
#include <errno.h>
#include <config.h>
@@ -167,7 +168,7 @@ namespace IPMI
}
///
- /// @brief BTSyncMessage ctor
+ /// @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)
@@ -180,6 +181,19 @@ namespace IPMI
}
///
+ /// @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)
@@ -227,9 +241,7 @@ namespace IPMI
// 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. Note
- // we need to use this mechanism rather than letting the caller
- // check iv_state as we may have just deleted ourselves.
+ // put a message on the queue which couldn't be sent.
return (iv_state != 0);
}
@@ -272,7 +284,7 @@ namespace IPMI
}
///
- /// @brief async msg transmit
+ /// @brief async msg response
///
void BTAsyncMessage::response(msg_q_t)
{
@@ -309,4 +321,51 @@ namespace IPMI
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);
+
+ /* @errorlog tag
+ * @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;
+ }
+
};
diff --git a/src/usr/ipmi/ipmibt.H b/src/usr/ipmi/ipmibt.H
index 75c43395f..0a757707a 100644
--- a/src/usr/ipmi/ipmibt.H
+++ b/src/usr/ipmi/ipmibt.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2012,2014 */
+/* Contributors Listed Below - COPYRIGHT 2012,2015 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -73,6 +73,7 @@ namespace IPMI
///
/// @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.
@@ -112,6 +113,7 @@ namespace IPMI
///
/// @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);
@@ -134,7 +136,7 @@ namespace IPMI
///
/// @brief BTSyncMessage dtor
///
- ~BTAsyncMessage(void)
+ virtual ~BTAsyncMessage(void)
{ delete[] iv_data; }
///
@@ -146,6 +148,32 @@ namespace IPMI
///
/// @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);
diff --git a/src/usr/ipmi/ipmidd.C b/src/usr/ipmi/ipmidd.C
index b0278fe73..45416dd3e 100644
--- a/src/usr/ipmi/ipmidd.C
+++ b/src/usr/ipmi/ipmidd.C
@@ -61,32 +61,36 @@ 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)
-// Registers. These are fixed for LPC/BT so we can hard-wire them
-#define REG_CONTROL 0xE4
-#define REG_HOSTBMC 0xE5
-#define 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.
-#define CTRL_B_BUSY (1 << 7)
-#define CTRL_H_BUSY (1 << 6)
-#define CTRL_OEM0 (1 << 5)
-#define CTRL_SMS_ATN (1 << 4)
-#define CTRL_B2H_ATN (1 << 3)
-#define CTRL_H2B_ATN (1 << 2)
-#define CTRL_CLR_RD_PTR (1 << 1)
-#define CTRL_CLR_WR_PTR (1 << 0)
-
-#define 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.
-#define INT_BMC_HWRST (1 << 7)
-
-// How long to sychronously wait for the device to change state (in ns)
-#define WAIT_TIME 100000000
+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 Performs an IPMI Message Read Operation
@@ -269,10 +273,20 @@ void IpmiDD::pollCtrl(void)
// If we see the SMS_ATN, there's an event waiting
else if (ctrl & CTRL_SMS_ATN)
{
- IPMI_TRAC(ERR_MRK "sending state sms/event, unexpected");
+ IPMI_TRAC(INFO_MRK "sending state sms/event");
msg = msg_allocate();
msg->type = IPMI::MSG_STATE_EVNT;
msg_send(mq, 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);
@@ -287,41 +301,7 @@ void IpmiDD::pollCtrl(void)
*/
inline errlHndl_t IpmiDD::reset(void)
{
- // Reset the BT interface, and flush messages. We eat messages off of
- // the interface until it goes idle. We assume, since we're resetting,
- // that there is nothing on the interface we're interested in.
- mutex_lock(&iv_mutex);
- IPMI_TRAC(ENTER_MRK "resetting the IPMI BT interface");
-
- uint8_t ctrl = 0;
- IPMI::BTMessage msg;
-
- errlHndl_t err = readLPC(REG_CONTROL, ctrl);
- IPMI_TRAC("reset: control register %x", ctrl);
- while ((ctrl & (CTRL_B2H_ATN | CTRL_SMS_ATN)) && (err == NULL))
- {
- // There should only be one, if any - but we'll log each one we find.
- IPMI_TRAC(INFO_MRK "found a waiting message during reset");
- err = receive(&msg);
- IPMI_TRAC("reset: received %x:%x", msg.iv_netfun, msg.iv_cmd);
- delete[] msg.iv_data;
-
- if (err) {break;}
-
- err = readLPC(REG_CONTROL, ctrl);
- IPMI_TRAC("reset: control register %x", ctrl);
- }
-
- // Commit this log. We're about to reset the PHY anyway, so maybe
- // that'll clear this error. If not, we'll report that error.
- if (err)
- {
- errlCommit(err, IPMI_COMP_ID);
- }
-
- mutex_unlock(&iv_mutex);
-
- IPMI_TRAC(EXIT_MRK "resetting the IPMI BT interface");
+ IPMI_TRAC("resetting the IPMI BT interface");
return writeLPC(REG_INTMASK, INT_BMC_HWRST);
}
@@ -425,8 +405,7 @@ errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg)
// 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 & CTRL_B2H_ATN) ? CTRL_B2H_ATN : CTRL_SMS_ATN);
+ err = writeLPC(REG_CONTROL, CTRL_B2H_ATN);
if (err) {break;}
// Tell the interface we're reading
@@ -437,9 +416,7 @@ errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg)
err = readLPC(REG_HOSTBMC, o_msg->iv_len);
if (err) { break; }
- // I don't think SMS messages have a completion code.
- o_msg->iv_len -= (ctrl & CTRL_B2H_ATN) ?
- IPMI_BT_HEADER_SIZE + 1 : IPMI_BT_HEADER_SIZE;
+ o_msg->iv_len -= IPMI_BT_HEADER_SIZE + 1;
err = readLPC(REG_HOSTBMC, o_msg->iv_netfun);
if (err) { break; }
@@ -450,12 +427,8 @@ errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg)
err = readLPC(REG_HOSTBMC, o_msg->iv_cmd);
if (err) { break; }
- // I don't think SMS messages have a completion code.
- if (ctrl & CTRL_B2H_ATN)
- {
- err = readLPC(REG_HOSTBMC, o_msg->iv_cc);
- if (err) { continue; }
- }
+ err = readLPC(REG_HOSTBMC, o_msg->iv_cc);
+ if (err) { break; }
o_msg->iv_data = new uint8_t[o_msg->iv_len];
@@ -477,8 +450,7 @@ errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg)
mutex_unlock(&iv_mutex);
- IPMI_TRAC(INFO_MRK "read %s %s %x:%x seq %x len %x cc %x",
- (ctrl & CTRL_B2H_ATN) ? "b2h" : "sms",
+ 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);
@@ -510,15 +482,6 @@ IpmiDD::IpmiDD(void):
{
mutex_init(&iv_mutex);
- // reset the BT interface - no idea what state the BMC thinks things are in
- errlHndl_t err = reset();
- if (err)
- {
- IPMI_TRAC(ERR_MRK "error resetting the BT interface");
- err->collectTrace(IPMI_COMP_NAME);
- errlCommit(err, IPMI_COMP_ID);
- }
-
// 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 );
diff --git a/src/usr/ipmi/ipmimsg.C b/src/usr/ipmi/ipmimsg.C
index 6b3b5fd4a..62145cbe4 100644
--- a/src/usr/ipmi/ipmimsg.C
+++ b/src/usr/ipmi/ipmimsg.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2012,2014 */
+/* Contributors Listed Below - COPYRIGHT 2012,2015 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -91,6 +91,9 @@ namespace IPMI
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);
diff --git a/src/usr/ipmi/ipmimsg.H b/src/usr/ipmi/ipmimsg.H
index a2ab77459..b7f6d966e 100644
--- a/src/usr/ipmi/ipmimsg.H
+++ b/src/usr/ipmi/ipmimsg.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2012,2014 */
+/* Contributors Listed Below - COPYRIGHT 2012,2015 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -34,16 +34,10 @@
namespace IPMI
{
- // Used in the factory for creating the proper subclass.
- enum message_type
- {
- TYPE_SYNC = 0,
- TYPE_ASYNC = 1,
- };
-
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_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
diff --git a/src/usr/ipmi/ipmirp.C b/src/usr/ipmi/ipmirp.C
index 747e552ec..fba8262d1 100644
--- a/src/usr/ipmi/ipmirp.C
+++ b/src/usr/ipmi/ipmirp.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2012,2014 */
+/* Contributors Listed Below - COPYRIGHT 2012,2015 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -31,6 +31,7 @@
#include "ipmiconfig.H"
#include "ipmidd.H"
#include <ipmi/ipmi_reasoncodes.H>
+#include <ipmi/ipmiif.H>
#include <devicefw/driverif.H>
#include <devicefw/userif.H>
@@ -66,6 +67,8 @@ IpmiRP::IpmiRP(void):
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),
@@ -104,6 +107,12 @@ void* IpmiRP::get_capabilities(void* unused)
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::start, NULL);
@@ -281,6 +290,150 @@ void IpmiRP::getInterfaceCapabilities(void)
}
/**
+ * @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);
+}
+
+/**
+ * @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
+
+ // 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)
+ {
+ /* @errorlog tag
+ * @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 IPMI event handling
+ */
+ errlHndl_t err =
+ new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ IPMI::MOD_IPMISRV_SEND,
+ 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);
+ }
+}
+
+/**
+ * @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);
+
+ // TODO: RTC: 120127, check to ensure this works properly on a newer BMC
+
+ uint8_t len = 0;
+ errlHndl_t err = send(IPMI::pnor_request(), len, data);
+ if (err)
+ {
+ err->collectTrace(IPMI_COMP_NAME);
+ errlCommit(err, IPMI_COMP_ID);
+ }
+}
+/**
+ * @brief Wait for events and read them
+ */
+void IpmiRP::lastChanceEventHandler(void)
+{
+ // Mark as an independent daemon so if it crashes we terminate.
+ task_detach();
+
+ // TODO: RTC: 108830, copy the code below to make a handler for power-off
+ //
+ // To create a event handler, all you need to do is create a message
+ // queue (or use one you have) and register for events. I left this
+ // code here as an example.
+ //
+ // Create a message queue and register with the resource provider
+ // msg_q_t queue = msg_q_create();
+ // registerForEvent(IPMI::power_off(), queue);
+
+
+ // 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 {
+ // 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;
+}
+
+/**
* @brief Entry point of the resource provider
*/
void IpmiRP::execute(void)
@@ -306,6 +459,9 @@ void IpmiRP::execute(void)
// 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);
+
while (true)
{
msg_t* msg = msg_wait(iv_msgQ);
@@ -354,11 +510,22 @@ void IpmiRP::execute(void)
response();
break;
- // Handle an event (SMS_ATN)
+ // 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:
- IPMI_TRAC(ERR_MRK "msg loop: unexpected ipmi sms");
- msg_free(msg);
- // TODO: RTC 116600 Handle SMS messages
+ {
+ 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
@@ -616,7 +783,6 @@ void IpmiRP::shutdownNow(void)
msg_respond(iv_msgQ, iv_shutdown_msg);
}
-
namespace IPMI
{
///
@@ -683,7 +849,8 @@ namespace IPMI
/// @brief Asynchronus message send
///
errlHndl_t send(const IPMI::command_t& i_cmd,
- const size_t i_len, uint8_t* i_data)
+ 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;
@@ -691,8 +858,8 @@ namespace IPMI
// 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,
- IPMI::TYPE_ASYNC);
+ 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());
diff --git a/src/usr/ipmi/ipmirp.H b/src/usr/ipmi/ipmirp.H
index b70d0247c..5f06be3da 100644
--- a/src/usr/ipmi/ipmirp.H
+++ b/src/usr/ipmi/ipmirp.H
@@ -79,6 +79,13 @@ class IpmiRP
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);
@@ -112,6 +119,19 @@ class IpmiRP
*/
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);
+
private:
/**
@@ -180,18 +200,18 @@ class IpmiRP
void response(IPMI::Message* i_msg, IPMI::completion_code i_cc);
/**
- * @brief Handle an indication from the interface indicating the
- * BMC interface has an event/sms message ready to read
+ * @brief Query the BMC for interface capabilities
* @param[in] void
+ * @note this fills in iv_bmc_timeout, etc.
*/
- void event(void);
+ void getInterfaceCapabilities(void);
/**
- * @brief Query the BMC for interface capabilities
+ * @brief Process incoming event messages if they're not processed by
+ * any other task.
* @param[in] void
- * @note this fills in iv_bmc_timeout, etc.
*/
- void getInterfaceCapabilities(void);
+ void lastChanceEventHandler(void);
/**
* @brief Clean up resources and reply to shutdown msg
@@ -199,10 +219,12 @@ class IpmiRP
*/
void shutdownNow(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
+ 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;
diff --git a/src/usr/ipmi/ipmiselrecord.C b/src/usr/ipmi/ipmiselrecord.C
new file mode 100644
index 000000000..5d82d4fe3
--- /dev/null
+++ b/src/usr/ipmi/ipmiselrecord.C
@@ -0,0 +1,54 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/ipmi/ipmiselrecord.C $ */
+/* */
+/* OpenPOWER HostBoot Project */
+/* */
+/* Contributors Listed Below - COPYRIGHT 2012,2015 */
+/* [+] 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/ipmi/makefile b/src/usr/ipmi/makefile
index 68b77d236..2c92a27a6 100644
--- a/src/usr/ipmi/makefile
+++ b/src/usr/ipmi/makefile
@@ -35,6 +35,7 @@ OBJS += ipmisensor.o
OBJS += ipmiwatchdog.o
OBJS += ipmifruinv.o
OBJS += ipmipowerstate.o
+OBJS += ipmiselrecord.o
#SUBDIRS += test.d
OpenPOWER on IntegriCloud