aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/ipmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/ipmi.c')
-rw-r--r--roms/skiboot/core/ipmi.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/roms/skiboot/core/ipmi.c b/roms/skiboot/core/ipmi.c
new file mode 100644
index 000000000..bbc1a7b69
--- /dev/null
+++ b/roms/skiboot/core/ipmi.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * in-band IPMI, probably over bt (or via FSP mbox on FSP)
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <bt.h>
+#include <ipmi.h>
+#include <opal.h>
+#include <device.h>
+#include <skiboot.h>
+#include <lock.h>
+#include <cpu.h>
+#include <timebase.h>
+#include <debug_descriptor.h>
+
+struct ipmi_backend *ipmi_backend = NULL;
+static struct lock sync_lock = LOCK_UNLOCKED;
+static struct ipmi_msg *sync_msg = NULL;
+
+void ipmi_free_msg(struct ipmi_msg *msg)
+{
+ /* ipmi_free_msg frees messages allocated by the
+ * backend. Without a backend we couldn't have allocated
+ * messages to free (we don't support removing backends
+ * yet). */
+ if (!ipmi_present()) {
+ prerror("IPMI: Trying to free message without backend\n");
+ return;
+ }
+
+ msg->backend->free_msg(msg);
+}
+
+void ipmi_init_msg(struct ipmi_msg *msg, int interface,
+ uint32_t code, void (*complete)(struct ipmi_msg *),
+ void *user_data, size_t req_size, size_t resp_size)
+{
+ /* We don't actually support multiple interfaces at the moment. */
+ assert(interface == IPMI_DEFAULT_INTERFACE);
+
+ msg->backend = ipmi_backend;
+ msg->cmd = IPMI_CMD(code);
+ msg->netfn = IPMI_NETFN(code) << 2;
+ msg->req_size = req_size;
+ msg->resp_size = resp_size;
+ msg->complete = complete;
+ msg->user_data = user_data;
+}
+
+struct ipmi_msg *ipmi_mkmsg_simple(uint32_t code, void *req_data, size_t req_size)
+{
+ return ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, code, ipmi_free_msg, NULL,
+ req_data, req_size, 0);
+}
+
+struct ipmi_msg *ipmi_mkmsg(int interface, uint32_t code,
+ void (*complete)(struct ipmi_msg *),
+ void *user_data, void *req_data, size_t req_size,
+ size_t resp_size)
+{
+ struct ipmi_msg *msg;
+
+ if (!ipmi_present())
+ return NULL;
+
+ msg = ipmi_backend->alloc_msg(req_size, resp_size);
+ if (!msg)
+ return NULL;
+
+ ipmi_init_msg(msg, interface, code, complete, user_data, req_size,
+ resp_size);
+
+ /* Commands are free to over ride this if they want to handle errors */
+ msg->error = ipmi_free_msg;
+
+ if (req_data)
+ memcpy(msg->data, req_data, req_size);
+
+ return msg;
+}
+
+int ipmi_queue_msg_head(struct ipmi_msg *msg)
+{
+ if (!ipmi_present())
+ return OPAL_HARDWARE;
+
+ if (!msg) {
+ prerror("%s: Attempting to queue NULL message\n", __func__);
+ return OPAL_PARAMETER;
+ }
+
+ return msg->backend->queue_msg_head(msg);
+}
+
+int ipmi_queue_msg(struct ipmi_msg *msg)
+{
+ /* Here we could choose which interface to use if we want to support
+ multiple interfaces. */
+ if (!ipmi_present())
+ return OPAL_HARDWARE;
+
+ if (!msg) {
+ prerror("%s: Attempting to queue NULL message\n", __func__);
+ return OPAL_PARAMETER;
+ }
+
+ return msg->backend->queue_msg(msg);
+}
+
+int ipmi_dequeue_msg(struct ipmi_msg *msg)
+{
+ if (!ipmi_present())
+ return OPAL_HARDWARE;
+
+ if (!msg) {
+ prerror("%s: Attempting to dequeue NULL message\n", __func__);
+ return OPAL_PARAMETER;
+ }
+
+ return msg->backend->dequeue_msg(msg);
+}
+
+void ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc, struct ipmi_msg *msg)
+{
+ msg->cc = cc;
+ if (msg->cmd != cmd) {
+ prerror("IPMI: Incorrect cmd 0x%02x in response\n", cmd);
+ cc = IPMI_ERR_UNSPECIFIED;
+ }
+
+ if ((msg->netfn >> 2) + 1 != (netfn >> 2)) {
+ prerror("IPMI: Incorrect netfn 0x%02x in response\n", netfn >> 2);
+ cc = IPMI_ERR_UNSPECIFIED;
+ }
+ msg->netfn = netfn;
+
+ if (cc != IPMI_CC_NO_ERROR) {
+ prlog(PR_DEBUG, "IPMI: Got error response. cmd=0x%x, netfn=0x%x,"
+ " rc=0x%02x\n", msg->cmd, msg->netfn >> 2, msg->cc);
+
+ assert(msg->error);
+ msg->error(msg);
+ } else if (msg->complete)
+ msg->complete(msg);
+
+ /* At this point the message has should have been freed by the
+ completion functions. */
+
+ /* If this is a synchronous message flag that we are done */
+ if (msg == sync_msg) {
+ sync_msg = NULL;
+ barrier();
+ }
+}
+
+void ipmi_queue_msg_sync(struct ipmi_msg *msg)
+{
+ void (*poll)(void) = msg->backend->poll;
+
+ if (!ipmi_present())
+ return;
+
+ if (!msg) {
+ prerror("%s: Attempting to queue NULL message\n", __func__);
+ return;
+ }
+
+ lock(&sync_lock);
+ while (sync_msg);
+ sync_msg = msg;
+ if (msg->backend->disable_retry && !opal_booting())
+ msg->backend->disable_retry(msg);
+ ipmi_queue_msg_head(msg);
+ unlock(&sync_lock);
+
+ /*
+ * BT response handling relies on a timer. We can't just run all
+ * timers because we may have been called with a lock that a timer
+ * wants, and they're generally not written to cope with that.
+ * So, just run whatever the IPMI backend needs to make forward
+ * progress.
+ */
+ while (sync_msg == msg) {
+ if (poll)
+ poll();
+ time_wait_ms(10);
+ }
+}
+
+static void ipmi_read_event_complete(struct ipmi_msg *msg)
+{
+ prlog(PR_DEBUG, "IPMI read event %02x complete: %d bytes. cc: %02x\n",
+ msg->cmd, msg->resp_size, msg->cc);
+
+ /* Handle power control & PNOR handshake events */
+ ipmi_parse_sel(msg);
+
+ ipmi_free_msg(msg);
+}
+
+static void ipmi_get_message_flags_complete(struct ipmi_msg *msg)
+{
+ uint8_t flags = msg->data[0];
+
+ ipmi_free_msg(msg);
+
+ prlog(PR_DEBUG, "IPMI Get Message Flags: %02x\n", flags);
+
+ /* Once we see an interrupt we assume the payload has
+ * booted. We disable the wdt and let the OS setup its own
+ * wdt.
+ *
+ * This is also where we consider the OS to be booted, so we set
+ * the boot count sensor */
+ if (flags & IPMI_MESSAGE_FLAGS_WATCHDOG_PRE_TIMEOUT) {
+ ipmi_wdt_stop();
+ ipmi_set_boot_count();
+ }
+
+ /* Message available in the event buffer? Queue a Read Event command
+ * to retrieve it. The flag is cleared by performing a read */
+ if (flags & IPMI_MESSAGE_FLAGS_EVENT_BUFFER) {
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_READ_EVENT,
+ ipmi_read_event_complete, NULL, NULL, 0, 16);
+ ipmi_queue_msg(msg);
+ }
+}
+
+void ipmi_sms_attention(void)
+{
+ struct ipmi_msg *msg;
+
+ if (!ipmi_present())
+ return;
+
+ /* todo: when we handle multiple IPMI interfaces, we'll need to
+ * ensure that this message is associated with the appropriate
+ * backend. */
+ msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_GET_MESSAGE_FLAGS,
+ ipmi_get_message_flags_complete, NULL, NULL, 0, 1);
+
+ ipmi_queue_msg(msg);
+}
+
+void ipmi_register_backend(struct ipmi_backend *backend)
+{
+ /* We only support one backend at the moment */
+ assert(backend->alloc_msg);
+ assert(backend->free_msg);
+ 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)
+{
+ return ipmi_backend != NULL;
+}