2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00
linux/drivers/soc/apple/rtkit.c
Janne Grunau e210227f02 soc: apple: rtkit: Cut syslog messages after the first '\0'
Certain messages from DCP contain NUL bytes in the random data after the
NUL terminated syslog message. Since the syslog message ends with '\n'
this results in a dev_info() message terminated with two newlines and an
empty printed line in the kernel log.

Signed-off-by: Janne Grunau <j@jannau.net>
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Link: https://lore.kernel.org/r/20250226-apple-soc-misc-v2-4-c3ec37f9021b@svenpeter.dev
Signed-off-by: Sven Peter <sven@svenpeter.dev>
2025-02-28 21:36:45 +00:00

965 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Apple RTKit IPC library
* Copyright (C) The Asahi Linux Contributors
*/
#include "rtkit-internal.h"
enum {
APPLE_RTKIT_PWR_STATE_OFF = 0x00, /* power off, cannot be restarted */
APPLE_RTKIT_PWR_STATE_SLEEP = 0x01, /* sleeping, can be restarted */
APPLE_RTKIT_PWR_STATE_IDLE = 0x201, /* sleeping, retain state */
APPLE_RTKIT_PWR_STATE_QUIESCED = 0x10, /* running but no communication */
APPLE_RTKIT_PWR_STATE_ON = 0x20, /* normal operating state */
APPLE_RTKIT_PWR_STATE_INIT = 0x220, /* init after starting the coproc */
};
enum {
APPLE_RTKIT_EP_MGMT = 0,
APPLE_RTKIT_EP_CRASHLOG = 1,
APPLE_RTKIT_EP_SYSLOG = 2,
APPLE_RTKIT_EP_DEBUG = 3,
APPLE_RTKIT_EP_IOREPORT = 4,
APPLE_RTKIT_EP_OSLOG = 8,
};
#define APPLE_RTKIT_MGMT_TYPE GENMASK_ULL(59, 52)
enum {
APPLE_RTKIT_MGMT_HELLO = 1,
APPLE_RTKIT_MGMT_HELLO_REPLY = 2,
APPLE_RTKIT_MGMT_STARTEP = 5,
APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE = 6,
APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK = 7,
APPLE_RTKIT_MGMT_EPMAP = 8,
APPLE_RTKIT_MGMT_EPMAP_REPLY = 8,
APPLE_RTKIT_MGMT_SET_AP_PWR_STATE = 0xb,
APPLE_RTKIT_MGMT_SET_AP_PWR_STATE_ACK = 0xb,
};
#define APPLE_RTKIT_MGMT_HELLO_MINVER GENMASK_ULL(15, 0)
#define APPLE_RTKIT_MGMT_HELLO_MAXVER GENMASK_ULL(31, 16)
#define APPLE_RTKIT_MGMT_EPMAP_LAST BIT_ULL(51)
#define APPLE_RTKIT_MGMT_EPMAP_BASE GENMASK_ULL(34, 32)
#define APPLE_RTKIT_MGMT_EPMAP_BITMAP GENMASK_ULL(31, 0)
#define APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE BIT_ULL(0)
#define APPLE_RTKIT_MGMT_STARTEP_EP GENMASK_ULL(39, 32)
#define APPLE_RTKIT_MGMT_STARTEP_FLAG BIT_ULL(1)
#define APPLE_RTKIT_MGMT_PWR_STATE GENMASK_ULL(15, 0)
#define APPLE_RTKIT_CRASHLOG_CRASH 1
#define APPLE_RTKIT_BUFFER_REQUEST 1
#define APPLE_RTKIT_BUFFER_REQUEST_SIZE GENMASK_ULL(51, 44)
#define APPLE_RTKIT_BUFFER_REQUEST_IOVA GENMASK_ULL(43, 0)
#define APPLE_RTKIT_SYSLOG_TYPE GENMASK_ULL(59, 52)
#define APPLE_RTKIT_SYSLOG_LOG 5
#define APPLE_RTKIT_SYSLOG_INIT 8
#define APPLE_RTKIT_SYSLOG_N_ENTRIES GENMASK_ULL(7, 0)
#define APPLE_RTKIT_SYSLOG_MSG_SIZE GENMASK_ULL(31, 24)
#define APPLE_RTKIT_OSLOG_TYPE GENMASK_ULL(63, 56)
#define APPLE_RTKIT_OSLOG_BUFFER_REQUEST 1
#define APPLE_RTKIT_OSLOG_SIZE GENMASK_ULL(55, 36)
#define APPLE_RTKIT_OSLOG_IOVA GENMASK_ULL(35, 0)
#define APPLE_RTKIT_MIN_SUPPORTED_VERSION 11
#define APPLE_RTKIT_MAX_SUPPORTED_VERSION 12
struct apple_rtkit_rx_work {
struct apple_rtkit *rtk;
u8 ep;
u64 msg;
struct work_struct work;
};
bool apple_rtkit_is_running(struct apple_rtkit *rtk)
{
if (rtk->crashed)
return false;
if ((rtk->iop_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON)
return false;
if ((rtk->ap_power_state & 0xff) != APPLE_RTKIT_PWR_STATE_ON)
return false;
return true;
}
EXPORT_SYMBOL_GPL(apple_rtkit_is_running);
bool apple_rtkit_is_crashed(struct apple_rtkit *rtk)
{
return rtk->crashed;
}
EXPORT_SYMBOL_GPL(apple_rtkit_is_crashed);
static int apple_rtkit_management_send(struct apple_rtkit *rtk, u8 type,
u64 msg)
{
int ret;
msg &= ~APPLE_RTKIT_MGMT_TYPE;
msg |= FIELD_PREP(APPLE_RTKIT_MGMT_TYPE, type);
ret = apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_MGMT, msg, NULL, false);
if (ret)
dev_err(rtk->dev, "RTKit: Failed to send management message: %d\n", ret);
return ret;
}
static void apple_rtkit_management_rx_hello(struct apple_rtkit *rtk, u64 msg)
{
u64 reply;
int min_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MINVER, msg);
int max_ver = FIELD_GET(APPLE_RTKIT_MGMT_HELLO_MAXVER, msg);
int want_ver = min(APPLE_RTKIT_MAX_SUPPORTED_VERSION, max_ver);
dev_dbg(rtk->dev, "RTKit: Min ver %d, max ver %d\n", min_ver, max_ver);
if (min_ver > APPLE_RTKIT_MAX_SUPPORTED_VERSION) {
dev_err(rtk->dev, "RTKit: Firmware min version %d is too new\n",
min_ver);
goto abort_boot;
}
if (max_ver < APPLE_RTKIT_MIN_SUPPORTED_VERSION) {
dev_err(rtk->dev, "RTKit: Firmware max version %d is too old\n",
max_ver);
goto abort_boot;
}
dev_info(rtk->dev, "RTKit: Initializing (protocol version %d)\n",
want_ver);
rtk->version = want_ver;
reply = FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MINVER, want_ver);
reply |= FIELD_PREP(APPLE_RTKIT_MGMT_HELLO_MAXVER, want_ver);
apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_HELLO_REPLY, reply);
return;
abort_boot:
rtk->boot_result = -EINVAL;
complete_all(&rtk->epmap_completion);
}
static void apple_rtkit_management_rx_epmap(struct apple_rtkit *rtk, u64 msg)
{
int i, ep;
u64 reply;
unsigned long bitmap = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BITMAP, msg);
u32 base = FIELD_GET(APPLE_RTKIT_MGMT_EPMAP_BASE, msg);
dev_dbg(rtk->dev,
"RTKit: received endpoint bitmap 0x%lx with base 0x%x\n",
bitmap, base);
for_each_set_bit(i, &bitmap, 32) {
ep = 32 * base + i;
dev_dbg(rtk->dev, "RTKit: Discovered endpoint 0x%02x\n", ep);
set_bit(ep, rtk->endpoints);
}
reply = FIELD_PREP(APPLE_RTKIT_MGMT_EPMAP_BASE, base);
if (msg & APPLE_RTKIT_MGMT_EPMAP_LAST)
reply |= APPLE_RTKIT_MGMT_EPMAP_LAST;
else
reply |= APPLE_RTKIT_MGMT_EPMAP_REPLY_MORE;
apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_EPMAP_REPLY, reply);
if (!(msg & APPLE_RTKIT_MGMT_EPMAP_LAST))
return;
for_each_set_bit(ep, rtk->endpoints, APPLE_RTKIT_APP_ENDPOINT_START) {
switch (ep) {
/* the management endpoint is started by default */
case APPLE_RTKIT_EP_MGMT:
break;
/* without starting these RTKit refuses to boot */
case APPLE_RTKIT_EP_SYSLOG:
case APPLE_RTKIT_EP_CRASHLOG:
case APPLE_RTKIT_EP_DEBUG:
case APPLE_RTKIT_EP_IOREPORT:
case APPLE_RTKIT_EP_OSLOG:
dev_dbg(rtk->dev,
"RTKit: Starting system endpoint 0x%02x\n", ep);
apple_rtkit_start_ep(rtk, ep);
break;
default:
dev_warn(rtk->dev,
"RTKit: Unknown system endpoint: 0x%02x\n",
ep);
}
}
rtk->boot_result = 0;
complete_all(&rtk->epmap_completion);
}
static void apple_rtkit_management_rx_iop_pwr_ack(struct apple_rtkit *rtk,
u64 msg)
{
unsigned int new_state = FIELD_GET(APPLE_RTKIT_MGMT_PWR_STATE, msg);
dev_dbg(rtk->dev, "RTKit: IOP power state transition: 0x%x -> 0x%x\n",
rtk->iop_power_state, new_state);
rtk->iop_power_state = new_state;
complete_all(&rtk->iop_pwr_ack_completion);
}
static void apple_rtkit_management_rx_ap_pwr_ack(struct apple_rtkit *rtk,
u64 msg)
{
unsigned int new_state = FIELD_GET(APPLE_RTKIT_MGMT_PWR_STATE, msg);
dev_dbg(rtk->dev, "RTKit: AP power state transition: 0x%x -> 0x%x\n",
rtk->ap_power_state, new_state);
rtk->ap_power_state = new_state;
complete_all(&rtk->ap_pwr_ack_completion);
}
static void apple_rtkit_management_rx(struct apple_rtkit *rtk, u64 msg)
{
u8 type = FIELD_GET(APPLE_RTKIT_MGMT_TYPE, msg);
switch (type) {
case APPLE_RTKIT_MGMT_HELLO:
apple_rtkit_management_rx_hello(rtk, msg);
break;
case APPLE_RTKIT_MGMT_EPMAP:
apple_rtkit_management_rx_epmap(rtk, msg);
break;
case APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE_ACK:
apple_rtkit_management_rx_iop_pwr_ack(rtk, msg);
break;
case APPLE_RTKIT_MGMT_SET_AP_PWR_STATE_ACK:
apple_rtkit_management_rx_ap_pwr_ack(rtk, msg);
break;
default:
dev_warn(
rtk->dev,
"RTKit: unknown management message: 0x%llx (type: 0x%02x)\n",
msg, type);
}
}
static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk,
struct apple_rtkit_shmem *buffer,
u8 ep, u64 msg)
{
u64 reply;
int err;
/* The different size vs. IOVA shifts look odd but are indeed correct this way */
if (ep == APPLE_RTKIT_EP_OSLOG) {
buffer->size = FIELD_GET(APPLE_RTKIT_OSLOG_SIZE, msg);
buffer->iova = FIELD_GET(APPLE_RTKIT_OSLOG_IOVA, msg) << 12;
} else {
buffer->size = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_SIZE, msg) << 12;
buffer->iova = FIELD_GET(APPLE_RTKIT_BUFFER_REQUEST_IOVA, msg);
}
buffer->buffer = NULL;
buffer->iomem = NULL;
buffer->is_mapped = false;
dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n",
buffer->size, &buffer->iova);
if (buffer->iova &&
(!rtk->ops->shmem_setup || !rtk->ops->shmem_destroy)) {
err = -EINVAL;
goto error;
}
if (rtk->ops->shmem_setup) {
err = rtk->ops->shmem_setup(rtk->cookie, buffer);
if (err)
goto error;
} else {
buffer->buffer = dma_alloc_coherent(rtk->dev, buffer->size,
&buffer->iova, GFP_KERNEL);
if (!buffer->buffer) {
err = -ENOMEM;
goto error;
}
}
if (!buffer->is_mapped) {
/* oslog uses different fields and needs a shifted IOVA instead of size */
if (ep == APPLE_RTKIT_EP_OSLOG) {
reply = FIELD_PREP(APPLE_RTKIT_OSLOG_TYPE,
APPLE_RTKIT_OSLOG_BUFFER_REQUEST);
reply |= FIELD_PREP(APPLE_RTKIT_OSLOG_SIZE, buffer->size);
reply |= FIELD_PREP(APPLE_RTKIT_OSLOG_IOVA,
buffer->iova >> 12);
} else {
reply = FIELD_PREP(APPLE_RTKIT_SYSLOG_TYPE,
APPLE_RTKIT_BUFFER_REQUEST);
reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_SIZE,
buffer->size >> 12);
reply |= FIELD_PREP(APPLE_RTKIT_BUFFER_REQUEST_IOVA,
buffer->iova);
}
apple_rtkit_send_message(rtk, ep, reply, NULL, false);
}
return 0;
error:
dev_err(rtk->dev, "RTKit: failed buffer request for 0x%zx bytes (%d)\n",
buffer->size, err);
buffer->buffer = NULL;
buffer->iomem = NULL;
buffer->iova = 0;
buffer->size = 0;
buffer->is_mapped = false;
return err;
}
static void apple_rtkit_free_buffer(struct apple_rtkit *rtk,
struct apple_rtkit_shmem *bfr)
{
if (bfr->size == 0)
return;
if (rtk->ops->shmem_destroy)
rtk->ops->shmem_destroy(rtk->cookie, bfr);
else if (bfr->buffer)
dma_free_coherent(rtk->dev, bfr->size, bfr->buffer, bfr->iova);
bfr->buffer = NULL;
bfr->iomem = NULL;
bfr->iova = 0;
bfr->size = 0;
bfr->is_mapped = false;
}
static void apple_rtkit_memcpy(struct apple_rtkit *rtk, void *dst,
struct apple_rtkit_shmem *bfr, size_t offset,
size_t len)
{
if (bfr->iomem)
memcpy_fromio(dst, bfr->iomem + offset, len);
else
memcpy(dst, bfr->buffer + offset, len);
}
static void apple_rtkit_crashlog_rx(struct apple_rtkit *rtk, u64 msg)
{
u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg);
u8 *bfr;
if (type != APPLE_RTKIT_CRASHLOG_CRASH) {
dev_warn(rtk->dev, "RTKit: Unknown crashlog message: %llx\n",
msg);
return;
}
if (!rtk->crashlog_buffer.size) {
apple_rtkit_common_rx_get_buffer(rtk, &rtk->crashlog_buffer,
APPLE_RTKIT_EP_CRASHLOG, msg);
return;
}
dev_err(rtk->dev, "RTKit: co-processor has crashed\n");
/*
* create a shadow copy here to make sure the co-processor isn't able
* to change the log while we're dumping it. this also ensures
* the buffer is in normal memory and not iomem for e.g. the SMC
*/
bfr = kzalloc(rtk->crashlog_buffer.size, GFP_KERNEL);
if (bfr) {
apple_rtkit_memcpy(rtk, bfr, &rtk->crashlog_buffer, 0,
rtk->crashlog_buffer.size);
apple_rtkit_crashlog_dump(rtk, bfr, rtk->crashlog_buffer.size);
} else {
dev_err(rtk->dev,
"RTKit: Couldn't allocate crashlog shadow buffer\n");
}
rtk->crashed = true;
if (rtk->ops->crashed)
rtk->ops->crashed(rtk->cookie, bfr, rtk->crashlog_buffer.size);
kfree(bfr);
}
static void apple_rtkit_ioreport_rx(struct apple_rtkit *rtk, u64 msg)
{
u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg);
switch (type) {
case APPLE_RTKIT_BUFFER_REQUEST:
apple_rtkit_common_rx_get_buffer(rtk, &rtk->ioreport_buffer,
APPLE_RTKIT_EP_IOREPORT, msg);
break;
/* unknown, must be ACKed or the co-processor will hang */
case 0x8:
case 0xc:
apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_IOREPORT, msg,
NULL, false);
break;
default:
dev_warn(rtk->dev, "RTKit: Unknown ioreport message: %llx\n",
msg);
}
}
static void apple_rtkit_syslog_rx_init(struct apple_rtkit *rtk, u64 msg)
{
rtk->syslog_n_entries = FIELD_GET(APPLE_RTKIT_SYSLOG_N_ENTRIES, msg);
rtk->syslog_msg_size = FIELD_GET(APPLE_RTKIT_SYSLOG_MSG_SIZE, msg);
rtk->syslog_msg_buffer = kzalloc(rtk->syslog_msg_size, GFP_KERNEL);
dev_dbg(rtk->dev,
"RTKit: syslog initialized: entries: %zd, msg_size: %zd\n",
rtk->syslog_n_entries, rtk->syslog_msg_size);
}
static bool should_crop_syslog_char(char c)
{
return c == '\n' || c == '\r' || c == ' ' || c == '\0';
}
static void apple_rtkit_syslog_rx_log(struct apple_rtkit *rtk, u64 msg)
{
u8 idx = msg & 0xff;
char log_context[24];
size_t entry_size = 0x20 + rtk->syslog_msg_size;
int msglen;
if (!rtk->syslog_msg_buffer) {
dev_warn(
rtk->dev,
"RTKit: received syslog message but no syslog_msg_buffer\n");
goto done;
}
if (!rtk->syslog_buffer.size) {
dev_warn(
rtk->dev,
"RTKit: received syslog message but syslog_buffer.size is zero\n");
goto done;
}
if (!rtk->syslog_buffer.buffer && !rtk->syslog_buffer.iomem) {
dev_warn(
rtk->dev,
"RTKit: received syslog message but no syslog_buffer.buffer or syslog_buffer.iomem\n");
goto done;
}
if (idx > rtk->syslog_n_entries) {
dev_warn(rtk->dev, "RTKit: syslog index %d out of range\n",
idx);
goto done;
}
apple_rtkit_memcpy(rtk, log_context, &rtk->syslog_buffer,
idx * entry_size + 8, sizeof(log_context));
apple_rtkit_memcpy(rtk, rtk->syslog_msg_buffer, &rtk->syslog_buffer,
idx * entry_size + 8 + sizeof(log_context),
rtk->syslog_msg_size);
log_context[sizeof(log_context) - 1] = 0;
msglen = strnlen(rtk->syslog_msg_buffer, rtk->syslog_msg_size - 1);
while (msglen > 0 &&
should_crop_syslog_char(rtk->syslog_msg_buffer[msglen - 1]))
msglen--;
rtk->syslog_msg_buffer[msglen] = 0;
dev_info(rtk->dev, "RTKit: syslog message: %s: %s\n", log_context,
rtk->syslog_msg_buffer);
done:
apple_rtkit_send_message(rtk, APPLE_RTKIT_EP_SYSLOG, msg, NULL, false);
}
static void apple_rtkit_syslog_rx(struct apple_rtkit *rtk, u64 msg)
{
u8 type = FIELD_GET(APPLE_RTKIT_SYSLOG_TYPE, msg);
switch (type) {
case APPLE_RTKIT_BUFFER_REQUEST:
apple_rtkit_common_rx_get_buffer(rtk, &rtk->syslog_buffer,
APPLE_RTKIT_EP_SYSLOG, msg);
break;
case APPLE_RTKIT_SYSLOG_INIT:
apple_rtkit_syslog_rx_init(rtk, msg);
break;
case APPLE_RTKIT_SYSLOG_LOG:
apple_rtkit_syslog_rx_log(rtk, msg);
break;
default:
dev_warn(rtk->dev, "RTKit: Unknown syslog message: %llx\n",
msg);
}
}
static void apple_rtkit_oslog_rx(struct apple_rtkit *rtk, u64 msg)
{
u8 type = FIELD_GET(APPLE_RTKIT_OSLOG_TYPE, msg);
switch (type) {
case APPLE_RTKIT_OSLOG_BUFFER_REQUEST:
apple_rtkit_common_rx_get_buffer(rtk, &rtk->oslog_buffer,
APPLE_RTKIT_EP_OSLOG, msg);
break;
default:
dev_warn(rtk->dev, "RTKit: Unknown oslog message: %llx\n",
msg);
}
}
static void apple_rtkit_rx_work(struct work_struct *work)
{
struct apple_rtkit_rx_work *rtk_work =
container_of(work, struct apple_rtkit_rx_work, work);
struct apple_rtkit *rtk = rtk_work->rtk;
switch (rtk_work->ep) {
case APPLE_RTKIT_EP_MGMT:
apple_rtkit_management_rx(rtk, rtk_work->msg);
break;
case APPLE_RTKIT_EP_CRASHLOG:
apple_rtkit_crashlog_rx(rtk, rtk_work->msg);
break;
case APPLE_RTKIT_EP_SYSLOG:
apple_rtkit_syslog_rx(rtk, rtk_work->msg);
break;
case APPLE_RTKIT_EP_IOREPORT:
apple_rtkit_ioreport_rx(rtk, rtk_work->msg);
break;
case APPLE_RTKIT_EP_OSLOG:
apple_rtkit_oslog_rx(rtk, rtk_work->msg);
break;
case APPLE_RTKIT_APP_ENDPOINT_START ... 0xff:
if (rtk->ops->recv_message)
rtk->ops->recv_message(rtk->cookie, rtk_work->ep,
rtk_work->msg);
else
dev_warn(
rtk->dev,
"Received unexpected message to EP%02d: %llx\n",
rtk_work->ep, rtk_work->msg);
break;
default:
dev_warn(rtk->dev,
"RTKit: message to unknown endpoint %02x: %llx\n",
rtk_work->ep, rtk_work->msg);
}
kfree(rtk_work);
}
static void apple_rtkit_rx(struct apple_mbox *mbox, struct apple_mbox_msg msg,
void *cookie)
{
struct apple_rtkit *rtk = cookie;
struct apple_rtkit_rx_work *work;
u8 ep = msg.msg1;
/*
* The message was read from a MMIO FIFO and we have to make
* sure all reads from buffers sent with that message happen
* afterwards.
*/
dma_rmb();
if (!test_bit(ep, rtk->endpoints))
dev_warn(rtk->dev,
"RTKit: Message to undiscovered endpoint 0x%02x\n",
ep);
if (ep >= APPLE_RTKIT_APP_ENDPOINT_START &&
rtk->ops->recv_message_early &&
rtk->ops->recv_message_early(rtk->cookie, ep, msg.msg0))
return;
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work)
return;
work->rtk = rtk;
work->ep = ep;
work->msg = msg.msg0;
INIT_WORK(&work->work, apple_rtkit_rx_work);
queue_work(rtk->wq, &work->work);
}
int apple_rtkit_send_message(struct apple_rtkit *rtk, u8 ep, u64 message,
struct completion *completion, bool atomic)
{
struct apple_mbox_msg msg = {
.msg0 = message,
.msg1 = ep,
};
if (rtk->crashed) {
dev_warn(rtk->dev,
"RTKit: Device is crashed, cannot send message\n");
return -EINVAL;
}
if (ep >= APPLE_RTKIT_APP_ENDPOINT_START &&
!apple_rtkit_is_running(rtk)) {
dev_warn(rtk->dev,
"RTKit: Endpoint 0x%02x is not running, cannot send message\n", ep);
return -EINVAL;
}
/*
* The message will be sent with a MMIO write. We need the barrier
* here to ensure any previous writes to buffers are visible to the
* device before that MMIO write happens.
*/
dma_wmb();
return apple_mbox_send(rtk->mbox, msg, atomic);
}
EXPORT_SYMBOL_GPL(apple_rtkit_send_message);
int apple_rtkit_poll(struct apple_rtkit *rtk)
{
return apple_mbox_poll(rtk->mbox);
}
EXPORT_SYMBOL_GPL(apple_rtkit_poll);
int apple_rtkit_start_ep(struct apple_rtkit *rtk, u8 endpoint)
{
u64 msg;
if (!test_bit(endpoint, rtk->endpoints))
return -EINVAL;
if (endpoint >= APPLE_RTKIT_APP_ENDPOINT_START &&
!apple_rtkit_is_running(rtk))
return -EINVAL;
msg = FIELD_PREP(APPLE_RTKIT_MGMT_STARTEP_EP, endpoint);
msg |= APPLE_RTKIT_MGMT_STARTEP_FLAG;
apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_STARTEP, msg);
return 0;
}
EXPORT_SYMBOL_GPL(apple_rtkit_start_ep);
struct apple_rtkit *apple_rtkit_init(struct device *dev, void *cookie,
const char *mbox_name, int mbox_idx,
const struct apple_rtkit_ops *ops)
{
struct apple_rtkit *rtk;
int ret;
if (!ops)
return ERR_PTR(-EINVAL);
rtk = kzalloc(sizeof(*rtk), GFP_KERNEL);
if (!rtk)
return ERR_PTR(-ENOMEM);
rtk->dev = dev;
rtk->cookie = cookie;
rtk->ops = ops;
init_completion(&rtk->epmap_completion);
init_completion(&rtk->iop_pwr_ack_completion);
init_completion(&rtk->ap_pwr_ack_completion);
bitmap_zero(rtk->endpoints, APPLE_RTKIT_MAX_ENDPOINTS);
set_bit(APPLE_RTKIT_EP_MGMT, rtk->endpoints);
if (mbox_name)
rtk->mbox = apple_mbox_get_byname(dev, mbox_name);
else
rtk->mbox = apple_mbox_get(dev, mbox_idx);
if (IS_ERR(rtk->mbox)) {
ret = PTR_ERR(rtk->mbox);
goto free_rtk;
}
rtk->mbox->rx = apple_rtkit_rx;
rtk->mbox->cookie = rtk;
rtk->wq = alloc_ordered_workqueue("rtkit-%s", WQ_HIGHPRI | WQ_MEM_RECLAIM,
dev_name(rtk->dev));
if (!rtk->wq) {
ret = -ENOMEM;
goto free_rtk;
}
ret = apple_mbox_start(rtk->mbox);
if (ret)
goto destroy_wq;
return rtk;
destroy_wq:
destroy_workqueue(rtk->wq);
free_rtk:
kfree(rtk);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(apple_rtkit_init);
static int apple_rtkit_wait_for_completion(struct completion *c)
{
long t;
t = wait_for_completion_interruptible_timeout(c,
msecs_to_jiffies(1000));
if (t < 0)
return t;
else if (t == 0)
return -ETIME;
else
return 0;
}
int apple_rtkit_reinit(struct apple_rtkit *rtk)
{
/* make sure we don't handle any messages while reinitializing */
apple_mbox_stop(rtk->mbox);
flush_workqueue(rtk->wq);
apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer);
apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer);
apple_rtkit_free_buffer(rtk, &rtk->oslog_buffer);
apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer);
kfree(rtk->syslog_msg_buffer);
rtk->syslog_msg_buffer = NULL;
rtk->syslog_n_entries = 0;
rtk->syslog_msg_size = 0;
bitmap_zero(rtk->endpoints, APPLE_RTKIT_MAX_ENDPOINTS);
set_bit(APPLE_RTKIT_EP_MGMT, rtk->endpoints);
reinit_completion(&rtk->epmap_completion);
reinit_completion(&rtk->iop_pwr_ack_completion);
reinit_completion(&rtk->ap_pwr_ack_completion);
rtk->crashed = false;
rtk->iop_power_state = APPLE_RTKIT_PWR_STATE_OFF;
rtk->ap_power_state = APPLE_RTKIT_PWR_STATE_OFF;
return apple_mbox_start(rtk->mbox);
}
EXPORT_SYMBOL_GPL(apple_rtkit_reinit);
static int apple_rtkit_set_ap_power_state(struct apple_rtkit *rtk,
unsigned int state)
{
u64 msg;
int ret;
reinit_completion(&rtk->ap_pwr_ack_completion);
msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state);
ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_AP_PWR_STATE,
msg);
if (ret)
return ret;
ret = apple_rtkit_wait_for_completion(&rtk->ap_pwr_ack_completion);
if (ret)
return ret;
if (rtk->ap_power_state != state)
return -EINVAL;
return 0;
}
static int apple_rtkit_set_iop_power_state(struct apple_rtkit *rtk,
unsigned int state)
{
u64 msg;
int ret;
reinit_completion(&rtk->iop_pwr_ack_completion);
msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, state);
ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE,
msg);
if (ret)
return ret;
ret = apple_rtkit_wait_for_completion(&rtk->iop_pwr_ack_completion);
if (ret)
return ret;
if (rtk->iop_power_state != state)
return -EINVAL;
return 0;
}
int apple_rtkit_boot(struct apple_rtkit *rtk)
{
int ret;
if (apple_rtkit_is_running(rtk))
return 0;
if (rtk->crashed)
return -EINVAL;
dev_dbg(rtk->dev, "RTKit: waiting for boot to finish\n");
ret = apple_rtkit_wait_for_completion(&rtk->epmap_completion);
if (ret)
return ret;
if (rtk->boot_result)
return rtk->boot_result;
dev_dbg(rtk->dev, "RTKit: waiting for IOP power state ACK\n");
ret = apple_rtkit_wait_for_completion(&rtk->iop_pwr_ack_completion);
if (ret)
return ret;
return apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_ON);
}
EXPORT_SYMBOL_GPL(apple_rtkit_boot);
int apple_rtkit_shutdown(struct apple_rtkit *rtk)
{
int ret;
/* if OFF is used here the co-processor will not wake up again */
ret = apple_rtkit_set_ap_power_state(rtk,
APPLE_RTKIT_PWR_STATE_QUIESCED);
if (ret)
return ret;
ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_SLEEP);
if (ret)
return ret;
return apple_rtkit_reinit(rtk);
}
EXPORT_SYMBOL_GPL(apple_rtkit_shutdown);
int apple_rtkit_idle(struct apple_rtkit *rtk)
{
int ret;
/* if OFF is used here the co-processor will not wake up again */
ret = apple_rtkit_set_ap_power_state(rtk,
APPLE_RTKIT_PWR_STATE_IDLE);
if (ret)
return ret;
ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_IDLE);
if (ret)
return ret;
rtk->iop_power_state = APPLE_RTKIT_PWR_STATE_IDLE;
rtk->ap_power_state = APPLE_RTKIT_PWR_STATE_IDLE;
return 0;
}
EXPORT_SYMBOL_GPL(apple_rtkit_idle);
int apple_rtkit_quiesce(struct apple_rtkit *rtk)
{
int ret;
ret = apple_rtkit_set_ap_power_state(rtk,
APPLE_RTKIT_PWR_STATE_QUIESCED);
if (ret)
return ret;
ret = apple_rtkit_set_iop_power_state(rtk,
APPLE_RTKIT_PWR_STATE_QUIESCED);
if (ret)
return ret;
ret = apple_rtkit_reinit(rtk);
if (ret)
return ret;
rtk->iop_power_state = APPLE_RTKIT_PWR_STATE_QUIESCED;
rtk->ap_power_state = APPLE_RTKIT_PWR_STATE_QUIESCED;
return 0;
}
EXPORT_SYMBOL_GPL(apple_rtkit_quiesce);
int apple_rtkit_wake(struct apple_rtkit *rtk)
{
u64 msg;
int ret;
if (apple_rtkit_is_running(rtk))
return -EINVAL;
reinit_completion(&rtk->iop_pwr_ack_completion);
/*
* Use open-coded apple_rtkit_set_iop_power_state since apple_rtkit_boot
* will wait for the completion anyway.
*/
msg = FIELD_PREP(APPLE_RTKIT_MGMT_PWR_STATE, APPLE_RTKIT_PWR_STATE_INIT);
ret = apple_rtkit_management_send(rtk, APPLE_RTKIT_MGMT_SET_IOP_PWR_STATE,
msg);
if (ret)
return ret;
return apple_rtkit_boot(rtk);
}
EXPORT_SYMBOL_GPL(apple_rtkit_wake);
void apple_rtkit_free(struct apple_rtkit *rtk)
{
apple_mbox_stop(rtk->mbox);
destroy_workqueue(rtk->wq);
apple_rtkit_free_buffer(rtk, &rtk->ioreport_buffer);
apple_rtkit_free_buffer(rtk, &rtk->crashlog_buffer);
apple_rtkit_free_buffer(rtk, &rtk->oslog_buffer);
apple_rtkit_free_buffer(rtk, &rtk->syslog_buffer);
kfree(rtk->syslog_msg_buffer);
kfree(rtk);
}
EXPORT_SYMBOL_GPL(apple_rtkit_free);
static void apple_rtkit_free_wrapper(void *data)
{
apple_rtkit_free(data);
}
struct apple_rtkit *devm_apple_rtkit_init(struct device *dev, void *cookie,
const char *mbox_name, int mbox_idx,
const struct apple_rtkit_ops *ops)
{
struct apple_rtkit *rtk;
int ret;
rtk = apple_rtkit_init(dev, cookie, mbox_name, mbox_idx, ops);
if (IS_ERR(rtk))
return rtk;
ret = devm_add_action_or_reset(dev, apple_rtkit_free_wrapper, rtk);
if (ret)
return ERR_PTR(ret);
return rtk;
}
EXPORT_SYMBOL_GPL(devm_apple_rtkit_init);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
MODULE_DESCRIPTION("Apple RTKit driver");