root/sys/arch/arm/apple/apple_rtkit.c
/*      $NetBSD: apple_rtkit.c,v 1.1 2022/04/27 08:06:20 skrll Exp $ */
/*      $OpenBSD: rtkit.c,v 1.3 2022/01/10 09:07:28 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/kmem.h>

#include <dev/fdt/fdtvar.h>

#include <arm/apple/apple_rtkit.h>
#include <arm/apple/apple_mbox.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_MASK            __BITS(59, 52)
#define RTKIT_MGMT_TYPE(x)              __SHIFTOUT((x), RTKIT_MGMT_TYPE_MASK)

#define RTKIT_MGMT_PWR_STATE_MASK       __BITS(7, 0)
#define RTKIT_MGMT_PWR_STATE(x)         __SHIFTOUT((x), RTKIT_MGMT_PWR_STATE_MASK)
#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_MASK    __BITS(15, 0)
#define RTKIT_MGMT_HELLO_MINVER(x)      __SHIFTOUT((x), RTKIT_MGMT_HELLO_MINVER_MASK)
#define RTKIT_MGMT_HELLO_MAXVER_MASK    __BITS(31, 16)
#define RTKIT_MGMT_HELLO_MAXVER(x)      __SHIFTOUT((x), RTKIT_MGMT_HELLO_MAXVER_MASK)

#define RTKIT_MGMT_STARTEP_EP_SHIFT     32
#define RTKIT_MGMT_STARTEP_EP_MASK      __BITS(39, 32)
#define RTKIT_MGMT_STARTEP_START        __BIT(1)

#define RTKIT_MGMT_EPMAP_LAST           __BIT(51)
#define RTKIT_MGMT_EPMAP_BASE_MASK      __BITS(34, 32)
#define RTKIT_MGMT_EPMAP_BASE(x)        __SHIFTOUT((x), RTKIT_MGMT_EPMAP_BASE_MASK)
#define RTKIT_MGMT_EPMAP_BITMAP_MASK    __BITS(31, 0)
#define RTKIT_MGMT_EPMAP_BITMAP(x)      __SHIFTOUT((x), RTKIT_MGMT_EPMAP_BITMAP_MASK)
#define RTKIT_MGMT_EPMAP_MORE           __BIT(0)

#define RTKIT_BUFFER_REQUEST            1
#define RTKIT_BUFFER_ADDR_MASK          __BITS(41, 0)
#define RTKIT_BUFFER_ADDR(x)            __SHIFTOUT((x), RTKIT_BUFFER_ADDR_MASK)
#define RTKIT_BUFFER_SIZE_MASK          __BITS(51, 44)
#define RTKIT_BUFFER_SIZE(x)            __SHIFTOUT((x), RTKIT_BUFFER_SIZE_MASK)

/* Versions we support. */
#define RTKIT_MINVER                    11
#define RTKIT_MAXVER                    12

struct rtkit_state {
        struct fdtbus_mbox_channel      *mc;
        int                     pwrstate;
        uint64_t                epmap;
        void                    (*callback[32])(void *, uint64_t);
        void                    *arg[32];
};

static int
rtkit_recv(struct fdtbus_mbox_channel *mc, struct apple_mbox_msg *msg)
{
        int error, timo;

        for (timo = 0; timo < 10000; timo++) {
                error = fdtbus_mbox_recv(mc, msg, sizeof(*msg));
                if (error == 0)
                        break;
                delay(10);
        }

        return error;
}

static int
rtkit_send(struct fdtbus_mbox_channel *mc, uint32_t endpoint,
    uint64_t type, uint64_t data)
{
        struct apple_mbox_msg msg;

        msg.data0 = __SHIFTIN(type, RTKIT_MGMT_TYPE_MASK) | data;
        msg.data1 = endpoint;
        return fdtbus_mbox_send(mc, &msg, sizeof(msg));
}

static int
rtkit_start(struct rtkit_state *state, uint32_t endpoint)
{
        struct fdtbus_mbox_channel *mc = state->mc;
        uint64_t reply;

        reply = __SHIFTIN(endpoint, RTKIT_MGMT_STARTEP_EP_MASK);
        reply |= RTKIT_MGMT_STARTEP_START;
        return rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_STARTEP, reply);
}

