979 lines · 24.6 KB · C
RawDownload
// 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) {		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;

Lines 1–500 of 979.