/*
 * Copyright (C) 2022 Apple Inc. All rights reserved.
 *
 * This document is the property of Apple Inc.
 * It is considered confidential and proprietary.
 *
 * This document may not be reproduced or transmitted in any form,
 * in whole or in part, without the express written permission of
 * Apple Inc.
 */

#pragma once

#include <stdint.h>
#include <stddef.h>

#define XNUPROXY__IPC_PACKED __attribute__((packed))

/** Tag fields describing message sent or received */
typedef struct {
    uint16_t words : 6;
    uint16_t capabilities : 3;
    uint16_t unwrapped_capabilities : 3;
    uint16_t non_blocking : 1;
    uint16_t label;
} XNUPROXY__IPC_PACKED xnuproxy_ipc_tag_fields_t;

#define XNUPROXY_IPC_TAG_FIELDS_FORMAT \
    "tag { " \
        "words: %uh, " \
        "capabilities: %uh, " \
        "unwrapped: %uh, " \
        "non_blocking: %s, " \
        "label: 0x%04hx " \
    "}"

#define XNUPROXY_IPC_TAG_FIELDS_FORMAT_ARG(fields) \
    fields.words, \
    fields.capabilities, \
    fields.unwrapped_capabilities, \
    fields.non_blocking ? "true" : "false", \
    fields.label

/** Common native word size */
typedef unsigned long xnuproxy_ipc_word_t;

/** Alignment of all fields in the IPC buffer */
#define XNUPROXY__IPC_FIELD_ALIGN _Alignas(xnuproxy_ipc_word_t)

/** Declaration used for all fields in the IPC buffer */
#define XNUPROXY__IPC_FIELD \
    XNUPROXY__IPC_FIELD_ALIGN \
    xnuproxy_ipc_word_t

#pragma mark "Stable IPC buffer "

/** Maximum number of bytes useable for IPC */
#define XNUPROXY__IPC_BYTES_STABLE (56 * 8)

/** Maximum number of words useable for IPC */
#define XNUPROXY__IPC_WORDS_STABLE \
    (XNUPROXY__IPC_BYTES_STABLE / sizeof(xnuproxy_ipc_word_t))

/** Tag representing message sent or received */
typedef xnuproxy_ipc_tag_fields_t xnuproxy__ipc_tag_stable_t;

/** IPC buffer object alignment */
#define XNUPROXY__IPC_BUFFER_ALIGN _Alignas(1 << 9)

/** Stable IPC buffer structure */
typedef struct {
#define _FIELD \
    /** Compatible version of IPC buffer */
    XNUPROXY__IPC_BUFFER_ALIGN
    xnuproxy_ipc_word_t version;
    /** Fields present in version 1 */
    XNUPROXY__IPC_FIELD_ALIGN
    struct {
        /** Current status of the corresponding exclaves thread */
        XNUPROXY__IPC_FIELD status;
        /** Value returned by exclaves thread downcall */
        XNUPROXY__IPC_FIELD return_value;
        /** ID of endpoint to downcall/upcall */
        XNUPROXY__IPC_FIELD endpoint;
        /** Badge of client upcall */
        XNUPROXY__IPC_FIELD upcall_badge;
        /** Tag associated with message in buffer  */
        XNUPROXY__IPC_FIELD_ALIGN
        xnuproxy__ipc_tag_stable_t tag;
        /** Area useable for IPC transfer */
        XNUPROXY__IPC_FIELD_ALIGN
        /** Message words */
        xnuproxy_ipc_word_t words[XNUPROXY__IPC_WORDS_STABLE];
    };
    /** Add new fields at the end in versioned structures */
} XNUPROXY__IPC_PACKED xnuproxy__ipc_buffer_stable_t;

#pragma mark "Unstable IPC buffer"

#define XNUPROXY__IPC_APPLY(macro, args) (macro args)

/** Number of ipc buffer message registers */
#define XNUPROXY__IPC_LEGACY_MRS 56

/** Number of ipc buffer capability registers */
#define XNUPROXY__IPC_LEGACY_CRS 4

#define XNUPROXY__IPC_BIT(offset) (1ul << offset)
#define XNUPROXY__IPC_MASK(offset) (XNUPROXY__IPC_BIT(offset) - 1ul)

/** Access a word bitfield for cL4 bitfield definitions */
#define XNUPROXY__IPC_BITFIELD_GET(offset, size, bitfield) \
    ((bitfield >> offset) & XNUPROXY__IPC_MASK(size))

