193 lines
4.7 KiB
C
193 lines
4.7 KiB
C
// 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);
|
|
}
|
|
}
|
|
|