summaryrefslogtreecommitdiff
path: root/riscv/riscv-probe/libfemto/arch
diff options
context:
space:
mode:
authorCarlos Maiolino <[email protected]>2025-07-10 22:18:39 +0200
committerCarlos Maiolino <[email protected]>2025-07-10 22:18:39 +0200
commit8c6fc0c15415b32080a848bbde640e104098cf13 (patch)
tree04a21bd28f9dc82c8e216390d6208ed93b9bcd11 /riscv/riscv-probe/libfemto/arch
Initial drop
Add some riscv code Signed-off-by: Carlos Maiolino <[email protected]>
Diffstat (limited to 'riscv/riscv-probe/libfemto/arch')
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/auxval.c15
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/csr.c284
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/device.c53
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/memory.c50
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/pmp.c191
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/spinlock.c142
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/start.c21
-rw-r--r--riscv/riscv-probe/libfemto/arch/riscv/trap.c65
8 files changed, 821 insertions, 0 deletions
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/auxval.c b/riscv/riscv-probe/libfemto/arch/riscv/auxval.c
new file mode 100644
index 0000000..eff10d3
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/auxval.c
@@ -0,0 +1,15 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+unsigned long getauxval(unsigned long key)
+{
+ auxval_t *auxv = __auxv;
+ while(auxv->key) {
+ if (auxv->key == key) {
+ return auxv->val;
+ }
+ auxv++;
+ }
+ return 0;
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/csr.c b/riscv/riscv-probe/libfemto/arch/riscv/csr.c
new file mode 100644
index 0000000..673e87e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/csr.c
@@ -0,0 +1,284 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+#include "arch/riscv/csr.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+static int all_csr_enums[] = {
+ csr_fflags,
+ csr_frm,
+ csr_fcsr,
+ csr_mcycle,
+ csr_minstret,
+ csr_mcycleh,
+ csr_minstreth,
+ csr_cycle,
+ csr_time,
+ csr_instret,
+ csr_cycleh,
+ csr_timeh,
+ csr_instreth,
+ csr_mvendorid,
+ csr_marchid,
+ csr_mimpid,
+ csr_mhartid,
+ csr_mstatus,
+ csr_misa,
+ csr_medeleg,
+ csr_mideleg,
+ csr_mie,
+ csr_mtvec,
+ csr_mcounteren,
+ csr_mscratch,
+ csr_mepc,
+ csr_mcause,
+ csr_mtval,
+ csr_mip,
+ csr_sstatus,
+ csr_sedeleg,
+ csr_sideleg,
+ csr_sie,
+ csr_stvec,
+ csr_scounteren,
+ csr_sscratch,
+ csr_sepc,
+ csr_scause,
+ csr_stval,
+ csr_sip,
+ csr_satp,
+ csr_pmpcfg0,
+ csr_pmpcfg1,
+ csr_pmpcfg2,
+ csr_pmpcfg3,
+ csr_pmpaddr0,
+ csr_pmpaddr1,
+ csr_pmpaddr2,
+ csr_pmpaddr3,
+ csr_pmpaddr4,
+ csr_pmpaddr5,
+ csr_pmpaddr6,
+ csr_pmpaddr7,
+ csr_pmpaddr8,
+ csr_pmpaddr9,
+ csr_pmpaddr10,
+ csr_pmpaddr11,
+ csr_pmpaddr12,
+ csr_pmpaddr13,
+ csr_pmpaddr14,
+ csr_pmpaddr15,
+ csr_none,
+};
+
+static const char* all_csr_names[] = {
+ "none",
+ "fflags",
+ "frm",
+ "fcsr",
+ "mcycle",
+ "minstret",
+ "mcycleh",
+ "minstreth",
+ "cycle",
+ "time",
+ "instret",
+ "cycleh",
+ "timeh",
+ "instreth",
+ "mvendorid",
+ "marchid",
+ "mimpid",
+ "mhartid",
+ "mstatus",
+ "misa",
+ "medeleg",
+ "mideleg",
+ "mie",
+ "mtvec",
+ "mcounteren",
+ "mscratch",
+ "mepc",
+ "mcause",
+ "mtval",
+ "mip",
+ "sstatus",
+ "sedeleg",
+ "sideleg",
+ "sie",
+ "stvec",
+ "scounteren",
+ "sscratch",
+ "sepc",
+ "scause",
+ "stval",
+ "sip",
+ "satp",
+ "pmpcfg0",
+ "pmpcfg1",
+ "pmpcfg2",
+ "pmpcfg3",
+ "pmpaddr0",
+ "pmpaddr1",
+ "pmpaddr2",
+ "pmpaddr3",
+ "pmpaddr4",
+ "pmpaddr5",
+ "pmpaddr6",
+ "pmpaddr7",
+ "pmpaddr8",
+ "pmpaddr9",
+ "pmpaddr10",
+ "pmpaddr11",
+ "pmpaddr12",
+ "pmpaddr13",
+ "pmpaddr14",
+ "pmpaddr15"
+};
+
+int* csr_enum_array()
+{
+ return all_csr_enums;
+}
+
+const char** csr_name_array()
+{
+ return all_csr_names;
+}
+
+long read_csr_enum(int csrenum)
+{
+ long result = -1;
+ switch (csrenum) {
+ case csr_fflags: result = read_csr(0x001); break;
+ case csr_frm: result = read_csr(0x002); break;
+ case csr_fcsr: result = read_csr(0x003); break;
+ case csr_mcycle: result = read_csr(0xB00); break;
+ case csr_minstret: result = read_csr(0xB02); break;
+ case csr_mcycleh: result = read_csr(0xB80); break;
+ case csr_minstreth: result = read_csr(0xB82); break;
+ case csr_cycle: result = read_csr(0xC00); break;
+ case csr_time: result = read_csr(0xC01); break;
+ case csr_instret: result = read_csr(0xC02); break;
+ case csr_cycleh: result = read_csr(0xC80); break;
+ case csr_timeh: result = read_csr(0xC81); break;
+ case csr_instreth: result = read_csr(0xC82); break;
+ case csr_mvendorid: result = read_csr(0xF11); break;
+ case csr_marchid: result = read_csr(0xF12); break;
+ case csr_mimpid: result = read_csr(0xF13); break;
+ case csr_mhartid: result = read_csr(0xF14); break;
+ case csr_mstatus: result = read_csr(0x300); break;
+ case csr_misa: result = read_csr(0x301); break;
+ case csr_medeleg: result = read_csr(0x302); break;
+ case csr_mideleg: result = read_csr(0x303); break;
+ case csr_mie: result = read_csr(0x304); break;
+ case csr_mtvec: result = read_csr(0x305); break;
+ case csr_mcounteren: result = read_csr(0x306); break;
+ case csr_mscratch: result = read_csr(0x340); break;
+ case csr_mepc: result = read_csr(0x341); break;
+ case csr_mcause: result = read_csr(0x342); break;
+ case csr_mtval: result = read_csr(0x343); break;
+ case csr_mip: result = read_csr(0x344); break;
+ case csr_sstatus: result = read_csr(0x100); break;
+ case csr_sedeleg: result = read_csr(0x102); break;
+ case csr_sideleg: result = read_csr(0x103); break;
+ case csr_sie: result = read_csr(0x104); break;
+ case csr_stvec: result = read_csr(0x105); break;
+ case csr_scounteren: result = read_csr(0x106); break;
+ case csr_sscratch: result = read_csr(0x140); break;
+ case csr_sepc: result = read_csr(0x141); break;
+ case csr_scause: result = read_csr(0x142); break;
+ case csr_stval: result = read_csr(0x143); break;
+ case csr_sip: result = read_csr(0x144); break;
+ case csr_satp: result = read_csr(0x180); break;
+ case csr_pmpcfg0: result = read_csr(0x3A0); break;
+ case csr_pmpcfg1: result = read_csr(0x3A1); break;
+ case csr_pmpcfg2: result = read_csr(0x3A2); break;
+ case csr_pmpcfg3: result = read_csr(0x3A3); break;
+ case csr_pmpaddr0: result = read_csr(0x3B0); break;
+ case csr_pmpaddr1: result = read_csr(0x3B1); break;
+ case csr_pmpaddr2: result = read_csr(0x3B2); break;
+ case csr_pmpaddr3: result = read_csr(0x3B3); break;
+ case csr_pmpaddr4: result = read_csr(0x3B4); break;
+ case csr_pmpaddr5: result = read_csr(0x3B5); break;
+ case csr_pmpaddr6: result = read_csr(0x3B6); break;
+ case csr_pmpaddr7: result = read_csr(0x3B7); break;
+ case csr_pmpaddr8: result = read_csr(0x3B8); break;
+ case csr_pmpaddr9: result = read_csr(0x3B9); break;
+ case csr_pmpaddr10: result = read_csr(0x3BA); break;
+ case csr_pmpaddr11: result = read_csr(0x3BB); break;
+ case csr_pmpaddr12: result = read_csr(0x3BC); break;
+ case csr_pmpaddr13: result = read_csr(0x3BD); break;
+ case csr_pmpaddr14: result = read_csr(0x3BE); break;
+ case csr_pmpaddr15: result = read_csr(0x3BF); break;
+ default: break;
+ }
+ return result;
+}
+
+void write_csr_enum(int csrenum, long value)
+{
+ switch (csrenum) {
+ case csr_fflags: write_csr(0x001, value); break;
+ case csr_frm: write_csr(0x002, value); break;
+ case csr_fcsr: write_csr(0x003, value); break;
+ case csr_mcycle: write_csr(0xB00, value); break;
+ case csr_minstret: write_csr(0xB02, value); break;
+ case csr_mcycleh: write_csr(0xB80, value); break;
+ case csr_minstreth: write_csr(0xB82, value); break;
+ case csr_cycle: write_csr(0xC00, value); break;
+ case csr_time: write_csr(0xC01, value); break;
+ case csr_instret: write_csr(0xC02, value); break;
+ case csr_cycleh: write_csr(0xC80, value); break;
+ case csr_timeh: write_csr(0xC81, value); break;
+ case csr_instreth: write_csr(0xC82, value); break;
+ case csr_mvendorid: write_csr(0xF11, value); break;
+ case csr_marchid: write_csr(0xF12, value); break;
+ case csr_mimpid: write_csr(0xF13, value); break;
+ case csr_mhartid: write_csr(0xF14, value); break;
+ case csr_mstatus: write_csr(0x300, value); break;
+ case csr_misa: write_csr(0x301, value); break;
+ case csr_medeleg: write_csr(0x302, value); break;
+ case csr_mideleg: write_csr(0x303, value); break;
+ case csr_mie: write_csr(0x304, value); break;
+ case csr_mtvec: write_csr(0x305, value); break;
+ case csr_mcounteren: write_csr(0x306, value); break;
+ case csr_mscratch: write_csr(0x340, value); break;
+ case csr_mepc: write_csr(0x341, value); break;
+ case csr_mcause: write_csr(0x342, value); break;
+ case csr_mtval: write_csr(0x343, value); break;
+ case csr_mip: write_csr(0x344, value); break;
+ case csr_sstatus: write_csr(0x100, value); break;
+ case csr_sedeleg: write_csr(0x102, value); break;
+ case csr_sideleg: write_csr(0x103, value); break;
+ case csr_sie: write_csr(0x104, value); break;
+ case csr_stvec: write_csr(0x105, value); break;
+ case csr_scounteren: write_csr(0x106, value); break;
+ case csr_sscratch: write_csr(0x140, value); break;
+ case csr_sepc: write_csr(0x141, value); break;
+ case csr_scause: write_csr(0x142, value); break;
+ case csr_stval: write_csr(0x143, value); break;
+ case csr_sip: write_csr(0x144, value); break;
+ case csr_satp: write_csr(0x180, value); break;
+ case csr_pmpcfg0: write_csr(0x3A0, value); break;
+ case csr_pmpcfg1: write_csr(0x3A1, value); break;
+ case csr_pmpcfg2: write_csr(0x3A2, value); break;
+ case csr_pmpcfg3: write_csr(0x3A3, value); break;
+ case csr_pmpaddr0: write_csr(0x3B0, value); break;
+ case csr_pmpaddr1: write_csr(0x3B1, value); break;
+ case csr_pmpaddr2: write_csr(0x3B2, value); break;
+ case csr_pmpaddr3: write_csr(0x3B3, value); break;
+ case csr_pmpaddr4: write_csr(0x3B4, value); break;
+ case csr_pmpaddr5: write_csr(0x3B5, value); break;
+ case csr_pmpaddr6: write_csr(0x3B6, value); break;
+ case csr_pmpaddr7: write_csr(0x3B7, value); break;
+ case csr_pmpaddr8: write_csr(0x3B8, value); break;
+ case csr_pmpaddr9: write_csr(0x3B9, value); break;
+ case csr_pmpaddr10: write_csr(0x3BA, value); break;
+ case csr_pmpaddr11: write_csr(0x3BB, value); break;
+ case csr_pmpaddr12: write_csr(0x3BC, value); break;
+ case csr_pmpaddr13: write_csr(0x3BD, value); break;
+ case csr_pmpaddr14: write_csr(0x3BE, value); break;
+ case csr_pmpaddr15: write_csr(0x3BF, value); break;
+ default: break;
+ }
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/device.c b/riscv/riscv-probe/libfemto/arch/riscv/device.c
new file mode 100644
index 0000000..9c3a6bd
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/device.c
@@ -0,0 +1,53 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+void register_console(console_device_t *dev)
+{
+ console_dev = dev;
+ if (dev->init) {
+ dev->init();
+ }
+}
+
+void register_poweroff(poweroff_device_t *dev)
+{
+ poweroff_dev = dev;
+ if (dev->init) {
+ dev->init();
+ }
+}
+
+static int default_getchar()
+{
+ asm volatile("ebreak");
+ return 0;
+}
+
+static int default_putchar(int ch)
+{
+ asm volatile("ebreak");
+ return 0;
+}
+
+static void default_poweroff(int status)
+{
+ asm volatile("ebreak");
+ while (1) {
+ asm volatile("" : : : "memory");
+ }
+}
+
+console_device_t console_none = {
+ NULL,
+ default_getchar,
+ default_putchar
+};
+
+poweroff_device_t poweroff_none = {
+ NULL,
+ default_poweroff,
+};
+
+console_device_t *console_dev = &console_none;
+poweroff_device_t *poweroff_dev = &poweroff_none;
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/memory.c b/riscv/riscv-probe/libfemto/arch/riscv/memory.c
new file mode 100644
index 0000000..440e67c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/memory.c
@@ -0,0 +1,50 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+#include "arch/riscv/trap.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+extern char _memory_start;
+
+static memory_info_t memory_info = {
+ .start = -1UL,
+ .end = -1UL
+};
+
+static volatile int save_cause = -1;
+
+static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
+{
+ save_cause = mcause;
+ write_csr(mepc, mepc + 4);
+}
+
+uintptr_t memory_probe_range(uintptr_t start, uintptr_t end)
+{
+ trap_fn save = get_trap_fn();
+ set_trap_fn(trap_save_cause);
+ volatile uintptr_t *p = (volatile uintptr_t *)start;
+ for (; p < (uintptr_t *)end; p += RISCV_PGSIZE) {
+ /* trap_save_cause adds 4 to the PC so we
+ * can't emit compressed instructions */
+ asm volatile (".option push");
+ asm volatile (".option norvc");
+ asm volatile ("" : : "r" (*p));
+ asm volatile (".option pop");
+ if (save_cause != -1) {
+ break;
+ }
+ }
+ set_trap_fn(save);
+ return ((uintptr_t)p)-1;
+}
+
+memory_info_t memory_probe()
+{
+ if (memory_info.start == -1UL) {
+ memory_info.start = (uintptr_t)&_memory_start,
+ memory_info.end = memory_probe_range((uintptr_t)&_memory_start, -1UL);
+ }
+ return memory_info;
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/pmp.c b/riscv/riscv-probe/libfemto/arch/riscv/pmp.c
new file mode 100644
index 0000000..a540527
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/pmp.c
@@ -0,0 +1,191 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+#include "arch/riscv/csr.h"
+#include "arch/riscv/trap.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+static pmp_info_t pmp_info = {
+ .width = -1,
+ .count = -1,
+ .granularity = -1
+};
+
+static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
+{
+ write_csr(mepc, mepc + 4);
+}
+
+pmp_info_t pmp_probe()
+{
+ trap_fn save;
+
+ if (pmp_info.count >= 0) {
+ return pmp_info;
+ }
+
+ /* loop through PMPs checking we can set any bits */
+ save = get_trap_fn();
+ set_trap_fn(trap_save_cause);
+ pmp_info.count = 0;
+ for (size_t i = 0; i < PMPADDR_COUNT; i++) {
+ uintptr_t addr, addrsave = read_csr_enum(csr_pmpaddr0 + i);
+ write_csr_enum(csr_pmpaddr0 + i, -1UL);
+ addr = read_csr_enum(csr_pmpaddr0 + i);
+ write_csr_enum(csr_pmpaddr0 + i, addrsave);
+ if (addr) {
+ pmp_info.count++;
+ if (i == 0) {
+ pmp_info.width = (sizeof(addr) << 3) - clz(addr) + 2;
+ pmp_info.granularity = ctz(addr) + 2;
+ }
+ } else {
+ if (i == 0) {
+ pmp_info.width = 0;
+ pmp_info.granularity = 0;
+ }
+ break;
+ }
+ }
+ set_trap_fn(save);
+
+ return pmp_info;
+}
+
+int pmp_entry_width()
+{
+ if (pmp_info.width < 0) {
+ pmp_probe();
+ }
+ return pmp_info.width;
+}
+
+int pmp_entry_granularity()
+{
+ if (pmp_info.granularity < 0) {
+ pmp_probe();
+ }
+ return pmp_info.granularity;
+}
+
+int pmp_entry_count()
+{
+ if (pmp_info.count < 0) {
+ pmp_probe();
+ }
+ return pmp_info.count;
+}
+
+void pmp_clear_all()
+{
+ trap_fn save = get_trap_fn();
+ set_trap_fn(trap_save_cause);
+ for (size_t i = 0; i < PMPCFG_COUNT; i++) {
+ write_csr_enum(csr_pmpcfg0 + i, 0);
+ }
+ for (size_t i = 0; i < PMPADDR_COUNT; i++) {
+ write_csr_enum(csr_pmpaddr0 + i, 0);
+ }
+ set_trap_fn(save);
+}
+
+void pmp_allow_all()
+{
+ const uintptr_t pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X;
+
+ if (pmp_entry_count() == 0) {
+ return;
+ }
+
+ pmp_clear_all();
+
+ /* borrowed from bbl */
+ asm volatile ("la t0, 1f\n\t"
+ "csrrw t0, mtvec, t0\n\t"
+ "csrw pmpaddr0, %1\n\t"
+ "csrw pmpcfg0, %0\n\t"
+ ".align 2\n\t"
+ "1: csrw mtvec, t0"
+ : : "r" (pmpc), "r" (-1UL) : "t0");
+}
+
+static int pmp_entry_set_pow2(unsigned n, uint8_t prot, uint64_t addr, uint64_t len)
+{
+ /* calculate PMP config register and offset */
+ int pmpcfg_csr = (__riscv_xlen == 32) ? csr_pmpcfg0 + (n >> 2) :
+ (__riscv_xlen == 64) ? csr_pmpcfg0 + (n >> 2) & ~1 : -1;
+ int pmpcfg_shift = (__riscv_xlen == 32) ? (n & 3) << 3 :
+ (__riscv_xlen == 64) ? (n & 7) << 3 : -1;
+ if (pmpcfg_csr < 0 || pmpcfg_shift < 0) {
+ return -1;
+ }
+
+ /* encode config */
+ prot |= (len == 4) ? PMP_NA4 : PMP_NAPOT;
+ uintptr_t pmpcfg = ((uintptr_t)prot) << pmpcfg_shift;
+ uintptr_t cfgmask = ~(0xff << pmpcfg_shift);
+
+ /* encode address */
+ int pmpaddr_csr = csr_pmpaddr0 + n;
+ uintptr_t pmpmask = (1UL << (ctz(len) - PMP_SHIFT)) - 1;
+ uintptr_t pmpaddr = ((addr >> PMP_SHIFT) & ~pmpmask) | (pmpmask >> 1);
+
+ /* write csrs */
+ pmpcfg |= (read_csr_enum(pmpcfg_csr) & cfgmask) | pmpcfg;
+ write_csr_enum(pmpcfg_csr, pmpcfg);
+ write_csr_enum(pmpaddr_csr, pmpaddr);
+
+ return 0;
+}
+
+static int pmp_entry_set_range(unsigned n, uint8_t prot, uint64_t addr, uint64_t len)
+{
+ /* calculate PMP config register and offset */
+ int pmpcfg_csr0 = (__riscv_xlen == 32) ? csr_pmpcfg0 + (n >> 2) :
+ (__riscv_xlen == 64) ? csr_pmpcfg0 + (n >> 2) & ~1 : -1;
+ int pmpcfg_csr1 = (__riscv_xlen == 32) ? csr_pmpcfg0 + ((n+1) >> 2) :
+ (__riscv_xlen == 64) ? csr_pmpcfg0 + ((n+1) >> 2) & ~1 : -1;
+ int pmpcfg_shift0 = (__riscv_xlen == 32) ? (n & 3) << 3 :
+ (__riscv_xlen == 64) ? (n & 7) << 3 : -1;
+ int pmpcfg_shift1 = (__riscv_xlen == 32) ? ((n+1) & 3) << 3 :
+ (__riscv_xlen == 64) ? ((n+1) & 7) << 3 : -1;
+ if (pmpcfg_csr0 < 0 || pmpcfg_csr1 < 0 || pmpcfg_shift0 < 0 || pmpcfg_shift1 < 0) {
+ return -1;
+ }
+
+ /* encode config */
+ uintptr_t pmpcfg0 = ((uintptr_t)prot) << pmpcfg_shift0;
+ uintptr_t cfgmask0 = ~(0xff << pmpcfg_shift0);
+ uintptr_t pmpcfg1 = ((uintptr_t)prot | PMP_TOR) << pmpcfg_shift1;
+ uintptr_t cfgmask1 = ~(0xff << pmpcfg_shift1);
+
+ /* encode address */
+ int pmpaddr_csr0 = csr_pmpaddr0 + n;
+ int pmpaddr_csr1 = csr_pmpaddr1 + n;
+ uintptr_t pmpaddr0 = addr >> PMP_SHIFT;
+ uintptr_t pmpaddr1 = (addr + len) >> PMP_SHIFT;
+
+ /* write csrs */
+ pmpcfg0 |= (read_csr_enum(pmpcfg_csr0) & cfgmask0) | pmpcfg0;
+ write_csr_enum(pmpcfg_csr0, pmpcfg0);
+ write_csr_enum(pmpaddr_csr0, pmpaddr0);
+ pmpcfg1 |= (read_csr_enum(pmpcfg_csr1) & cfgmask1) | pmpcfg1;
+ write_csr_enum(pmpcfg_csr1, pmpcfg1);
+ write_csr_enum(pmpaddr_csr1, pmpaddr1);
+
+ return 0;
+}
+
+int pmp_entry_set(unsigned n, uint8_t prot, uint64_t addr, uint64_t len)
+{
+ /* check parameters */
+ if (n >= PMPADDR_COUNT || len < 4) {
+ return -1;
+ }
+ if (ispow2(len)) {
+ return pmp_entry_set_pow2(n, prot, addr, len);
+ } else {
+ return pmp_entry_set_range(n, prot, addr, len);
+ }
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/spinlock.c b/riscv/riscv-probe/libfemto/arch/riscv/spinlock.c
new file mode 100644
index 0000000..b8bbe72
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/spinlock.c
@@ -0,0 +1,142 @@
+// See LICENSE for license details.
+
+#include <arch/riscv/spinlock.h>
+
+/**
+ * spinlock lock
+ *
+ * \param lock pointer to lock
+ *
+ * lui a2,0x10
+ * 1: lr.w.aq a4,(a0)
+ * addw a5,a2,a4
+ * sc.w.rl a3,a5,(a0)
+ * bnez a4,1b
+ * srliw a4,a5,0x10
+ * fence r,rw
+ * 2: slliw a5,a5,0x10
+ * srliw a5,a5,0x10
+ * bne a5,a4,3f
+ * ret
+ * 3: lw a5,0(a0)
+ * fence r,rw
+ * j 2b
+ */
+
+void spinlock_lock(spinlock_t *lock)
+{
+ ticketdata_t ld;
+ ticket_t ticket_num;
+
+ /* increment tail to acquire a ticket number */
+ do {
+ ld = _lr_aq_w(&lock->data) + ((ticketdata_t)1 << TICKET_SHIFT);
+ } while (_sc_rl_w(&lock->data, ld));
+
+ /* fetch our ticket number */
+ ticket_num = ticketlock_tail(ld);
+
+ /* subsequent modifications are ordered after lock read */
+ _barrier_acquire();
+
+ /* wail until our ticket number is head */
+ while (ticketlock_head(ld) != ticket_num) {
+ ld = _l_aq_w(&lock->data);
+ }
+}
+
+/**
+ * spinlock trylock
+ *
+ * \param lock pointer to lock
+ * \return non zero if lock was successfully taken
+ *
+ * lui a5,0x10
+ * lr.w.aq a4,(a0)
+ * addw a5,a5,a4
+ * slliw a3,a5,0x10
+ * srliw a3,a3,0x10
+ * srliw a4,a5,0x10
+ * beq a3,a4,2f
+ * 1: li a0,0
+ * ret
+ * 2: sc.w.rl a4,a5,(a0)
+ * bnez a4,1b
+ * fence r,rw
+ * li a0,1
+ * ret
+ */
+
+int spinlock_trylock(spinlock_t *lock)
+{
+ ticketdata_t ld;
+ ticket_t ticket_num;
+ int result;
+
+ /* increment tail to the next ticket number */
+ ld = _lr_aq_w(&lock->data) + ((ticketdata_t)1 << TICKET_SHIFT);
+ ticket_num = ticketlock_tail(ld);
+
+ /* return true if our ticket is next and lock update succeeded */
+ result = ticketlock_head(ld) == ticket_num && !_sc_rl_w(&lock->data, ld);
+
+ if (result) {
+ /* subsequent modifications are ordered after lock read */
+ _barrier_acquire();
+ }
+
+ return result;
+}
+
+/**
+ * spinlock unlock
+ *
+ * \param lock pointer to lock
+ *
+ * fence rw,w
+ * 1: lr.w.aq a4,(a0)
+ * srliw a5,a4,0x10
+ * addiw a4,a4,1
+ * slliw a4,a4,0x10
+ * srliw a4,a4,0x10
+ * slliw a5,a5,0x10
+ * or a5,a5,a4
+ * sc.w.rl a4,a5,(a0)
+ * bnez a5,1b
+ * ret
+ */
+void spinlock_unlock(spinlock_t *lock)
+{
+ ticketdata_t ld;
+
+ /* prior modifications are ordered before lock write */
+ _barrier_release();
+
+ /* increment head to release lock to the next ticket holder */
+ do {
+ ld = _lr_aq_w(&lock->data);
+ ld = ticketlock_data(ticketlock_tail(ld), ticketlock_head(ld) + 1);
+ } while (_sc_rl_w(&lock->data, ld));
+}
+
+/**
+ * spinlock waiters
+ *
+ * \param lock pointer to lock
+ * \return number of waiters
+ *
+ * lw a5,0(a0)
+ * fence r,rw
+ * srliw a0,a5,0x10
+ * slliw a5,a5,0x10
+ * srliw a5,a5,0x10
+ * subw a0,a0,a5
+ * addiw a0,a0,1
+ * ret
+ */
+int spinlock_waiters(spinlock_t *lock)
+{
+ ticketdata_t ld = _l_aq_w(&lock->data);
+
+ return (int)ticketlock_tail(ld) - (int)ticketlock_head(ld) + 1;
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/start.c b/riscv/riscv-probe/libfemto/arch/riscv/start.c
new file mode 100644
index 0000000..60f48a2
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/start.c
@@ -0,0 +1,21 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+extern char _bss_start;
+extern char _bss_end;
+extern char _memory_end;
+
+int main(int argc, char **argv);
+
+__attribute__((noreturn)) void libfemto_start_main()
+{
+ char *argv[] = { "femto", NULL };
+ memset(&_bss_start, 0, &_bss_end - &_bss_start);
+ arch_setup();
+ _malloc_addblock(&_bss_end, &_memory_end - &_bss_end);
+ exit(main(1, argv));
+ __builtin_unreachable();
+}
diff --git a/riscv/riscv-probe/libfemto/arch/riscv/trap.c b/riscv/riscv-probe/libfemto/arch/riscv/trap.c
new file mode 100644
index 0000000..6a7d80c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/arch/riscv/trap.c
@@ -0,0 +1,65 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+#include "arch/riscv/trap.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+static trap_fn tfn = 0;
+
+const char * riscv_excp_names[16] = {
+ "misaligned_fetch",
+ "fault_fetch",
+ "illegal_instruction",
+ "breakpoint",
+ "misaligned_load",
+ "fault_load",
+ "misaligned_store",
+ "fault_store",
+ "user_ecall",
+ "supervisor_ecall",
+ "hypervisor_ecall",
+ "machine_ecall",
+ "exec_page_fault",
+ "load_page_fault",
+ "reserved",
+ "store_page_fault"
+};
+
+const char * riscv_intr_names[16] = {
+ "u_software",
+ "s_software",
+ "h_software",
+ "m_software",
+ "u_timer",
+ "s_timer",
+ "h_timer",
+ "m_timer",
+ "u_external",
+ "s_external",
+ "h_external",
+ "m_external",
+ "reserved",
+ "reserved",
+ "reserved",
+ "reserved"
+};
+
+trap_fn get_trap_fn()
+{
+ return tfn;
+}
+
+void set_trap_fn(trap_fn fn)
+{
+ tfn = fn;
+}
+
+void trap_handler(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
+{
+ if (tfn) {
+ tfn(regs, mcause, mepc);
+ } else {
+ die("machine mode: unhandlable trap %d @ %p", mcause, mepc);
+ }
+}