/** Set the field in a bitfield */
#define XNUPROXY__IPC_BITFIELD_FIELD(offset, size, value) \
    ((value & XNUPROXY__IPC_MASK(size)) << offset)

/* Bitfield for tag capabilities */
#define XNUPROXY__IPC_TAG_WORDS_LEGACY 0, 6

/* Bitfield for tag capabilities */
#define XNUPROXY__IPC_TAG_CAPABILITIES_LEGACY 6, 3

/* Bitfield for tag capabilities unwrapped */
#define XNUPROXY__IPC_TAG_UNWRAPPED_CAPABILITIES_LEGACY 9, 3

/* Bitfield for tag non-blocking bit */
#define XNUPROXY__IPC_TAG_NON_BLOCKING_LEGACY 12, 1

/* Bitfield for tag label */
#define XNUPROXY__IPC_TAG_LABEL_LEGACY 16, 16

/** Tag representing message sent or received */
typedef xnuproxy_ipc_word_t xnuproxy__ipc_tag_legacy_t;

/** Unstable/legacy IPC buffer structure */
typedef struct {
    /* message registers */
    xnuproxy_ipc_word_t mrs[XNUPROXY__IPC_LEGACY_MRS];
    /* source capability registers */
    xnuproxy_ipc_word_t scr[XNUPROXY__IPC_LEGACY_CRS];
    /* destination capability registers */
    xnuproxy_ipc_word_t dcr[XNUPROXY__IPC_LEGACY_CRS];
} XNUPROXY__IPC_PACKED xnuproxy__ipc_buffer_legacy_t;

#pragma mark "Compatibility shim"

/* Switch this to 1 once all dependents (xnu) have adopted
 * the compatibility shim: rdar://? */
#define XNUPROXY__IPC_BUFFER_USE_STABLE 0

#if XNUPROXY__IPC_BUFFER_USE_STABLE

/* Use the stable API definition */

#define XNUPROXY_IPC_WORD_COUNT XNUPROXY__IPC_WORDS_STABLE
#define XNUPROXY_IPC_BYTE_COUNT XNUPROXY__IPC_BYTES_STABLE
/** Use the stable tag */
typedef xnuproxy__ipc_tag_stable_t xnuproxy_ipc_tag_t;
/** Use the stable buffer representation */
typedef xnuproxy__ipc_buffer_stable_t xnuproxy_ipc_buffer_t;

#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */

/* Use unstable API determined by cL4 */

#define XNUPROXY_IPC_WORD_COUNT XNUPROXY__IPC_LEGACY_MRS
#define XNUPROXY_IPC_BYTE_COUNT \
    (XNUPROXY__IPC_LEGACY_MRS * sizeof(xnuproxy_ipc_word_t))
/** Use the unstable tag */
typedef xnuproxy__ipc_tag_legacy_t xnuproxy_ipc_tag_t;
/** Use the unstable representation */
typedef xnuproxy__ipc_buffer_legacy_t xnuproxy_ipc_buffer_t;

#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */

/**
 * @typedef  xnuproxy_ipc_status_t
 * @abstract Message status types
 *
 * @const XNUPROXY_IPC_STATUS_NONE
 * There is no message to be processed.
 *
 * @const XNUPROXY_IPC_STATUS_PROCESSING
 * The message is being processed.
 *
 * @const XNUPROXY_IPC_STATUS_REPLY
 * The message is a reply.
 *
 * @const XNUPROXY_IPC_STATUS_UPCALL
 * The message is an upcall.
 */
typedef enum : xnuproxy_ipc_word_t {
    XNUPROXY_IPC_STATUS_NONE __attribute__((__swift_name__("none"))) = 0,
    XNUPROXY_IPC_STATUS_PROCESSING __attribute__((__swift_name__("processing"))) = 1,
    XNUPROXY_IPC_STATUS_REPLY __attribute__((__swift_name__("reply"))) = 2,
    XNUPROXY_IPC_STATUS_UPCALL __attribute__((__swift_name__("upcall"))) = 3,
} __attribute__((enum_extensibility(open))) xnuproxy_ipc_status_t;

