Add an implementation for the protocol to communicate with coprocessors
authorkettenis <kettenis@openbsd.org>
Sun, 19 Dec 2021 23:47:24 +0000 (23:47 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 19 Dec 2021 23:47:24 +0000 (23:47 +0000)
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
sys/arch/arm64/dev/aplns.c
sys/arch/arm64/dev/rtkit.c [new file with mode: 0644]
sys/arch/arm64/dev/rtkit.h [new file with mode: 0644]

index 5be4f94..8e1572f 100644 (file)
@@ -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
index 11c1680..846df28 100644 (file)
@@ -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 <dlg@openbsd.org>
  *
 #include <dev/ic/nvmereg.h>
 #include <dev/ic/nvmevar.h>
 
+#include <arm64/dev/rtkit.h>
+
+#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 (file)
index 0000000..d8b1f76
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $OpenBSD: rtkit.c,v 1.1 2021/12/19 23:47:24 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 <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_misc.h>
+#include <dev/ofw/fdt.h>
+
+#include <arm64/dev/aplmbox.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_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 (file)
index 0000000..c8df0b6
--- /dev/null
@@ -0,0 +1,3 @@
+/* public domain */
+
+int    rtkit_init(struct mbox_channel *);