994 lines
23 KiB
C
994 lines
23 KiB
C
/* $OpenBSD: rtkit.c,v 1.15 2024/01/15 16:57:31 kettenis Exp $ */
|
|
/*
|
|
* Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/task.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/fdt.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <dev/ofw/openfirm.h>
|
|
#include <dev/ofw/ofw_misc.h>
|
|
#include <dev/ofw/fdt.h>
|
|
|
|
#include <arm64/dev/aplmbox.h>
|
|
#include <arm64/dev/rtkit.h>
|
|
|
|
#define RTKIT_EP_MGMT 0
|
|
#define RTKIT_EP_CRASHLOG 1
|
|
#define RTKIT_EP_SYSLOG 2
|
|
#define RTKIT_EP_DEBUG 3
|
|
#define RTKIT_EP_IOREPORT 4
|
|
#define RTKIT_EP_OSLOG 8
|
|
#define RTKIT_EP_UNKNOWN 10
|
|
|
|
#define RTKIT_MGMT_TYPE(x) (((x) >> 52) & 0xff)
|
|
#define RTKIT_MGMT_TYPE_SHIFT 52
|
|
|
|
#define RTKIT_MGMT_PWR_STATE(x) (((x) >> 0) & 0xffff)
|
|
|
|
#define RTKIT_MGMT_HELLO 1
|
|
#define RTKIT_MGMT_HELLO_ACK 2
|
|
#define RTKIT_MGMT_STARTEP 5
|
|
#define RTKIT_MGMT_IOP_PWR_STATE 6
|
|
#define RTKIT_MGMT_IOP_PWR_STATE_ACK 7
|
|
#define RTKIT_MGMT_EPMAP 8
|
|
#define RTKIT_MGMT_AP_PWR_STATE 11
|
|
|
|
#define RTKIT_MGMT_HELLO_MINVER(x) (((x) >> 0) & 0xffff)
|
|
#define RTKIT_MGMT_HELLO_MINVER_SHIFT 0
|
|
#define RTKIT_MGMT_HELLO_MAXVER(x) (((x) >> 16) & 0xffff)
|
|
#define RTKIT_MGMT_HELLO_MAXVER_SHIFT 16
|
|
|
|
#define RTKIT_MGMT_STARTEP_EP_SHIFT 32
|
|
#define RTKIT_MGMT_STARTEP_START (1ULL << 1)
|
|
|
|
#define RTKIT_MGMT_EPMAP_LAST (1ULL << 51)
|
|
#define RTKIT_MGMT_EPMAP_BASE(x) (((x) >> 32) & 0x7)
|
|
#define RTKIT_MGMT_EPMAP_BASE_SHIFT 32
|
|
#define RTKIT_MGMT_EPMAP_BITMAP(x) (((x) >> 0) & 0xffffffff)
|
|
#define RTKIT_MGMT_EPMAP_MORE (1ULL << 0)
|
|
|
|
#define RTKIT_BUFFER_REQUEST 1
|
|
#define RTKIT_BUFFER_ADDR(x) (((x) >> 0) & 0xfffffffffff)
|
|
#define RTKIT_BUFFER_SIZE(x) (((x) >> 44) & 0xff)
|
|
#define RTKIT_BUFFER_SIZE_SHIFT 44
|
|
|
|
#define RTKIT_SYSLOG_LOG 5
|
|
#define RTKIT_SYSLOG_LOG_IDX(x) (((x) >> 0) & 0xff)
|
|
#define RTKIT_SYSLOG_INIT 8
|
|
#define RTKIT_SYSLOG_INIT_N_ENTRIES(x) (((x) >> 0) & 0xff)
|
|
#define RTKIT_SYSLOG_INIT_MSG_SIZE(x) (((x) >> 24) & 0xff)
|
|
|
|
#define RTKIT_IOREPORT_UNKNOWN1 8
|
|
#define RTKIT_IOREPORT_UNKNOWN2 12
|
|
|
|
#define RTKIT_OSLOG_TYPE(x) (((x) >> 56) & 0xff)
|
|
#define RTKIT_OSLOG_TYPE_SHIFT (56 - RTKIT_MGMT_TYPE_SHIFT)
|
|
#define RTKIT_OSLOG_BUFFER_REQUEST 1
|
|
#define RTKIT_OSLOG_BUFFER_ADDR(x) (((x) >> 0) & 0xfffffffff)
|
|
#define RTKIT_OSLOG_BUFFER_SIZE(x) (((x) >> 36) & 0xfffff)
|
|
#define RTKIT_OSLOG_BUFFER_SIZE_SHIFT 36
|
|
#define RTKIT_OSLOG_UNKNOWN1 3
|
|
#define RTKIT_OSLOG_UNKNOWN2 4
|
|
#define RTKIT_OSLOG_UNKNOWN3 5
|
|
|
|
/* Versions we support. */
|
|
#define RTKIT_MINVER 11
|
|
#define RTKIT_MAXVER 12
|
|
|
|
struct rtkit_dmamem {
|
|
bus_dmamap_t rdm_map;
|
|
bus_dma_segment_t rdm_seg;
|
|
size_t rdm_size;
|
|
caddr_t rdm_kva;
|
|
};
|
|
|
|
struct rtkit_state {
|
|
struct mbox_channel *mc;
|
|
struct rtkit *rk;
|
|
int flags;
|
|
char *crashlog;
|
|
bus_addr_t crashlog_addr;
|
|
bus_size_t crashlog_size;
|
|
struct task crashlog_task;
|
|
char *ioreport;
|
|
bus_addr_t ioreport_addr;
|
|
bus_size_t ioreport_size;
|
|
struct task ioreport_task;
|
|
char *oslog;
|
|
bus_addr_t oslog_addr;
|
|
bus_size_t oslog_size;
|
|
struct task oslog_task;
|
|
char *syslog;
|
|
bus_addr_t syslog_addr;
|
|
bus_size_t syslog_size;
|
|
struct task syslog_task;
|
|
uint8_t syslog_n_entries;
|
|
uint8_t syslog_msg_size;
|
|
char *syslog_msg;
|
|
uint16_t iop_pwrstate;
|
|
uint16_t ap_pwrstate;
|
|
uint64_t epmap;
|
|
void (*callback[32])(void *, uint64_t);
|
|
void *arg[32];
|
|
struct rtkit_dmamem dmamem[32];
|
|
int ndmamem;
|
|
};
|
|
|
|
int
|
|
rtkit_recv(struct mbox_channel *mc, struct aplmbox_msg *msg)
|
|
{
|
|
return mbox_recv(mc, msg, sizeof(*msg));
|
|
}
|
|
|
|
int
|
|
rtkit_send(struct mbox_channel *mc, uint32_t endpoint,
|
|
uint64_t type, uint64_t data)
|
|
{
|
|
struct aplmbox_msg msg;
|
|
|
|
msg.data0 = (type << RTKIT_MGMT_TYPE_SHIFT) | data;
|
|
msg.data1 = endpoint;
|
|
return mbox_send(mc, &msg, sizeof(msg));
|
|
}
|
|
|
|
bus_addr_t
|
|
rtkit_alloc(struct rtkit_state *state, bus_size_t size, caddr_t *kvap)
|
|
{
|
|
struct rtkit *rk = state->rk;
|
|
bus_dma_segment_t seg;
|
|
bus_dmamap_t map;
|
|
caddr_t kva;
|
|
int nsegs;
|
|
|
|
if (state->ndmamem >= nitems(state->dmamem))
|
|
return (bus_addr_t)-1;
|
|
|
|
if (bus_dmamem_alloc(rk->rk_dmat, size, 16384, 0,
|
|
&seg, 1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO))
|
|
return (bus_addr_t)-1;
|
|
|
|
if (bus_dmamem_map(rk->rk_dmat, &seg, 1, size,
|
|
&kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT)) {
|
|
bus_dmamem_free(rk->rk_dmat, &seg, 1);
|
|
return (bus_addr_t)-1;
|
|
}
|
|
|
|
if (bus_dmamap_create(rk->rk_dmat, size, 1, size, 0,
|
|
BUS_DMA_WAITOK, &map)) {
|
|
bus_dmamem_unmap(rk->rk_dmat, kva, size);
|
|
bus_dmamem_free(rk->rk_dmat, &seg, 1);
|
|
return (bus_addr_t)-1;
|
|
}
|
|
|
|
if (bus_dmamap_load_raw(rk->rk_dmat, map, &seg, 1, size,
|
|
BUS_DMA_WAITOK)) {
|
|
bus_dmamap_destroy(rk->rk_dmat, map);
|
|
bus_dmamem_unmap(rk->rk_dmat, kva, size);
|
|
bus_dmamem_free(rk->rk_dmat, &seg, 1);
|
|
return (bus_addr_t)-1;
|
|
}
|
|
|
|
if (rk->rk_map) {
|
|
if (rk->rk_map(rk->rk_cookie, seg.ds_addr, seg.ds_len)) {
|
|
bus_dmamap_unload(rk->rk_dmat, map);
|
|
bus_dmamap_destroy(rk->rk_dmat, map);
|
|
bus_dmamem_unmap(rk->rk_dmat, kva, size);
|
|
bus_dmamem_free(rk->rk_dmat, &seg, 1);
|
|
return (bus_addr_t)-1;
|
|
}
|
|
}
|
|
|
|
state->dmamem[state->ndmamem].rdm_map = map;
|
|
state->dmamem[state->ndmamem].rdm_seg = seg;
|
|
state->dmamem[state->ndmamem].rdm_size = size;
|
|
state->dmamem[state->ndmamem].rdm_kva = kva;
|
|
state->ndmamem++;
|
|
|
|
*kvap = kva;
|
|
return map->dm_segs[0].ds_addr;
|
|
}
|
|
|
|
int
|
|
rtkit_start(struct rtkit_state *state, uint32_t endpoint)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
uint64_t reply;
|
|
|
|
reply = ((uint64_t)endpoint << RTKIT_MGMT_STARTEP_EP_SHIFT);
|
|
reply |= RTKIT_MGMT_STARTEP_START;
|
|
return rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_STARTEP, reply);
|
|
}
|
|
|
|
int
|
|
rtkit_handle_mgmt(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
uint64_t minver, maxver, ver;
|
|
uint64_t base, bitmap, reply;
|
|
uint32_t endpoint;
|
|
int error;
|
|
|
|
switch (RTKIT_MGMT_TYPE(msg->data0)) {
|
|
case RTKIT_MGMT_HELLO:
|
|
minver = RTKIT_MGMT_HELLO_MINVER(msg->data0);
|
|
maxver = RTKIT_MGMT_HELLO_MAXVER(msg->data0);
|
|
if (minver > RTKIT_MAXVER) {
|
|
printf("%s: unsupported minimum firmware version %lld\n",
|
|
__func__, minver);
|
|
return EINVAL;
|
|
}
|
|
if (maxver < RTKIT_MINVER) {
|
|
printf("%s: unsupported maximum firmware version %lld\n",
|
|
__func__, maxver);
|
|
return EINVAL;
|
|
}
|
|
ver = min(RTKIT_MAXVER, maxver);
|
|
error = rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_HELLO_ACK,
|
|
(ver << RTKIT_MGMT_HELLO_MINVER_SHIFT) |
|
|
(ver << RTKIT_MGMT_HELLO_MAXVER_SHIFT));
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_MGMT_IOP_PWR_STATE_ACK:
|
|
state->iop_pwrstate = RTKIT_MGMT_PWR_STATE(msg->data0);
|
|
wakeup(&state->iop_pwrstate);
|
|
break;
|
|
case RTKIT_MGMT_AP_PWR_STATE:
|
|
state->ap_pwrstate = RTKIT_MGMT_PWR_STATE(msg->data0);
|
|
wakeup(&state->ap_pwrstate);
|
|
break;
|
|
case RTKIT_MGMT_EPMAP:
|
|
base = RTKIT_MGMT_EPMAP_BASE(msg->data0);
|
|
bitmap = RTKIT_MGMT_EPMAP_BITMAP(msg->data0);
|
|
state->epmap |= (bitmap << (base * 32));
|
|
reply = (base << RTKIT_MGMT_EPMAP_BASE_SHIFT);
|
|
if (msg->data0 & RTKIT_MGMT_EPMAP_LAST)
|
|
reply |= RTKIT_MGMT_EPMAP_LAST;
|
|
else
|
|
reply |= RTKIT_MGMT_EPMAP_MORE;
|
|
error = rtkit_send(state->mc, RTKIT_EP_MGMT,
|
|
RTKIT_MGMT_EPMAP, reply);
|
|
if (error)
|
|
return error;
|
|
if (msg->data0 & RTKIT_MGMT_EPMAP_LAST) {
|
|
for (endpoint = 1; endpoint < 32; endpoint++) {
|
|
if ((state->epmap & (1ULL << endpoint)) == 0)
|
|
continue;
|
|
|
|
switch (endpoint) {
|
|
case RTKIT_EP_CRASHLOG:
|
|
case RTKIT_EP_SYSLOG:
|
|
case RTKIT_EP_DEBUG:
|
|
case RTKIT_EP_IOREPORT:
|
|
case RTKIT_EP_OSLOG:
|
|
error = rtkit_start(state, endpoint);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_EP_UNKNOWN:
|
|
break;
|
|
default:
|
|
printf("%s: skipping endpoint %d\n",
|
|
__func__, endpoint);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s: unhandled management event 0x%016lld\n",
|
|
__func__, msg->data0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct rtkit_crashlog_header {
|
|
uint32_t fourcc;
|
|
uint32_t version;
|
|
uint32_t size;
|
|
uint32_t flags;
|
|
uint8_t unknown[16];
|
|
};
|
|
|
|
struct rtkit_crashlog_mbx {
|
|
uint64_t msg1;
|
|
uint64_t msg0;
|
|
uint32_t timestamp;
|
|
uint8_t unknown[4];
|
|
};
|
|
|
|
struct rtkit_crashlog_rg8 {
|
|
uint64_t unknown0;
|
|
uint64_t reg[31];
|
|
uint64_t sp;
|
|
uint64_t pc;
|
|
uint64_t psr;
|
|
uint64_t cpacr;
|
|
uint64_t fpsr;
|
|
uint64_t fpcr;
|
|
uint64_t fpreg[64];
|
|
uint64_t far;
|
|
uint64_t unknown1;
|
|
uint64_t esr;
|
|
uint64_t unknown2;
|
|
};
|
|
|
|
#define RTKIT_FOURCC(s) ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])
|
|
|
|
void
|
|
rtkit_crashlog_dump_str(char *buf, size_t size)
|
|
{
|
|
char *end = buf + size - 1;
|
|
char *newl;
|
|
uint32_t idx;
|
|
|
|
if (size < 5)
|
|
return;
|
|
|
|
idx = lemtoh32((uint32_t *)buf);
|
|
buf += sizeof(uint32_t);
|
|
|
|
*end = 0;
|
|
while (buf < end) {
|
|
if (*buf == 0)
|
|
return;
|
|
newl = memchr(buf, '\n', buf - end);
|
|
if (newl)
|
|
*newl = 0;
|
|
printf("RTKit Cstr %x: %s\n", idx, buf);
|
|
if (!newl)
|
|
return;
|
|
buf = newl + 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
rtkit_crashlog_dump_ver(char *buf, size_t size)
|
|
{
|
|
char *end = buf + size - 1;
|
|
|
|
if (size < 17)
|
|
return;
|
|
|
|
buf += 16;
|
|
|
|
*end = 0;
|
|
printf("RTKit Cver %s\n", buf);
|
|
}
|
|
|
|
void
|
|
rtkit_crashlog_dump_mbx(char *buf, size_t size)
|
|
{
|
|
struct rtkit_crashlog_mbx mbx;
|
|
char *end = buf + size;
|
|
|
|
buf += 28;
|
|
size -= 28;
|
|
|
|
while (buf + sizeof(mbx) <= end) {
|
|
memcpy(&mbx, buf, sizeof(mbx));
|
|
printf("RTKit Cmbx: 0x%016llx 0x%016llx @0x%08x\n",
|
|
mbx.msg0, mbx.msg1, mbx.timestamp);
|
|
buf += sizeof(mbx);
|
|
}
|
|
}
|
|
|
|
void
|
|
rtkit_crashlog_dump_rg8(char *buf, size_t size)
|
|
{
|
|
struct rtkit_crashlog_rg8 rg8;
|
|
int i;
|
|
|
|
if (size < sizeof(rg8))
|
|
return;
|
|
|
|
memcpy(&rg8, buf, sizeof(rg8));
|
|
printf("RTKit Crg8: psr %016llx\n", rg8.psr);
|
|
printf("RTKit Crg8: pc %016llx\n", rg8.pc);
|
|
printf("RTKit Crg8: esr %016llx\n", rg8.esr);
|
|
printf("RTKit Crg8: far %016llx\n", rg8.far);
|
|
printf("RTKit Crg8: sp %016llx\n", rg8.sp);
|
|
for (i = 0; i < nitems(rg8.reg); i++)
|
|
printf("RTKit Crg8: reg[%d] %016llx\n", i, rg8.reg[i]);
|
|
}
|
|
|
|
void
|
|
rtkit_crashlog_dump(char *buf, size_t size)
|
|
{
|
|
struct rtkit_crashlog_header hdr;
|
|
size_t off;
|
|
|
|
if (size < sizeof(hdr))
|
|
return;
|
|
|
|
memcpy(&hdr, buf, sizeof(hdr));
|
|
if (letoh32(hdr.fourcc) != RTKIT_FOURCC("CLHE")) {
|
|
printf("RTKit: Invalid header\n");
|
|
return;
|
|
}
|
|
|
|
if (letoh32(hdr.size) > size) {
|
|
printf("RTKit: Invalid header size\n");
|
|
return;
|
|
}
|
|
|
|
off = sizeof(hdr);
|
|
while (off < letoh32(hdr.size)) {
|
|
uint32_t fourcc, size;
|
|
|
|
fourcc = lemtoh32((uint32_t *)(buf + off));
|
|
size = lemtoh32((uint32_t *)(buf + off + 12));
|
|
if (fourcc == RTKIT_FOURCC("CLHE"))
|
|
break;
|
|
if (fourcc == RTKIT_FOURCC("Cstr"))
|
|
rtkit_crashlog_dump_str(buf + off + 16, size - 16);
|
|
if (fourcc == RTKIT_FOURCC("Cver"))
|
|
rtkit_crashlog_dump_ver(buf + off + 16, size - 16);
|
|
if (fourcc == RTKIT_FOURCC("Cmbx"))
|
|
rtkit_crashlog_dump_mbx(buf + off + 16, size - 16);
|
|
if (fourcc == RTKIT_FOURCC("Crg8"))
|
|
rtkit_crashlog_dump_rg8(buf + off + 16, size - 16);
|
|
off += size;
|
|
}
|
|
}
|
|
|
|
void
|
|
rtkit_handle_crashlog_buffer(void *arg)
|
|
{
|
|
struct rtkit_state *state = arg;
|
|
struct mbox_channel *mc = state->mc;
|
|
struct rtkit *rk = state->rk;
|
|
bus_addr_t addr = state->crashlog_addr;
|
|
bus_size_t size = state->crashlog_size;
|
|
|
|
if (addr) {
|
|
paddr_t pa = addr;
|
|
vaddr_t va;
|
|
|
|
if (rk && rk->rk_logmap) {
|
|
pa = rk->rk_logmap(rk->rk_cookie, addr);
|
|
if (pa == (paddr_t)-1)
|
|
return;
|
|
}
|
|
|
|
state->crashlog = km_alloc(size * PAGE_SIZE,
|
|
&kv_any, &kp_none, &kd_waitok);
|
|
va = (vaddr_t)state->crashlog;
|
|
|
|
while (size-- > 0) {
|
|
pmap_kenter_cache(va, pa, PROT_READ,
|
|
PMAP_CACHE_CI);
|
|
va += PAGE_SIZE;
|
|
pa += PAGE_SIZE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (rk) {
|
|
addr = rtkit_alloc(state, size << PAGE_SHIFT,
|
|
&state->crashlog);
|
|
if (addr == (bus_addr_t)-1)
|
|
return;
|
|
}
|
|
|
|
rtkit_send(mc, RTKIT_EP_CRASHLOG, RTKIT_BUFFER_REQUEST,
|
|
(size << RTKIT_BUFFER_SIZE_SHIFT) | addr);
|
|
}
|
|
|
|
int
|
|
rtkit_handle_crashlog(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
|
|
switch (RTKIT_MGMT_TYPE(msg->data0)) {
|
|
case RTKIT_BUFFER_REQUEST:
|
|
addr = RTKIT_BUFFER_ADDR(msg->data0);
|
|
size = RTKIT_BUFFER_SIZE(msg->data0);
|
|
|
|
if (state->crashlog) {
|
|
char *buf;
|
|
|
|
printf("\nRTKit crashed:\n");
|
|
|
|
buf = malloc(size * PAGE_SIZE, M_TEMP, M_NOWAIT);
|
|
if (buf) {
|
|
memcpy(buf, state->crashlog, size * PAGE_SIZE);
|
|
rtkit_crashlog_dump(buf, size * PAGE_SIZE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
state->crashlog_addr = addr;
|
|
state->crashlog_size = size;
|
|
if (cold)
|
|
rtkit_handle_crashlog_buffer(state);
|
|
else
|
|
task_add(systq, &state->crashlog_task);
|
|
break;
|
|
default:
|
|
printf("%s: unhandled crashlog event 0x%016llx\n",
|
|
__func__, msg->data0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rtkit_handle_syslog_log(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
char context[24];
|
|
size_t syslog_msg_size;
|
|
char *syslog_msg;
|
|
int idx, pos;
|
|
|
|
if ((state->flags & RK_SYSLOG) == 0)
|
|
return;
|
|
|
|
if (state->syslog_msg == NULL)
|
|
return;
|
|
idx = RTKIT_SYSLOG_LOG_IDX(msg->data0);
|
|
if (idx > state->syslog_n_entries)
|
|
return;
|
|
|
|
syslog_msg_size = state->syslog_msg_size + 32;
|
|
syslog_msg = state->syslog + (idx * syslog_msg_size + 8);
|
|
memcpy(context, syslog_msg, sizeof(context));
|
|
context[sizeof(context) - 1] = 0;
|
|
|
|
syslog_msg += sizeof(context);
|
|
memcpy(state->syslog_msg, syslog_msg, state->syslog_msg_size);
|
|
state->syslog_msg[state->syslog_msg_size - 1] = 0;
|
|
|
|
pos = strlen(state->syslog_msg) - 1;
|
|
while (pos >= 0) {
|
|
if (state->syslog_msg[pos] != ' ' &&
|
|
state->syslog_msg[pos] != '\n' &&
|
|
state->syslog_msg[pos] != '\r')
|
|
break;
|
|
state->syslog_msg[pos--] = 0;
|
|
}
|
|
|
|
printf("RTKit syslog %d: %s:%s\n", idx, context, state->syslog_msg);
|
|
}
|
|
|
|
void
|
|
rtkit_handle_syslog_buffer(void *arg)
|
|
{
|
|
struct rtkit_state *state = arg;
|
|
struct mbox_channel *mc = state->mc;
|
|
struct rtkit *rk = state->rk;
|
|
bus_addr_t addr = state->syslog_addr;
|
|
bus_size_t size = state->syslog_size;
|
|
|
|
if (rk) {
|
|
addr = rtkit_alloc(state, size << PAGE_SHIFT,
|
|
&state->syslog);
|
|
if (addr == (bus_addr_t)-1)
|
|
return;
|
|
}
|
|
|
|
rtkit_send(mc, RTKIT_EP_SYSLOG, RTKIT_BUFFER_REQUEST,
|
|
(size << RTKIT_BUFFER_SIZE_SHIFT) | addr);
|
|
}
|
|
|
|
int
|
|
rtkit_handle_syslog(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
int error;
|
|
|
|
switch (RTKIT_MGMT_TYPE(msg->data0)) {
|
|
case RTKIT_BUFFER_REQUEST:
|
|
addr = RTKIT_BUFFER_ADDR(msg->data0);
|
|
size = RTKIT_BUFFER_SIZE(msg->data0);
|
|
if (addr)
|
|
break;
|
|
|
|
state->syslog_addr = addr;
|
|
state->syslog_size = size;
|
|
if (cold)
|
|
rtkit_handle_syslog_buffer(state);
|
|
else
|
|
task_add(systq, &state->syslog_task);
|
|
break;
|
|
case RTKIT_SYSLOG_INIT:
|
|
state->syslog_n_entries =
|
|
RTKIT_SYSLOG_INIT_N_ENTRIES(msg->data0);
|
|
state->syslog_msg_size =
|
|
RTKIT_SYSLOG_INIT_MSG_SIZE(msg->data0);
|
|
state->syslog_msg = malloc(state->syslog_msg_size,
|
|
M_DEVBUF, M_NOWAIT);
|
|
break;
|
|
case RTKIT_SYSLOG_LOG:
|
|
rtkit_handle_syslog_log(state, msg);
|
|
error = rtkit_send(mc, RTKIT_EP_SYSLOG,
|
|
RTKIT_MGMT_TYPE(msg->data0), msg->data0);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
default:
|
|
printf("%s: unhandled syslog event 0x%016llx\n",
|
|
__func__, msg->data0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rtkit_handle_ioreport_buffer(void *arg)
|
|
{
|
|
struct rtkit_state *state = arg;
|
|
struct mbox_channel *mc = state->mc;
|
|
struct rtkit *rk = state->rk;
|
|
bus_addr_t addr = state->ioreport_addr;
|
|
bus_size_t size = state->ioreport_size;
|
|
|
|
if (rk) {
|
|
addr = rtkit_alloc(state, size << PAGE_SHIFT,
|
|
&state->ioreport);
|
|
if (addr == (bus_addr_t)-1)
|
|
return;
|
|
}
|
|
|
|
rtkit_send(mc, RTKIT_EP_IOREPORT, RTKIT_BUFFER_REQUEST,
|
|
(size << RTKIT_BUFFER_SIZE_SHIFT) | addr);
|
|
}
|
|
|
|
int
|
|
rtkit_handle_ioreport(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
int error;
|
|
|
|
switch (RTKIT_MGMT_TYPE(msg->data0)) {
|
|
case RTKIT_BUFFER_REQUEST:
|
|
addr = RTKIT_BUFFER_ADDR(msg->data0);
|
|
size = RTKIT_BUFFER_SIZE(msg->data0);
|
|
if (addr)
|
|
break;
|
|
|
|
state->ioreport_addr = addr;
|
|
state->ioreport_size = size;
|
|
if (cold)
|
|
rtkit_handle_ioreport_buffer(state);
|
|
else
|
|
task_add(systq, &state->ioreport_task);
|
|
break;
|
|
case RTKIT_IOREPORT_UNKNOWN1:
|
|
case RTKIT_IOREPORT_UNKNOWN2:
|
|
/* These unknown events have to be acked to make progress. */
|
|
error = rtkit_send(mc, RTKIT_EP_IOREPORT,
|
|
RTKIT_MGMT_TYPE(msg->data0), msg->data0);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
default:
|
|
printf("%s: unhandled ioreport event 0x%016llx\n",
|
|
__func__, msg->data0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rtkit_handle_oslog_buffer(void *arg)
|
|
{
|
|
struct rtkit_state *state = arg;
|
|
struct mbox_channel *mc = state->mc;
|
|
struct rtkit *rk = state->rk;
|
|
bus_addr_t addr = state->oslog_addr;
|
|
bus_size_t size = state->oslog_size;
|
|
|
|
if (rk) {
|
|
addr = rtkit_alloc(state, size, &state->oslog);
|
|
if (addr == (bus_addr_t)-1)
|
|
return;
|
|
}
|
|
|
|
rtkit_send(mc, RTKIT_EP_OSLOG,
|
|
(RTKIT_OSLOG_BUFFER_REQUEST << RTKIT_OSLOG_TYPE_SHIFT),
|
|
(size << RTKIT_OSLOG_BUFFER_SIZE_SHIFT) | (addr >> PAGE_SHIFT));
|
|
}
|
|
|
|
int
|
|
rtkit_handle_oslog(struct rtkit_state *state, struct aplmbox_msg *msg)
|
|
{
|
|
bus_addr_t addr;
|
|
bus_size_t size;
|
|
|
|
switch (RTKIT_OSLOG_TYPE(msg->data0)) {
|
|
case RTKIT_OSLOG_BUFFER_REQUEST:
|
|
addr = RTKIT_OSLOG_BUFFER_ADDR(msg->data0) << PAGE_SHIFT;
|
|
size = RTKIT_OSLOG_BUFFER_SIZE(msg->data0);
|
|
if (addr)
|
|
break;
|
|
|
|
state->oslog_addr = addr;
|
|
state->oslog_size = size;
|
|
if (cold)
|
|
rtkit_handle_oslog_buffer(state);
|
|
else
|
|
task_add(systq, &state->oslog_task);
|
|
break;
|
|
case RTKIT_OSLOG_UNKNOWN1:
|
|
case RTKIT_OSLOG_UNKNOWN2:
|
|
case RTKIT_OSLOG_UNKNOWN3:
|
|
break;
|
|
default:
|
|
printf("%s: unhandled oslog event 0x%016llx\n",
|
|
__func__, msg->data0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtkit_poll(struct rtkit_state *state)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
struct aplmbox_msg msg;
|
|
void (*callback)(void *, uint64_t);
|
|
void *arg;
|
|
uint32_t endpoint;
|
|
int error;
|
|
|
|
error = rtkit_recv(mc, &msg);
|
|
if (error)
|
|
return error;
|
|
|
|
if (state->flags & RK_DEBUG)
|
|
printf("%s: 0x%016llx 0x%02x\n", __func__, msg.data0, msg.data1);
|
|
endpoint = msg.data1;
|
|
switch (endpoint) {
|
|
case RTKIT_EP_MGMT:
|
|
error = rtkit_handle_mgmt(state, &msg);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_EP_CRASHLOG:
|
|
error = rtkit_handle_crashlog(state, &msg);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_EP_SYSLOG:
|
|
error = rtkit_handle_syslog(state, &msg);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_EP_IOREPORT:
|
|
error = rtkit_handle_ioreport(state, &msg);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
case RTKIT_EP_OSLOG:
|
|
error = rtkit_handle_oslog(state, &msg);
|
|
if (error)
|
|
return error;
|
|
break;
|
|
default:
|
|
if (endpoint >= 32 && endpoint < 64 &&
|
|
state->callback[endpoint - 32]) {
|
|
callback = state->callback[endpoint - 32];
|
|
arg = state->arg[endpoint - 32];
|
|
callback(arg, msg.data0);
|
|
break;
|
|
}
|
|
|
|
printf("%s: unhandled endpoint %d\n", __func__, msg.data1);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
rtkit_rx_callback(void *cookie)
|
|
{
|
|
rtkit_poll(cookie);
|
|
}
|
|
|
|
struct rtkit_state *
|
|
rtkit_init(int node, const char *name, int flags, struct rtkit *rk)
|
|
{
|
|
struct rtkit_state *state;
|
|
struct mbox_client client;
|
|
|
|
state = malloc(sizeof(*state), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
client.mc_rx_callback = rtkit_rx_callback;
|
|
client.mc_rx_arg = state;
|
|
if (flags & RK_WAKEUP)
|
|
client.mc_flags = MC_WAKEUP;
|
|
else
|
|
client.mc_flags = 0;
|
|
|
|
state->mc = mbox_channel(node, name, &client);
|
|
if (state->mc == NULL) {
|
|
free(state, M_DEVBUF, sizeof(*state));
|
|
return NULL;
|
|
}
|
|
state->rk = rk;
|
|
state->flags = flags;
|
|
|
|
state->iop_pwrstate = RTKIT_MGMT_PWR_STATE_SLEEP;
|
|
state->ap_pwrstate = RTKIT_MGMT_PWR_STATE_QUIESCED;
|
|
|
|
task_set(&state->crashlog_task, rtkit_handle_crashlog_buffer, state);
|
|
task_set(&state->syslog_task, rtkit_handle_syslog_buffer, state);
|
|
task_set(&state->ioreport_task, rtkit_handle_ioreport_buffer, state);
|
|
task_set(&state->oslog_task, rtkit_handle_oslog_buffer, state);
|
|
|
|
return state;
|
|
}
|
|
|
|
int
|
|
rtkit_boot(struct rtkit_state *state)
|
|
{
|
|
/* Wake up! */
|
|
return rtkit_set_iop_pwrstate(state, RTKIT_MGMT_PWR_STATE_ON);
|
|
}
|
|
|
|
void
|
|
rtkit_shutdown(struct rtkit_state *state)
|
|
{
|
|
struct rtkit *rk = state->rk;
|
|
int i;
|
|
|
|
rtkit_set_ap_pwrstate(state, RTKIT_MGMT_PWR_STATE_QUIESCED);
|
|
rtkit_set_iop_pwrstate(state, RTKIT_MGMT_PWR_STATE_SLEEP);
|
|
|
|
KASSERT(state->iop_pwrstate == RTKIT_MGMT_PWR_STATE_SLEEP);
|
|
KASSERT(state->ap_pwrstate == RTKIT_MGMT_PWR_STATE_QUIESCED);
|
|
state->epmap = 0;
|
|
|
|
state->crashlog = NULL;
|
|
state->ioreport = NULL;
|
|
state->oslog = NULL;
|
|
state->syslog = NULL;
|
|
|
|
/* Clean up our memory allocations. */
|
|
for (i = 0; i < state->ndmamem; i++) {
|
|
if (rk->rk_unmap) {
|
|
rk->rk_unmap(rk->rk_cookie,
|
|
state->dmamem[i].rdm_seg.ds_addr,
|
|
state->dmamem[i].rdm_seg.ds_len);
|
|
}
|
|
bus_dmamap_unload(rk->rk_dmat, state->dmamem[i].rdm_map);
|
|
bus_dmamap_destroy(rk->rk_dmat, state->dmamem[i].rdm_map);
|
|
bus_dmamem_unmap(rk->rk_dmat, state->dmamem[i].rdm_kva,
|
|
state->dmamem[i].rdm_size);
|
|
bus_dmamem_free(rk->rk_dmat, &state->dmamem[i].rdm_seg, 1);
|
|
}
|
|
state->ndmamem = 0;
|
|
}
|
|
|
|
int
|
|
rtkit_set_ap_pwrstate(struct rtkit_state *state, uint16_t pwrstate)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
int error, timo;
|
|
|
|
if (state->ap_pwrstate == pwrstate)
|
|
return 0;
|
|
|
|
error = rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_AP_PWR_STATE,
|
|
pwrstate);
|
|
if (error)
|
|
return error;
|
|
|
|
if (cold) {
|
|
for (timo = 0; timo < 100000; timo++) {
|
|
error = rtkit_poll(state);
|
|
if (error == EWOULDBLOCK) {
|
|
delay(10);
|
|
continue;
|
|
}
|
|
if (error)
|
|
return error;
|
|
|
|
if (state->ap_pwrstate == pwrstate)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
while (state->ap_pwrstate != pwrstate) {
|
|
error = tsleep_nsec(&state->ap_pwrstate, PWAIT, "appwr",
|
|
SEC_TO_NSEC(1));
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtkit_set_iop_pwrstate(struct rtkit_state *state, uint16_t pwrstate)
|
|
{
|
|
struct mbox_channel *mc = state->mc;
|
|
int error, timo;
|
|
|
|
if (state->iop_pwrstate == (pwrstate & 0xff))
|
|
return 0;
|
|
|
|
error = rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_IOP_PWR_STATE,
|
|
pwrstate);
|
|
if (error)
|
|
return error;
|
|
|
|
if (cold) {
|
|
for (timo = 0; timo < 100000; timo++) {
|
|
error = rtkit_poll(state);
|
|
if (error == EWOULDBLOCK) {
|
|
delay(10);
|
|
continue;
|
|
}
|
|
if (error)
|
|
return error;
|
|
|
|
if (state->iop_pwrstate == (pwrstate & 0xff))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
while (state->iop_pwrstate != (pwrstate & 0xff)) {
|
|
error = tsleep_nsec(&state->iop_pwrstate, PWAIT, "ioppwr",
|
|
SEC_TO_NSEC(1));
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rtkit_start_endpoint(struct rtkit_state *state, uint32_t endpoint,
|
|
void (*callback)(void *, uint64_t), void *arg)
|
|
{
|
|
if (endpoint < 32 || endpoint >= 64)
|
|
return EINVAL;
|
|
|
|
if ((state->epmap & (1ULL << endpoint)) == 0)
|
|
return EINVAL;
|
|
|
|
state->callback[endpoint - 32] = callback;
|
|
state->arg[endpoint - 32] = arg;
|
|
return rtkit_start(state, endpoint);
|
|
}
|
|
|
|
int
|
|
rtkit_send_endpoint(struct rtkit_state *state, uint32_t endpoint,
|
|
uint64_t data)
|
|
{
|
|
return rtkit_send(state->mc, endpoint, 0, data);
|
|
}
|