aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/opal-msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/opal-msg.c')
-rw-r--r--roms/skiboot/core/opal-msg.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/roms/skiboot/core/opal-msg.c b/roms/skiboot/core/opal-msg.c
new file mode 100644
index 000000000..65a2476b2
--- /dev/null
+++ b/roms/skiboot/core/opal-msg.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * OPAL Message queue between host and skiboot
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#define pr_fmt(fmt) "opalmsg: " fmt
+#include <skiboot.h>
+#include <opal-msg.h>
+#include <opal-api.h>
+#include <lock.h>
+
+#define OPAL_MAX_MSGS (OPAL_MSG_TYPE_MAX + OPAL_MAX_ASYNC_COMP - 1)
+
+struct opal_msg_entry {
+ struct list_node link;
+ void (*consumed)(void *data, int status);
+ bool extended;
+ void *data;
+ struct opal_msg msg;
+};
+
+static LIST_HEAD(msg_free_list);
+static LIST_HEAD(msg_pending_list);
+
+static struct lock opal_msg_lock = LOCK_UNLOCKED;
+
+int _opal_queue_msg(enum opal_msg_type msg_type, void *data,
+ void (*consumed)(void *data, int status),
+ size_t params_size, const void *params)
+{
+ struct opal_msg_entry *entry;
+ uint64_t entry_size;
+
+ if ((params_size + OPAL_MSG_HDR_SIZE) > OPAL_MSG_SIZE) {
+ prlog(PR_DEBUG, "param_size (0x%x) > opal_msg param size (0x%x)\n",
+ (u32)params_size, (u32)(OPAL_MSG_SIZE - OPAL_MSG_HDR_SIZE));
+ return OPAL_PARAMETER;
+ }
+
+ lock(&opal_msg_lock);
+
+ if (params_size > OPAL_MSG_FIXED_PARAMS_SIZE) {
+ entry_size = sizeof(struct opal_msg_entry) + params_size;
+ entry_size -= OPAL_MSG_FIXED_PARAMS_SIZE;
+ entry = zalloc(entry_size);
+ if (entry)
+ entry->extended = true;
+ } else {
+ entry = list_pop(&msg_free_list, struct opal_msg_entry, link);
+ if (!entry) {
+ prerror("No available node in the free list, allocating\n");
+ entry = zalloc(sizeof(struct opal_msg_entry));
+ }
+ }
+ if (!entry) {
+ prerror("Allocation failed\n");
+ unlock(&opal_msg_lock);
+ return OPAL_RESOURCE;
+ }
+
+ entry->consumed = consumed;
+ entry->data = data;
+ entry->msg.msg_type = cpu_to_be32(msg_type);
+ entry->msg.size = cpu_to_be32(params_size);
+ memcpy(entry->msg.params, params, params_size);
+
+ list_add_tail(&msg_pending_list, &entry->link);
+ opal_update_pending_evt(OPAL_EVENT_MSG_PENDING,
+ OPAL_EVENT_MSG_PENDING);
+ unlock(&opal_msg_lock);
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t opal_get_msg(uint64_t *buffer, uint64_t size)
+{
+ struct opal_msg_entry *entry;
+ void (*callback)(void *data, int status);
+ void *data;
+ uint64_t msg_size;
+ int rc = OPAL_SUCCESS;
+
+ if (size < sizeof(struct opal_msg) || !buffer)
+ return OPAL_PARAMETER;
+
+ if (!opal_addr_valid(buffer))
+ return OPAL_PARAMETER;
+
+ lock(&opal_msg_lock);
+
+ entry = list_pop(&msg_pending_list, struct opal_msg_entry, link);
+ if (!entry) {
+ unlock(&opal_msg_lock);
+ return OPAL_RESOURCE;
+ }
+
+ msg_size = OPAL_MSG_HDR_SIZE + be32_to_cpu(entry->msg.size);
+ if (size < msg_size) {
+ /* Send partial data to Linux */
+ prlog(PR_NOTICE, "Sending partial data [msg_type : 0x%x, "
+ "msg_size : 0x%x, buf_size : 0x%x]\n",
+ be32_to_cpu(entry->msg.msg_type),
+ (u32)msg_size, (u32)size);
+
+ entry->msg.size = cpu_to_be32(size - OPAL_MSG_HDR_SIZE);
+ msg_size = size;
+ rc = OPAL_PARTIAL;
+ }
+
+ memcpy((void *)buffer, (void *)&entry->msg, msg_size);
+ callback = entry->consumed;
+ data = entry->data;
+
+ if (entry->extended)
+ free(entry);
+ else
+ list_add(&msg_free_list, &entry->link);
+
+ if (list_empty(&msg_pending_list))
+ opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0);
+
+ unlock(&opal_msg_lock);
+
+ if (callback)
+ callback(data, rc);
+
+ return rc;
+}
+opal_call(OPAL_GET_MSG, opal_get_msg, 2);
+
+static int64_t opal_check_completion(uint64_t *buffer, uint64_t size,
+ uint64_t token)
+{
+ struct opal_msg_entry *entry, *next_entry;
+ void (*callback)(void *data, int status) = NULL;
+ int rc = OPAL_BUSY;
+ void *data = NULL;
+
+ if (!opal_addr_valid(buffer))
+ return OPAL_PARAMETER;
+
+ lock(&opal_msg_lock);
+ list_for_each_safe(&msg_pending_list, entry, next_entry, link) {
+ if (be32_to_cpu(entry->msg.msg_type) == OPAL_MSG_ASYNC_COMP &&
+ be64_to_cpu(entry->msg.params[0]) == token) {
+ list_del(&entry->link);
+ callback = entry->consumed;
+ data = entry->data;
+ list_add(&msg_free_list, &entry->link);
+ if (list_empty(&msg_pending_list))
+ opal_update_pending_evt(OPAL_EVENT_MSG_PENDING,
+ 0);
+ rc = OPAL_SUCCESS;
+ break;
+ }
+ }
+
+ if (rc == OPAL_SUCCESS && size >= sizeof(struct opal_msg))
+ memcpy(buffer, &entry->msg, sizeof(entry->msg));
+
+ unlock(&opal_msg_lock);
+
+ if (callback)
+ callback(data, OPAL_SUCCESS);
+
+ return rc;
+
+}
+opal_call(OPAL_CHECK_ASYNC_COMPLETION, opal_check_completion, 3);
+
+void opal_init_msg(void)
+{
+ struct opal_msg_entry *entry;
+ int i;
+
+ for (i = 0; i < OPAL_MAX_MSGS; i++, entry++) {
+ entry = zalloc(sizeof(*entry));
+ if (!entry)
+ goto err;
+ list_add_tail(&msg_free_list, &entry->link);
+ }
+ return;
+
+err:
+ for (; i > 0; i--) {
+ entry = list_pop(&msg_free_list, struct opal_msg_entry, link);
+ if (entry)
+ free(entry);
+ }
+}
+