diff options
-rw-r--r-- | src/include/usr/ipmi/ipmi_reasoncodes.H | 3 | ||||
-rw-r--r-- | src/include/usr/ipmi/ipmiif.H | 77 | ||||
-rw-r--r-- | src/usr/ipmi/ipmibt.C | 69 | ||||
-rw-r--r-- | src/usr/ipmi/ipmibt.H | 32 | ||||
-rw-r--r-- | src/usr/ipmi/ipmidd.C | 131 | ||||
-rw-r--r-- | src/usr/ipmi/ipmimsg.C | 5 | ||||
-rw-r--r-- | src/usr/ipmi/ipmimsg.H | 12 | ||||
-rw-r--r-- | src/usr/ipmi/ipmirp.C | 185 | ||||
-rw-r--r-- | src/usr/ipmi/ipmirp.H | 42 | ||||
-rw-r--r-- | src/usr/ipmi/ipmiselrecord.C | 54 | ||||
-rw-r--r-- | src/usr/ipmi/makefile | 1 |
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 |