static int
rtkit_handle_mgmt(struct rtkit_state *state, struct apple_mbox_msg *msg)
{
        struct fdtbus_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("unsupported minimum firmware version "
                            "%"PRId64"\n", minver);
                        return EINVAL;
                }
                if (maxver < RTKIT_MINVER) {
                        printf("unsupported maximum firmware version "
                        "%"PRId64"\n", maxver);
                        return EINVAL;
                }
                ver = MIN(RTKIT_MAXVER, maxver);
                error = rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_HELLO_ACK,
                    __SHIFTIN(ver, RTKIT_MGMT_HELLO_MINVER_MASK) |
                    __SHIFTIN(ver, RTKIT_MGMT_HELLO_MAXVER_MASK));
                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 = __SHIFTIN(base, RTKIT_MGMT_EPMAP_BASE_MASK);
                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 & __BIT(endpoint)) == 0)
                                        continue;

                                switch (endpoint) {
                                case RTKIT_EP_CRASHLOG:
                                case RTKIT_EP_DEBUG:
                                case RTKIT_EP_IOREPORT:
                                        error = rtkit_start(state, endpoint);
                                        if (error)
                                                return error;
                                        break;
                                }
                        }
                }
                break;
        default:
                printf("unhandled management event "
                    "0x%016"PRIx64"\n", msg->data0);
                return EIO;
        }

        return 0;
}

static int
rtkit_handle_crashlog(struct rtkit_state *state, struct apple_mbox_msg *msg)
{
        struct fdtbus_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);
                // XXXNH WTF is this conditional
                if (addr)
                        break;

                error = rtkit_send(mc, RTKIT_EP_CRASHLOG, RTKIT_BUFFER_REQUEST,
                    __SHIFTIN(size, RTKIT_BUFFER_SIZE_MASK) | addr);
                if (error)
                        return error;
                break;
        default:
                printf("unhandled crashlog event "
                    "0x%016"PRIx64"\n", msg->data0);
                return EIO;
        }

        return 0;
}

static int
rtkit_handle_ioreport(struct rtkit_state *state, struct apple_mbox_msg *msg)
{
        struct fdtbus_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);
                // XXXNH WTF is this conditional
                if (addr)
                        break;
                error = rtkit_send(mc, RTKIT_EP_IOREPORT, RTKIT_BUFFER_REQUEST,
                    __SHIFTIN(size, RTKIT_BUFFER_SIZE_MASK) | addr);
                if (error)
                        return error;
                break;
        default:
                printf("unhandled ioreport event"
                    "0x%016"PRIx64"\n", msg->data0);
                return EIO;
        }

        return 0;
}

int
rtkit_poll(struct rtkit_state *state)
{
        struct fdtbus_mbox_channel *mc = state->mc;
        struct apple_mbox_msg msg;
        void (*callback)(void *, uint64_t);
        void *arg;
        uint32_t endpoint;
        int error;

        error = rtkit_recv(mc, &msg);
        if (error)
                return error;

        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_IOREPORT:
                error = rtkit_handle_ioreport(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("unhandled endpoint %d\n", msg.data1);
                return EIO;
        }

        return 0;
}

static void
rtkit_rx_callback(void *cookie)
{
        rtkit_poll(cookie);
}

struct rtkit_state *
rtkit_init(int node, const char *name)
{
        struct rtkit_state *state;

        state = kmem_zalloc(sizeof(*state), KM_SLEEP);
        if (name) {
                state->mc = fdtbus_mbox_get(node, name, rtkit_rx_callback, state);
        } else {
                state->mc = fdtbus_mbox_get_index(node, 0, rtkit_rx_callback, state);
        }
        if (state->mc == NULL) {
                kmem_free(state, sizeof(*state));

                return NULL;
        }

        return state;
}

int
rtkit_boot(struct rtkit_state *state)
{
        struct fdtbus_mbox_channel *mc = state->mc;
        int error;

        /* Wake up! */
        error = rtkit_send(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)
                rtkit_poll(state);

        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 & __BIT(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);
}