From e92561c3352a4af3b1b8506fae61652cde4ee3d5 Mon Sep 17 00:00:00 2001 From: Brian Silver Date: Mon, 6 Oct 2014 13:45:16 -0500 Subject: Add response timeouts and event requests Change-Id: I2a763e5e3ea59e6afb7b7ab7d088fb236ee3428e Depends-On: I8f6a590b29d9171389d10abc5b6e68f91ac94d16 RTC: 116300 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/13856 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III --- src/include/usr/ipmi/ipmi_reasoncodes.H | 2 + src/include/usr/ipmi/ipmiif.H | 102 +++++- src/usr/ipmi/ipmibt.C | 166 +++++++--- src/usr/ipmi/ipmibt.H | 80 +++-- src/usr/ipmi/ipmiconfig.C | 45 +++ src/usr/ipmi/ipmiconfig.H | 55 ++++ src/usr/ipmi/ipmidd.C | 6 +- src/usr/ipmi/ipmimsg.C | 27 +- src/usr/ipmi/ipmimsg.H | 36 ++- src/usr/ipmi/ipmirp.C | 553 +++++++++++++++++++++++--------- src/usr/ipmi/ipmirp.H | 69 ++++ src/usr/ipmi/makefile | 1 + 12 files changed, 880 insertions(+), 262 deletions(-) create mode 100644 src/usr/ipmi/ipmiconfig.C create mode 100644 src/usr/ipmi/ipmiconfig.H (limited to 'src') diff --git a/src/include/usr/ipmi/ipmi_reasoncodes.H b/src/include/usr/ipmi/ipmi_reasoncodes.H index 12a9391bc..3e66dbcd8 100644 --- a/src/include/usr/ipmi/ipmi_reasoncodes.H +++ b/src/include/usr/ipmi/ipmi_reasoncodes.H @@ -40,6 +40,8 @@ namespace IPMI RC_INVALID_QRESPONSE = IPMI_COMP_ID | 0x01, RC_INVALID_SENDRECV = IPMI_COMP_ID | 0x02, RC_INVALID_SEND = IPMI_COMP_ID | 0x03, + RC_WAITER_NOT_FOUND = IPMI_COMP_ID | 0x04, + RC_ASYNC_BAD_CC = IPMI_COMP_ID | 0x05, }; }; diff --git a/src/include/usr/ipmi/ipmiif.H b/src/include/usr/ipmi/ipmiif.H index 431b006ef..040244041 100644 --- a/src/include/usr/ipmi/ipmiif.H +++ b/src/include/usr/ipmi/ipmiif.H @@ -27,6 +27,7 @@ #include #include +#include namespace IPMI { @@ -74,10 +75,76 @@ namespace IPMI NETFUN_NONE = (0x30 << 2), }; + // IPMI Completion Codes + enum completion_code + { + CC_OK = 0x00, + CC_BUSY = 0xc0, + CC_INVALID = 0xc1, + CC_CMDLUN = 0xc2, + CC_TIMEOUT = 0xc3, + CC_NOSPACE = 0xc4, + CC_BADRESV = 0xc5, + CC_TRUNC = 0xc6, + CC_BADLEN = 0xc7, + CC_TOOLONG = 0xc8, + CC_OORANGE = 0xc9, + CC_LONGREPLY = 0xca, + CC_BADSENSOR = 0xcb, + CC_REQINVAL = 0xcc, + CC_CMDSENSOR = 0xcd, + CC_CANTREPLY = 0xce, + CC_DUPREQ = 0xcf, + CC_SDRUPDATE = 0xd0, + CC_FMWUPDATE = 0xd1, + CC_BMCINIT = 0xd2, + CC_BADDEST = 0xd3, + CC_NOPERM = 0xd4, + CC_NOTSUP = 0xd5, + CC_ILLPARAM = 0xd6, + CC_UNKBAD = 0xff + }; + + // + // Network function, command pairs. + // + typedef std::pair command_t; + + + // Application messages + inline const command_t get_device_id(void) + { return std::make_pair(NETFUN_APP, 0x01); } + + inline const command_t set_watchdog(void) + { return std::make_pair(NETFUN_APP, 0x24); } + + inline const command_t get_capabilities(void) + { return std::make_pair(NETFUN_APP, 0x36); } + + + // Storage messages + inline const command_t set_sel_time(void) + { return std::make_pair(NETFUN_STORAGE, 0x49); } + + + // Sensor messages + inline const command_t platform_event(void) + { return std::make_pair(NETFUN_SENSOR, 0x02); } + + + // Some helper messages + // Used to create an empty message for reception + inline const command_t no_command(void) + { return std::make_pair(NETFUN_NONE, 0x00); } + + // This is a message used only for testing. The ipmid responder + // will drop this message so we can test timeouts. + inline const command_t test_drop(void) + { return std::make_pair(NETFUN_APP, 0x3e); } + /** * Send message asynchronously - * @param[in] i_netfun, the network function - * @param[in] i_cmd, the command + * @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 * @example uint8_t* data = new uint8_t[length]; @@ -85,14 +152,13 @@ namespace IPMI * * @return errlHndl_t, NULL on success */ - errlHndl_t send(const network_function i_netfun, - const uint8_t i_cmd, const size_t i_len, + errlHndl_t send(const command_t& i_cmd, + const size_t i_len, uint8_t* i_data); /** * Send message synchronously - * @param[in] i_netfun, the network function - * @param[in] i_cmd, the command + * @param[in] i_cmd, the command and network function * @param[out] o_completion_code, the completion code * @param[in,out] io_len, the length of the buffer * @param[in] i_data, the buffer - must be new'd @@ -101,10 +167,30 @@ namespace IPMI * * @return errlHndl_t, NULL on success */ - errlHndl_t sendrecv(const network_function i_netfun, - const uint8_t i_cmd, uint8_t& o_completion_code, + errlHndl_t sendrecv(const command_t& i_cmd, + completion_code& o_completion_code, size_t& io_len, uint8_t*& io_data); + /** + * Synchronously send an event + * @param[in] i_sensor_type, the sensor type + * @param[in] i_sensor_num, the sensor number + * @param[in] i_assertion, bool true is assert, false is deassert. + * @param[in] i_type, event type + * @param[out] o_completion_code, completion code + * @param[in] i_len, number of data bytes (1-3) + * @param[in] i_data, data bytes + * + * @return errlHndl_t, NULL on success + */ + 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); + /** * Get the max buffer size * @param void diff --git a/src/usr/ipmi/ipmibt.C b/src/usr/ipmi/ipmibt.C index d1be32bbe..5f4e02950 100644 --- a/src/usr/ipmi/ipmibt.C +++ b/src/usr/ipmi/ipmibt.C @@ -48,16 +48,13 @@ namespace IPMI { /// /// @brief msg ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, - const uint8_t i_cmd, const uint8_t i_len, + BTMessage::BTMessage(const command_t& i_cmd, const uint8_t i_len, uint8_t* i_data): - Message(i_netfun, i_cmd, i_len, 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 @@ -69,7 +66,7 @@ namespace IPMI /// /// @brief Transimit - send the data out the device interface /// - errlHndl_t BTMessage::xmit(void) + 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. @@ -93,11 +90,31 @@ namespace IPMI unused_size, DeviceFW::IPMIBT); - // If we're not going to remain on the i_sendq, we need to - // delete the data. + // 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::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; @@ -138,40 +155,36 @@ namespace IPMI /// /// @brief BTSyncMessage ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, - const uint8_t i_cmd, const uint8_t i_len, + BTSyncMessage::BTSyncMessage(const command_t& i_cmd, + const uint8_t i_len, uint8_t* i_data): - BTMessage(i_netfun, i_cmd, i_len, i_data) + BTMessage(i_cmd, i_len, i_data) { } /// /// @brief BTSyncMessage ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, - const uint8_t i_cmd, const uint8_t i_len, + BTAsyncMessage::BTAsyncMessage(const command_t& i_cmd, + const uint8_t i_len, uint8_t* i_data): - BTMessage(i_netfun, i_cmd, i_len, i_data) + BTMessage(i_cmd, i_len, i_data) { } /// /// @brief sync msg transmit /// - bool BTSyncMessage::xmit(respond_q_t& i_respondq) + bool BTSyncMessage::xmit(void) { - errlHndl_t err = BTMessage::xmit(); + errlHndl_t err = BTMessage::phy_xmit(); if (err) { @@ -192,17 +205,6 @@ namespace IPMI } } - // Otherwise, we either were transmitted ok or we were told EAGAIN. - // We can tell this by iv_state - if it's not EAGAIN, we need to go hang - // out on the response queue. - else if (iv_state != EAGAIN) - { - i_respondq[iv_seq] = iv_msg; - } - else { - IPMI_TRAC(INFO_MRK "busy, queue head %x:%x", iv_netfun, iv_cmd); - } - // 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. @@ -212,10 +214,9 @@ namespace IPMI /// /// @brief async msg transmit /// - bool BTAsyncMessage::xmit(respond_q_t&) + bool BTAsyncMessage::xmit(void) { - errlHndl_t err = BTMessage::xmit(); - bool io_error = (iv_state != 0); + errlHndl_t err = BTMessage::phy_xmit(); if (err) { @@ -224,23 +225,88 @@ namespace IPMI errlCommit(err, IPMI_COMP_ID); } - // If we didn't have an error but we got back an EAGAIN - // we've been queued up for a retry. Otherwise, we're free - // to commit suicide. - else if (iv_state != EAGAIN) - { - // Yes, this is OK - there is no further reference to this object. - delete this; - } - else { - IPMI_TRAC(INFO_MRK "busy, queue head %x:%x", iv_netfun, iv_cmd); - } - // 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. - return io_error; + 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); + + /* @errorlog tag + * @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 transmit + /// + 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. + 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); + + /* @errorlog tag + * @errortype ERRL_SEV_INFORMATIONAL + * @moduleid IPMI::MOD_IPMISRV_REPLY + * @reasoncode IPMI::RC_ASYNC_BAD_CC + * @userdata1 command of message + * @userdata2 completion code + * @devdesc an async message 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_ASYNC_BAD_CC, + iv_cmd, + iv_cc, + true); + + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + } + + // 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 6414e59b8..75c43395f 100644 --- a/src/usr/ipmi/ipmibt.H +++ b/src/usr/ipmi/ipmibt.H @@ -27,15 +27,9 @@ #include "ipmimsg.H" -// How many bytes are in the IPMI BT message header +/// So, IPMI_BT_MAX_DATA == BMC receive size - IPMI_BT_HEADER_SIZE; #define IPMI_BT_HEADER_SIZE 3 -// Per BMC Firmware Specification v3.6 James, et al the ASPEED -// is limited to a 64 byte IPMI message buffer. -#define IPMI_BT_MAX_MESSAGE_SIZE 64 - -#define IPMI_BT_MAX_DATA (IPMI_BT_MAX_MESSAGE_SIZE - IPMI_BT_HEADER_SIZE) - namespace IPMI { // IPMI block-transfer message base class @@ -44,33 +38,33 @@ namespace IPMI public: /// /// @brief msg ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command + /// @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 network_function i_netfun = NETFUN_NONE, - const uint8_t i_cmd = 0, + 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 xmit(void); + errlHndl_t phy_xmit(void); /// /// @brief transmit a message. - /// @param[in] i_respondq, a map sequence->msg_t /// @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(respond_q_t& i_respondq) - {return true;}; + virtual bool xmit(void) + {return true;} /// /// @brief receive a message. @@ -78,13 +72,22 @@ namespace IPMI errlHndl_t recv(void); /// - /// @brief the maximum buffer size of the underlying transport - /// @param void - /// @return size_t, the max buffer size + /// @brief complete the processing when a response arrives + /// @return void + /// @note we're not pure abstract as we want to be able to + /// instantiate an object of BTMessage for reading. /// - size_t max_buffer(void) - { return IPMI_BT_MAX_DATA; } + 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 @@ -93,22 +96,26 @@ namespace IPMI public: /// /// @brief BTSyncMessage ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, const uint8_t i_cmd, + BTSyncMessage(const command_t& i_cmd, const uint8_t i_len, uint8_t* i_data); /// /// @brief transmit a message. - /// @param[in] i_respondq, a map sequence->msg_t /// /// @return true iff there was no transmission error /// - bool xmit(respond_q_t& i_respondq); + bool xmit(void); + + /// + /// @brief complete the processing when a response arrives + /// @return void + /// + void response(msg_q_t i_msgQ); + }; // IPMI BT asynchronous message @@ -117,22 +124,31 @@ namespace IPMI public: /// /// @brief BTSyncMessage ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, const uint8_t i_cmd, + BTAsyncMessage(const command_t& i_cmd, const uint8_t i_len, uint8_t* i_data); + /// + /// @brief BTSyncMessage dtor + /// + ~BTAsyncMessage(void) + { delete[] iv_data; } + /// /// @brief transmit a message. - /// @param[in] respond_q_t unused /// /// @return true iff there was no transmission error /// - bool xmit(respond_q_t&); + bool xmit(void); + + /// + /// @brief complete the processing when a response arrives + /// @return void + /// + void response(msg_q_t i_msgQ); }; }; // end namespace IPMI diff --git a/src/usr/ipmi/ipmiconfig.C b/src/usr/ipmi/ipmiconfig.C new file mode 100644 index 000000000..5175b49a5 --- /dev/null +++ b/src/usr/ipmi/ipmiconfig.C @@ -0,0 +1,45 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmiconfig.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#include +#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 = 1; + + // Number of allowed outstanding requests default +const uint8_t IPMI::g_outstanding_req = 0xff; + + // 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/ipmi/ipmiconfig.H b/src/usr/ipmi/ipmiconfig.H new file mode 100644 index 000000000..673be6128 --- /dev/null +++ b/src/usr/ipmi/ipmiconfig.H @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmiconfig.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#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/ipmi/ipmidd.C b/src/usr/ipmi/ipmidd.C index 3456cac19..4484daca5 100644 --- a/src/usr/ipmi/ipmidd.C +++ b/src/usr/ipmi/ipmidd.C @@ -232,7 +232,6 @@ void IpmiDD::pollCtrl(void) errlCommit(err, IPMI_COMP_ID); break; } - else { // If we're idle, tell the resoure provider to check for any @@ -284,16 +283,19 @@ inline errlHndl_t IpmiDD::reset(void) 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 @@ -435,7 +437,7 @@ errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg) if (err) { break; } // I don't think SMS messages have a completion code. - if (CTRL_B2H_ATN) + if (ctrl & CTRL_B2H_ATN) { err = readLPC(REG_HOSTBMC, o_msg->iv_cc); if (err) { continue; } diff --git a/src/usr/ipmi/ipmimsg.C b/src/usr/ipmi/ipmimsg.C index 09aa61b77..6b3b5fd4a 100644 --- a/src/usr/ipmi/ipmimsg.C +++ b/src/usr/ipmi/ipmimsg.C @@ -46,39 +46,38 @@ namespace IPMI { /// /// @brief msg ctor - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 network_function i_netfun, - const uint8_t i_cmd, const uint8_t i_len, + 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_netfun), + iv_netfun(i_cmd.first), iv_seq(iv_key), - iv_cmd(i_cmd), + 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; } /// /// @brief static factory - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 /// - Message* Message::factory(const network_function i_netfun, - const uint8_t i_cmd, const uint8_t i_len, + /// @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; @@ -87,10 +86,10 @@ namespace IPMI switch(i_type) { case TYPE_SYNC: - new_message = new BTSyncMessage(i_netfun, i_cmd, i_len, i_data); + new_message = new BTSyncMessage(i_cmd, i_len, i_data); break; case TYPE_ASYNC: - new_message = new BTAsyncMessage(i_netfun, i_cmd, i_len, i_data); + new_message = new BTAsyncMessage(i_cmd, i_len, i_data); break; default: // We have ourselves a bug diff --git a/src/usr/ipmi/ipmimsg.H b/src/usr/ipmi/ipmimsg.H index 71a504017..a2ab77459 100644 --- a/src/usr/ipmi/ipmimsg.H +++ b/src/usr/ipmi/ipmimsg.H @@ -25,6 +25,7 @@ #ifndef __IPMI_IPMIMSG_H #define __IPMI_IPMIMSG_H +#include #include #include #include @@ -41,6 +42,7 @@ namespace IPMI }; typedef std::list send_q_t; + typedef std::list timeout_q_t; typedef std::map respond_q_t; // IPMI message base class. A thing which expects to be sent down a @@ -51,24 +53,25 @@ namespace IPMI public: /// /// @brief static factory - /// @param[in] i_netfun, the network function - /// @param[in] i_cmd, the network command - /// @param[in] i_data, the data for the command + /// @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 /// - static Message* factory(const network_function i_netfun = NETFUN_NONE, - const uint8_t i_cmd = 0, + /// @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 network_function i_netfun = NETFUN_NONE, - const uint8_t i_cmd = 0, + Message(const command_t& i_cmd = no_command(), const uint8_t i_len = 0, uint8_t* i_data = NULL); @@ -77,22 +80,30 @@ namespace IPMI /// 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 maximum buffer size of the underlying transport + /// @brief the header size of a message for the underlying transport /// @param void - /// @return size_t, the max buffer size + /// @return size_t, the header size /// - virtual size_t max_buffer(void) = 0; + virtual size_t header_size(void) = 0; /// /// @brief transmit a message. - /// @param[in] i_respondq, a map: iv_key->msg_t /// @return true iff there was no transmission error /// - virtual bool xmit(respond_q_t& i_respondq) = 0; + 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. @@ -114,6 +125,7 @@ namespace IPMI 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 diff --git a/src/usr/ipmi/ipmirp.C b/src/usr/ipmi/ipmirp.C index 008b66f8c..30a0a6784 100644 --- a/src/usr/ipmi/ipmirp.C +++ b/src/usr/ipmi/ipmirp.C @@ -28,6 +28,7 @@ */ #include "ipmirp.H" +#include "ipmiconfig.H" #include #include #include @@ -62,8 +63,16 @@ TASK_ENTRY_MACRO( IpmiRP::daemonProcess ); IpmiRP::IpmiRP(void): iv_msgQ(msg_q_create()), iv_sendq(), - iv_respondq() + iv_timeoutq(), + iv_respondq(), + 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) { + mutex_init(&iv_mutex); + sync_cond_init(&iv_cv); } /** @@ -80,36 +89,187 @@ void* IpmiRP::start(void* unused) return NULL; } +void* IpmiRP::timeout_thread(void* unused) +{ + Singleton::instance().timeoutThread(); + return NULL; +} + +void* IpmiRP::get_capabilities(void* unused) +{ + Singleton::instance().getInterfaceCapabilities(); + return NULL; +} + void IpmiRP::daemonProcess(errlHndl_t& o_errl) { task_create(&IpmiRP::start, NULL); } /** - * @brief Return the maximum data size to allocate + * @brief Return the transport header size */ -inline size_t IpmiRP::maxBuffer(void) +size_t getXportHeaderSize(void) { - // shared_ptrs would be handy here, fwiw. + // Get the header size from the physical transport. IPMI::Message* msg = IPMI::Message::factory(); - size_t mbs = msg->max_buffer(); + 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. + size_t mbs = iv_xmit_buffer_size - xport_header_size; + + mutex_unlock(&iv_mutex); + return mbs; } /** - * @brief Entry point of the resource provider + * @brief Start routine of the time-out handler */ -void IpmiRP::execute(void) +void IpmiRP::timeoutThread(void) { // Mark as an independent daemon so if it crashes we terminate. task_detach(); - IPMI_TRAC(ENTER_MRK "message loop"); + // 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(msq_msg->extra_data); + + // The diffence 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(); - // TODO: RTC 116300 Mark the daemon as started in the interface. - // TODO: RTC 116300 Query the BMC for timeouts, other config - // TODO: RTC 116300 Hold off transmitters until the BMC is ready? + // Get him off the responseq, and reply back to the waiter that + // there was a timeout + response(msg, IPMI::CC_TIMEOUT); + mutex_unlock(&iv_mutex); + } + else + { + mutex_unlock(&iv_mutex); + nanosleep( 0, timeout - now ); + } + } + + 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-capabilties 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_capabilties 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); + + iv_outstanding_req = data[0]; + iv_xmit_buffer_size = data[1]; + iv_recv_buffer_size = data[2]; + 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); + + delete[] data; + + return; +} + +/** + * @brief Entry point of the resource provider + */ +void IpmiRP::execute(void) +{ + bool 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 processesing. @@ -120,78 +280,86 @@ void IpmiRP::execute(void) // finish flushing the pipe. INITSERVICE::registerShutdownEvent(iv_msgQ, IPMI::MSG_STATE_SHUTDOWN, INITSERVICE::MBOX_PRIORITY); - do { - while (true) + // 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); + + while (true) + { + msg_t* msg = msg_wait(iv_msgQ); + + const IPMI::msg_type msg_type = + static_cast(msg->type); + + // Invert the "default" by checking here. This allows the compiler + // to warn us if the enum has an unhandled 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: + iv_sendq.push_back(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) + case IPMI::MSG_STATE_EVNT: + IPMI_TRAC(ERR_MRK "msg loop: unexpected ipmi sms"); + msg_free(msg); + // TODO: RTC 116600 Handle SMS messages + 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: + IPMI_TRAC(INFO_MRK "ipmi shutting down"); + shutdown_pending = 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)) { - msg_t* msg = msg_wait(iv_msgQ); - - const IPMI::msg_type msg_type = - static_cast(msg->type); - - // Invert the "default" by checking here. This allows the compiler - // to warn us if the enum has an unhandled 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. If we get a transmission - // error (EAGAIN counts) then the interface is likely - // not idle, and so we don't want to bother with idle below - case IPMI::MSG_STATE_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. - iv_sendq.push_back(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; - - case IPMI::MSG_STATE_RESP: - msg_free(msg); - response(); - break; - case IPMI::MSG_STATE_EVNT: - IPMI_TRAC(ERR_MRK "msg loop: unexpected ipmi sms"); - msg_free(msg); - // TODO: RTC 116300 Handle SMS messages - break; - - // Accept no more messages. Anything in the sendq is doomed. - // This should be OK - either they were async messages in which - // case they'd appear to never have been sent or they're sync - // in which case the higher layers should have handled this case - // in their shutdown processing. - case IPMI::MSG_STATE_SHUTDOWN: - IPMI_TRAC(INFO_MRK "ipmi shuting down"); - // TODO: RTC 116887 Hold off transmitters, drain queues. - // Patrick suggests handling this like mailboxes. - msg_respond(iv_msgQ, msg); - 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(); - } + idle(); } - } while (false); + // TODO: RTC 106887 Hold off transmitters, drain queues. + // Patrick suggests handling this like mailboxes. + if (shutdown_pending && iv_respondq.empty() && iv_sendq.empty()) + { + msg_respond(iv_msgQ, msg); + break; + } + } + IPMI_TRAC(EXIT_MRK "message loop"); return; } @@ -216,7 +384,7 @@ void IpmiRP::idle(void) // 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(iv_respondq)) + if (msg->xmit()) { break; } @@ -246,34 +414,78 @@ void IpmiRP::response(void) 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 // repsonse. If there isn't a message looking for this response, - // log that fact and drop this on the floor. - // TO THINK ABOUT: Could there be a command which generated - // more than one response packet from the BMC? If this happens, - // these messages should be handled like SMS messages - sent to - // the appropriate queue for a waiter to take care of. So if a - // message can generate more than one packet of response, something - // needs to register for the overflow. So far we have not seen - // any such beast ... - IPMI::respond_q_t::iterator itr = iv_respondq.find(rcv_buf->iv_key); + // that's an error. 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()) { - // Every async message goes through this path. The semantics - // somewhat contrary to IPMI semantics in that we have the ability - // to generate "async" messages when the IPMI spec says there's no - // such thing. We decided to just drop the response on the floor. - // This is good for "fire and forget" situations where we don't - // really care if the BMC gets the message or processes it - // correctly. However, the BMC doesn't know we don't care about the - // response and sends it. This code path does the dropping of the - // response. - delete[] rcv_buf->iv_data; + 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); + + /* @errorlog tag + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid IPMI::MOD_IPMISRV_REPLY + * @reasoncode IPMI::RC_WAITER_NOT_FOUND + * @userdata1 the network function/lun + * @userdata2 the command which was in error + * @devdesc there was no matching message on + * the response queue + * @custdesc Unexpected IPMI message from the BMC + */ + errlHndl_t err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + IPMI::MOD_IPMISRV_REPLY, + IPMI::RC_WAITER_NOT_FOUND, + i_msg->iv_netfun, i_msg->iv_cmd, true); + + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + + 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 @@ -282,46 +494,58 @@ void IpmiRP::response(void) static_cast(original_msg->extra_data); // Hand ownership of the data to the original requestor - ipmi_msg->iv_data = rcv_buf->iv_data; - ipmi_msg->iv_len = rcv_buf->iv_len; - - // Send the response to the original caller of sendrecv() - int rc = msg_respond(iv_msgQ, original_msg); - if (rc) - { - // Not much we're going to do here, so lets commit an error and - // the original request will timeout. - - /* @errorlog tag - * @errortype ERRL_SEV_UNRECOVERABLE - * @moduleid IPMI::MOD_IPMISRV_REPLY - * @reasoncode IPMI::RC_INVALID_QRESPONSE - * @userdata1 rc from msg_respond() - * @devdesc msg_respond() failed - */ - err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - IPMI::MOD_IPMISRV_REPLY, - IPMI::RC_INVALID_QRESPONSE, - rc, - 0, - true); - err->collectTrace(IPMI_COMP_NAME); - - IPMI_TRAC(ERR_MRK "msg_respond() i/o error (response) %d", rc); - errlCommit(err, IPMI_COMP_ID); + 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; - // Remove the response data. - delete[] ipmi_msg->iv_data; - - break; - } + // 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); - delete rcv_buf; 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 @@ -329,21 +553,20 @@ namespace IPMI /// /// @brief Synchronus message send /// - errlHndl_t sendrecv(const IPMI::network_function i_netfun, - const uint8_t i_cmd, uint8_t& o_completion_code, + 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::instance().msgQueue(); - IPMI::Message* ipmi_msg = IPMI::Message::factory(i_netfun, i_cmd, - io_len, io_data, + 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", i_netfun, i_cmd); + 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 he message, check to see if @@ -363,6 +586,7 @@ namespace IPMI * @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, @@ -380,7 +604,7 @@ namespace IPMI // 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 = ipmi_msg->iv_cc; + o_completion_code = static_cast(ipmi_msg->iv_cc); delete ipmi_msg; return err; @@ -389,8 +613,7 @@ namespace IPMI /// /// @brief Asynchronus message send /// - errlHndl_t send(const IPMI::network_function i_netfun, - const uint8_t i_cmd, + errlHndl_t send(const IPMI::command_t& i_cmd, const size_t i_len, uint8_t* i_data) { static msg_q_t mq = Singleton::instance().msgQueue(); @@ -399,14 +622,13 @@ 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_netfun, i_cmd, - i_len, i_data, + IPMI::Message* ipmi_msg = IPMI::Message::factory(i_cmd, i_len, i_data, IPMI::TYPE_ASYNC); // I think if the buffer is too large this is a programming error. assert(i_len <= max_buffer()); - IPMI_TRAC("queuing async %x:%x", i_netfun, i_cmd); + 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) @@ -417,6 +639,7 @@ namespace IPMI * @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, @@ -438,8 +661,50 @@ namespace IPMI /// inline size_t max_buffer(void) { - static const size_t mbs = Singleton::instance().maxBuffer(); - return mbs; + return Singleton::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; } }; diff --git a/src/usr/ipmi/ipmirp.H b/src/usr/ipmi/ipmirp.H index 7e535fe67..2edf24e9a 100644 --- a/src/usr/ipmi/ipmirp.H +++ b/src/usr/ipmi/ipmirp.H @@ -65,6 +65,19 @@ class IpmiRP */ 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); + /** * Default constructor */ @@ -93,6 +106,12 @@ class IpmiRP 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); + private: /** @@ -100,6 +119,11 @@ class IpmiRP */ 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 @@ -139,6 +163,22 @@ class IpmiRP */ 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 Handle an indication from the interface indicating the * BMC interface has an event/sms message ready to read @@ -146,10 +186,39 @@ class IpmiRP */ void event(void); + /** + * @brief Query the BMC for interface capabilities + * @param[in] void + * @note this fills in iv_bmc_timeout, etc. + */ + void getInterfaceCapabilities(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 + // 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; + + // Recomended number of retries + uint8_t iv_retries; + + // Disallow copying this class. IpmiRP& operator=(const IpmiRP&); IpmiRP(const IpmiRP&); diff --git a/src/usr/ipmi/makefile b/src/usr/ipmi/makefile index fb2a19473..3d8ca3a8c 100644 --- a/src/usr/ipmi/makefile +++ b/src/usr/ipmi/makefile @@ -29,6 +29,7 @@ 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 #SUBDIRS += test.d -- cgit v1.2.1