std1.0.0-beta-7 SafeC docs

Source hal/aarch64.sc

View in the repository

// SafeC Standard Library — AArch64 HAL Implementation
#pragma once
#include <std/hal/aarch64.h>

// ── System registers ──────────────────────────────────────────────────────────

namespace std {

// Definitions of the globals aarch64.h declares extern.
struct Aa64Timer aa64_timer;
struct GicDist gic_dist;
struct GicCpu gic_cpu;


inline unsigned long aa64_read_mpidr() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, mpidr_el1" : "=r"(v)); }
    return v;
}
inline unsigned long aa64_read_currentel() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, CurrentEL" : "=r"(v)); }
    return (v >> 2) & (unsigned long)3;
}
inline unsigned long aa64_read_daif() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, daif" : "=r"(v)); }
    return v;
}
inline void aa64_write_daif(unsigned long val) {
    unsafe { asm volatile ("msr daif, %0" : : "r"(val)); }
}
void aa64_irq_enable()  { unsafe { asm volatile ("msr daifclr, #2"); } }
void aa64_irq_disable() { unsafe { asm volatile ("msr daifset, #2"); } }
void aa64_fiq_enable()  { unsafe { asm volatile ("msr daifclr, #1"); } }
void aa64_fiq_disable() { unsafe { asm volatile ("msr daifset, #1"); } }
void aa64_isb()   { unsafe { asm volatile ("isb" : : : "memory"); } }
void aa64_dsb_sy(){ unsafe { asm volatile ("dsb sy" : : : "memory"); } }
void aa64_dmb_sy(){ unsafe { asm volatile ("dmb sy" : : : "memory"); } }

// ── Generic Timer ─────────────────────────────────────────────────────────────


inline unsigned long long Aa64Timer::read_cntpct() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, cntpct_el0" : "=r"(v)); }
    return (unsigned long long)v;
}

inline unsigned long Aa64Timer::read_cntfrq() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, cntfrq_el0" : "=r"(v)); }
    return v;
}

inline void Aa64Timer::set_tval(unsigned int tval) {
    unsafe { asm volatile ("msr cntp_tval_el0, %0" : : "r"((unsigned long)tval)); }
}

inline void Aa64Timer::enable() {
    // CNTP_CTL_EL0: bit0=ENABLE, bit1=IMASK (clear), bit2=ISTATUS
    unsafe { asm volatile ("msr cntp_ctl_el0, %0" : : "r"((unsigned long)1)); }
}

inline void Aa64Timer::disable() {
    unsafe { asm volatile ("msr cntp_ctl_el0, %0" : : "r"((unsigned long)0)); }
}

inline int Aa64Timer::fire_pending() const {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("mrs %0, cntp_ctl_el0" : "=r"(v)); }
    // ISTATUS = bit 2
    if ((v >> 2) & (unsigned long)1) { return 1; }
    return 0;
}

// ── GIC helpers ───────────────────────────────────────────────────────────────

static unsigned int gic_read32_(unsigned long addr) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)addr;
        return *p;
    }
    return (unsigned int)0;
}
static void gic_write32_(unsigned long addr, unsigned int val) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)addr;
        *p = val;
    }
}

// ── GicDist ───────────────────────────────────────────────────────────────────
// GICD_CTLR   +0x000  GICD_ISENABLER +0x100  GICD_ICENABLER +0x180
// GICD_ISPENDR+0x200  GICD_ICPENDR   +0x280
// GICD_IPRIORITYR +0x400  GICD_ITARGETSR +0x800  GICD_ICFGR +0xC00


inline void GicDist::enable_group0() {
    gic_write32_(self.base, (unsigned int)1);
}

inline void GicDist::enable_irq(unsigned int irq) {
    unsigned long reg = self.base + (unsigned long)0x100 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    gic_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

inline void GicDist::disable_irq(unsigned int irq) {
    unsigned long reg = self.base + (unsigned long)0x180 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    gic_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

inline void GicDist::set_priority(unsigned int irq, unsigned char priority) {
    unsigned long reg = self.base + (unsigned long)0x400 + (unsigned long)irq;
    unsafe {
        volatile unsigned char* p = (volatile unsigned char*)reg;
        *p = priority;
    }
}

inline void GicDist::set_target(unsigned int irq, unsigned char cpu_mask) {
    unsigned long reg = self.base + (unsigned long)0x800 + (unsigned long)irq;
    unsafe {
        volatile unsigned char* p = (volatile unsigned char*)reg;
        *p = cpu_mask;
    }
}

inline void GicDist::set_config(unsigned int irq, int edge_triggered) {
    unsigned long reg = self.base + (unsigned long)0xC00 + (unsigned long)(irq / (unsigned int)16) * (unsigned long)4;
    unsigned int  shift = (irq % (unsigned int)16) * (unsigned int)2;
    unsigned int val = gic_read32_(reg);
    if (edge_triggered != 0) {
        val = val | ((unsigned int)2 << shift);
    } else {
        val = val & ~((unsigned int)3 << shift);
    }
    gic_write32_(reg, val);
}

inline int GicDist::is_pending(unsigned int irq) const {
    unsigned long reg = self.base + (unsigned long)0x200 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    unsigned int val = gic_read32_(reg);
    if ((val >> (irq % (unsigned int)32)) & (unsigned int)1) { return 1; }
    return 0;
}

inline void GicDist::clear_pending(unsigned int irq) {
    unsigned long reg = self.base + (unsigned long)0x280 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    gic_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

// ── GicCpu ────────────────────────────────────────────────────────────────────
// GICC_CTLR +0x000  GICC_PMR +0x004  GICC_IAR +0x00C  GICC_EOIR +0x010
// GICC_RPR  +0x014

inline void GicCpu::enable(unsigned char min_priority) {
    gic_write32_(self.base + (unsigned long)4, (unsigned int)min_priority);
    gic_write32_(self.base, (unsigned int)1);
}

inline void GicCpu::disable() {
    gic_write32_(self.base, (unsigned int)0);
}

inline unsigned int GicCpu::ack() {
    return gic_read32_(self.base + (unsigned long)0xC) & (unsigned int)0x3FF;
}

inline void GicCpu::eoi(unsigned int irq) {
    gic_write32_(self.base + (unsigned long)0x10, irq & (unsigned int)0x3FF);
}

inline unsigned int GicCpu::running_priority() const {
    return gic_read32_(self.base + (unsigned long)0x14);
}

inline void gic_init(unsigned long dist_base, unsigned long cpu_base) {
    gic_dist.base = dist_base;
    gic_cpu.base  = cpu_base;
}

} // namespace std