summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrian Silver <bsilver@us.ibm.com>2014-10-06 13:45:16 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2014-11-12 08:26:50 -0600
commite92561c3352a4af3b1b8506fae61652cde4ee3d5 (patch)
tree0c4fe19f433cb22180ea42b0e84fb584b7b85bec /src
parent17aadfc41822980c53ca70340dabcf84ac611601 (diff)
downloadtalos-hostboot-e92561c3352a4af3b1b8506fae61652cde4ee3d5.tar.gz
talos-hostboot-e92561c3352a4af3b1b8506fae61652cde4ee3d5.zip
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 <iawillia@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r--src/include/usr/ipmi/ipmi_reasoncodes.H2
-rw-r--r--src/include/usr/ipmi/ipmiif.H102
-rw-r--r--src/usr/ipmi/ipmibt.C166
-rw-r--r--src/usr/ipmi/ipmibt.H80
-rw-r--r--src/usr/ipmi/ipmiconfig.C45
-rw-r--r--src/usr/ipmi/ipmiconfig.H55
-rw-r--r--src/usr/ipmi/ipmidd.C6
-rw-r--r--src/usr/ipmi/ipmimsg.C27
-rw-r--r--src/usr/ipmi/ipmimsg.H36
-rw-r--r--src/usr/ipmi/ipmirp.C553
-rw-r--r--src/usr/ipmi/ipmirp.H69
-rw-r--r--src/usr/ipmi/makefile1
12 files changed, 880 insertions, 262 deletions
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 <sys/msg.h>
#include <errl/errlentry.H>
+#include <map>
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<network_function, uint8_t> 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,11 +167,31 @@ 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<IpmiRP>::instance().queueForResponse(*this);
+ }
+
+ // Otherwise we had no error, but were told EAGAIN, which means the
+ // interface was busy.
+ else
+ {
+ IPMI_TRAC(INFO_MRK "busy, queue head %x:%x", iv_netfun, iv_cmd);
+ }
}
return err;
@@ -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 <stdint.h>
+#include "ipmiconfig.H"
+
+ //
+ // Information contained in the Get Interface Capabilities command
+ //
+ // Request to response time default, in seconds
+const uint8_t IPMI::g_bmc_timeout = 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 <time.h>
#include <map>
#include <list>
#include <sys/msg.h>
@@ -41,6 +42,7 @@ namespace IPMI
};
typedef std::list<msg_t*> send_q_t;
+ typedef std::list<msg_t*> timeout_q_t;
typedef std::map<uint8_t,msg_t*> respond_q_t;
// 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 <ipmi/ipmi_reasoncodes.H>
#include <devicefw/driverif.H>
#include <devicefw/userif.H>
@@ -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<IpmiRP>::instance().timeoutThread();
+ return NULL;
+}
+
+void* IpmiRP::get_capabilities(void* unused)
+{
+ Singleton<IpmiRP>::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<IPMI::Message*>(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<IPMI::msg_type>(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<IPMI::msg_type>(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<IPMI::Message*>(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<IpmiRP>::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::completion_code>(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<IpmiRP>::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<IpmiRP>::instance().maxBuffer();
- return mbs;
+ return Singleton<IpmiRP>::instance().maxBuffer();
+ }
+
+ ///
+ /// @brief Synchronously send an event
+ ///
+ errlHndl_t send_event(const uint8_t i_sensor_type,
+ const uint8_t i_sensor_number,
+ const bool i_assertion,
+ const uint8_t i_type,
+ completion_code& o_completion_code,
+ const size_t i_len,
+ uint8_t* i_data)
+ {
+ static const size_t event_header = 5;
+
+ // Sanity check
+ assert((i_len > 0) && (i_len < 4),
+ "event request i_len incorrect %d", i_len);
+ assert(i_type < 0x80, "event request i_type out of range %x", i_type);
+
+ size_t len = event_header + i_len;
+ uint8_t* data = new uint8_t[len];
+ IPMI::completion_code cc = IPMI::CC_OK;
+
+ data[0] = 0x01; // More or less fixed, see table 5.4
+ data[1] = 0x04; // Fixed in the IPMI spec table 29.5
+ data[2] = i_sensor_type;
+ data[3] = i_sensor_number;
+ data[4] = (i_assertion ? 0x80 : 0x00) + i_type;
+ for (size_t i = 0; i < i_len; i++)
+ {
+ data[event_header + i] = i_data[i];
+ }
+
+ // We're done with i_data, but the caller deletes it. Note there's
+ // no response data to an event - so there's nothing to copy over,
+ // no reference to i_data, nothing.
+
+ errlHndl_t err = sendrecv(IPMI::platform_event(), cc, len, data);
+
+ o_completion_code = cc;
+ delete[] data;
+ return err;
}
};
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
@@ -66,6 +66,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
*/
IpmiRP(void);
@@ -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:
/**
@@ -101,6 +120,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
* @note i_msg is not const because it contains a return code
@@ -140,16 +164,61 @@ 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
* @param[in] void
*/
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
OpenPOWER on IntegriCloud