diff options
Diffstat (limited to 'arch/arm/mach-apple/rtkit.c')
-rw-r--r-- | arch/arm/mach-apple/rtkit.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/arch/arm/mach-apple/rtkit.c b/arch/arm/mach-apple/rtkit.c new file mode 100644 index 00000000000..b8f4771e5e7 --- /dev/null +++ b/arch/arm/mach-apple/rtkit.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2021 Mark Kettenis <kettenis@openbsd.org> + * (C) Copyright 2021 Copyright The Asahi Linux Contributors + */ + +#include <mailbox.h> +#include <malloc.h> + +#include <asm/arch/rtkit.h> +#include <linux/apple-mailbox.h> +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/types.h> + +#define APPLE_RTKIT_EP_MGMT 0 +#define APPLE_RTKIT_EP_CRASHLOG 1 +#define APPLE_RTKIT_EP_SYSLOG 2 +#define APPLE_RTKIT_EP_DEBUG 3 +#define APPLE_RTKIT_EP_IOREPORT 4 +#define APPLE_RTKIT_EP_TRACEKIT 10 + +/* Messages for management endpoint. */ +#define APPLE_RTKIT_MGMT_TYPE GENMASK(59, 52) + +#define APPLE_RTKIT_MGMT_PWR_STATE GENMASK(15, 0) + +#define APPLE_RTKIT_MGMT_HELLO 1 +#define APPLE_RTKIT_MGMT_HELLO_REPLY 2 +#define APPLE_RTKIT_MGMT_HELLO_MINVER GENMASK(15, 0) +#define APPLE_RTKIT_MGMT_HELLO_MAXVER GENMASK(31, 16) + +#define APPLE_RTKIT_MGMT_STARTEP 5 +#define APPLE_RTKIT_MGMT_STARTEP_EP GENMASK(39, 32) +#define APPLE_RTKIT_MGMT_STARTEP_FLAG BIT(1) + +#define APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE 6 +#define APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK 7 + +#define APPLE_RTKIT_MGMT_EPMAP 8 +#define APPLE_RTKIT_MGMT_EPMAP_LAST BIT(51) +#define APPLE_RTKIT_MGMT_EPMAP_BASE GENMASK(34, 32) +#define APPLE_RTKIT_MGMT_EPMAP_BITMAP GENMASK(31, 0) + +#define APPLE_RTKIT_MGMT_EPMAP_REPLY 8 +#define APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE BIT(0) + +#define APPLE_RTKIT_MIN_SUPPORTED_VERSION 11 +#define APPLE_RTKIT_MAX_SUPPORTED_VERSION 12 + +/* Messages for internal endpoints. */ +#define APPLE_RTKIT_BUFFER_REQUEST 1 +#define APPLE_RTKIT_BUFFER_REQUEST_SIZE GENMASK(51, 44) +#define APPLE_RTKIT_BUFFER_REQUEST_IOVA GENMASK(41, 0) + +#define TIMEOUT_1SEC_US 1000000 + +struct apple_rtkit { + struct mbox_chan *chan; + void *cookie; + apple_rtkit_shmem_setup shmem_setup; + apple_rtkit_shmem_destroy shmem_destroy; + + struct apple_rtkit_buffer syslog_buffer; + struct apple_rtkit_buffer crashlog_buffer; + struct apple_rtkit_buffer ioreport_buffer; +}; + +struct apple_rtkit *apple_rtkit_init(struct mbox_chan *chan, void *cookie, + apple_rtkit_shmem_setup shmem_setup, + apple_rtkit_shmem_destroy shmem_destroy) +{ + struct apple_rtkit *rtk; + + rtk = calloc(sizeof(*rtk), 1); + if (!rtk) + return NULL; + + rtk->chan = chan; + rtk->cookie = cookie; + rtk->shmem_setup = shmem_setup; + rtk->shmem_destroy = shmem_destroy; + + return rtk; +} + +void apple_rtkit_free(struct apple_rtkit *rtk) +{ + if (rtk->shmem_destroy) { + if (rtk->syslog_buffer.buffer) + rtk->shmem_destroy(rtk->cookie, &rtk->syslog_buffer); + if (rtk->crashlog_buffer.buffer) + rtk->shmem_destroy(rtk->cookie, &rtk->crashlog_buffer); + if (rtk->ioreport_buffer.buffer) + rtk->shmem_destroy(rtk->cookie, &rtk->ioreport_buffer); + } + free(rtk); +} + +static int rtkit_handle_buf_req(struct apple_rtkit *rtk, int endpoint, struct apple_mbox_msg *msg) +{ + struct apple_rtkit_buffer *buf; + size_t num_4kpages; + int ret; + + num_4kpages = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_SIZE, msg->msg0); + + if (num_4kpages == 0) { + printf("%s: unexpected request for buffer without size\n", __func__); + return -1; + } + + switch (endpoint) { + case APPLE_RTKIT_EP_CRASHLOG: + buf = &rtk->crashlog_buffer; + break; + case APPLE_RTKIT_EP_SYSLOG: + buf = &rtk->syslog_buffer; + break; + case APPLE_RTKIT_EP_IOREPORT: + buf = &rtk->ioreport_buffer; + break; + default: + printf("%s: unexpected endpoint %d\n", __func__, endpoint); + return -1; + } + + buf->dva = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_IOVA, msg->msg0); + buf->size = num_4kpages << 12; + buf->is_mapped = false; + + if (rtk->shmem_setup) { + ret = rtk->shmem_setup(rtk->cookie, buf); + if (ret < 0) { + printf("%s: shmen_setup failed for endpoint %d\n", __func__, + endpoint); + return ret; + } + } + + if (!buf->is_mapped) { + msg->msg0 = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_BUFFER_REQUEST) | + FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_SIZE, num_4kpages) | + FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_IOVA, buf->dva); + msg->msg1 = endpoint; + + return mbox_send(rtk->chan, msg); + } + + return 0; +} + +int apple_rtkit_boot(struct apple_rtkit *rtk) +{ + struct apple_mbox_msg msg; + int endpoints[256]; + int nendpoints = 0; + int endpoint; + int min_ver, max_ver, want_ver; + int msgtype, pwrstate; + u64 reply; + u32 bitmap, base; + int i, ret; + + /* Wakup the IOP. */ + msg.msg0 = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE) | + FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_ON); + msg.msg1 = APPLE_RTKIT_EP_MGMT; + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + + /* Wait for protocol version negotiation message. */ + ret = mbox_recv(rtk->chan, &msg, TIMEOUT_1SEC_US); + if (ret < 0) + return ret; + + endpoint = msg.msg1; + msgtype = FIELD_GET(APPLE_RTKIT_MGMT_TYPE, msg.msg0); + if (endpoint != APPLE_RTKIT_EP_MGMT) { + printf("%s: unexpected endpoint %d\n", __func__, endpoint); + return -EINVAL; + } + if (msgtype != APPLE_RTKIT_MGMT_HELLO) { + printf("%s: unexpected message type %d\n", __func__, msgtype); + return -EINVAL; + } + + min_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MINVER, msg.msg0); + max_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MAXVER, msg.msg0); + want_ver = min(APPLE_RTKIT_MAX_SUPPORTED_VERSION, max_ver); + + if (min_ver > APPLE_RTKIT_MAX_SUPPORTED_VERSION) { + printf("%s: firmware min version %d is too new\n", + __func__, min_ver); + return -ENOTSUPP; + } + + if (max_ver < APPLE_RTKIT_MIN_SUPPORTED_VERSION) { + printf("%s: firmware max version %d is too old\n", + __func__, max_ver); + return -ENOTSUPP; + } + + /* Ack version. */ + msg.msg0 = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_MGMT_HELLO_REPLY) | + FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MINVER, want_ver) | + FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MAXVER, want_ver); + msg.msg1 = APPLE_RTKIT_EP_MGMT; + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + +wait_epmap: + /* Wait for endpoint map message. */ + ret = mbox_recv(rtk->chan, &msg, TIMEOUT_1SEC_US); + if (ret < 0) + return ret; + + endpoint = msg.msg1; + msgtype = FIELD_GET(APPLE_RTKIT_MGMT_TYPE, msg.msg0); + if (endpoint != APPLE_RTKIT_EP_MGMT) { + printf("%s: unexpected endpoint %d\n", __func__, endpoint); + return -EINVAL; + } + if (msgtype != APPLE_RTKIT_MGMT_EPMAP) { + printf("%s: unexpected message type %d\n", __func__, msgtype); + return -EINVAL; + } + + bitmap = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BITMAP, msg.msg0); + base = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BASE, msg.msg0); + for (i = 0; i < 32; i++) { + if (bitmap & (1U << i)) + endpoints[nendpoints++] = base * 32 + i; + } + + /* Ack endpoint map. */ + reply = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_MGMT_EPMAP_REPLY) | + FIELD_PREP(APPLE_RTKIT_MGMT_EPMAP_BASE, base); + if (msg.msg0 & APPLE_RTKIT_MGMT_EPMAP_LAST) + reply |= APPLE_RTKIT_MGMT_EPMAP_LAST; + else + reply |= APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE; + msg.msg0 = reply; + msg.msg1 = APPLE_RTKIT_EP_MGMT; + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + + if (reply & APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE) + goto wait_epmap; + + for (i = 0; i < nendpoints; i++) { + /* Start only necessary endpoints. The syslog endpoint is + * particularly noisy and its message can't easily be handled + * within U-Boot. + */ + switch (endpoints[i]) { + case APPLE_RTKIT_EP_MGMT: + case APPLE_RTKIT_EP_SYSLOG: + case APPLE_RTKIT_EP_DEBUG: + case APPLE_RTKIT_EP_TRACEKIT: + continue; + default: + break; + } + + /* Request endpoint. */ + msg.msg0 = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_MGMT_STARTEP) | + FIELD_PREP(APPLE_RTKIT_MGMT_STARTEP_EP, endpoints[i]) | + APPLE_RTKIT_MGMT_STARTEP_FLAG; + msg.msg1 = APPLE_RTKIT_EP_MGMT; + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + } + + pwrstate = APPLE_RTKIT_PWR_STATE_SLEEP; + while (pwrstate != APPLE_RTKIT_PWR_STATE_ON) { + ret = mbox_recv(rtk->chan, &msg, TIMEOUT_1SEC_US); + if (ret < 0) + return ret; + + endpoint = msg.msg1; + msgtype = FIELD_GET(APPLE_RTKIT_MGMT_TYPE, msg.msg0); + + if (endpoint == APPLE_RTKIT_EP_CRASHLOG || + endpoint == APPLE_RTKIT_EP_SYSLOG || + endpoint == APPLE_RTKIT_EP_IOREPORT) { + if (msgtype == APPLE_RTKIT_BUFFER_REQUEST) { + ret = rtkit_handle_buf_req(rtk, endpoint, &msg); + if (ret < 0) + return ret; + continue; + } + } + + if (endpoint == APPLE_RTKIT_EP_IOREPORT) { + // these two messages have to be ack-ed for proper startup + if (msgtype == 0xc || msgtype == 0x8) { + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + continue; + } + } + + if (endpoint != APPLE_RTKIT_EP_MGMT) { + printf("%s: unexpected endpoint %d\n", __func__, endpoint); + return -EINVAL; + } + if (msgtype != APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK) { + printf("%s: unexpected message type %d\n", __func__, msgtype); + return -EINVAL; + } + + pwrstate = FIELD_GET(APPLE_RTKIT_MGMT_PWR_STATE, msg.msg0); + } + + return 0; +} + +int apple_rtkit_shutdown(struct apple_rtkit *rtk, int pwrstate) +{ + struct apple_mbox_msg msg; + int ret; + + msg.msg0 = FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE) | + FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, pwrstate); + msg.msg1 = APPLE_RTKIT_EP_MGMT; + ret = mbox_send(rtk->chan, &msg); + if (ret < 0) + return ret; + + ret = mbox_recv(rtk->chan, &msg, TIMEOUT_1SEC_US); + if (ret < 0) + return ret; + + return 0; +} |