From b2a374da98aa710b4c55556f9a9047d4d4a8665d Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Thu, 23 Oct 2014 16:14:09 +1100 Subject: 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 Signed-off-by: Benjamin Herrenschmidt --- core/ipmi.c | 8 ++- hw/bt.c | 36 +++++++++--- hw/ipmi/Makefile.inc | 2 +- hw/ipmi/ipmi-opal.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++ include/ipmi.h | 13 +++++ include/opal.h | 5 +- platforms/astbmc/common.c | 2 +- 7 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 hw/ipmi/ipmi-opal.c 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 #include -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 +#include +#include +#include +#include +#include + +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 +#include #include /* @@ -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); } - -- cgit v1.1