rt 0.19.1

A real-time operating system capable of full preemption
Documentation
#include <gic.h>

#include <rt/arch/semihosting.h>
#include <rt/arch/sysreg.h>

#include <rt/log.h>
#include <rt/panic.h>
#include <rt/tick.h>
#include <rt/trace.h>
#include <rt/trap.h>

#include <stdint.h>

__attribute__((noreturn)) void rt_panic(const char *msg)
{
    semihosting_write0(msg);
    semihosting_write0("\n");
    semihosting_exception(ADP_STOPPED_OS_SPECIFIC);
}

__attribute__((noreturn)) void rt_trap(void)
{
    semihosting_exit_success();
}

static void semihosting_write_hex(uint64_t value)
{
    static const char hex_chars[] = "0123456789abcdef";
    char buf[19];
    buf[0] = '0';
    buf[1] = 'x';
    for (int i = 15; i >= 0; --i)
    {
        buf[2 + (15 - i)] = hex_chars[(value >> (i * 4)) & 0xf];
    }
    buf[18] = '\0';
    semihosting_write0(buf);
}

void unhandled_exception(void);
__attribute__((noreturn)) void unhandled_exception(void)
{
    uint64_t esr = esr_el1();
    uint64_t elr = elr_el1();
    uint64_t far = far_el1();
    uint64_t sp0 = sp_el0();
    uint64_t sp1;
    __asm__ __volatile__("mov %0, sp" : "=r"(sp1));

    semihosting_write0("unhandled exception\n");
    semihosting_write0("ESR_EL1: ");
    semihosting_write_hex(esr);
    semihosting_write0("\nELR_EL1: ");
    semihosting_write_hex(elr);
    semihosting_write0("\nFAR_EL1: ");
    semihosting_write_hex(far);
    semihosting_write0("\nSP_EL0: ");
    semihosting_write_hex(sp0);
    semihosting_write0("\nSP_EL1: ");
    semihosting_write_hex(sp1);
    semihosting_write0("\n");
    semihosting_exception(ADP_STOPPED_RUNTIME_ERROR_UNKNOWN);
}

#include "handlers.h"

#define NUM_INTERRUPTS 1024

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverride-init"
// NOTE: The syscall SGI is handled before gic_vector dispatch in the assembly.
void (*const gic_vector[NUM_INTERRUPTS])(void) = {
    [0 ... NUM_INTERRUPTS - 1] = default_irq_handler,
    [GIC_INTID_TICK] = timer_irq_handler,
    [GIC_INTID_GROUP0] = reserved_irq_handler,
    [GIC_INTID_RESERVED] = reserved_irq_handler,
    [GIC_INTID_NMI] = reserved_irq_handler,
    [GIC_INTID_SPURIOUS] = spurious_irq_handler,
};
#pragma GCC diagnostic pop

void init(void);
void init(void)
{
    cpacr_el1_set(CPACR_EL1_FPEN_ENABLE);
    __asm__("isb" :::);

    // Enable system register access to the GIC CPU interface.
    icc_sre_el1_set(ICC_SRE_SRE | ICC_SRE_DFB | ICC_SRE_DIB);
    __asm__("isb" :::);

    GICD->ctlr = GICD_CTLR_ENABLE_G1NS | GICD_CTLR_ARE_NS;

    // Wake the redistributor so SGI/PPI interrupts are forwarded.
    GICR->rd.waker &= ~GICR_WAKER_PROCESSOR_SLEEP;
    while (GICR->rd.waker & GICR_WAKER_CHILDREN_ASLEEP)
    {
    }

    GICR->sgi.ipriorityr[GIC_INTID_SYSCALL] = GIC_PRIORITY_SYSCALL;
    GICR->sgi.ipriorityr[GIC_INTID_TICK] = GIC_PRIORITY_TICK;

    const uint32_t interrupts =
        (UINT32_C(1) << GIC_INTID_SYSCALL) | (UINT32_C(1) << GIC_INTID_TICK);

    // Set the syscall and tick interrupts as group 1 (IRQ).
    GICR->sgi.igroupr0 = interrupts;
    GICR->sgi.isenabler0 = interrupts;

    icc_pmr_el1_set(GIC_PRIORITY_IDLE);

    icc_igrpen1_el1_set(1);

    // Set the initial timer compare value 1ms in the future.
    cntp_cval_el0_set(cntfrq_el0() / 1000);
    cntp_ctl_el0_set(CNTP_CTL_ENABLE);
}

#if RT_LOG_ENABLE
void rt_logf(const char *format, ...)
{
    (void)format;
}

void rt_log_flush(void)
{
}
#endif