rt 0.19.1

A real-time operating system capable of full preemption
Documentation
#pragma once

#include <rt/arch/coprocessor.h>
#include <rt/arch/cpsr.h>
#include <rt/arch/mode.h>

#include <vic.h>

#define BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)

struct context
{
#if RT_ARM_FP
    /* FPEXC controls whether floating-point is enabled. Give each task its own
     * copy of this register where floating-point is initially disabled, so
     * attempts to use floating-point in tasks cause undefined instruction
     * exceptions that can be handled by enabling floating-point and
     * re-executing the instruction. See section B1.11.3 of the Arm
     * Architecture Reference Manual ARMv7-A and ARMv7-R Edition. */
    uint32_t fpexc;
#endif // RT_ARM_FP

    uint32_t r4, r5, r6, r7, r8, r9, r10, r11;
    uint32_t r0, r1, r2, r3, r12, lr, pc, psr;
};

static void profile_context_init(struct context *ctx)
{
    /* Tasks start in rt_task_entry, so the initial thumb bit should be set
     * if rt_task_entry is a thumb-mode function. */
    ctx->psr = MODE_SYS | (BIG_ENDIAN ? CPSR_E : 0) |
               ((((uintptr_t)rt_task_entry & 1) != 0) ? CPSR_T : 0);
#if RT_ARM_FP
    // Initialize FPEXC to floating-point disabled.
    ctx->fpexc = 0;
#endif // RT_ARM_FP
}

void rt_task_drop_privilege(void)
{
    __asm__("dsb; cps %0" : : "i"(MODE_USR) : "memory");
}

static inline uint32_t cpsr_mode(void)
{
    uint32_t cpsr;
    __asm__ __volatile__("mrs %0, cpsr" : "=r"(cpsr));
    return cpsr & MODE_MASK;
}

static inline uint32_t spsr_mode(void)
{
    uint32_t spsr;
    __asm__ __volatile__("mrs %0, spsr" : "=r"(spsr));
    return spsr & MODE_MASK;
}

static inline bool mode_is_exception(uint32_t mode)
{
    return (MODE_USR < mode) && (mode < MODE_SYS);
}

bool rt_interrupt_is_active(void)
{
    /* NOTE: this assumes that nested interrupts don't use system mode.
     * Interrupt nesting should use supervisor mode, which doesn't require each
     * task stack to accommodate interrupts. */
    return mode_is_exception(cpsr_mode());
}

/* On A/R-profile, the svc handler doesn't need to preserve volatile
 * registers, so we declare them as being clobbered. */
void rt_syscall_0(enum rt_syscall syscall)
{
    register enum rt_syscall r0 __asm__("r0") = syscall;
    __asm__ __volatile__("svc 0"
                         : "+r"(r0)
                         :
                         : "r1", "r2", "r3", "r12", "lr", "memory");
}

void rt_syscall_1(enum rt_syscall syscall, uintptr_t arg0)
{
    register enum rt_syscall r0 __asm__("r0") = syscall;
    register uintptr_t r1 __asm__("r1") = arg0;
    __asm__ __volatile__("svc 0"
                         : "+r"(r0), "+r"(r1)
                         :
                         : "r2", "r3", "r12", "lr", "memory");
}

void rt_syscall_2(enum rt_syscall syscall, uintptr_t arg0, uintptr_t arg1)
{
    register enum rt_syscall r0 __asm__("r0") = syscall;
    register uintptr_t r1 __asm__("r1") = arg0;
    register uintptr_t r2 __asm__("r2") = arg1;
    __asm__ __volatile__("svc 0"
                         : "+r"(r0), "+r"(r1), "+r"(r2)
                         :
                         : "r3", "r12", "lr", "memory");
}

void rt_syscall_3(enum rt_syscall syscall, uintptr_t arg0, uintptr_t arg1,
                  uintptr_t arg2)
{
    register enum rt_syscall r0 __asm__("r0") = syscall;
    register uintptr_t r1 __asm__("r1") = arg0;
    register uintptr_t r2 __asm__("r2") = arg1;
    register uintptr_t r3 __asm__("r3") = arg2;
    __asm__ __volatile__("svc 0"
                         : "+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3)
                         :
                         : "r12", "lr", "memory");
}

void rt_syscall_pend(void)
{
    vic_syscall_pend();
}

__attribute__((weak)) void rt_cycle_init(void)
{
#if RT_CYCLE_ENABLE
    // Enable counters and reset the cycle counter.
    pmcr_oreq(PMCR_E | PMCR_C);
    // Enable the cycle counter.
    pmcntenset_oreq(PMCNTEN_C);
    // Enable user-mode access to the cycle counter.
    pmuserenr_set(PMUSERENR_EN);
    __asm__("isb" :::);
#endif
}

__attribute__((weak)) uint32_t rt_cycle(void)
{
#if RT_CYCLE_ENABLE
    return pmccntr();
#else
    return 0;
#endif
}

void rt_tls_set(void *tls)
{
    tpidruro_set((uintptr_t)tls);
}