diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/build/tools/listdeps.pl | 3 | ||||
-rw-r--r-- | src/include/usr/devicefw/userif.H | 5 | ||||
-rw-r--r-- | src/include/usr/hbotcompid.H | 8 | ||||
-rw-r--r-- | src/include/usr/ipmi/ipmi_reasoncodes.H | 46 | ||||
-rw-r--r-- | src/include/usr/ipmi/ipmiif.H | 119 | ||||
-rw-r--r-- | src/makefile | 1 | ||||
-rw-r--r-- | src/sys/vfs/vfs_main.C | 3 | ||||
-rw-r--r-- | src/usr/errl/test/errluserdetailtest.H | 14 | ||||
-rw-r--r-- | src/usr/initservice/extinitsvc/extinitsvctasks.H | 14 | ||||
-rw-r--r-- | src/usr/ipmi/HBconfig | 10 | ||||
-rw-r--r-- | src/usr/ipmi/ipmibt.C | 246 | ||||
-rw-r--r-- | src/usr/ipmi/ipmibt.H | 140 | ||||
-rw-r--r-- | src/usr/ipmi/ipmidd.C | 495 | ||||
-rw-r--r-- | src/usr/ipmi/ipmidd.H | 124 | ||||
-rw-r--r-- | src/usr/ipmi/ipmimsg.C | 104 | ||||
-rw-r--r-- | src/usr/ipmi/ipmimsg.H | 127 | ||||
-rw-r--r-- | src/usr/ipmi/ipmirp.C | 445 | ||||
-rw-r--r-- | src/usr/ipmi/ipmirp.H | 158 | ||||
-rw-r--r-- | src/usr/ipmi/makefile | 35 | ||||
-rw-r--r-- | src/usr/makefile | 1 |
20 files changed, 2089 insertions, 9 deletions
diff --git a/src/build/tools/listdeps.pl b/src/build/tools/listdeps.pl index 4c45186ee..2b59a3a29 100755 --- a/src/build/tools/listdeps.pl +++ b/src/build/tools/listdeps.pl @@ -212,6 +212,7 @@ my %resident_modules = ( "liblpc.so" => '1', "libconsole.so" => '1', "liberrldisplay.so" => '1', + "libipmi.so" => '1', ); # has with library to istep list file were the DepMod array is kept @@ -406,5 +407,3 @@ sub usage exit 0; } - - diff --git a/src/include/usr/devicefw/userif.H b/src/include/usr/devicefw/userif.H index 69d334634..05c7cb214 100644 --- a/src/include/usr/devicefw/userif.H +++ b/src/include/usr/devicefw/userif.H @@ -60,6 +60,7 @@ namespace DeviceFW EEPROM, GPIO, LPC, + IPMIBT, // As opposed to other phy's LAST_ACCESS_TYPE, }; @@ -166,10 +167,10 @@ namespace DeviceFW /** * Construct the device addressing parameters for the SCAN device ops. - * @param[in] i_ring - The ring address to scan + * @param[in] i_ring - The ring address to scan * @param[in] i_ringlen - The length of the ring to scan in bits * NOTE: This value is the scanring length must - * match the scandef file value. + * match the scandef file value. * @param[in] i_flag - Specific requests on the scan such as * check the header, or set pulse option. * Flag options are located in: src/include/usr/scan/scanif.H diff --git a/src/include/usr/hbotcompid.H b/src/include/usr/hbotcompid.H index e3ade8fbb..a96bec9d6 100644 --- a/src/include/usr/hbotcompid.H +++ b/src/include/usr/hbotcompid.H @@ -340,6 +340,14 @@ const compId_t LPC_COMP_ID = 0x2400; const char LPC_COMP_NAME[] = "lpc"; //@} +/** @name IPMI + * IPMI + */ +//@{ +const compId_t IPMI_COMP_ID = 0x2500; +const char IPMI_COMP_NAME[] = "ipmi"; +//@} + /** @name RESERVED * Reserved component ID. x3100 is the component ID diff --git a/src/include/usr/ipmi/ipmi_reasoncodes.H b/src/include/usr/ipmi/ipmi_reasoncodes.H new file mode 100644 index 000000000..12a9391bc --- /dev/null +++ b/src/include/usr/ipmi/ipmi_reasoncodes.H @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/ipmi/ipmi_reasoncodes.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_REASONCODES_H +#define __IPMI_REASONCODES_H + +#include <hbotcompid.H> + +namespace IPMI +{ + enum IPMIModuleId + { + MOD_IPMISRV_SEND = 0x01, // IPMI::send/IPMI::sendrecv + MOD_IPMISRV_REPLY = 0x02, // IPMI::respond + }; + + enum IPMIReasonCode + { + RC_INVALID_QRESPONSE = IPMI_COMP_ID | 0x01, + RC_INVALID_SENDRECV = IPMI_COMP_ID | 0x02, + RC_INVALID_SEND = IPMI_COMP_ID | 0x03, + }; +}; + +#endif diff --git a/src/include/usr/ipmi/ipmiif.H b/src/include/usr/ipmi/ipmiif.H new file mode 100644 index 000000000..431b006ef --- /dev/null +++ b/src/include/usr/ipmi/ipmiif.H @@ -0,0 +1,119 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/ipmi/ipmiif.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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_IPMIIF_H +#define __IPMI_IPMIIF_H + +#include <sys/msg.h> +#include <errl/errlentry.H> + +namespace IPMI +{ + // Message types which travel on the IPMI message queues + enum msg_type + { + // Sent when the interface goes idle *and* we had write which + // was told the interface was busy. We won't get notified of + // the general idle state. + MSG_STATE_IDLE, + + // Ready to read a response + MSG_STATE_RESP, + + // Ready to read an event/sms + MSG_STATE_EVNT, + + // A message which needs to be sent + MSG_STATE_SEND, + + MSG_STATE_SHUTDOWN, + + // Used to check range. Leave as last. + MSG_LAST_TYPE = MSG_STATE_SHUTDOWN, + }; + + // IPMI network functions + enum network_function + { + // These are the command network functions, the response + // network functions are the function + 1. So to determine + // the proper network function which issued the command + // associated with a response, subtract 1. + // Note: these are also shifted left to make room for the LUN. + NETFUN_CHASSIS = (0x00 << 2), + NETFUN_BRIDGE = (0x02 << 2), + NETFUN_SENSOR = (0x04 << 2), + NETFUN_APP = (0x06 << 2), + NETFUN_FIRMWARE = (0x08 << 2), + NETFUN_STORAGE = (0x0a << 2), + NETFUN_TRANPORT = (0x0c << 2), + + // Overload the OEM netfun for a "none" netfun. We use this as a + // default for objects which will have their netfun filled in + NETFUN_NONE = (0x30 << 2), + }; + + /** + * Send message asynchronously + * @param[in] i_netfun, the network function + * @param[in] i_cmd, the 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]; + * @warning do *not* delete data, the resource provider will do that. + * + * @return errlHndl_t, NULL on success + */ + errlHndl_t send(const network_function i_netfun, + const uint8_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[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 + * @example uint8_t* data = new uint8_t[length]; + * @example delete[] data; + * + * @return errlHndl_t, NULL on success + */ + errlHndl_t sendrecv(const network_function i_netfun, + const uint8_t i_cmd, uint8_t& o_completion_code, + size_t& io_len, uint8_t*& io_data); + + /** + * Get the max buffer size + * @param void + * + * @return the maximum space allowed for data, per message + * (max message for the transport - the header size) + */ + inline size_t max_buffer(void); + +}; // end namespace IPMI + +#endif diff --git a/src/makefile b/src/makefile index f33622541..554c24691 100644 --- a/src/makefile +++ b/src/makefile @@ -164,6 +164,7 @@ EXTENDED_MODULES += $(if $(CONFIG_GPIODD),gpio,) EXTENDED_MODULES += $(if $(CONFIG_CONSOLE),console) EXTENDED_MODULES += $(if $(CONFIG_CONSOLE_OUTPUT_ERRORDISPLAY),errldisplay) EXTENDED_MODULES += $(if $(CONFIG_SET_NOMINAL_PSTATE),pstates) +EXTENDED_MODULES += $(if $(CONFIG_BMC_IPMI),ipmi) TESTCASE_MODULES += cxxtest TESTCASE_MODULES += testtrace diff --git a/src/sys/vfs/vfs_main.C b/src/sys/vfs/vfs_main.C index bf31df680..db6388cb7 100644 --- a/src/sys/vfs/vfs_main.C +++ b/src/sys/vfs/vfs_main.C @@ -201,6 +201,3 @@ void* vfs_start_entrypoint(VfsSystemModule * i_module) } return ptr; } - - - diff --git a/src/usr/errl/test/errluserdetailtest.H b/src/usr/errl/test/errluserdetailtest.H index 714f7f0d9..17b9e454e 100644 --- a/src/usr/errl/test/errluserdetailtest.H +++ b/src/usr/errl/test/errluserdetailtest.H @@ -32,6 +32,7 @@ * */ +#include <stdio.h> #include <cxxtest/TestSuite.H> #include <errl/errlentry.H> #include <errl/errluserdetails.H> @@ -93,6 +94,18 @@ public: ErrlUserDetailsString stringUD("String test - string 3"); stringUD.addToLog(errl); + // Add a little test for making strings on the fly + { + const int test_data = 0xfeedface; + const char* format = "msg_respond() i/o error (transmit) 0x%x"; + const size_t output_size = strlen(format) + (sizeof(int) * 2) + 1; + char output[output_size]; + snprintf(output, output_size, format, test_data); + + // Add msg_respond rc + ERRORLOG::ErrlUserDetailsString(output).addToLog(errl); + } + // shove a lot of traces here, so that we test the truncate in the // write to PNOR errl->collectTrace("TARG", 1024); @@ -469,4 +482,3 @@ public: }; #endif - diff --git a/src/usr/initservice/extinitsvc/extinitsvctasks.H b/src/usr/initservice/extinitsvc/extinitsvctasks.H index 9996bc994..47297f0ec 100644 --- a/src/usr/initservice/extinitsvc/extinitsvctasks.H +++ b/src/usr/initservice/extinitsvc/extinitsvctasks.H @@ -150,8 +150,20 @@ const TaskInfo g_exttaskinfolist[] = { } }, + /** + * @brief IPMI resource provider + */ +#ifdef CONFIG_BMC_IPMI + { + "libipmi.so", // taskname + NULL, // no ptr to fnct + { - + START_TASK, // task type + EXT_IMAGE, // Extended Module + } + }, +#endif /** * @brief FSI SCOM Device Driver */ diff --git a/src/usr/ipmi/HBconfig b/src/usr/ipmi/HBconfig new file mode 100644 index 000000000..0980a3eb2 --- /dev/null +++ b/src/usr/ipmi/HBconfig @@ -0,0 +1,10 @@ +config BMC_IPMI + default y if BMC_BT_LPC_IPMI + help + Determines if Hostboot communicates to the BMC using IPMI + +config BMC_BT_LPC_IPMI + default n + depends on BMC_IPMI + help + Determines if the BMC uses the LPC bus for block-transfer IPMI traffic diff --git a/src/usr/ipmi/ipmibt.C b/src/usr/ipmi/ipmibt.C new file mode 100644 index 000000000..d1be32bbe --- /dev/null +++ b/src/usr/ipmi/ipmibt.C @@ -0,0 +1,246 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmibt.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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 */ +/** + * @file ipmibt.C + * @brief code for the IPMI BT message class + */ + +#include <devicefw/driverif.H> +#include <devicefw/userif.H> +#include <ipmi/ipmi_reasoncodes.H> +#include <errl/errlmanager.H> + +#include <util/lockfree/counter.H> + +#include "ipmibt.H" +#include "ipmirp.H" +#include <errno.h> +#include <config.h> + +// Defined in ipmidd.C +extern trace_desc_t * g_trac_ipmi; +#define IPMI_TRAC(printf_string,args...) \ + TRACFCOMP(g_trac_ipmi,"bt: "printf_string,##args) + +namespace IPMI +{ + /// + /// @brief msg ctor + /// @param[in] i_netfun, the network function + /// @param[in] i_cmd, the network 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::BTMessage(const network_function i_netfun, + const uint8_t i_cmd, const uint8_t i_len, + uint8_t* i_data): + Message(i_netfun, i_cmd, i_len, i_data) + { + // Sometimes we need to get back to this IPMI msg from the msg_t, + // and sometimes we need to get the msg_t from the IPMI msg. So they + // know about each other. + iv_msg->extra_data = static_cast<void*>(this); + iv_msg->type = MSG_STATE_SEND; + } + + /// + /// @brief Transimit - send the data out the device interface + /// + errlHndl_t BTMessage::xmit(void) + { + // When a uint8_t is constructed, it's initialied to 0. So, + // this initializes the sequence counter to 0. + static Util::Lockfree::Counter<uint8_t> seq; + + // Assign a "unique" sequence number. Note that we don't + // leverage the network function to create a sequence + // number, we just keep an 8 bit counter. This *should* + // be ok - it means we will get back the response to any + // particular message before we send another 254 messages. + // This seems safe. + iv_seq = seq.next(); + + // Initialize the error state of the message + iv_state = 0; + + size_t unused_size; + errlHndl_t err = deviceOp(DeviceFW::WRITE, + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + static_cast<void*>(this), + unused_size, + DeviceFW::IPMIBT); + + // If we're not going to remain on the i_sendq, we need to + // delete the data. + if ((err) || (iv_state != EAGAIN)) + { + delete[] iv_data; + } + + return err; + } + + /// + /// @brief Receive - get bits off the block-transfer interface + /// + errlHndl_t BTMessage::recv(void) + { + // Check to make sure we are in the right state (coding error) + assert(iv_data == NULL); + + // Go down to the device and read. Note the driver is BT specific + // and we're BT specific so we can send down a BTMessage object. + size_t unused_length; + errlHndl_t err = deviceOp(DeviceFW::READ, + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + static_cast<void*>(this), + unused_length, + DeviceFW::IPMIBT); + if (err) + { + delete[] iv_data; + + // If the reading the reponse fails, the caller may still call + // delete[] on the pointer we return to them. This makes sure that + // for this case, they're just deleting NULL. + iv_data = NULL; + iv_len = 0; + } + + // For BT messages, the sequence number is the key - for other xports + // it might be different. Note the sequence number is a reference to + // our base's iv_key, so we're done. + return err; + } + + /// + /// @brief BTSyncMessage ctor + /// @param[in] i_netfun, the network function + /// @param[in] i_cmd, the network 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) + /// + BTSyncMessage::BTSyncMessage(const network_function i_netfun, + const uint8_t i_cmd, const uint8_t i_len, + uint8_t* i_data): + BTMessage(i_netfun, 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_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, + uint8_t* i_data): + BTMessage(i_netfun, i_cmd, i_len, i_data) + { + } + + /// + /// @brief sync msg transmit + /// + bool BTSyncMessage::xmit(respond_q_t& i_respondq) + { + errlHndl_t err = BTMessage::xmit(); + + if (err) + { + // Something went wrong, so we need to respond back with the error + iv_errl = err; + + msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue(); + int rc = msg_respond(mq, iv_msg); + if (rc) + { + // Yuk. We can't respond back to our caller with that error. So, + // we'll commit it. I don't see much sense in creating another + // error log, so we'll just trace the msg_respond() failure. + IPMI_TRAC(ERR_MRK "msg_respond() i/o error (transmit) %d", rc); + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + iv_errl = NULL; + } + } + + // 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. + return (iv_state != 0); + } + + /// + /// @brief async msg transmit + /// + bool BTAsyncMessage::xmit(respond_q_t&) + { + errlHndl_t err = BTMessage::xmit(); + bool io_error = (iv_state != 0); + + if (err) + { + // Not much we're going to do here, so just commit the error. + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + } + + // If we 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; + } +}; diff --git a/src/usr/ipmi/ipmibt.H b/src/usr/ipmi/ipmibt.H new file mode 100644 index 000000000..6414e59b8 --- /dev/null +++ b/src/usr/ipmi/ipmibt.H @@ -0,0 +1,140 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmibt.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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_IPMIBT_H +#define __IPMI_IPMIBT_H + +#include "ipmimsg.H" + +// How many bytes are in the IPMI BT message header +#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 + class BTMessage : public Message + { + public: + /// + /// @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_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, + const uint8_t i_len = 0, + uint8_t* i_data = NULL); + + /// + /// @brief transmit a message. + /// @return Error from operation + /// + errlHndl_t 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;}; + + /// + /// @brief receive a message. + /// + errlHndl_t recv(void); + + /// + /// @brief the maximum buffer size of the underlying transport + /// @param void + /// @return size_t, the max buffer size + /// + size_t max_buffer(void) + { return IPMI_BT_MAX_DATA; } + + }; + + // IPMI BT synchronous message + class BTSyncMessage : public BTMessage + { + 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_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, + 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); + }; + + // IPMI BT asynchronous message + class BTAsyncMessage : public BTMessage + { + 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_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, + const uint8_t i_len, uint8_t* i_data); + + /// + /// @brief transmit a message. + /// @param[in] respond_q_t unused + /// + /// @return true iff there was no transmission error + /// + bool xmit(respond_q_t&); + }; + +}; // end namespace IPMI + +#endif diff --git a/src/usr/ipmi/ipmidd.C b/src/usr/ipmi/ipmidd.C new file mode 100644 index 000000000..3456cac19 --- /dev/null +++ b/src/usr/ipmi/ipmidd.C @@ -0,0 +1,495 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmidd.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,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 */ +/** + * @file ipmidd.C + * + * @brief Implementation of the IPMI Device Driver + */ + +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include <devicefw/driverif.H> +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <errl/errludlogregister.H> +#include <errl/errludstring.H> +#include "ipmidd.H" +#include "ipmirp.H" +#include <ipmi/ipmiif.H> +#include <initservice/initserviceif.H> +#include <util/align.H> +#include <lpc/lpcif.H> +#include <config.h> + +#include <sys/msg.h> +#include <errno.h> + +#include <sys/time.h> +#include <sys/task.h> + +/*****************************************************************************/ +// D e f i n e s +/*****************************************************************************/ + +trace_desc_t* g_trac_ipmi; +TRAC_INIT(&g_trac_ipmi, IPMI_COMP_NAME, 6*KILOBYTE, TRACE::BUFFER_SLOW); + +#define IPMI_TRAC(printf_string,args...) \ + TRACFCOMP(g_trac_ipmi,"dd: "printf_string,##args) + +// Registers. These are fixed for LPC/BT so we can hard-wire them +#define REG_CONTROL 0xE4 +#define REG_HOSTBMC 0xE5 +#define REG_INTMASK 0xE6 + +// Control register bits. The control register is interesting in that writing +// 0's never does anything; all registers are either set to 1 when written +// with a 1 or toggled (1/0) when written with a one. So, we don't ever need +// to read-modify-write, we can just write an or'd mask of bits. +#define CTRL_B_BUSY (1 << 7) +#define CTRL_H_BUSY (1 << 6) +#define CTRL_OEM0 (1 << 5) +#define CTRL_SMS_ATN (1 << 4) +#define CTRL_B2H_ATN (1 << 3) +#define CTRL_H2B_ATN (1 << 2) +#define CTRL_CLR_RD_PTR (1 << 1) +#define CTRL_CLR_WR_PTR (1 << 0) + +#define IDLE_STATE (CTRL_B_BUSY | CTRL_B2H_ATN | CTRL_SMS_ATN | CTRL_H2B_ATN) + +// Bit in the INMASK register which signals to the BMC +// to reset it's end of things. +#define INT_BMC_HWRST (1 << 7) + +// How long to sychronously wait for the device to change state (in ns) +#define WAIT_TIME 100000000 + +/** + * @brief Performs an IPMI Message Read Operation + * This function performs a IPMI Message Read operation. It follows a + * pre-defined prototype functions in order to be registered with the + * device-driver framework. + * + * @param[in] i_opType Operation type READ + * @param[in] i_target IPMI target, ignored use the master sentinel + * @param[out] o_buffer Pointer to a BT message we're going to fill in. + * @param[out] o_buflen Always sizeof(uint8_t) + * @param[in] i_accessType DeviceFW::IPMIBT + * @param[in] i_args This is an argument list for DD framework. + * In this function, it is unused + * @return errlHndl_t + */ +errlHndl_t ddRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* o_buffer, + size_t& o_buflen, + int64_t i_accessType, + va_list i_args) +{ + // If someone passed in the wrong target, we have a coding error + assert(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target, + "ipmi read expects master processor target"); + + // We are the BT driver, so it's safe to assume they sent us a BTMessage. + return Singleton<IpmiDD>::instance().receive( + static_cast<IPMI::BTMessage*>(o_buffer)); +} + +/** + * @brief Performs an IPMI Message Write Operation + * This function performs a IPMI Message Write operation. It follows a + * pre-defined prototype functions in order to be registered with the + * device-driver framework. + * + * @param[in] i_opType Operation type WRITE + * @param[in] i_target IPMI target, ignored use the master sentinel + * @param[in] i_buffer Pointer to a BT message we're going to transmit + * @param[in] i_buflen Always sizeof(uint8_t) + * @param[in] i_accessType DeviceFW::IPMIBT + * @param[in] i_args This is an argument list for DD framework. + * In this function, it is unused + * @return errlHndl_t + */ +errlHndl_t ddWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* i_buffer, + size_t& i_buflen, + int64_t i_accessType, + va_list i_args) +{ + // If someone passed in the wrong target, we have a coding error + assert(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target, + "ipmi write expects master processor target"); + + // We are the BT driver, so it's safe to assume they sent us a BTMessage + return Singleton<IpmiDD>::instance().send( + static_cast<IPMI::BTMessage*>(i_buffer)); +} + +// Register IPMIDD access functions to DD framework +DEVICE_REGISTER_ROUTE(DeviceFW::READ, + DeviceFW::IPMIBT, + TARGETING::TYPE_PROC, + ddRead); + +DEVICE_REGISTER_ROUTE(DeviceFW::WRITE, + DeviceFW::IPMIBT, + TARGETING::TYPE_PROC, + ddWrite); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +/** + * @brief Read an address from LPC space + */ +errlHndl_t IpmiDD::readLPC(const uint32_t i_addr, uint8_t& o_data) +{ + static size_t size = sizeof(uint8_t); + errlHndl_t err = deviceOp( DeviceFW::READ, + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + static_cast<void*>(&o_data), + size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO, i_addr) ); + return err; +} + +/** + * @brief Write an address from LPC space + */ +errlHndl_t IpmiDD::writeLPC(const uint32_t i_addr, + uint8_t i_data) +{ + static size_t size = sizeof(uint8_t); + errlHndl_t err = deviceOp(DeviceFW::WRITE, + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + static_cast<void*>(&i_data), + size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO, i_addr) ); + return err; +} + +/** + * @brief Static function wrapper to pass into task_create + */ +static void* poll_control_register( void* /* unused */ ) +{ + IPMI_TRAC(ENTER_MRK "poll_control_register" ); + Singleton<IpmiDD>::instance().pollCtrl(); + return NULL; +} + +/** + * @brief Poll the control register + */ +void IpmiDD::pollCtrl(void) +{ + // Mark as an independent daemon so if it crashes we terminate. + task_detach(); + + // We send a message to this message queue when the ipmi state changes. + // Don't free these messages - these messages are sent async so the + // consumer will free the message. + static msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue(); + msg_t* msg = NULL; + + uint8_t ctrl = 0; + + while(1) + { + mutex_lock(&iv_mutex); + errlHndl_t err = readLPC(REG_CONTROL, ctrl); + + // Not sure there's much we can do here but commit the log + // and let this thread fail. + if (err) + { + IPMI_TRAC(ERR_MRK "polling loop encountered an error, exiting"); + errlCommit(err, IPMI_COMP_ID); + break; + } + + else + { + // If we're idle, tell the resoure provider to check for any + // pending messages which were delayed due to contention. But don't + // send a message everytime we see idle, only if there we suspect + // we sent EAGAINs. + if (((ctrl & IDLE_STATE) == 0) && iv_eagains) + { + msg = msg_allocate(); + msg->type = IPMI::MSG_STATE_IDLE; + msg_send(mq, msg); + iv_eagains = false; + } + + // If we see the B2H_ATN, there's a response waiting + else if (ctrl & CTRL_B2H_ATN) + { + msg = msg_allocate(); + msg->type = IPMI::MSG_STATE_RESP; + msg_send(mq, msg); + } + + // If we see the SMS_ATN, there's an event waiting + else if (ctrl & CTRL_SMS_ATN) + { + IPMI_TRAC(ERR_MRK "sending state sms/event, unexpected"); + msg = msg_allocate(); + msg->type = IPMI::MSG_STATE_EVNT; + msg_send(mq, msg); + } + } + mutex_unlock(&iv_mutex); + nanosleep(0, WAIT_TIME); + } +} + +/** + * @brief Performs a reset of the BT hardware + */ +inline errlHndl_t IpmiDD::reset(void) +{ + // Reset the BT interface, and flush messages. We eat messages off of + // the interface until it goes idle. We assume, since we're resetting, + // that there is nothing on the interface we're interested in. + mutex_lock(&iv_mutex); + IPMI_TRAC(ENTER_MRK "resetting the IPMI BT interface"); + + uint8_t ctrl = 0; + IPMI::BTMessage msg; + + errlHndl_t err = readLPC(REG_CONTROL, ctrl); + 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); + delete[] msg.iv_data; + + if (err) {break;} + + err = readLPC(REG_CONTROL, ctrl); + } + + // Commit this log. We're about to reset the PHY anyway, so maybe + // that'll clear this error. If not, we'll report that error. + if (err) + { + errlCommit(err, IPMI_COMP_ID); + } + + mutex_unlock(&iv_mutex); + + IPMI_TRAC(EXIT_MRK "resetting the IPMI BT interface"); + return writeLPC(REG_INTMASK, INT_BMC_HWRST); +} + +/** + * @brief Performs an IPMI Message Write Operation + */ +errlHndl_t IpmiDD::send(IPMI::BTMessage* i_msg) +{ + errlHndl_t err = NULL; + uint8_t ctrl = 0; + + mutex_lock(&iv_mutex); + do + { + err = readLPC(REG_CONTROL, ctrl); + if (err) { break; } + + // If the interface isn't idle, tell the caller to come back + if ((ctrl & IDLE_STATE) != 0) + { + i_msg->iv_state = EAGAIN; + iv_eagains = true; + continue; + } + + // Tell the interface we're writing. Per p. 135 of the + // spec we *do not* set H_BUSY. + err = writeLPC(REG_CONTROL, CTRL_CLR_WR_PTR); + if (err) { break; } + + // Add the header size on as req_len is only the length of the data + err = writeLPC(REG_HOSTBMC, i_msg->iv_len + IPMI_BT_HEADER_SIZE); + if (err) { break; } + + err = writeLPC(REG_HOSTBMC, i_msg->iv_netfun); + if (err) { break; } + + err = writeLPC(REG_HOSTBMC, i_msg->iv_seq); + if (err) { break; } + + err = writeLPC(REG_HOSTBMC, i_msg->iv_cmd); + if (err) { break; } + + for( size_t i = 0; (i < i_msg->iv_len) && (err == NULL); ++i) + { + err = writeLPC(REG_HOSTBMC, i_msg->iv_data[i]); + } + if (err) { break; } + + // If all is well, alert the host we sent bits. + err = writeLPC(REG_CONTROL, CTRL_H2B_ATN); + if (err) {break;} + + } while(false); + + mutex_unlock(&iv_mutex); + + // If we have an error, try to reset the interface. + if (err) + { + reset(); + } + + // Don't bother reporting a write if we returned EAGAIN, the + // upper layers will report the re-queue or whatever. + if (i_msg->iv_state != EAGAIN) + { + IPMI_TRAC(INFO_MRK "write %s %x:%x seq %x len %x", + err ? "err" : "ok", + i_msg->iv_netfun, i_msg->iv_cmd, i_msg->iv_seq, + i_msg->iv_len); + } + + return err; +} + +/** + * @brief Read a response to an issued command, or an sms + */ +errlHndl_t IpmiDD::receive(IPMI::BTMessage* o_msg) +{ + errlHndl_t err = NULL; + uint8_t ctrl = 0; + bool marked_busy = false; + + mutex_lock(&iv_mutex); + + do + { + err = readLPC(REG_CONTROL, ctrl); + if (err) { break; } + + // Tell the interface we're busy. + err = writeLPC(REG_CONTROL, CTRL_H_BUSY); + if (err) {break;} + + marked_busy = true; + + // Clear the pending state from the control register. + // Note the spec distinctly says "after setting H_BUSY, + // the host should clear this bit" - not at the same time. + // This is the hand-shake; H_BUSY gates the BMC which allows + // us to clear the ATN bits. Don't get fancy. + err = writeLPC(REG_CONTROL, + (ctrl & CTRL_B2H_ATN) ? CTRL_B2H_ATN : CTRL_SMS_ATN); + if (err) {break;} + + // Tell the interface we're reading + err = writeLPC(REG_CONTROL, CTRL_CLR_RD_PTR); + if (err) {break;} + + // The first byte is the length, grab it so we can allocate a buffer. + err = readLPC(REG_HOSTBMC, o_msg->iv_len); + if (err) { break; } + + // I don't think SMS messages have a completion code. + o_msg->iv_len -= (ctrl & CTRL_B2H_ATN) ? + IPMI_BT_HEADER_SIZE + 1 : IPMI_BT_HEADER_SIZE; + + err = readLPC(REG_HOSTBMC, o_msg->iv_netfun); + if (err) { break; } + + err = readLPC(REG_HOSTBMC, o_msg->iv_seq); + if (err) { break; } + + err = readLPC(REG_HOSTBMC, o_msg->iv_cmd); + if (err) { break; } + + // I don't think SMS messages have a completion code. + if (CTRL_B2H_ATN) + { + err = readLPC(REG_HOSTBMC, o_msg->iv_cc); + if (err) { continue; } + } + + o_msg->iv_data = new uint8_t[o_msg->iv_len]; + + for( size_t i = 0; (i < o_msg->iv_len) && (err == NULL); ++i) + { + err = readLPC(REG_HOSTBMC, o_msg->iv_data[i]); + } + if (err) { break; } + + } while(0); + + if (marked_busy) + { + // Clear the busy state (write 1 to toggle). Note if we get + // an error from the writeLPC, we toss it and return the first + // error as it likely has better information in it. + delete writeLPC(REG_CONTROL, CTRL_H_BUSY); + } + + mutex_unlock(&iv_mutex); + + IPMI_TRAC(INFO_MRK "read %s %s %x:%x seq %x len %x cc %x", + (ctrl & CTRL_B2H_ATN) ? "b2h" : "sms", + err ? "err" : "ok", + o_msg->iv_netfun, o_msg->iv_cmd, o_msg->iv_seq, + o_msg->iv_len, o_msg->iv_cc); + + return err; +} + +/** + * @brief Constructor + */ +IpmiDD::IpmiDD(void): + iv_eagains(false) +{ + mutex_init(&iv_mutex); + + // reset the BT interface - no idea what state the BMC thinks things are in + errlHndl_t err = reset(); + if (err) + { + IPMI_TRAC(ERR_MRK "error resetting the BT interface"); + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + } + + // Start task to poll the control register + // This is a singleton so this will only be called once, right? + task_create( poll_control_register, NULL ); + + return; +} diff --git a/src/usr/ipmi/ipmidd.H b/src/usr/ipmi/ipmidd.H new file mode 100644 index 000000000..d20dd6132 --- /dev/null +++ b/src/usr/ipmi/ipmidd.H @@ -0,0 +1,124 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmidd.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,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_IPMIDD_H +#define __IPMI_IMPIDD_H + +#include <sys/msg.h> +#include <ipmi/ipmiif.H> +#include "ipmibt.H" + +/** @file ipmidd.H + * @brief Provides the interfaces to the IPMI Device Driver + */ + +/** + * @brief IPMI Device Driver Class + * Provides read/write message capabilities. + */ +class IpmiDD +{ + public: + /** + * @brief Poll the control register + * + * @parm void + */ + void pollCtrl(void); + + /** + * @brief Performs an IPMI message read operation + * + * @param[out] o_msg - Destination buffer for data + * + * @return errlHndl_t NULL on success + */ + + errlHndl_t receive(IPMI::BTMessage* o_msg); + + /** + * @brief Performs an IPMI message write operation + * + * @param[in] i_msg - Location of data to be written + * + * @return errlHndl_t NULL on success + */ + errlHndl_t send(IPMI::BTMessage* i_msg); + + /** + * @brief Performs a reset of the BT hardware + * + * @param void + * + * @return errlHndl_t NULL on success + */ + errlHndl_t reset(void); + + /** + * @brief Constructor + * + * @parm void + */ + IpmiDD(void); + + private: + /** + * @brief Read an address from LPC space + * + * @parm i_addr Absolute LPC Address + * @parm o_data Buffer to read data into + * + * @return Error from operation + */ + errlHndl_t readLPC(const uint32_t i_addr, uint8_t& o_data); + + /** + * @brief Write an address from LPC space + * + * @parm i_addr Absolute LPC Address + * @parm i_data Data to write + * + * @return Error from operation + */ + errlHndl_t writeLPC(const uint32_t i_addr, uint8_t i_data); + + private: // Variables + + /** + * @brief Mutex used to protect internal state + */ + mutex_t iv_mutex; + + /** + * @brief True if we told the RP to try a write again + */ + bool iv_eagains; + + // Disallow copying this class. + IpmiDD& operator=(const IpmiDD&); + IpmiDD(const IpmiDD&); +}; + + +#endif diff --git a/src/usr/ipmi/ipmimsg.C b/src/usr/ipmi/ipmimsg.C new file mode 100644 index 000000000..09aa61b77 --- /dev/null +++ b/src/usr/ipmi/ipmimsg.C @@ -0,0 +1,104 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmimsg.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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 */ +/** + * @file ipmi.C + * @brief code for the IPMI message class + */ + +#include <errl/errlmanager.H> + +#include "ipmimsg.H" + +// This is because the factory lives in here. +#include "ipmibt.H" + +#include <kernel/console.H> +#include <config.h> + +// Defined in ipmidd.C +extern trace_desc_t * g_trac_ipmi; +#define IPMI_TRAC(printf_string,args...) \ + TRACFCOMP(g_trac_ipmi,"msg: "printf_string,##args) + +namespace IPMI +{ + /// + /// @brief msg ctor + /// @param[in] i_netfun, the network function + /// @param[in] i_cmd, the network 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) + /// + Message::Message(const network_function i_netfun, + const uint8_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_seq(iv_key), + iv_cmd(i_cmd), + iv_cc(0), + iv_state(0), + iv_errl(NULL), + iv_data(i_data) + { + } + + /// + /// @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_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, + uint8_t* i_data, const message_type i_type) + { + Message* new_message = NULL; + + // CHECK: Put an ifdef here for the config'd transport type. + switch(i_type) + { + case TYPE_SYNC: + new_message = new BTSyncMessage(i_netfun, i_cmd, i_len, i_data); + break; + case TYPE_ASYNC: + new_message = new BTAsyncMessage(i_netfun, i_cmd, i_len, i_data); + break; + default: + // We have ourselves a bug + assert(false, "ipmi message factory: unk type %d\n", i_type); + break; + } + + return new_message; + } + +}; diff --git a/src/usr/ipmi/ipmimsg.H b/src/usr/ipmi/ipmimsg.H new file mode 100644 index 000000000..71a504017 --- /dev/null +++ b/src/usr/ipmi/ipmimsg.H @@ -0,0 +1,127 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmimsg.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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_IPMIMSG_H +#define __IPMI_IPMIMSG_H + +#include <map> +#include <list> +#include <sys/msg.h> +#include <errl/errlentry.H> +#include <ipmi/ipmiif.H> + +namespace IPMI +{ + // Used in the factory for creating the proper subclass. + enum message_type + { + TYPE_SYNC = 0, + TYPE_ASYNC = 1, + }; + + typedef std::list<msg_t*> send_q_t; + typedef std::map<uint8_t,msg_t*> respond_q_t; + + // IPMI message base class. A thing which expects to be sent down a + // msg_q (so has a msg_t) and defines operations performed by a generic + // IPMI resource manager. Details are left to the subclasses. + class Message + { + public: + /// + /// @brief static factory + /// @param[in] i_netfun, the network function + /// @param[in] i_cmd, the network 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 (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, + const uint8_t i_len = 0, + uint8_t* i_data = NULL, + const message_type i_type = TYPE_SYNC); + + /// + /// @brief Message ctor + /// + Message(const network_function i_netfun = NETFUN_NONE, + const uint8_t i_cmd = 0, + const uint8_t i_len = 0, + uint8_t* i_data = NULL); + + /// + /// @brief Message dtor + /// + virtual ~Message(void) + { + msg_free(iv_msg); + } + + /// + /// @brief the maximum buffer size of the underlying transport + /// @param void + /// @return size_t, the max buffer size + /// + virtual size_t max_buffer(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; + + /// + /// @brief receive a message. + /// @return Error from operation + /// @note fills our iv_key with the proper information + /// + virtual errlHndl_t recv(void) = 0; + + msg_t* iv_msg; // Pointer back to our msg_q msg_t + uint8_t iv_key; // key used by the respond queue + + // Note: Some of these might turn out to be transport specific + // if so, we'll just move them down in to the subclasses + uint8_t iv_len; // Length + uint8_t iv_netfun; // Network Function + uint8_t& iv_seq; // Sequence number, reference to iv_key + uint8_t iv_cmd; // Command + uint8_t iv_cc; // Completion Code + uint8_t iv_state; // Driver things, like EAGAIN + errlHndl_t iv_errl; // Pointer to the errlHandl_t if needed + uint8_t* iv_data; // Pointer to the message data + + private: + // Disallow copying this class. Should suffice for disabling copy for + // all subclasses too. + Message& operator=(const Message&); + Message(const Message&); + }; + +}; // end namespace IPMI + +#endif diff --git a/src/usr/ipmi/ipmirp.C b/src/usr/ipmi/ipmirp.C new file mode 100644 index 000000000..008b66f8c --- /dev/null +++ b/src/usr/ipmi/ipmirp.C @@ -0,0 +1,445 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmirp.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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 */ +/** + * @file ipmirp.C + * @brief IPMI resource provider definition + */ + +#include "ipmirp.H" +#include <ipmi/ipmi_reasoncodes.H> +#include <devicefw/driverif.H> +#include <devicefw/userif.H> + +#include <config.h> +#include <sys/task.h> +#include <initservice/taskargs.H> +#include <initservice/initserviceif.H> +#include <sys/vfs.h> + +#include <targeting/common/commontargeting.H> +#include <kernel/ipc.H> +#include <arch/ppc.H> +#include <errl/errlmanager.H> +#include <sys/time.h> +#include <sys/misc.h> +#include <errno.h> + +// Defined in ipmidd.C +extern trace_desc_t * g_trac_ipmi; +#define IPMI_TRAC(printf_string,args...) \ + TRACFCOMP(g_trac_ipmi,"rp: "printf_string,##args) + +/** + * setup _start and handle barrier + */ +TASK_ENTRY_MACRO( IpmiRP::daemonProcess ); + +/** + * @brief Constructor + */ +IpmiRP::IpmiRP(void): + iv_msgQ(msg_q_create()), + iv_sendq(), + iv_respondq() +{ +} + +/** + * @brief Destructor + */ +IpmiRP::~IpmiRP(void) +{ + msg_q_destroy(iv_msgQ); +} + +void* IpmiRP::start(void* unused) +{ + Singleton<IpmiRP>::instance().execute(); + return NULL; +} + +void IpmiRP::daemonProcess(errlHndl_t& o_errl) +{ + task_create(&IpmiRP::start, NULL); +} + +/** + * @brief Return the maximum data size to allocate + */ +inline size_t IpmiRP::maxBuffer(void) +{ + // shared_ptrs would be handy here, fwiw. + IPMI::Message* msg = IPMI::Message::factory(); + size_t mbs = msg->max_buffer(); + delete msg; + return mbs; +} + +/** + * @brief Entry point of the resource provider + */ +void IpmiRP::execute(void) +{ + // Mark as an independent daemon so if it crashes we terminate. + task_detach(); + + IPMI_TRAC(ENTER_MRK "message loop"); + + // 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? + + // Register shutdown events with init service. + // Done at the "end" of shutdown processesing. + // This will flush out any IPMI messages which were sent as + // part of the shutdown processing. We chose MBOX priority + // as we don't want to accidentally get this message after + // interrupt processing has stopped in case we need intr to + // finish flushing the pipe. + INITSERVICE::registerShutdownEvent(iv_msgQ, IPMI::MSG_STATE_SHUTDOWN, + INITSERVICE::MBOX_PRIORITY); + do { + + 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. 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(); + } + } + + } while (false); + + return; +} + +/// +/// @brief Go in to the idle state +/// +void IpmiRP::idle(void) +{ + // If the interface is idle, we can write anything we need to write. + for (IPMI::send_q_t::iterator i = iv_sendq.begin(); i != iv_sendq.end();) + { + // If we have a problem transmitting a message, then we just stop + // here and wait for the next time the interface transitions to idle. + // Note that there are two failure cases: the first is that there is + // a problem transmitting. In this case we told the other end of the + // message queue, and so the life of this message is over. The other + // case is that the interface turned out to be busy in which case + // this message can sit on the queue and it'll be next. + + IPMI::Message* msg = static_cast<IPMI::Message*>((*i)->extra_data); + + // If there was an i/o error, we do nothing - leave this message on + // the queue. Don't touch msg after xmit returns. If the message was + // sent, and it was async, msg has been destroyed. + if (msg->xmit(iv_respondq)) + { + break; + } + i = iv_sendq.erase(i); + } + return; +} + +/// +/// @brief Handle a response to a message we sent +/// +void IpmiRP::response(void) +{ + IPMI::Message* rcv_buf = IPMI::Message::factory(); + + do + { + // Go down to the device and read. Fills in iv_key. + errlHndl_t err = rcv_buf->recv(); + + if (err) + { + // Not much we're going to do here, so lets commit the error and + // the original request will timeout. + err->collectTrace(IPMI_COMP_NAME); + errlCommit(err, IPMI_COMP_ID); + break; + } + + // 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); + 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; + break; + } + + msg_t* original_msg = itr->second; + iv_respondq.erase(itr); + + // Hand the allocated buffer over to the original message's + // ipmi_msg_t It will be responsible for de-allocating it + // when it's dtor is called. + IPMI::Message* ipmi_msg = + static_cast<IPMI::Message*>(original_msg->extra_data); + + // Hand ownership of the data to the original 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); + + // Remove the response data. + delete[] ipmi_msg->iv_data; + + break; + } + + } while(false); + + delete rcv_buf; + return; +} + + + +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, + 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::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); + int rc = msg_sendrecv(mq, ipmi_msg->iv_msg); + + // If the kernel didn't give a hassle about he message, check to see if + // there was an error reported back from the other end of the queue. If + // this message made it to the other end of the queue, then our memory + // (io_data) is in the proper state. + if (rc == 0) { + err = ipmi_msg->iv_errl; + } + + // Otherwise, lets make an errl out of our return code + else + { + /* @errorlog tag + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid IPMI::MOD_IPMISRV_SEND + * @reasoncode IPMI::RC_INVALID_SENDRECV + * @userdata1 rc from msq_sendrecv() + * @devdesc msg_sendrecv() failed + */ + err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + IPMI::MOD_IPMISRV_SEND, + IPMI::RC_INVALID_SENDRECV, + rc, + 0, + true); + err->collectTrace(IPMI_COMP_NAME); + + // ... and clean up the memory for the caller + delete[] io_data; + } + + // The length and the data are the response, if there was one. All of + // there are pretty much invalid if there was an error. + io_len = ipmi_msg->iv_len; + io_data = ipmi_msg->iv_data; + o_completion_code = ipmi_msg->iv_cc; + delete ipmi_msg; + + return err; + } + + /// + /// @brief Asynchronus message send + /// + errlHndl_t send(const IPMI::network_function i_netfun, + const uint8_t i_cmd, + const size_t i_len, uint8_t* i_data) + { + static msg_q_t mq = Singleton<IpmiRP>::instance().msgQueue(); + errlHndl_t err = NULL; + + // We don't delete this message, the message will self destruct + // after it's been transmitted. Note it could be placed on the send + // queue and we are none the wiser - so we can't delete it. + IPMI::Message* ipmi_msg = IPMI::Message::factory(i_netfun, 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); + int rc = msg_send(mq, ipmi_msg->iv_msg); + + if (rc) + { + /* @errorlog tag + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid IPMI::MOD_IPMISRV_SEND + * @reasoncode IPMI::RC_INVALID_SEND + * @userdata1 rc from msq_send() + * @devdesc msg_send() failed + */ + err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + IPMI::MOD_IPMISRV_SEND, + IPMI::RC_INVALID_SEND, + rc, + 0, + true); + err->collectTrace(IPMI_COMP_NAME); + + // ... and clean up the memory for the caller + delete[] i_data; + } + + return err; + } + + /// + /// @brief Maximum buffer for data (max xport - header) + /// + inline size_t max_buffer(void) + { + static const size_t mbs = Singleton<IpmiRP>::instance().maxBuffer(); + return mbs; + } + +}; diff --git a/src/usr/ipmi/ipmirp.H b/src/usr/ipmi/ipmirp.H new file mode 100644 index 000000000..7e535fe67 --- /dev/null +++ b/src/usr/ipmi/ipmirp.H @@ -0,0 +1,158 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/ipmi/ipmirp.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2012,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_IPMIRP_H +#define __IPMI_IMPIRP_H + +/** + * @file ipmirp.H + * @brief IPMI service provider declariation + */ + +#include <stdint.h> +#include <builtins.h> +#include <ipmi/ipmiif.H> +#include "ipmibt.H" +#include <errl/errlentry.H> +#include <sys/msg.h> +#include <sys/sync.h> +#include <list> +#include <map> +#include <util/locked/list.H> +#include <intr/interrupt.H> +#include <limits.h> + +namespace TARGETING +{ + class Target; +}; + +class IpmiRP +{ + public: + + /** + * Initialize the IPMI service + * @param[in] o_errl, NULL if OK + */ + static void daemonProcess(errlHndl_t& o_errl); + + /** + * Thread start routine for the resource provider + * @param[in] void*, unused + */ + static void* start(void* unused); + + /** + * Default constructor + */ + IpmiRP(void); + + /** + * Destructor + */ + ~IpmiRP(void); + + /** + * The mailbox service provider task + */ + void msgHandler(void); + + /** + * Return the max data buffer to allocate for the underlying transport. + */ + inline size_t maxBuffer(void); + + /** + * @brief Get the message queue associated with this RP + * @param[in] void + * @return, a msg_q_t which is the message queue + */ + msg_q_t msgQueue(void) + { return iv_msgQ; } + + private: + + /** + * Entry point for the resource provider + */ + void execute(void); + + /** + * @brief Transmit a message over the IPMI interface + * @param[in] i_msg, ptr to the message_q message + * @note i_msg is not const because it contains a return code + */ + int xmit(msg_t* i_msg); + + /** + * @brief Transmit a synchronous message over the IPMI interface + * @param[in] i_msg, ptr to the message_q message + * @note this is a message used internally to send a message + * over the IPMI interface and wait for a response from the BMC + * @note i_msg is not const because it contains a return code + */ + void xmit_sync(msg_t* i_msg); + + /** + * @brief Transmit an asynchronous message over the IPMI interface + * @param[in] i_msg, ptr to the message_q message + * @note this is a message used internally to send a message + * over the IPMI interface and not wait for a response from the BMC + * @note i_msg is not const because it contains a return code + */ + void xmit_async(msg_t* i_msg); + + /** + * @brief Handle a message from the interface indicating the + * interface has gone idle (and can be written to.) + * @param[in] void + */ + void idle(void); + + /** + * @brief Handle an indication from the interface indicating the + * BMC interface has a response message ready to read + * @param[in] void + */ + void response(void); + + /** + * @brief Handle an indication from the interface indicating the + * BMC interface has an event/sms message ready to read + * @param[in] void + */ + void event(void); + + msg_q_t iv_msgQ; //!< ipmi mesage queue + IPMI::send_q_t iv_sendq; //!< msg to send queue + IPMI::respond_q_t iv_respondq; //!< msg respond pending list + + // Disallow copying this class. + IpmiRP& operator=(const IpmiRP&); + IpmiRP(const IpmiRP&); +}; + +#endif diff --git a/src/usr/ipmi/makefile b/src/usr/ipmi/makefile new file mode 100644 index 000000000..fb2a19473 --- /dev/null +++ b/src/usr/ipmi/makefile @@ -0,0 +1,35 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/ipmi/makefile $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2011,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 +ROOTPATH = ../../.. +MODULE = ipmi + +OBJS += ipmimsg.o +OBJS += $(if $(CONFIG_BMC_BT_LPC_IPMI),ipmibt.o) +OBJS += ipmirp.o +OBJS += $(if $(CONFIG_BMC_BT_LPC_IPMI),ipmidd.o) + +#SUBDIRS += test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/makefile b/src/usr/makefile index 02f622520..ec0f713f0 100644 --- a/src/usr/makefile +++ b/src/usr/makefile @@ -63,5 +63,6 @@ SUBDIRS += gpio.d SUBDIRS += lpc.d SUBDIRS += console.d SUBDIRS += errldisplay.d +SUBDIRS += ipmi.d include ${ROOTPATH}/config.mk |