aboutsummaryrefslogtreecommitdiff
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
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>
-rw-r--r--core/ipmi.c8
-rw-r--r--hw/bt.c36
-rw-r--r--hw/ipmi/Makefile.inc2
-rw-r--r--hw/ipmi/ipmi-opal.c146
-rw-r--r--include/ipmi.h13
-rw-r--r--include/opal.h5
-rw-r--r--platforms/astbmc/common.c2
7 files changed, 198 insertions, 14 deletions
diff --git a/core/ipmi.c b/core/ipmi.c
index 60b7a1d..28b8022 100644
--- a/core/ipmi.c
+++ b/core/ipmi.c
@@ -20,7 +20,7 @@
#include <opal.h>
#include <device.h>
-static struct ipmi_backend *ipmi_backend = NULL;
+struct ipmi_backend *ipmi_backend = NULL;
void ipmi_free_msg(struct ipmi_msg *msg)
{
@@ -49,7 +49,7 @@ struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code,
msg->backend = ipmi_backend;
msg->cmd = IPMI_CMD(code);
- msg->netfn = IPMI_NETFN(code);
+ msg->netfn = IPMI_NETFN(code) << 2;
msg->req_size = req_size;
msg->resp_size = resp_size;
msg->complete = complete;
@@ -80,10 +80,11 @@ void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg)
cc = IPMI_ERR_UNSPECIFIED;
}
- if (msg->netfn + 1 != netfn) {
+ if ((msg->netfn >> 2) + 1 != (netfn >> 2)) {
prerror("IPMI: Incorrect netfn 0x%02x in response\n", netfn);
cc = IPMI_ERR_UNSPECIFIED;
}
+ msg->netfn = netfn;
if (cc != IPMI_CC_NO_ERROR) {
prerror("IPMI: Got error response 0x%02x\n", msg->cc);
@@ -105,6 +106,7 @@ void ipmi_register_backend(struct ipmi_backend *backend)
assert(backend->queue_msg);
assert(backend->dequeue_msg);
ipmi_backend = backend;
+ ipmi_backend->opal_event_ipmi_recv = opal_dynamic_event_alloc();
}
bool ipmi_present(void)
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);
+}
diff --git a/include/ipmi.h b/include/ipmi.h
index 9543515..42906e6 100644
--- a/include/ipmi.h
+++ b/include/ipmi.h
@@ -18,6 +18,7 @@
#define __IPMI_H
#include <stdint.h>
+#include <ccan/list/list.h>
#include <stdbool.h>
/*
@@ -108,8 +109,14 @@
#define IPMI_DEFAULT_INTERFACE 0
+#define IPMI_MAX_REQ_SIZE 64
+#define IPMI_MAX_RESP_SIZE 64
+
struct ipmi_backend;
struct ipmi_msg {
+ /* Can be used by command implementations to track requests */
+ struct list_node link;
+
struct ipmi_backend *backend;
uint8_t netfn;
uint8_t cmd;
@@ -128,12 +135,15 @@ struct ipmi_msg {
};
struct ipmi_backend {
+ uint64_t opal_event_ipmi_recv;
struct ipmi_msg *(*alloc_msg)(size_t, size_t);
void (*free_msg)(struct ipmi_msg *);
int (*queue_msg)(struct ipmi_msg *);
int (*dequeue_msg)(struct ipmi_msg *);
};
+extern struct ipmi_backend *ipmi_backend;
+
/* Initialise the IPMI interface */
void ipmi_init(void);
@@ -166,4 +176,7 @@ void ipmi_register_backend(struct ipmi_backend *backend);
/* Register rtc ipmi commands with as opal callbacks. */
void ipmi_rtc_init(void);
+/* Register ipmi host interface access callbacks */
+void ipmi_opal_init(void);
+
#endif
diff --git a/include/opal.h b/include/opal.h
index 8a833df..730aee7 100644
--- a/include/opal.h
+++ b/include/opal.h
@@ -36,6 +36,7 @@
#define OPAL_HARDWARE_FROZEN -13
#define OPAL_WRONG_STATE -14
#define OPAL_ASYNC_COMPLETION -15
+#define OPAL_EMPTY -16
/* API Tokens (in r0) */
#define OPAL_TEST 0
@@ -142,7 +143,9 @@
#define OPAL_READ_TPO 104
#define OPAL_GET_DPO_STATUS 105
#define OPAL_I2C_REQUEST 106
-#define OPAL_LAST 106
+#define OPAL_IPMI_SEND 107
+#define OPAL_IPMI_RECV 108
+#define OPAL_LAST 108
#ifndef __ASSEMBLY__
diff --git a/platforms/astbmc/common.c b/platforms/astbmc/common.c
index 17dd66f..69fa8af 100644
--- a/platforms/astbmc/common.c
+++ b/platforms/astbmc/common.c
@@ -51,6 +51,7 @@ void astbmc_init(void)
/* Register the BT interface with the IPMI layer */
bt_init();
ipmi_rtc_init();
+ ipmi_opal_init();
/* As soon as IPMI is up, inform BMC we are in "S0" */
ipmi_set_power_state(IPMI_PWR_SYS_S0_WORKING, IPMI_PWR_NOCHANGE);
@@ -228,4 +229,3 @@ void astbmc_early_init(void)
/* Setup UART and use it as console with interrupts */
uart_init(true);
}
-