diff options
author | Brian Silver <bsilver@us.ibm.com> | 2014-09-30 08:22:11 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2014-10-23 04:51:02 -0500 |
commit | a9010ccc1130b81e45d1151bb5de9453d31c08a5 (patch) | |
tree | ecc60da4bd3623cc97851dfa75e98293f9c77bdd /src/usr/ipmi/ipmirp.C | |
parent | a6b67089037c83373f548749a463dfd769938b77 (diff) | |
download | talos-hostboot-a9010ccc1130b81e45d1151bb5de9453d31c08a5.tar.gz talos-hostboot-a9010ccc1130b81e45d1151bb5de9453d31c08a5.zip |
IPMI Block Transfer implementation
Change-Id: I8f6a590b29d9171389d10abc5b6e68f91ac94d16
RTC: 114907
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/13721
Tested-by: Jenkins Server
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr/ipmi/ipmirp.C')
-rw-r--r-- | src/usr/ipmi/ipmirp.C | 445 |
1 files changed, 445 insertions, 0 deletions
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; + } + +}; |