diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/ipmi-opal.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/ipmi-opal.c')
-rw-r--r-- | roms/skiboot/core/ipmi-opal.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/roms/skiboot/core/ipmi-opal.c b/roms/skiboot/core/ipmi-opal.c new file mode 100644 index 000000000..cc45b409b --- /dev/null +++ b/roms/skiboot/core/ipmi-opal.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * IPMI OPAL calls + * + * Copyright 2013-2018 IBM Corp. + */ + +#include <stdlib.h> +#include <string.h> +#include <ipmi.h> +#include <lock.h> +#include <opal.h> +#include <device.h> +#include <ccan/list/list.h> + +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_TRACE, "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, __be64 *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_del_msg; + } + + if (interface != IPMI_DEFAULT_INTERFACE) { + prerror("IPMI: Invalid interface 0x%llx in opal_ipmi_recv\n", interface); + rc = OPAL_PARAMETER; + goto out_del_msg; + } + + if (be64_to_cpu(*msg_len) - sizeof(struct opal_ipmi_msg) < msg->resp_size + 1) { + rc = OPAL_RESOURCE; + goto out_del_msg; + } + + 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_TRACE, "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 = cpu_to_be64(msg->resp_size + sizeof(struct opal_ipmi_msg) + 1); + ipmi_free_msg(msg); + + return OPAL_SUCCESS; + +out_del_msg: + list_del(&msg->link); + if (list_empty(&msgq)) + opal_update_pending_evt(ipmi_backend->opal_event_ipmi_recv, 0); + ipmi_free_msg(msg); +out_unlock: + unlock(&msgq_lock); + return rc; +} + +void ipmi_opal_init(void) +{ + struct dt_node *opal_ipmi, *opal_event = NULL; + + 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)); + + if (proc_gen >= proc_gen_p9) + opal_event = dt_find_by_name(opal_node, "event"); + if (opal_event) + dt_add_property_cells(opal_ipmi, "interrupt-parent", + opal_event->phandle); + + opal_register(OPAL_IPMI_SEND, opal_ipmi_send, 3); + opal_register(OPAL_IPMI_RECV, opal_ipmi_recv, 3); +} |