/*
 * 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.
 */

// TODO: Make this header visible to XNU

#pragma once

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

/** Size of the panic buffer (a single frame) */
#define XNUPROXY_PANIC_BUFFER_SIZE (1 << 16)

/** Size reserved for descriptive string */
#define XNUPROXY_PANIC_MESSAGE_LEN 128

/** Alignment of the panic buffer  (frame-aligned) */
#define XNUPROXY_PANIC_BUFFER_ALIGN XNUPROXY_PANIC_BUFFER_SIZE

/** Space reserved for formatted panic message (first half of frame) */
#define XNUPROXY_PANIC_RENDERED_LEN (XNUPROXY_PANIC_BUFFER_SIZE / 2)

/** Number of bytes reserved to store a process name */
#define XNUPROXY_PANIC_NAME_BYTES 64

/** Number of 32-bit words used to store the image UUID */
#define XNUPROXY_PANIC_UUID_BYTES (128 / CHAR_BIT)

/** States of the panic buffer to indicate whether a panic occurred at cL4 user-level */
typedef enum : size_t {
    XNUPROXY_PANIC_UNSET = 0,
    XNUPROXY_PANIC_PARTIAL = 1,
    XNUPROXY_PANIC_COMPLETE = 2,
} xnuproxy_panic_status_t;

/** Four-character constant identifier */
typedef uint32_t xnuproxy_panic_four_cc_t;

/** Structured information about panicked thread */
typedef struct {
    /** Status of the panic buffer */
    _Atomic(xnuproxy_panic_four_cc_t) status;

    /** 64-bit identifier of address space in which the thread was executing */
    uint64_t address_space_id;

    /** Component for which the thread was executing */
    struct {
        /** Descriptive name of the process */
        char name[XNUPROXY_PANIC_NAME_BYTES];
        /** 64-bit identifier */
        uint64_t numeric_id;
        /** 64-bit selector value */
        uint64_t selector;
    } component;

    /** FourCC identifiers for thread */
    struct {
        /** Address space */
        xnuproxy_panic_four_cc_t space;
        /** Tightbeam component */
        xnuproxy_panic_four_cc_t component;
        /** Endpoint to which panicked thread is bound */
        xnuproxy_panic_four_cc_t endpoint;
        /** Panicked thread */
        xnuproxy_panic_four_cc_t thread;
    } four_cc;

    /** Thread context data */
    struct {
        /** Address of the thread structure */
        size_t address;
        /** Address of the thread's TSS array */
        size_t tss_base;
        /** Addres of the thread's IPC buffer */
        size_t ipc_buffer;
        /** ID of the execution context */
        size_t scheduling_context_id;
        /** ID of the execution context */
        size_t execution_context_id;
        /** Numeric ID of the thread */
        size_t thread_numeric_id;
        /** ID of the endpoint  */
        size_t endpoint_id;
        /** The thread's stack region */
        struct {
            /** The lowest-value address of the stack region */
            size_t start;
            /** The size of the stack region */
            size_t size;
            /** The inital value of the call stack */
            size_t call_base;
        } stack;
        /** Subset of the thread's registers */
        struct {
            /** Link register */
            size_t lr;
            /** Program counter */
            size_t pc;
            /** Stack pointer */
            size_t sp;
            /** Status register */
            size_t cpsr;
        } registers;
    } thread;

    /** Backtrace bounds */
    struct {
        /** Number of frame entries */
        size_t frames;
        /** First index in image array */
        size_t image_base;
    } backtrace;
} xnuproxy_panicked_thread_t;

/** Maximum size of backtrace in bytes */
#define XNUPROXY__PANIC_BACKTRACE_BYTES ( \
    XNUPROXY_PANIC_BUFFER_SIZE - ( \
        XNUPROXY_PANIC_RENDERED_LEN + \
        XNUPROXY_PANIC_MESSAGE_LEN + \
        sizeof(xnuproxy_panicked_thread_t) \
    ) \
)

/** Image UUID from backtrace */
typedef struct  {
    char uuid[XNUPROXY_PANIC_UUID_BYTES];
} xnuproxy_panic_backtrace_image_t;
static_assert(
    sizeof(xnuproxy_panic_backtrace_image_t) == XNUPROXY_PANIC_UUID_BYTES,
    "Panic backtrace image is UUID sized"
);

#define XNUPROXY__PANIC_BACKTRACE_IMAGES \
    (XNUPROXY__PANIC_BACKTRACE_BYTES / sizeof(xnuproxy_panic_backtrace_image_t))

/** Return address from backtrace */
typedef struct  {
    /** Offset of address in respective image */
    uintptr_t offset : 48;
    /** Index into image array containing corresponding UUID */
    uintptr_t image : 16;
} xnuproxy_panic_backtrace_word_t;
static_assert(
    sizeof(xnuproxy_panic_backtrace_word_t) == sizeof(uintptr_t),
    "Panic bactrace word is only one word"
);

/** Maximum number of words of recorded backtrace */
#define XNUPROXY__PANIC_BACKTRACE_WORDS \
    ( XNUPROXY__PANIC_BACKTRACE_BYTES / sizeof(xnuproxy_panic_backtrace_word_t))

/** Shared buffer of panic data with XNU */
typedef struct {
    /** Panic message */
    _Alignas(XNUPROXY_PANIC_BUFFER_ALIGN)
    char rendered[XNUPROXY_PANIC_RENDERED_LEN];

    /** formatted panic string */
    char message[XNUPROXY_PANIC_MESSAGE_LEN];

    /** Panicked thread state */
    xnuproxy_panicked_thread_t panicked_thread;

    /** Backtrace of panicked thread */
    union {
        /** Image UUIDS */
        xnuproxy_panic_backtrace_image_t
        images[XNUPROXY__PANIC_BACKTRACE_IMAGES];
        /** Bactrace words with references to images */
        xnuproxy_panic_backtrace_word_t
        words[XNUPROXY__PANIC_BACKTRACE_WORDS];
    } backtrace;
} xnuproxy_panic_buffer_t;

_Static_assert(
    _Alignof(xnuproxy_panic_buffer_t) == XNUPROXY_PANIC_BUFFER_ALIGN,
    "Panic buffer fits in frame"
);
_Static_assert(
    sizeof(xnuproxy_panic_buffer_t) <= XNUPROXY_PANIC_BUFFER_SIZE,
    "Panic buffer fits in frame"
);
