From 0b0ff99ed6604d0d6b4740cea1e8d9520b18fa7c Mon Sep 17 00:00:00 2001 From: kettenis Date: Sun, 19 Dec 2021 23:47:24 +0000 Subject: [PATCH] Add an implementation for the protocol to communicate with coprocessors running firmware based Apple's RTKit OS. Use this code to bring up the storage controller that implements Apple's flavour of NVMe. ok dlg@, patrick@ --- sys/arch/arm64/conf/files.arm64 | 7 +- sys/arch/arm64/dev/aplns.c | 77 ++++++++- sys/arch/arm64/dev/rtkit.c | 293 ++++++++++++++++++++++++++++++++ sys/arch/arm64/dev/rtkit.h | 3 + 4 files changed, 369 insertions(+), 11 deletions(-) create mode 100644 sys/arch/arm64/dev/rtkit.c create mode 100644 sys/arch/arm64/dev/rtkit.h diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64 index 5be4f94a34e..8e1572f1d22 100644 --- a/sys/arch/arm64/conf/files.arm64 +++ b/sys/arch/arm64/conf/files.arm64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm64,v 1.48 2021/12/19 13:07:36 kettenis Exp $ +# $OpenBSD: files.arm64,v 1.49 2021/12/19 23:47:24 kettenis Exp $ maxpartitions 16 maxusers 2 8 128 @@ -120,6 +120,9 @@ include "dev/sdmmc/files.sdmmc" # Machine-independent FDT drivers include "dev/fdt/files.fdt" +define rtkit +file arch/arm64/dev/rtkit.c rtkit + device ampintc: fdt attach ampintc at fdt device ampintcmsi @@ -177,7 +180,7 @@ attach aplpmgr at fdt file arch/arm64/dev/aplpmgr.c aplpmgr # Apple NVME Storage -device aplns {} +device aplns {}: rtkit attach aplns at fdt attach nvme at aplns with nvme_ans file arch/arm64/dev/aplns.c aplns | nvme_ans diff --git a/sys/arch/arm64/dev/aplns.c b/sys/arch/arm64/dev/aplns.c index 11c16805e36..846df28ab2e 100644 --- a/sys/arch/arm64/dev/aplns.c +++ b/sys/arch/arm64/dev/aplns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplns.c,v 1.6 2021/12/09 11:38:27 kettenis Exp $ */ +/* $OpenBSD: aplns.c,v 1.7 2021/12/19 23:47:24 kettenis Exp $ */ /* * Copyright (c) 2014, 2021 David Gwynne * @@ -40,7 +40,20 @@ #include #include +#include + +#define ANS_CPU_CTRL 0x0044 +#define ANS_CPU_CTRL_RUN (1 << 4) + +#define ANS_MAX_PEND_CMDS_CTRL 0x01210 +#define ANS_MAX_QUEUE_DEPTH 64 +#define ANS_BOOT_STATUS 0x01300 +#define ANS_BOOT_STATUS_OK 0xde71ce55 #define ANS_MODESEL_REG 0x01304 +#define ANS_UNKNOWN_CTRL 0x24008 +#define ANS_PRP_NULL_CHECK (1 << 11) +#define ANS_LINEAR_SQ_CTRL 0x24908 +#define ANS_LINEAR_SQ_CTRL_EN (1 << 0) #define ANS_LINEAR_ASQ_DB 0x2490c #define ANS_LINEAR_IOSQ_DB 0x24910 @@ -100,7 +113,10 @@ aplns_attach(struct device *parent, struct device *self, void *aux) struct nvme_ans_softc { struct nvme_softc asc_nvme; + bus_space_tag_t asc_iot; + bus_space_handle_t asc_ioh; + struct mbox_channel *asc_mbox; struct nvme_dmamem *asc_nvmmu; }; @@ -156,13 +172,25 @@ nvme_ans_attach(struct device *parent, struct device *self, void *aux) struct nvme_ans_softc *asc = (struct nvme_ans_softc *)self; struct nvme_softc *sc = &asc->asc_nvme; struct fdt_attach_args *faa = aux; + uint32_t ctrl, status; - printf(": "); + if (faa->fa_nreg < 2) { + printf(": no registers\n"); + return; + } - if (bus_space_map(faa->fa_iot, - faa->fa_reg[0].addr, faa->fa_reg[0].size, - 0, &sc->sc_ioh) != 0) { - printf("unable to map registers\n"); + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + asc->asc_iot = faa->fa_iot; + if (bus_space_map(asc->asc_iot, faa->fa_reg[1].addr, + faa->fa_reg[1].size, 0, &asc->asc_ioh)) { + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + printf(": can't map registers\n"); return; } @@ -171,12 +199,42 @@ nvme_ans_attach(struct device *parent, struct device *self, void *aux) sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, nvme_intr, sc, sc->sc_dev.dv_xname); if (sc->sc_ih == NULL) { - printf("unable to establish interrupt\n"); + printf(": can't establish interrupt\n"); goto unmap; } + asc->asc_mbox = mbox_channel(faa->fa_node, NULL, NULL); + if (asc->asc_mbox == NULL) { + printf(": can't map mailbox channel\n"); + goto disestablish; + } + + ctrl = bus_space_read_4(asc->asc_iot, asc->asc_ioh, ANS_CPU_CTRL); + bus_space_write_4(asc->asc_iot, asc->asc_ioh, ANS_CPU_CTRL, + ctrl | ANS_CPU_CTRL_RUN); + + status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ANS_BOOT_STATUS); + if (status != ANS_BOOT_STATUS_OK) + rtkit_init(asc->asc_mbox); + + status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ANS_BOOT_STATUS); + if (status != ANS_BOOT_STATUS_OK) { + printf(": firmware not ready\n"); + goto disestablish; + } + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ANS_LINEAR_SQ_CTRL, + ANS_LINEAR_SQ_CTRL_EN); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ANS_MAX_PEND_CMDS_CTRL, + (ANS_MAX_QUEUE_DEPTH << 16) | ANS_MAX_QUEUE_DEPTH); + + ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ANS_UNKNOWN_CTRL); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ANS_UNKNOWN_CTRL, + ctrl & ~ANS_PRP_NULL_CHECK); + + printf(": "); + sc->sc_dmat = faa->fa_dmat; - sc->sc_iot = faa->fa_iot; sc->sc_ios = faa->fa_reg[0].size; sc->sc_ops = &nvme_ans_ops; sc->sc_openings = 1; @@ -193,7 +251,8 @@ disestablish: sc->sc_ih = NULL; unmap: - bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + bus_space_unmap(asc->asc_iot, asc->asc_ioh, faa->fa_reg[1].size); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); sc->sc_ios = 0; } diff --git a/sys/arch/arm64/dev/rtkit.c b/sys/arch/arm64/dev/rtkit.c new file mode 100644 index 00000000000..d8b1f76f980 --- /dev/null +++ b/sys/arch/arm64/dev/rtkit.c @@ -0,0 +1,293 @@ +/* $OpenBSD: rtkit.c,v 1.1 2021/12/19 23:47:24 kettenis Exp $ */ +/* + * Copyright (c) 2021 Mark Kettenis + * + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#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_MGMT_TYPE(x) (((x) >> 52) & 0xff) +#define RTKIT_MGMT_TYPE_SHIFT 52 + +#define RTKIT_MGMT_PWR_STATE(x) (((x) >> 0) & 0xff) +#define RTKIT_MGMT_PWR_STATE_ON 0x20 + +#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_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 + +/* Versions we support. */ +#define RTKIT_MINVER 11 +#define RTKIT_MAXVER 12 + +struct rtkit_state { + struct mbox_channel *mc; + int pwrstate; + uint64_t epmap; +}; + +int +rtkit_recv(struct mbox_channel *mc, struct aplmbox_msg *msg) +{ + int error, timo; + + for (timo = 0; timo < 10000; timo++) { + error = mbox_recv(mc, msg, sizeof(*msg)); + if (error == 0) + break; + delay(10); + } + + return error; +} + +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)); +} + +int +rtkit_start_ep(struct rtkit_state *state, uint64_t ep) +{ + struct mbox_channel *mc = state->mc; + uint64_t reply; + + if ((state->epmap & (1ULL << ep)) == 0) + return EINVAL; + + reply = (ep << 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; + int error; + uint8_t ep; + + 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("unsupported minimum firmware version %lld\n", + minver); + return EINVAL; + } + if (maxver < RTKIT_MINVER) { + printf("unsupported maximum firmware version %lld\n", + 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->pwrstate = RTKIT_MGMT_PWR_STATE(msg->data0); + 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 (ep = 1; ep < 32; ep++) { + if ((state->epmap & (1ULL << ep)) == 0) + continue; + + switch (ep) { + case RTKIT_EP_CRASHLOG: + case RTKIT_EP_DEBUG: + case RTKIT_EP_IOREPORT: + error = rtkit_start_ep(state, ep); + if (error) + return error; + break; + } + } + } + break; + default: + printf("unhandle management event 0x%016lld\n", msg->data0); + return EIO; + } + + return 0; +} + +int +rtkit_handle_crashlog(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; + + error = rtkit_send(mc, RTKIT_EP_CRASHLOG, RTKIT_BUFFER_REQUEST, + size << RTKIT_BUFFER_SIZE_SHIFT | addr); + if (error) + return error; + break; + default: + printf("unhandle crashlog event 0x%016llx\n", msg->data0); + return EIO; + } + + return 0; +} + +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; + + error = rtkit_send(mc, RTKIT_EP_IOREPORT, RTKIT_BUFFER_REQUEST, + size << RTKIT_BUFFER_SIZE_SHIFT | addr); + if (error) + return error; + break; + default: + printf("unhandle ioreport event 0x%016llx\n", msg->data0); + return EIO; + } + + return 0; +} + +int +rtkit_init(struct mbox_channel *mc) +{ + struct rtkit_state state; + struct aplmbox_msg msg; + int error; + + memset(&state, 0, sizeof(state)); + state.mc = mc; + + /* Wake up! */ + error = rtkit_send(state.mc, RTKIT_EP_MGMT, RTKIT_MGMT_IOP_PWR_STATE, + RTKIT_MGMT_PWR_STATE_ON); + if (error) + return error; + + while (state.pwrstate != RTKIT_MGMT_PWR_STATE_ON) { + error = rtkit_recv(state.mc, &msg); + if (error) + return error; + + switch (msg.data1) { + 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_IOREPORT: + error = rtkit_handle_ioreport(&state, &msg); + if (error) + return error; + break; + default: + printf("unhandled endpoint %d\n", msg.data1); + return EIO; + } + } + + return 0; +} diff --git a/sys/arch/arm64/dev/rtkit.h b/sys/arch/arm64/dev/rtkit.h new file mode 100644 index 00000000000..c8df0b6f32d --- /dev/null +++ b/sys/arch/arm64/dev/rtkit.h @@ -0,0 +1,3 @@ +/* public domain */ + +int rtkit_init(struct mbox_channel *); -- 2.20.1