aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2014-10-23 16:14:09 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-10-30 16:35:51 +1100
commitb2a374da98aa710b4c55556f9a9047d4d4a8665d (patch)
tree31055569f5e456f721f011d2646be7ee6b1e9f57 /hw
parentb118964db9acd6a36ae1ffe70c09715479a58095 (diff)
downloadskiboot-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.c36
-rw-r--r--hw/ipmi/Makefile.inc2
-rw-r--r--hw/ipmi/ipmi-opal.c146
3 files changed, 175 insertions, 9 deletions
diff --git a/hw/bt.c b/hw/bt.c
index 561ed14..1fe25dc 100644
--- a/hw/bt.c
+++ b/hw/bt.c
@@ -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);
+}