diff options
author | Alistair Popple <alistair@popple.id.au> | 2014-10-23 16:14:09 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-10-30 16:35:51 +1100 |
commit | b2a374da98aa710b4c55556f9a9047d4d4a8665d (patch) | |
tree | 31055569f5e456f721f011d2646be7ee6b1e9f57 /hw | |
parent | b118964db9acd6a36ae1ffe70c09715479a58095 (diff) | |
download | skiboot-b2a374da98aa710b4c55556f9a9047d4d4a8665d.zip skiboot-b2a374da98aa710b4c55556f9a9047d4d4a8665d.tar.gz skiboot-b2a374da98aa710b4c55556f9a9047d4d4a8665d.tar.bz2 |
ipmi: Add an opal interface to the ipmi stack
This patch adds two opal calls (opal_ipmi_send and opal_ipmi_recv)
to allow an operating system to send and receive arbitrary ipmi
messages to the BMC.
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/bt.c | 36 | ||||
-rw-r--r-- | hw/ipmi/Makefile.inc | 2 | ||||
-rw-r--r-- | hw/ipmi/ipmi-opal.c | 146 |
3 files changed, 175 insertions, 9 deletions
@@ -58,7 +58,9 @@ /* * How long (in TB ticks) before a message is timed out. */ -#define BT_MSG_TIMEOUT (secs_to_tb(1)) +#define BT_MSG_TIMEOUT (secs_to_tb(3)) + +#define BT_QUEUE_DEBUG 0 enum bt_states { BT_STATE_IDLE = 0, @@ -76,7 +78,6 @@ struct bt_msg { struct list_node link; unsigned long tb; uint8_t seq; - uint8_t lun; struct ipmi_msg ipmi_msg; }; @@ -132,7 +133,6 @@ static int bt_add_ipmi_msg(struct ipmi_msg *ipmi_msg) { struct bt_msg *bt_msg = container_of(ipmi_msg, struct bt_msg, ipmi_msg); - bt_msg->lun = 0; lock(&bt.lock); bt_msg->tb = mftb(); bt_msg->seq = ipmi_seq++; @@ -197,7 +197,7 @@ static bool bt_try_send_msg(void) bt_outb(ipmi_msg->req_size + BT_MIN_REQ_LEN, BT_HOST2BMC); /* Byte 2 - NetFn/LUN */ - bt_outb((ipmi_msg->netfn << 2) | (bt_msg->lun & 0x3), BT_HOST2BMC); + bt_outb(ipmi_msg->netfn, BT_HOST2BMC); /* Byte 3 - Seq */ bt_outb(bt_msg->seq, BT_HOST2BMC); @@ -290,16 +290,13 @@ static bool bt_get_resp(void) } ipmi_msg->resp_size = resp_len; - bt_msg->lun = netfn & 0x3; - netfn = netfn >> 2; - /* Byte 6:N - Data */ for (i = 0; i < resp_len; i++) ipmi_msg->data[i] = bt_inb(BT_HOST2BMC); bt_set_h_busy(false); if (cc != IPMI_CC_NO_ERROR) - prerror("BT: Host error 0x%02x receiving BT/IPMI response\n", cc); + prerror("BT: Host error 0x%02x receiving BT/IPMI response for msg 0x%02x\n", cc, seq); /* Make sure the other side is idle before we move to the idle state */ bt_set_state(BT_STATE_B_BUSY); @@ -310,6 +307,10 @@ static bool bt_get_resp(void) /* * Call the IPMI layer to finish processing the message. */ +#if BT_QUEUE_DEBUG + prlog(PR_DEBUG, "cmd 0x%02x done\n", seq); +#endif + ipmi_cmd_done(cmd, netfn, cc, ipmi_msg); /* Immediately send the next message */ @@ -337,6 +338,25 @@ static void bt_poll(void *data __unused) bool ret = true; do { + +#if BT_QUEUE_DEBUG + struct bt_msg *msg; + static bool printed = false; + lock(&bt.lock); + if (!list_empty(&bt.msgq)) { + printed = false; + prlog(PR_DEBUG, "-------- BT Msg Queue --------\n"); + list_for_each(&bt.msgq, msg, link) { + prlog(PR_DEBUG, "Seq: 0x%02x Cmd: 0x%02x\n", msg->seq, msg->ipmi_msg.cmd); + } + prlog(PR_DEBUG, "-----------------------------\n"); + } else if (!printed) { + printed = true; + prlog(PR_DEBUG, "----- BT Msg Queue Empty -----\n"); + } + unlock(&bt.lock); +#endif + bt_expire_old_msg(); switch(bt.state) { diff --git a/hw/ipmi/Makefile.inc b/hw/ipmi/Makefile.inc index c939f6c..b27ef93 100644 --- a/hw/ipmi/Makefile.inc +++ b/hw/ipmi/Makefile.inc @@ -1,5 +1,5 @@ SUBDIRS += hw/ipmi -IPMI_OBJS = ipmi-rtc.o ipmi-power.o +IPMI_OBJS = ipmi-rtc.o ipmi-power.o ipmi-opal.o IPMI = hw/ipmi/built-in.o $(IPMI): $(IPMI_OBJS:%=hw/ipmi/%) diff --git a/hw/ipmi/ipmi-opal.c b/hw/ipmi/ipmi-opal.c new file mode 100644 index 0000000..4a90468 --- /dev/null +++ b/hw/ipmi/ipmi-opal.c @@ -0,0 +1,146 @@ +/* Copyright 2013-2014 IBM 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. + */ + +#include <stdlib.h> +#include <ipmi.h> +#include <lock.h> +#include <opal.h> +#include <device.h> +#include <ccan/list/list.h> + +enum { + OPAL_IPMI_MSG_FORMAT_VERSION_1 = 1, +}; + +struct opal_ipmi_msg { + uint8_t version; + uint8_t netfn; + uint8_t cmd; + uint8_t data[]; +}; + +static struct lock msgq_lock = LOCK_UNLOCKED; +static struct list_head msgq = LIST_HEAD_INIT(msgq); + +static void opal_send_complete(struct ipmi_msg *msg) +{ + lock(&msgq_lock); + list_add_tail(&msgq, &msg->link); + opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, + ipmi_backend->opal_event_ipmi_recv); + unlock(&msgq_lock); +} + +static int64_t opal_ipmi_send(uint64_t interface, + struct opal_ipmi_msg *opal_ipmi_msg, uint64_t msg_len) +{ + struct ipmi_msg *msg; + + if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { + prerror("OPAL IPMI: Incorrect version\n"); + return OPAL_UNSUPPORTED; + } + + msg_len -= sizeof(struct opal_ipmi_msg); + if (msg_len > IPMI_MAX_REQ_SIZE) { + prerror("OPAL IPMI: Invalid request length\n"); + return OPAL_PARAMETER; + } + + prlog(PR_DEBUG, "opal_ipmi_send(cmd: 0x%02x netfn: 0x%02x len: 0x%02llx)\n", + opal_ipmi_msg->cmd, opal_ipmi_msg->netfn >> 2, msg_len); + + msg = ipmi_mkmsg(interface, + IPMI_CODE(opal_ipmi_msg->netfn >> 2, opal_ipmi_msg->cmd), + opal_send_complete, NULL, opal_ipmi_msg->data, + msg_len, IPMI_MAX_RESP_SIZE); + if (!msg) + return OPAL_RESOURCE; + + msg->complete = opal_send_complete; + msg->error = opal_send_complete; + return ipmi_queue_msg(msg); +} + +static int64_t opal_ipmi_recv(uint64_t interface, + struct opal_ipmi_msg *opal_ipmi_msg, uint64_t *msg_len) +{ + struct ipmi_msg *msg; + int64_t rc; + + lock(&msgq_lock); + msg = list_top(&msgq, struct ipmi_msg, link); + + if (!msg) { + rc = OPAL_EMPTY; + goto out_unlock; + } + + if (opal_ipmi_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) { + prerror("OPAL IPMI: Incorrect version\n"); + rc = OPAL_UNSUPPORTED; + goto out_unlock; + } + + if (interface != IPMI_DEFAULT_INTERFACE) { + prerror("IPMI: Invalid interface 0x%llx in opal_ipmi_recv\n", interface); + rc = OPAL_EMPTY; + goto out_unlock; + } + + if (*msg_len - sizeof(struct opal_ipmi_msg) < msg->resp_size + 1) { + rc = OPAL_RESOURCE; + goto out_unlock; + } + + list_del(&msg->link); + if (list_empty(&msgq)) + opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, 0); + unlock(&msgq_lock); + + opal_ipmi_msg->cmd = msg->cmd; + opal_ipmi_msg->netfn = msg->netfn; + opal_ipmi_msg->data[0] = msg->cc; + memcpy(&opal_ipmi_msg->data[1], msg->data, msg->resp_size); + + prlog(PR_DEBUG, "opal_ipmi_recv(cmd: 0x%02x netfn: 0x%02x resp_size: 0x%02x)\n", + msg->cmd, msg->netfn >> 2, msg->resp_size); + + /* Add one as the completion code is returned in the message data */ + *msg_len = msg->resp_size + sizeof(struct opal_ipmi_msg) + 1; + ipmi_free_msg(msg); + + return OPAL_SUCCESS; + +out_unlock: + unlock(&msgq_lock); + return rc; +} + +void ipmi_opal_init(void) +{ + struct dt_node *opal_ipmi; + + opal_ipmi = dt_new(opal_node, "ipmi"); + dt_add_property_strings(opal_ipmi, "compatible", "ibm,opal-ipmi"); + dt_add_property_cells(opal_ipmi, "ibm,ipmi-interface-id", + IPMI_DEFAULT_INTERFACE); + dt_add_property_cells(opal_ipmi, "interrupts", + ilog2(ipmi_backend->opal_event_ipmi_recv)); + + opal_register(OPAL_IPMI_SEND, opal_ipmi_send, 3); + opal_register(OPAL_IPMI_RECV, opal_ipmi_recv, 3); +} |