/** IPC satus word */
static inline volatile xnuproxy_ipc_status_t * _Nonnull
xnuproxy_ipc_status(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->status;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->dcr[2];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for IPC status  */
#define XNUPROXY_IPC_STATUS(buffer) \
    (*xnuproxy_ipc_status(buffer))

/** Downcall return value */
static inline volatile xnuproxy_ipc_word_t * _Nonnull
xnuproxy_ipc_return_value(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->return_value;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->dcr[3];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for return value  */
#define XNUPROXY_IPC_RETURN_VALUE(buffer) \
    (*xnuproxy_ipc_return_value(buffer))

/** ID of endpoint to call */
static inline volatile xnuproxy_ipc_word_t * _Nonnull
xnuproxy_ipc_endpoint(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->endpoint;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->scr[2];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for downcall endpoint  */
#define XNUPROXY_IPC_ENDPOINT(buffer) \
    (*xnuproxy_ipc_endpoint(buffer))

/** Badge of upcall */
static inline volatile xnuproxy_ipc_word_t * _Nonnull
xnuproxy_ipc_upcall_badge(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->upcall_badge;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->scr[3];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for upcall badge  */
#define XNUPROXY_IPC_UPCALL_BADGE(buffer) \
    (*xnuproxy_ipc_upcall_badge(buffer))

/** Tag associated with current message */
static inline volatile xnuproxy_ipc_tag_t * _Nonnull
xnuproxy_ipc_tag(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->tag;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->mrs[0];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for upcall badge  */
#define XNUPROXY_IPC_TAG(buffer) \
    (*xnuproxy_ipc_tag(buffer))

/** Convert tag value into fields */
static inline xnuproxy_ipc_tag_fields_t
xnuproxy_ipc_tag_fields(xnuproxy_ipc_tag_t tag)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return tag;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
#define _GET_FIELD(field) XNUPROXY__IPC_APPLY( \
    XNUPROXY__IPC_BITFIELD_GET, ( \
        XNUPROXY__IPC_TAG_ ## field ## _LEGACY, \
        tag \
    ) \
)
    return (xnuproxy_ipc_tag_fields_t) {
        .words = _GET_FIELD(WORDS),
        .capabilities = _GET_FIELD(CAPABILITIES),
        .unwrapped_capabilities = _GET_FIELD(UNWRAPPED_CAPABILITIES),
        .non_blocking = _GET_FIELD(NON_BLOCKING),
        .label = _GET_FIELD(LABEL),
    };
#undef _GET_FIELD
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Convert tag fields into value */
static inline xnuproxy_ipc_tag_t
xnuproxy_ipc_tag_from_fields(xnuproxy_ipc_tag_fields_t fields)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return fields;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
#define _FIELD(bitfield, field) XNUPROXY__IPC_APPLY( \
    XNUPROXY__IPC_BITFIELD_FIELD, ( \
        XNUPROXY__IPC_TAG_ ## bitfield ## _LEGACY, \
        (xnuproxy_ipc_tag_t)(fields.field) \
    ) \
)
    return 0
        | _FIELD(WORDS, words)
        | _FIELD(CAPABILITIES, capabilities)
        | _FIELD(UNWRAPPED_CAPABILITIES, unwrapped_capabilities)
        | _FIELD(NON_BLOCKING, non_blocking)
        | _FIELD(LABEL, label);
#undef _FIELD
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

typedef xnuproxy_ipc_word_t
xnuproxy_ipc_word_array_t[XNUPROXY_IPC_WORD_COUNT];

/** Message words */
static inline volatile xnuproxy_ipc_word_array_t * _Nonnull
xnuproxy_ipc_words(volatile xnuproxy_ipc_buffer_t *buffer)
{
#if XNUPROXY__IPC_BUFFER_USE_STABLE
    return &buffer->words;
#else /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
    return &buffer->mrs[2];
#endif /* !XNUPROXY_IPC_BUFFER_USE_STABLE */
}

/** Macro useable as lvalue for message words  */
#define XNUPROXY_IPC_WORDS(buffer) \
    (*xnuproxy_ipc_words(buffer))

typedef char xnuproxy_ipc_byte_array_t[XNUPROXY_IPC_BYTE_COUNT];

/** Message words as bytes */
static inline volatile xnuproxy_ipc_byte_array_t * _Nonnull
xnuproxy_ipc_bytes(volatile xnuproxy_ipc_buffer_t *buffer)
{
    return (volatile char *)xnuproxy_ipc_words(buffer);
}

/** Macro useable as lvalue for message words  */
#define XNUPROXY_IPC_BYTES(buffer) \
    (*xnuproxy_ipc_bytes(buffer))
