summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--riscv/.gitignore2
-rw-r--r--riscv/README.md1
-rwxr-xr-xriscv/array_search.rvbin0 -> 1080 bytes
-rw-r--r--riscv/array_search.s39
-rwxr-xr-xriscv/exit/exit.rvbin0 -> 860 bytes
-rw-r--r--riscv/exit/exit.s5
-rw-r--r--riscv/exit/main.s5
-rw-r--r--riscv/location_counter.s18
-rwxr-xr-xriscv/lvcounter.rvbin0 -> 788 bytes
-rwxr-xr-xriscv/pow.rvbin0 -> 840 bytes
-rw-r--r--riscv/pow.s39
-rw-r--r--riscv/riscv-probe/.gitignore1
-rw-r--r--riscv/riscv-probe/LICENSE25
-rw-r--r--riscv/riscv-probe/Makefile128
-rw-r--r--riscv/riscv-probe/README.md347
-rw-r--r--riscv/riscv-probe/env/common/constants.s18
-rw-r--r--riscv/riscv-probe/env/common/crtm.s89
-rw-r--r--riscv/riscv-probe/env/common/rv32/macros.s23
-rw-r--r--riscv/riscv-probe/env/common/rv64/macros.s23
-rw-r--r--riscv/riscv-probe/env/coreip-e2-arty/crt.s1
-rw-r--r--riscv/riscv-probe/env/coreip-e2-arty/default.lds46
-rw-r--r--riscv/riscv-probe/env/coreip-e2-arty/setup.c20
-rw-r--r--riscv/riscv-probe/env/default/crt.s1
-rw-r--r--riscv/riscv-probe/env/default/default.lds45
-rw-r--r--riscv/riscv-probe/env/default/setup.c9
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_e/crt.s1
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_e/default.lds47
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_e/setup.c17
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_u/crt.s1
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_u/default.lds46
-rw-r--r--riscv/riscv-probe/env/qemu-sifive_u/setup.c17
-rw-r--r--riscv/riscv-probe/env/semihost/crt.s28
-rw-r--r--riscv/riscv-probe/env/semihost/default.lds45
-rw-r--r--riscv/riscv-probe/env/semihost/setup.c12
-rw-r--r--riscv/riscv-probe/env/spike/crt.s1
-rw-r--r--riscv/riscv-probe/env/spike/default.lds52
-rw-r--r--riscv/riscv-probe/env/spike/setup.c18
-rw-r--r--riscv/riscv-probe/env/virt/crt.s1
-rw-r--r--riscv/riscv-probe/env/virt/default.lds46
-rw-r--r--riscv/riscv-probe/env/virt/setup.c17
-rw-r--r--riscv/riscv-probe/examples/abort/main.c6
-rw-r--r--riscv/riscv-probe/examples/abort/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/alloca/main.c17
-rw-r--r--riscv/riscv-probe/examples/alloca/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/argv0/main.c6
-rw-r--r--riscv/riscv-probe/examples/argv0/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/enclave/enclave.c79
-rw-r--r--riscv/riscv-probe/examples/enclave/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/hang/hang.c23
-rw-r--r--riscv/riscv-probe/examples/hang/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/hello/hello.c6
-rw-r--r--riscv/riscv-probe/examples/hello/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/malloc/main.c21
-rw-r--r--riscv/riscv-probe/examples/malloc/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/memory/main.c17
-rw-r--r--riscv/riscv-probe/examples/memory/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/probe/probe.c71
-rw-r--r--riscv/riscv-probe/examples/probe/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/symbols/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/symbols/symbols.c28
-rw-r--r--riscv/riscv-probe/examples/user/rules.mk1
-rw-r--r--riscv/riscv-probe/examples/user/user.c27
-rw-r--r--riscv/riscv-probe/libfemto/README.md28
-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
-rw-r--r--riscv/riscv-probe/libfemto/drivers/htif.c91
-rw-r--r--riscv/riscv-probe/libfemto/drivers/ns16550a.c65
-rw-r--r--riscv/riscv-probe/libfemto/drivers/semihost.c60
-rw-r--r--riscv/riscv-probe/libfemto/drivers/sifive_test.c28
-rw-r--r--riscv/riscv-probe/libfemto/drivers/sifive_uart.c58
-rw-r--r--riscv/riscv-probe/libfemto/include/alloca.h3
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/csr.h81
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/encoding.h197
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/machine.h183
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/pte.h121
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/spinlock.h192
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/stdatomic.h592
-rw-r--r--riscv/riscv-probe/libfemto/include/arch/riscv/trap.h51
-rw-r--r--riscv/riscv-probe/libfemto/include/auxval.h34
-rw-r--r--riscv/riscv-probe/libfemto/include/device.h39
-rw-r--r--riscv/riscv-probe/libfemto/include/elf.h545
-rw-r--r--riscv/riscv-probe/libfemto/include/endian.h95
-rw-r--r--riscv/riscv-probe/libfemto/include/femto.h14
-rw-r--r--riscv/riscv-probe/libfemto/include/list.h495
-rw-r--r--riscv/riscv-probe/libfemto/include/spinlock.h6
-rw-r--r--riscv/riscv-probe/libfemto/include/stdarg.h8
-rw-r--r--riscv/riscv-probe/libfemto/include/stdatomic.h61
-rw-r--r--riscv/riscv-probe/libfemto/include/stdbits.h63
-rw-r--r--riscv/riscv-probe/libfemto/include/stddef.h8
-rw-r--r--riscv/riscv-probe/libfemto/include/stdint.h52
-rw-r--r--riscv/riscv-probe/libfemto/include/stdio.h20
-rw-r--r--riscv/riscv-probe/libfemto/include/stdlib.h17
-rw-r--r--riscv/riscv-probe/libfemto/include/string.h21
-rw-r--r--riscv/riscv-probe/libfemto/std/abort.c8
-rw-r--r--riscv/riscv-probe/libfemto/std/clz.c22
-rw-r--r--riscv/riscv-probe/libfemto/std/ctz.c22
-rw-r--r--riscv/riscv-probe/libfemto/std/exit.c12
-rw-r--r--riscv/riscv-probe/libfemto/std/getchar.c9
-rw-r--r--riscv/riscv-probe/libfemto/std/malloc.c146
-rw-r--r--riscv/riscv-probe/libfemto/std/memchr.c11
-rw-r--r--riscv/riscv-probe/libfemto/std/memcmp.c15
-rw-r--r--riscv/riscv-probe/libfemto/std/memcpy.c12
-rw-r--r--riscv/riscv-probe/libfemto/std/memset.c12
-rw-r--r--riscv/riscv-probe/libfemto/std/printf.c15
-rw-r--r--riscv/riscv-probe/libfemto/std/putchar.c9
-rw-r--r--riscv/riscv-probe/libfemto/std/puts.c9
-rw-r--r--riscv/riscv-probe/libfemto/std/snprintf.c14
-rw-r--r--riscv/riscv-probe/libfemto/std/strchr.c11
-rw-r--r--riscv/riscv-probe/libfemto/std/strcmp.c9
-rw-r--r--riscv/riscv-probe/libfemto/std/strlen.c12
-rw-r--r--riscv/riscv-probe/libfemto/std/strncmp.c9
-rw-r--r--riscv/riscv-probe/libfemto/std/strncpy.c15
-rw-r--r--riscv/riscv-probe/libfemto/std/vprintf.c14
-rw-r--r--riscv/riscv-probe/libfemto/std/vsnprintf.c109
-rwxr-xr-xriscv/sections/sections.rvbin0 -> 992 bytes
-rw-r--r--riscv/sections/sections.s23
-rw-r--r--riscv/test.s6
-rwxr-xr-xriscv/unalign.rvbin0 -> 856 bytes
-rw-r--r--riscv/unalign.s23
125 files changed, 6142 insertions, 0 deletions
diff --git a/riscv/.gitignore b/riscv/.gitignore
new file mode 100644
index 0000000..874c63c
--- /dev/null
+++ b/riscv/.gitignore
@@ -0,0 +1,2 @@
+*.o
+
diff --git a/riscv/README.md b/riscv/README.md
new file mode 100644
index 0000000..10679d2
--- /dev/null
+++ b/riscv/README.md
@@ -0,0 +1 @@
+Collection of riscv ASM code
diff --git a/riscv/array_search.rv b/riscv/array_search.rv
new file mode 100755
index 0000000..0fa3160
--- /dev/null
+++ b/riscv/array_search.rv
Binary files differ
diff --git a/riscv/array_search.s b/riscv/array_search.s
new file mode 100644
index 0000000..bae9800
--- /dev/null
+++ b/riscv/array_search.s
@@ -0,0 +1,39 @@
+.section .data
+arr: .word 3, 5, 9, 33, 10, 59, 22, 95, 23, 24
+
+.section .text
+.globl _start
+
+
+#
+# a5 - Holds the current array index address
+# a1 - The loop counter
+# t4 - total elements in the array
+# t1 - the index - actually used to calculate the next
+# item, always the array member size, can be a loop invariant
+# t2 - holds the new index address, I'm pretty sure this can use a5 again
+get_largest_number:
+ la a5, arr
+ lw a0, (a5)
+
+ li a1, 1
+ li t4, 10
+ slli t1, a1, 2
+
+for:
+ bge a1, t4, for_exit
+ add a5, a5, t1
+ lw t3, (a5)
+ blt t3, a0, skip
+ mv a0, t3
+skip:
+ addi a1, a1, 1
+ j for
+
+for_exit:
+ ret
+
+_start:
+ call get_largest_number
+ li a7, 93
+ ecall
diff --git a/riscv/exit/exit.rv b/riscv/exit/exit.rv
new file mode 100755
index 0000000..370f2dc
--- /dev/null
+++ b/riscv/exit/exit.rv
Binary files differ
diff --git a/riscv/exit/exit.s b/riscv/exit/exit.s
new file mode 100644
index 0000000..5e55de5
--- /dev/null
+++ b/riscv/exit/exit.s
@@ -0,0 +1,5 @@
+.globl exit
+exit:
+ li a0, 24
+ li a7, 93
+ ecall
diff --git a/riscv/exit/main.s b/riscv/exit/main.s
new file mode 100644
index 0000000..426ea4c
--- /dev/null
+++ b/riscv/exit/main.s
@@ -0,0 +1,5 @@
+.globl _start
+_start:
+ li a0, 10
+ li a1, 20
+ jal exit
diff --git a/riscv/location_counter.s b/riscv/location_counter.s
new file mode 100644
index 0000000..de0fbfe
--- /dev/null
+++ b/riscv/location_counter.s
@@ -0,0 +1,18 @@
+.globl _start
+
+.section .data
+# x: .word 42 # Use a label to store the retval in the file's mem address space
+.set retval, 88 # Use .set to create a symbol in the object/executable's symbol table
+
+.section .text
+ # with the return value
+_start:
+# la t1, x # If we have the value stored in the .data section, we need to
+ # use 'la' to retrieve the address and
+# lw a0, (t1) # 'lw' to load it into the register
+
+ li a0, retval # We have retval defined in the file's symbol table (use nm or readelf)
+ # to display the symbol table.
+
+ li a7, 93
+ ecall
diff --git a/riscv/lvcounter.rv b/riscv/lvcounter.rv
new file mode 100755
index 0000000..248f3b2
--- /dev/null
+++ b/riscv/lvcounter.rv
Binary files differ
diff --git a/riscv/pow.rv b/riscv/pow.rv
new file mode 100755
index 0000000..66ba998
--- /dev/null
+++ b/riscv/pow.rv
Binary files differ
diff --git a/riscv/pow.s b/riscv/pow.s
new file mode 100644
index 0000000..8aa339d
--- /dev/null
+++ b/riscv/pow.s
@@ -0,0 +1,39 @@
+# Calculate a^b. Where: a0=a, a1=b
+#
+# Also, this is a good example of how numeric labels can be used
+#
+# Label 1: can be defined and redefined everywhere, and we can use
+# a suffix when referencing it, to specify if we want to reference
+# the previous or the next definition of the label, according to the
+# current position. 'b' for before 'f' for after.
+#
+# Ex:
+#
+# beqz a1, 1f # refers to the symbol 1 defined after this instruction
+#
+
+.globl _start
+.section .text
+pow:
+ mv a2, a0
+ li a0, 1
+
+1:
+ beqz a1, 1f
+ mul a0, a0, a2
+ addi a1, a1, -1
+ j 1b
+
+1:
+ ret
+
+_start:
+ li a0, 2
+ li a1, 3
+ jal pow
+
+ # Setup exit ecall
+ # Return value is already in a0
+
+ li a7, 93
+ ecall
diff --git a/riscv/riscv-probe/.gitignore b/riscv/riscv-probe/.gitignore
new file mode 100644
index 0000000..567609b
--- /dev/null
+++ b/riscv/riscv-probe/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/riscv/riscv-probe/LICENSE b/riscv/riscv-probe/LICENSE
new file mode 100644
index 0000000..8d9fb2e
--- /dev/null
+++ b/riscv/riscv-probe/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2013, The Regents of the University of California (Regents).
+Copyright (c) 2018-2019, The libfemto authors
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the Regents nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
+OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
+HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
+MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/riscv/riscv-probe/Makefile b/riscv/riscv-probe/Makefile
new file mode 100644
index 0000000..a1fb2fe
--- /dev/null
+++ b/riscv/riscv-probe/Makefile
@@ -0,0 +1,128 @@
+CROSS_COMPILE ?= riscv64-unknown-elf-
+
+AR = $(CROSS_COMPILE)ar
+
+CFLAGS = -mcmodel=medany -ffunction-sections -fdata-sections
+LDFLAGS = -nostartfiles -nostdlib -nostdinc -static -lgcc \
+ -Wl,--nmagic -Wl,--gc-sections
+INCLUDES = -Ienv/common
+
+libfemto_dirs = libfemto/std libfemto/drivers libfemto/arch/riscv
+libfemto_src = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.c)))
+libfemto_asm = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.s)))
+libfemto_objs = $(patsubst %.s,%.o,$(libfemto_asm)) \
+ $(patsubst %.c,%.o,$(libfemto_src))
+
+#
+# Compiler configurations and target environment definitions
+#
+
+subdirs = examples
+
+libs = libfemto
+
+configs = rv32imac rv64imac
+
+CC_rv32imac = $(CROSS_COMPILE)gcc
+CFLAGS_rv32imac = -Os -march=rv32imac -mabi=ilp32 -Ienv/common/rv32
+LDFLAGS_rv32imac =
+
+CC_rv64imac = $(CROSS_COMPILE)gcc
+CFLAGS_rv64imac = -Os -march=rv64imac -mabi=lp64 -Ienv/common/rv64
+LDFLAGS_rv64imac =
+
+targets = rv32imac:default \
+ rv64imac:default \
+ rv32imac:spike \
+ rv64imac:spike \
+ rv32imac:virt \
+ rv64imac:virt \
+ rv32imac:qemu-sifive_e \
+ rv64imac:qemu-sifive_e \
+ rv32imac:qemu-sifive_u \
+ rv64imac:qemu-sifive_u \
+ rv32imac:coreip-e2-arty
+
+#
+# make rules
+#
+
+all: all_programs
+
+clean:
+ rm -fr build
+
+backup: clean
+ tar czf ../$(shell basename $(shell pwd)).tar.gz .
+
+#
+# To view commands use: make V=1
+#
+
+ifdef V
+cmd = @mkdir -p $2 ; echo "$3"; $3
+else
+cmd = @echo "$1"; mkdir -p $2 ; $3
+endif
+
+#
+# Build system functions to generate pattern rules for all configs
+#
+
+define pattern =
+build/obj/$(2)/%.o: %.$(3)
+ $(call cmd,$(1).$(2) $$@,$$(@D),$(CC_$(2)) $(CFLAGS_$(2)) $(CFLAGS) \
+ $$(INCLUDES) -c $$^ -o $$@)
+endef
+
+$(foreach c,$(configs),$(eval $(call pattern,CC,$(c),c)))
+$(foreach c,$(configs),$(eval $(call pattern,AS,$(c),s)))
+
+#
+# Build system functions to generate library rules for all configs
+#
+
+define archive =
+build/lib/$(2)/$(3).a: $(addprefix build/obj/$(2)/,$($(3)_objs))
+ $(call cmd,$(1).$(2) $$@,$$(@D),$(AR) cr $$@ $$^)
+LIBS_$(2) += build/lib/$(2)/$(3).a
+endef
+
+define lib =
+$(foreach c,$(configs),$(eval $(call archive,AR,$(c),$(1))))
+INCLUDES += -I$(1)/include
+endef
+
+$(foreach l,$(libs),$(eval $(call lib,$(l))))
+
+#
+# Build system functions to generate build rules for all subdirs
+#
+
+sub_makes := $(foreach dir,$(subdirs),$(wildcard ${dir}/*/rules.mk))
+$(foreach makefile,$(sub_makes),$(eval include $(makefile)))
+sub_dirs := $(foreach m,$(sub_makes),$(m:/rules.mk=))
+module_name = $(lastword $(subst /, ,$(1)))
+module_objs = $(addprefix build/obj/$(3)/,$(addprefix $(2)/,$($(1)_objs)))
+config_arch = $(word 1,$(subst :, ,$(1)))
+config_env = $(word 2,$(subst :, ,$(1)))
+
+define rule =
+build/bin/$(3)/$(4)/$(1): \
+build/obj/$(3)/env/$(4)/crt.o build/obj/$(3)/env/$(4)/setup.o $(2) $$(LIBS_$(3))
+ $$(call cmd,LD.$(3) $$@,$$(@D),$(CC_$(3)) $(CFLAGS_$(3)) $$(CFLAGS) \
+ $$(LDFLAGS_$(3)) $$(LDFLAGS) -T env/$(4)/default.lds $$^ -o $$@)
+endef
+
+define module =
+program_names += $(foreach cfg,$(targets),build/bin/$(call \
+ config_arch,$(cfg))/$(call config_env,$(cfg))/$(1))
+
+$(foreach cfg,$(targets),$(eval $(call rule,$(1),$(call \
+ module_objs,$(1),$(2),$(call config_arch,$(cfg))),$(call \
+ config_arch,$(cfg)),$(call config_env,$(cfg)))))
+endef
+
+$(foreach d,$(sub_dirs),$(eval $(call module,$(call module_name,$(d)),$(d))))
+
+all_programs: $(program_names)
diff --git a/riscv/riscv-probe/README.md b/riscv/riscv-probe/README.md
new file mode 100644
index 0000000..577e09d
--- /dev/null
+++ b/riscv/riscv-probe/README.md
@@ -0,0 +1,347 @@
+# riscv-probe
+
+Simple machine mode program to probe RISC-V control and status registers.
+
+riscv-probe currently builds for
+[Spike](https://github.com/riscv/riscv-isa-sim),
+[QEMU](https://github.com/riscv/riscv-qemu) and the
+[SiFive E21](https://www.sifive.com/products/risc-v-core-ip/e2/e21/) core.
+riscv-probe is a testing tool designed be used to compare CSRs (Control and
+Status Registers) between mutliple RISC-V simulators and RISC-V hardware
+implementations.
+
+riscv-probe contains libfemto which is a lightweight bare-metal C library
+conforming to a reduced set ot the _POSIX.1-2017 / IEEE 1003.1-2017_ standard.
+libfemto can be used as a starting point for bare metal RISC-V programs that
+require interrupt handling, basic string routines and printf.
+
+## Dependencies
+
+A recent version of `riscv-tools` with a multilib build of RISC-V GCC.
+
+- [riscv-tools](https://github.com/riscv/riscv-tools)
+ - [riscv-isa-sim](https://github.com/riscv/riscv-isa-sim)
+ - [riscv-openocd](https://github.com/riscv/riscv-openocd)
+ - [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain)
+- [riscv-qemu](https://github.com/riscv/riscv-qemu)
+
+## Build
+
+The build system uses `CROSS_COMPILE` as the toolchain prefix and expects
+the toolchain to be present in the `PATH` environment variable. The default
+value for `CROSS_COMPILE` is `riscv64-unknown-elf-` however this can be
+overridden e.g. `make CROSS_COMPILE=riscv64-unknown-linux-gnu-`. The build
+system expects a multilib toolchain as it uses the same toolchain to build
+for _riscv32_ and _riscv64_. Make sure to use `--enable-multilib` when
+configuring [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain).
+The examples are all built with `-nostartfiles -nostdlib -nostdinc` so either
+the RISC-V GCC Newlib toolchain or RISC-V GCC Glibc Linux toolchain can be used.
+
+To build the examples after environent setup, type:
+
+```
+make
+```
+
+## Invocation
+
+To invoke the probe example in spike and RISC-V QEMU:
+
+- `$ spike --isa=RV32IMAFDC build/bin/rv32imac/spike/probe`
+- `$ spike --isa=RV64IMAFDC build/bin/rv64imac/spike/probe`
+- `$ qemu-system-riscv32 -nographic -machine spike -kernel build/bin/rv32imac/spike/probe` -bios none
+- `$ qemu-system-riscv64 -nographic -machine spike -kernel build/bin/rv64imac/spike/probe` -bios none
+- `$ qemu-system-riscv32 -nographic -machine virt -kernel build/bin/rv32imac/virt/probe` -bios none
+- `$ qemu-system-riscv64 -nographic -machine virt -kernel build/bin/rv64imac/virt/probe` -bios none
+- `$ qemu-system-riscv32 -nographic -machine sifive_e -kernel build/bin/rv32imac/qemu-sifive_e/probe` -bios none
+- `$ qemu-system-riscv64 -nographic -machine sifive_e -kernel build/bin/rv64imac/qemu-sifive_e/probe` -bios none
+- `$ qemu-system-riscv32 -nographic -machine sifive_u -kernel build/bin/rv32imac/qemu-sifive_u/probe` -bios none
+- `$ qemu-system-riscv64 -nographic -machine sifive_u -kernel build/bin/rv64imac/qemu-sifive_u/probe` -bios none
+
+## libfemto
+
+libfemto is a lightweight bare-metal C library for embedded RISC-V development.
+libfemto provides:
+
+- Reduced set of the _POSIX.1-2017 / IEEE 1003.1-2017_ standard
+- Simple lightweight hardware configuration mechanism
+- RISC-V machine mode functions and macros
+- Console and power device drivers
+
+libfemto implements a reduced set of the _POSIX.1-2017 / IEEE 1003.1-2017_
+standard, with the addition of glibc's `getauxval` API to access hardware
+configuration in an auxiliary vector (`__auxv`) that contains tuples
+describing the target environment. The auxiliary vector is intended as a
+lightweight mechanism to pass dynamic configuration information on embedded
+targets, serving as an alternative to compile time constants used during
+hardware initialization. The auxiliary vector API has been repurposed to
+allow retrieval of hardware configuration parameters such as clock
+frequencies and device base addresses for use as a compact alternative to
+(DTB) Device Tree Binary, which is not available on small embedded targets.
+
+libfemto contains the following device drivers:
+
+- HTIF (Host Target Interface)
+- NS16550A UART Console
+- SiFive UART Console
+- SiFive Test Device
+- Semihosting Syscalls
+
+### Environments
+
+This project contains a simple build system that allows building applications
+targeting multiple embedded environments. A distinguishing characteristic of
+the build system is that program objects do not need to be recompiled to target
+a different environment, rather they are relinked with a different hardware
+configuration and setup function. The config object causes the correct drivers
+to be linked via compile time dependencies expressed by symbol references.
+The following environments are currently supported:
+
+- _default_ - environment where IO defaults to `ebreak`
+- _spike_- the RISC-V ISA Simulator Golden Model
+- _virt_ - the RISC-V VirtIO Virtual Machine
+- _qemu-sifive_e_ - QEMU Functional Model of the SiFive E Series
+- _qemu-sifive_u_ - QEMU Functional Model of the SiFive U Series
+- _coreip-e2-arty_ - SiFive E2 CoreIP Arty A7 FPGA evaluation image
+
+To create a new environment simply add a directory to `env` with two files:
+
+- `default.lds` - linker script describing the target's memory layout
+- `config.c` - environment specific configuration
+
+The following is an example configuration from `env/<boardname>/config.c`
+showing the auxiliary vector used by `getauxval` via the `setup` function
+called by `_start` before entering `main`.
+
+```
+auxval_t __auxv[] = {
+ { UART0_CLOCK_FREQ, 32000000 },
+ { UART0_BAUD_RATE, 115200 },
+ { SIFIVE_UART0_CTRL_ADDR, 0x20000000 },
+ { SIFIVE_TEST_CTRL_ADDR, 0x4000 },
+ { 0, 0 }
+};
+
+void setup()
+{
+ /*
+ * clock setup code should be placed here and should modify the
+ * uart clock speed before calling register_console, which calls
+ * uart_init and reads the uart clock speed from the config array.
+ */
+ register_console(&console_sifive_uart);
+ register_poweroff(&poweroff_sifive_test);
+}
+```
+
+## Examples
+
+The build system automatically includes any directory added to `examples`
+which contains a `rules.mk` file.
+
+### hello
+
+The following is the `rules.mk` file from the _hello_ example:
+
+```
+$ cat examples/hello/rules.mk
+hello_objs = hello.o
+```
+
+and `hello.c`
+
+```
+$ cat examples/hello/hello.c
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("hello\n");
+}
+```
+
+### symbols
+
+libfemto linker scripts define the following special symbols:
+
+Symbol | Value
+------ | -----
+`_text_start` | start of `.text` section
+`_text_end` | end of `.text` section
+`_rodata_start` | start of `.rodata` section
+`_rodata_end` | end of `.rodata` section
+`_data_start` | start of `.data` section
+`_data_end` | end of `.data` section
+`_bss_start` | start of `.bss` section
+`_bss_end` | end of `.bss` section
+`_memory_start` | start of RAM
+`_memory_end` | end of RAM
+
+The symbols example program shows how to access these special symbols. They
+can be used for example to locate data section in a flat image in ROM to copy
+into memory and to zero the bss section.
+
+### user
+
+Simple example program that sets up PMP (Physical Memory Protection)
+to allow all memory accesses and then switches to U mode. The code to
+set up PMP to allow all memory access is `pmp_allow_all()` in `machine.h`.
+The code is derived from _bbl_.
+
+Note: PMP is enabled by default on systems that implement it, so it is
+mandatory to include code to configure PMPs to allow memory access when
+switching from M mode to S mode or U mode, otherwise memory accesses will trap.
+
+### enclave
+
+Simple enclave example using RISC-V PMP (Physical Memory Protection)
+
+Physical memory protection is enabled for the program text (RX)
+program data (RW), UART, and the hart is switch to U mode, however,
+there is only one program and the default trap vector points into
+the executable text, so traps are handled in M mode, in the same
+executable text segment.
+
+A future example will load a second process and provide ecall APIs
+to the process running in the protected enclave.
+
+### probe
+
+`riscv-probe` is a utility that probes the Control and Status Register
+address space of a RISC-V emulator, FPGA or board:
+
+#### qemu-system-riscv32
+
+```
+$ qemu-system-riscv32 -nographic -machine spike_v1.10 -kernel build/bin/rv32/spike/probe
+isa: rv32imafdcsu
+csr: fflags illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: frm illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: fcsr illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: mcycle 0xdbfa9cbd
+csr: minstret 0xdc03f6a4
+csr: mcycleh 0x0007c452
+csr: minstreth 0x0007c452
+csr: cycle 0xdc1d7e08
+csr: time illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: instret 0xdc393bf6
+csr: cycleh 0x0007c452
+csr: timeh illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: instreth 0x0007c452
+csr: mvendorid 0x00000000
+csr: marchid 0x00000000
+csr: mimpid 0x00000000
+csr: mhartid 0x00000000
+csr: mstatus 0x00000000
+csr: misa 0x4014112d
+csr: medeleg 0x00000000
+csr: mideleg 0x00000000
+csr: mie 0x00000000
+csr: mtvec 0x80000004
+csr: mcounteren 0x00000000
+csr: mscratch 0x00000000
+csr: mepc 0x800002a4
+csr: mcause 0x00000002
+csr: mtval 0x00000000
+csr: mip 0x00000000
+csr: sstatus 0x00000000
+csr: sedeleg illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: sideleg illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: sie 0x00000000
+csr: stvec 0x00000000
+csr: scounteren 0x00000000
+csr: sscratch 0x00000000
+csr: sepc 0x00000000
+csr: scause 0x00000000
+csr: stval 0x00000000
+csr: sip 0x00000000
+csr: satp 0x00000000
+csr: pmpcfg0 0x00000000
+csr: pmpcfg1 0x00000000
+csr: pmpcfg2 0x00000000
+csr: pmpcfg3 0x00000000
+csr: pmpaddr0 0x00000000
+csr: pmpaddr1 0x00000000
+csr: pmpaddr2 0x00000000
+csr: pmpaddr3 0x00000000
+csr: pmpaddr4 0x00000000
+csr: pmpaddr5 0x00000000
+csr: pmpaddr6 0x00000000
+csr: pmpaddr7 0x00000000
+csr: pmpaddr8 0x00000000
+csr: pmpaddr9 0x00000000
+csr: pmpaddr10 0x00000000
+csr: pmpaddr11 0x00000000
+csr: pmpaddr12 0x00000000
+csr: pmpaddr13 0x00000000
+csr: pmpaddr14 0x00000000
+csr: pmpaddr15 0x00000000
+```
+
+#### qemu-system-riscv64
+
+```
+$ qemu-system-riscv64 -nographic -machine spike_v1.10 -kernel build/bin/rv64/spike/probe
+isa: rv64imafdcsu
+csr: fflags illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: frm illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: fcsr illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: mcycle 0x0007c452dfeeddd3
+csr: minstret 0x0007c452dff8a765
+csr: mcycleh illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: minstreth illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: cycle 0x0007c452e01f105f
+csr: time illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: instret 0x0007c452e03d0a50
+csr: cycleh illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: timeh illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: instreth illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: mvendorid 0x0000000000000000
+csr: marchid 0x0000000000000000
+csr: mimpid 0x0000000000000000
+csr: mhartid 0x0000000000000000
+csr: mstatus 0x0000000000000000
+csr: misa 0x800000000014112d
+csr: medeleg 0x0000000000000000
+csr: mideleg 0x0000000000000000
+csr: mie 0x0000000000000000
+csr: mtvec 0x0000000080000004
+csr: mcounteren 0x0000000000000000
+csr: mscratch 0x0000000000000000
+csr: mepc 0x00000000800002f4
+csr: mcause 0x0000000000000002
+csr: mtval 0x0000000000000000
+csr: mip 0x0000000000000000
+csr: sstatus 0x0000000000000000
+csr: sedeleg illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: sideleg illegal_instruction cause=0x00000002 mtval=0x00000000
+csr: sie 0x0000000000000000
+csr: stvec 0x0000000000000000
+csr: scounteren 0x0000000000000000
+csr: sscratch 0x0000000000000000
+csr: sepc 0x0000000000000000
+csr: scause 0x0000000000000000
+csr: stval 0x0000000000000000
+csr: sip 0x0000000000000000
+csr: satp 0x0000000000000000
+csr: pmpcfg0 0x0000000000000000
+csr: pmpcfg1 0x0000000000000000
+csr: pmpcfg2 0x0000000000000000
+csr: pmpcfg3 0x0000000000000000
+csr: pmpaddr0 0x0000000000000000
+csr: pmpaddr1 0x0000000000000000
+csr: pmpaddr2 0x0000000000000000
+csr: pmpaddr3 0x0000000000000000
+csr: pmpaddr4 0x0000000000000000
+csr: pmpaddr5 0x0000000000000000
+csr: pmpaddr6 0x0000000000000000
+csr: pmpaddr7 0x0000000000000000
+csr: pmpaddr8 0x0000000000000000
+csr: pmpaddr9 0x0000000000000000
+csr: pmpaddr10 0x0000000000000000
+csr: pmpaddr11 0x0000000000000000
+csr: pmpaddr12 0x0000000000000000
+csr: pmpaddr13 0x0000000000000000
+csr: pmpaddr14 0x0000000000000000
+csr: pmpaddr15 0x0000000000000000
+```
diff --git a/riscv/riscv-probe/env/common/constants.s b/riscv/riscv-probe/env/common/constants.s
new file mode 100644
index 0000000..d08387c
--- /dev/null
+++ b/riscv/riscv-probe/env/common/constants.s
@@ -0,0 +1,18 @@
+# See LICENSE for license details.
+
+.equ MAX_HARTS, 4
+.equ SAVE_REGS, 16
+.equ STACK_SIZE, 1024
+.equ STACK_SHIFT, 10
+.equ CONTEXT_SIZE, (SAVE_REGS * REGBYTES)
+
+.globl _text_start
+.globl _text_end
+.globl _rodata_start
+.globl _rodata_end
+.globl _data_start
+.globl _data_end
+.globl _bss_start
+.globl _bss_end
+.global _memory_start;
+.global _memory_end;
diff --git a/riscv/riscv-probe/env/common/crtm.s b/riscv/riscv-probe/env/common/crtm.s
new file mode 100644
index 0000000..9dc84b2
--- /dev/null
+++ b/riscv/riscv-probe/env/common/crtm.s
@@ -0,0 +1,89 @@
+# See LICENSE for license details.
+
+.include "macros.s"
+.include "constants.s"
+
+#
+# start of trap handler
+#
+
+.section .text.init,"ax",@progbits
+.globl _start
+
+_start:
+ # setup default trap vector
+ la t0, trap_vector
+ csrw mtvec, t0
+
+ # set up stack pointer based on hartid
+ csrr t0, mhartid
+ slli t0, t0, STACK_SHIFT
+ la sp, stacks + STACK_SIZE
+ add sp, sp, t0
+
+ # park all harts excpet hart 0
+ csrr a0, mhartid
+ bnez a0, park
+
+ # jump to libfemto_start_main
+ j libfemto_start_main
+
+ # sleeping harts mtvec calls trap_fn upon receiving IPI
+park:
+ wfi
+ j park
+
+ .align 2
+trap_vector:
+ # Save registers.
+ addi sp, sp, -CONTEXT_SIZE
+ sxsp ra, 0
+ sxsp a0, 1
+ sxsp a1, 2
+ sxsp a2, 3
+ sxsp a3, 4
+ sxsp a4, 5
+ sxsp a5, 6
+ sxsp a6, 7
+ sxsp a7, 8
+ sxsp t0, 9
+ sxsp t1, 10
+ sxsp t2, 11
+ sxsp t3, 12
+ sxsp t4, 13
+ sxsp t5, 14
+ sxsp t6, 15
+
+ # Invoke the handler.
+ mv a0, sp
+ csrr a1, mcause
+ csrr a2, mepc
+ jal trap_handler
+
+ # Restore registers.
+ lxsp ra, 0
+ lxsp a0, 1
+ lxsp a1, 2
+ lxsp a2, 3
+ lxsp a3, 4
+ lxsp a4, 5
+ lxsp a5, 6
+ lxsp a6, 7
+ lxsp a7, 8
+ lxsp t0, 9
+ lxsp t1, 10
+ lxsp t2, 11
+ lxsp t3, 12
+ lxsp t4, 13
+ lxsp t5, 14
+ lxsp t6, 15
+ addi sp, sp, CONTEXT_SIZE
+
+ # Return
+ mret
+
+ .bss
+ .align 4
+ .global stacks
+stacks:
+ .skip STACK_SIZE * MAX_HARTS
diff --git a/riscv/riscv-probe/env/common/rv32/macros.s b/riscv/riscv-probe/env/common/rv32/macros.s
new file mode 100644
index 0000000..d987d0c
--- /dev/null
+++ b/riscv/riscv-probe/env/common/rv32/macros.s
@@ -0,0 +1,23 @@
+# See LICENSE for license details.
+
+.equ REGBYTES, 4
+
+.macro lx a, b
+lw \a, \b
+.endm
+
+.macro sx a, b
+sw \a, \b
+.endm
+
+.macro lxsp a, b
+lw \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro sxsp a, b
+sw \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro .ptr a
+.4byte \a
+.endm
diff --git a/riscv/riscv-probe/env/common/rv64/macros.s b/riscv/riscv-probe/env/common/rv64/macros.s
new file mode 100644
index 0000000..abc76f0
--- /dev/null
+++ b/riscv/riscv-probe/env/common/rv64/macros.s
@@ -0,0 +1,23 @@
+# See LICENSE for license details.
+
+.equ REGBYTES, 8
+
+.macro lx a, b
+ld \a, \b
+.endm
+
+.macro sx a, b
+sd \a, \b
+.endm
+
+.macro lxsp a, b
+ld \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro sxsp a, b
+sd \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro .ptr a
+.8byte \a
+.endm
diff --git a/riscv/riscv-probe/env/coreip-e2-arty/crt.s b/riscv/riscv-probe/env/coreip-e2-arty/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/coreip-e2-arty/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/coreip-e2-arty/default.lds b/riscv/riscv-probe/env/coreip-e2-arty/default.lds
new file mode 100644
index 0000000..b7aa9b2
--- /dev/null
+++ b/riscv/riscv-probe/env/coreip-e2-arty/default.lds
@@ -0,0 +1,46 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ flash (rxai!w) : ORIGIN = 0x40400000, LENGTH = 128M
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 64K
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >flash AT>flash :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >flash AT>flash :text
+
+ .data : {
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/coreip-e2-arty/setup.c b/riscv/riscv-probe/env/coreip-e2-arty/setup.c
new file mode 100644
index 0000000..9dbecba
--- /dev/null
+++ b/riscv/riscv-probe/env/coreip-e2-arty/setup.c
@@ -0,0 +1,20 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { UART0_CLOCK_FREQ, 32000000 },
+ { UART0_BAUD_RATE, 115200 },
+ { SIFIVE_UART0_CTRL_ADDR, 0x20000000 },
+ { 0, 0 }
+};
+
+void arch_setup()
+{
+ /*
+ * clock setup code should be placed here and should modify the
+ * uart clock speed before calling register_console, which calls
+ * uart_init and reads the uart clock speed from the config array.
+ */
+ register_console(&console_sifive_uart);
+}
diff --git a/riscv/riscv-probe/env/default/crt.s b/riscv/riscv-probe/env/default/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/default/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/default/default.lds b/riscv/riscv-probe/env/default/default.lds
new file mode 100644
index 0000000..c103c78
--- /dev/null
+++ b/riscv/riscv-probe/env/default/default.lds
@@ -0,0 +1,45 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >ram AT>ram :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >ram AT>ram :text
+
+ .data : {
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/default/setup.c b/riscv/riscv-probe/env/default/setup.c
new file mode 100644
index 0000000..47674ca
--- /dev/null
+++ b/riscv/riscv-probe/env/default/setup.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { 0, 0 }
+};
+
+void arch_setup() {}
diff --git a/riscv/riscv-probe/env/qemu-sifive_e/crt.s b/riscv/riscv-probe/env/qemu-sifive_e/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_e/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/qemu-sifive_e/default.lds b/riscv/riscv-probe/env/qemu-sifive_e/default.lds
new file mode 100644
index 0000000..d27cc0c
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_e/default.lds
@@ -0,0 +1,47 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ flash (rxai!w) : ORIGIN = 0x20400000, LENGTH = 128M
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 64K
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >flash AT>flash :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >flash AT>flash :text
+
+ .data : {
+ . = ALIGN(4096);
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/qemu-sifive_e/setup.c b/riscv/riscv-probe/env/qemu-sifive_e/setup.c
new file mode 100644
index 0000000..840e778
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_e/setup.c
@@ -0,0 +1,17 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { UART0_CLOCK_FREQ, 32000000 },
+ { UART0_BAUD_RATE, 115200 },
+ { SIFIVE_UART0_CTRL_ADDR, 0x10013000 },
+ { SIFIVE_TEST_CTRL_ADDR, 0x100000 },
+ { 0, 0 }
+};
+
+void arch_setup()
+{
+ register_console(&console_sifive_uart);
+ register_poweroff(&poweroff_sifive_test);
+}
diff --git a/riscv/riscv-probe/env/qemu-sifive_u/crt.s b/riscv/riscv-probe/env/qemu-sifive_u/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_u/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/qemu-sifive_u/default.lds b/riscv/riscv-probe/env/qemu-sifive_u/default.lds
new file mode 100644
index 0000000..9c1a7df
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_u/default.lds
@@ -0,0 +1,46 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >ram AT>ram :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >ram AT>ram :text
+
+ .data : {
+ . = ALIGN(4096);
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/qemu-sifive_u/setup.c b/riscv/riscv-probe/env/qemu-sifive_u/setup.c
new file mode 100644
index 0000000..840e778
--- /dev/null
+++ b/riscv/riscv-probe/env/qemu-sifive_u/setup.c
@@ -0,0 +1,17 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { UART0_CLOCK_FREQ, 32000000 },
+ { UART0_BAUD_RATE, 115200 },
+ { SIFIVE_UART0_CTRL_ADDR, 0x10013000 },
+ { SIFIVE_TEST_CTRL_ADDR, 0x100000 },
+ { 0, 0 }
+};
+
+void arch_setup()
+{
+ register_console(&console_sifive_uart);
+ register_poweroff(&poweroff_sifive_test);
+}
diff --git a/riscv/riscv-probe/env/semihost/crt.s b/riscv/riscv-probe/env/semihost/crt.s
new file mode 100644
index 0000000..b75cba8
--- /dev/null
+++ b/riscv/riscv-probe/env/semihost/crt.s
@@ -0,0 +1,28 @@
+# See LICENSE for license details.
+
+.include "macros.s"
+.include "constants.s"
+
+#
+# start of trap handler
+#
+
+.section .text.init,"ax",@progbits
+.globl _start
+
+_start:
+
+ # set up stack pointer based on hartid (in a0)
+ mv t0, a0
+ slli t0, t0, STACK_SHIFT
+ la sp, stacks + STACK_SIZE
+ add sp, sp, t0
+
+ # jump to libfemto_start_main
+ j libfemto_start_main
+
+ .bss
+ .align 4
+ .global stacks
+stacks:
+ .skip STACK_SIZE * MAX_HARTS
diff --git a/riscv/riscv-probe/env/semihost/default.lds b/riscv/riscv-probe/env/semihost/default.lds
new file mode 100644
index 0000000..c103c78
--- /dev/null
+++ b/riscv/riscv-probe/env/semihost/default.lds
@@ -0,0 +1,45 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >ram AT>ram :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >ram AT>ram :text
+
+ .data : {
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/semihost/setup.c b/riscv/riscv-probe/env/semihost/setup.c
new file mode 100644
index 0000000..407ee14
--- /dev/null
+++ b/riscv/riscv-probe/env/semihost/setup.c
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { 0, 0 }
+};
+
+void arch_setup() {
+ register_console(&console_semihost);
+ register_poweroff(&poweroff_semihost);
+}
diff --git a/riscv/riscv-probe/env/spike/crt.s b/riscv/riscv-probe/env/spike/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/spike/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/spike/default.lds b/riscv/riscv-probe/env/spike/default.lds
new file mode 100644
index 0000000..eb78711
--- /dev/null
+++ b/riscv/riscv-probe/env/spike/default.lds
@@ -0,0 +1,52 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+ htif PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >ram AT>ram :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >ram AT>ram :text
+
+ .htif : {
+ . = ALIGN(4096);
+ *(.htif)
+ } >ram AT>ram :text
+
+ .data : {
+ . = ALIGN(4096);
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/spike/setup.c b/riscv/riscv-probe/env/spike/setup.c
new file mode 100644
index 0000000..54d4bf1
--- /dev/null
+++ b/riscv/riscv-probe/env/spike/setup.c
@@ -0,0 +1,18 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+extern uint64_t tohost;
+extern uint64_t fromhost;
+
+auxval_t __auxv[] = {
+ { RISCV_HTIF_BASE_ADDR, 0 },
+ { 0, 0 }
+};
+
+void arch_setup()
+{
+ __auxv[0].val = (uintptr_t)(&tohost < &fromhost ? &tohost : &fromhost);
+ register_console(&console_htif);
+ register_poweroff(&poweroff_htif);
+}
diff --git a/riscv/riscv-probe/env/virt/crt.s b/riscv/riscv-probe/env/virt/crt.s
new file mode 100644
index 0000000..dcdf1e1
--- /dev/null
+++ b/riscv/riscv-probe/env/virt/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/riscv/riscv-probe/env/virt/default.lds b/riscv/riscv-probe/env/virt/default.lds
new file mode 100644
index 0000000..9c1a7df
--- /dev/null
+++ b/riscv/riscv-probe/env/virt/default.lds
@@ -0,0 +1,46 @@
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( _start )
+
+MEMORY
+{
+ ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ text PT_LOAD;
+ data PT_LOAD;
+ bss PT_LOAD;
+}
+
+SECTIONS
+{
+ .text : {
+ PROVIDE(_text_start = .);
+ *(.text.init) *(.text .text.*)
+ PROVIDE(_text_end = .);
+ } >ram AT>ram :text
+
+ .rodata : {
+ PROVIDE(_rodata_start = .);
+ *(.rodata .rodata.*)
+ PROVIDE(_rodata_end = .);
+ } >ram AT>ram :text
+
+ .data : {
+ . = ALIGN(4096);
+ PROVIDE(_data_start = .);
+ *(.sdata .sdata.*) *(.data .data.*)
+ PROVIDE(_data_end = .);
+ } >ram AT>ram :data
+
+ .bss :{
+ PROVIDE(_bss_start = .);
+ *(.sbss .sbss.*) *(.bss .bss.*)
+ PROVIDE(_bss_end = .);
+ } >ram AT>ram :bss
+
+ PROVIDE(_memory_start = ORIGIN(ram));
+ PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/riscv/riscv-probe/env/virt/setup.c b/riscv/riscv-probe/env/virt/setup.c
new file mode 100644
index 0000000..dcd87a3
--- /dev/null
+++ b/riscv/riscv-probe/env/virt/setup.c
@@ -0,0 +1,17 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+ { UART0_CLOCK_FREQ, 1843200 },
+ { UART0_BAUD_RATE, 115200 },
+ { NS16550A_UART0_CTRL_ADDR, 0x10000000 },
+ { SIFIVE_TEST_CTRL_ADDR, 0x100000 },
+ { 0, 0 }
+};
+
+void arch_setup()
+{
+ register_console(&console_ns16550a);
+ register_poweroff(&poweroff_sifive_test);
+}
diff --git a/riscv/riscv-probe/examples/abort/main.c b/riscv/riscv-probe/examples/abort/main.c
new file mode 100644
index 0000000..cf5bcc0
--- /dev/null
+++ b/riscv/riscv-probe/examples/abort/main.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+ abort();
+}
diff --git a/riscv/riscv-probe/examples/abort/rules.mk b/riscv/riscv-probe/examples/abort/rules.mk
new file mode 100644
index 0000000..d569b90
--- /dev/null
+++ b/riscv/riscv-probe/examples/abort/rules.mk
@@ -0,0 +1 @@
+abort_objs = main.o
diff --git a/riscv/riscv-probe/examples/alloca/main.c b/riscv/riscv-probe/examples/alloca/main.c
new file mode 100644
index 0000000..af9696a
--- /dev/null
+++ b/riscv/riscv-probe/examples/alloca/main.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <alloca.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ const size_t nelems = 4;
+ const size_t elemsize = 128;
+ char *alloc[nelems];
+
+ for (size_t i = 0; i < nelems; i++) {
+ if (alloc[i] = alloca(elemsize)) {
+ memset(alloc[i], 0, elemsize);
+ }
+ printf("alloca[%d]=0x%x\n", i, alloc[i]);
+ }
+}
diff --git a/riscv/riscv-probe/examples/alloca/rules.mk b/riscv/riscv-probe/examples/alloca/rules.mk
new file mode 100644
index 0000000..198ef24
--- /dev/null
+++ b/riscv/riscv-probe/examples/alloca/rules.mk
@@ -0,0 +1 @@
+alloca_objs = main.o
diff --git a/riscv/riscv-probe/examples/argv0/main.c b/riscv/riscv-probe/examples/argv0/main.c
new file mode 100644
index 0000000..7699fe2
--- /dev/null
+++ b/riscv/riscv-probe/examples/argv0/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("%s\n", argv[0]);
+}
diff --git a/riscv/riscv-probe/examples/argv0/rules.mk b/riscv/riscv-probe/examples/argv0/rules.mk
new file mode 100644
index 0000000..0ceeeaa
--- /dev/null
+++ b/riscv/riscv-probe/examples/argv0/rules.mk
@@ -0,0 +1 @@
+argv0_objs = main.o
diff --git a/riscv/riscv-probe/examples/enclave/enclave.c b/riscv/riscv-probe/examples/enclave/enclave.c
new file mode 100644
index 0000000..93015cc
--- /dev/null
+++ b/riscv/riscv-probe/examples/enclave/enclave.c
@@ -0,0 +1,79 @@
+#include "femto.h"
+
+/*
+ * Simple enclave example using RISC-V PMP (Physical Memory Protection)
+ *
+ * NOTE: Physical memory protection is enabled for the program text (RX)
+ * program data (RW), UART, and the hart is switch to U mode, however,
+ * there is only one program and the default trap vector points into
+ * the executable text, so traps are handled in M mode, in the same
+ * executable text segment. A more complete example needs to load a
+ * second process and provide ecall APIs to the protected enclave.
+ * This is not yet a true secure enclave until two disjunctive worlds
+ * are have been implemented with measured boot and signed code.
+ */
+
+extern char _text_start;
+extern char _rodata_end;
+extern char _data_start;
+extern char _bss_end;
+
+static uintptr_t uart_keys[] = {
+ SIFIVE_UART0_CTRL_ADDR,
+ NS16550A_UART0_CTRL_ADDR,
+ RISCV_HTIF_BASE_ADDR,
+ 0
+};
+
+#ifdef __riscv
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+#include "arch/riscv/csr.h"
+#endif
+
+int main()
+{
+ uintptr_t uart = 0;
+ uintptr_t *uart_k = uart_keys;
+ const uintptr_t uartlen = 32;
+
+ /* locate UART address using known configuration keys */
+ while (*uart_k && !(uart = getauxval(*uart_k++)));
+
+ /* locate ROM/Flash (text+rodata) and RAM (data+bss) */
+ uintptr_t rx_s = (uintptr_t)&_text_start;
+ uintptr_t rx_l = roundpow2((uintptr_t)(&_rodata_end - &_text_start));
+ uintptr_t rw_s = (uintptr_t)&_data_start;
+ uintptr_t rw_l = roundpow2((uintptr_t)(&_bss_end - &_data_start));
+
+ /* print our findings */
+ printf("text: 0x%x - 0x%x\n", rx_s, rx_s + rx_l - 1);
+ printf("data: 0x%x - 0x%x\n", rw_s, rw_s + rw_l - 1);
+ if (uart) {
+ printf("uart: 0x%x - 0x%x\n", uart, uart + uartlen - 1);
+ }
+
+#ifdef __riscv
+ pmp_info_t info = pmp_probe();
+ if (info.count == 0) {
+ puts("pmp-not-supported");
+ return 0;
+ } else {
+ printf("pmp.count: %d\n", info.count);
+ printf("pmp.width: %d\n", info.width);
+ printf("pmp.granularity: %d\n", info.granularity);
+ }
+ /* set up physical memory protection */
+ pmp_entry_set(0, PMP_R | PMP_X, rx_s, rx_l);
+ pmp_entry_set(1, PMP_R | PMP_W, rw_s, rw_l);
+ if (uart) {
+ pmp_entry_set(2, PMP_R | PMP_W, uart, uartlen);
+ }
+
+ /* switch to user mode enclave */
+ mode_set_and_continue(PRV_U);
+ puts("riscv-enclave");
+#else
+ puts("architecture-not-supported");
+#endif
+}
diff --git a/riscv/riscv-probe/examples/enclave/rules.mk b/riscv/riscv-probe/examples/enclave/rules.mk
new file mode 100644
index 0000000..046cbae
--- /dev/null
+++ b/riscv/riscv-probe/examples/enclave/rules.mk
@@ -0,0 +1 @@
+enclave_objs = enclave.o
diff --git a/riscv/riscv-probe/examples/hang/hang.c b/riscv/riscv-probe/examples/hang/hang.c
new file mode 100644
index 0000000..066ca9e
--- /dev/null
+++ b/riscv/riscv-probe/examples/hang/hang.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ volatile int a;
+ int b, c, d, e, f, g, h, i, j, k, l, m;
+ printf("hang\n");
+ for (;;) {
+ a = b;
+ b = c;
+ c = d;
+ d = e;
+ e = f;
+ f = g;
+ g = h;
+ h = i;
+ i = j;
+ j = k;
+ k = l;
+ l = m;
+ m = a;
+ }
+}
diff --git a/riscv/riscv-probe/examples/hang/rules.mk b/riscv/riscv-probe/examples/hang/rules.mk
new file mode 100644
index 0000000..8a04005
--- /dev/null
+++ b/riscv/riscv-probe/examples/hang/rules.mk
@@ -0,0 +1 @@
+hang_objs = hang.o
diff --git a/riscv/riscv-probe/examples/hello/hello.c b/riscv/riscv-probe/examples/hello/hello.c
new file mode 100644
index 0000000..d65ed6a
--- /dev/null
+++ b/riscv/riscv-probe/examples/hello/hello.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("hello\n");
+}
diff --git a/riscv/riscv-probe/examples/hello/rules.mk b/riscv/riscv-probe/examples/hello/rules.mk
new file mode 100644
index 0000000..4d43f63
--- /dev/null
+++ b/riscv/riscv-probe/examples/hello/rules.mk
@@ -0,0 +1 @@
+hello_objs = hello.o
diff --git a/riscv/riscv-probe/examples/malloc/main.c b/riscv/riscv-probe/examples/malloc/main.c
new file mode 100644
index 0000000..cde2038
--- /dev/null
+++ b/riscv/riscv-probe/examples/malloc/main.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ const size_t nelems = 8;
+ const size_t elemsize = 128;
+ char *alloc[nelems];
+
+ for (size_t i = 0; i < nelems; i++) {
+ if (alloc[i] = malloc(elemsize)) {
+ memset(alloc[i], 0, elemsize);
+ }
+ printf("malloc[%d]=0x%x\n", i, alloc[i]);
+ }
+
+ for (size_t i = 0; i < nelems; i++) {
+ free(alloc[i]);
+ }
+} \ No newline at end of file
diff --git a/riscv/riscv-probe/examples/malloc/rules.mk b/riscv/riscv-probe/examples/malloc/rules.mk
new file mode 100644
index 0000000..82b0bc6
--- /dev/null
+++ b/riscv/riscv-probe/examples/malloc/rules.mk
@@ -0,0 +1 @@
+malloc_objs = main.o
diff --git a/riscv/riscv-probe/examples/memory/main.c b/riscv/riscv-probe/examples/memory/main.c
new file mode 100644
index 0000000..01c5d0b
--- /dev/null
+++ b/riscv/riscv-probe/examples/memory/main.c
@@ -0,0 +1,17 @@
+#include "femto.h"
+#ifdef __riscv
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+#endif
+
+extern char _memory_start;
+
+int main(int argc, char **argv)
+{
+#ifdef __riscv
+ memory_info_t info = memory_probe();
+ printf("memory_start=0x%lx - 0x%lx\n", info.start, info.end);
+#else
+ puts("architecture-not-supported");
+#endif
+}
diff --git a/riscv/riscv-probe/examples/memory/rules.mk b/riscv/riscv-probe/examples/memory/rules.mk
new file mode 100644
index 0000000..1d0ad8c
--- /dev/null
+++ b/riscv/riscv-probe/examples/memory/rules.mk
@@ -0,0 +1 @@
+memory_objs = main.o
diff --git a/riscv/riscv-probe/examples/probe/probe.c b/riscv/riscv-probe/examples/probe/probe.c
new file mode 100644
index 0000000..4cc4384
--- /dev/null
+++ b/riscv/riscv-probe/examples/probe/probe.c
@@ -0,0 +1,71 @@
+#include "femto.h"
+
+#ifdef __riscv
+#include "arch/riscv/csr.h"
+#include "arch/riscv/trap.h"
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+
+#define MCAUSE_UNSET 0xabbaabba
+
+static volatile uintptr_t save_mcause;
+
+static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
+{
+ save_mcause = mcause;
+ write_csr(mepc, mepc + 4);
+}
+
+static const char* isa_string(char *buf, size_t bufsz)
+{
+ static const char *ext = "iemafdqlcbjtpvnsu";
+
+ const char *p = ext;
+ char *q = buf + snprintf(buf, bufsz, "rv%d", xlen());
+ do {
+ if (has_ext(*p)) {
+ *q++ = *p;
+ }
+ } while (*++p);
+ *q = '\0';
+
+ return buf;
+}
+
+static void probe_all_csrs()
+{
+ int *csrenum = csr_enum_array();
+ const char **csrnames = csr_name_array();
+ const char* ws = " ";
+ set_trap_fn(trap_save_cause);
+ while (*csrenum != csr_none) {
+ save_mcause = MCAUSE_UNSET;
+ long value = read_csr_enum(*csrenum);
+ const char* csrname = csrnames[*csrenum];
+ if (save_mcause != MCAUSE_UNSET) {
+ int async = save_mcause < 0;
+ int cause = save_mcause & (((uintptr_t)-1) >> async);
+ printf("csr: %s%s %s cause=%ld mtval=0x%lx\n",
+ csrname, ws + strlen(csrname), cause < 16
+ ? (async ? riscv_intr_names : riscv_excp_names)[cause]
+ : "(unknown)", save_mcause, read_csr_enum(csr_mtval));
+ } else {
+ printf("csr: %s%s 0x%lx\n",
+ csrname, ws + strlen(csrname), value);
+ }
+ csrenum++;
+ }
+}
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef __riscv
+ char buf[32];
+ printf("isa: %s\n", isa_string(buf, sizeof(buf)));
+ probe_all_csrs();
+ printf("\n");
+#else
+ puts("architecture-not-supported");
+#endif
+}
diff --git a/riscv/riscv-probe/examples/probe/rules.mk b/riscv/riscv-probe/examples/probe/rules.mk
new file mode 100644
index 0000000..928f066
--- /dev/null
+++ b/riscv/riscv-probe/examples/probe/rules.mk
@@ -0,0 +1 @@
+probe_objs = probe.o
diff --git a/riscv/riscv-probe/examples/symbols/rules.mk b/riscv/riscv-probe/examples/symbols/rules.mk
new file mode 100644
index 0000000..ba4e934
--- /dev/null
+++ b/riscv/riscv-probe/examples/symbols/rules.mk
@@ -0,0 +1 @@
+symbols_objs = symbols.o
diff --git a/riscv/riscv-probe/examples/symbols/symbols.c b/riscv/riscv-probe/examples/symbols/symbols.c
new file mode 100644
index 0000000..d05807f
--- /dev/null
+++ b/riscv/riscv-probe/examples/symbols/symbols.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+
+/* program to print linker script provided symbols */
+
+extern char _text_start;
+extern char _text_end;
+extern char _rodata_start;
+extern char _rodata_end;
+extern char _data_start;
+extern char _data_end;
+extern char _bss_start;
+extern char _bss_end;
+extern char _memory_start;
+extern char _memory_end;
+
+int main(int argc, char **argv)
+{
+ printf("_text_start=0x%x\n", &_text_start);
+ printf("_text_end=0x%x\n", &_text_end);
+ printf("_rodata_start=0x%x\n", &_rodata_start);
+ printf("_rodata_end=0x%x\n", &_rodata_end);
+ printf("_data_start=0x%x\n", &_data_start);
+ printf("_data_end=0x%x\n", &_data_end);
+ printf("_bss_start=0x%x\n", &_bss_start);
+ printf("_bss_end=0x%x\n", &_bss_end);
+ printf("_memory_start=0x%x\n", &_memory_start);
+ printf("_memory_end=0x%x\n", &_memory_end);
+}
diff --git a/riscv/riscv-probe/examples/user/rules.mk b/riscv/riscv-probe/examples/user/rules.mk
new file mode 100644
index 0000000..63c6868
--- /dev/null
+++ b/riscv/riscv-probe/examples/user/rules.mk
@@ -0,0 +1 @@
+user_objs = user.o
diff --git a/riscv/riscv-probe/examples/user/user.c b/riscv/riscv-probe/examples/user/user.c
new file mode 100644
index 0000000..e37397f
--- /dev/null
+++ b/riscv/riscv-probe/examples/user/user.c
@@ -0,0 +1,27 @@
+#include "femto.h"
+
+#ifdef __riscv
+#include "arch/riscv/encoding.h"
+#include "arch/riscv/machine.h"
+#endif
+
+int main(int argc, char **argv)
+{
+ /*
+ * Set up PMP (Physical Memory Protection)
+ *
+ * PMP is optional; bit if implemented enforcement is mandatory
+ * and user-mode is prevented access to all memory by default.
+ * We should change this code to restrict user mode to its .text
+ * .rodata, .data, .bss and devices.
+ */
+#ifdef __riscv
+ if (pmp_entry_count() > 0) {
+ pmp_allow_all();
+ }
+ mode_set_and_continue(PRV_U);
+ puts("riscv-user-mode");
+#else
+ puts("architecture-not-supported");
+#endif
+}
diff --git a/riscv/riscv-probe/libfemto/README.md b/riscv/riscv-probe/libfemto/README.md
new file mode 100644
index 0000000..86cd934
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/README.md
@@ -0,0 +1,28 @@
+# libfemto
+
+libfemto is a lightweight bare-metal C library for embedded RISC-V development.
+libfemto provides:
+
+- Reduced set of the _POSIX.1-2017 / IEEE 1003.1-2017_ standard
+- Simple lightweight hardware configuration mechanism
+- RISC-V machine mode functions and macros
+- Console and power device drivers
+
+libfemto implements a reduced set of the _POSIX.1-2017 / IEEE 1003.1-2017_
+standard, with the addition of glibc's `getauxval` API to access hardware
+configuration in an auxiliary vector (`__auxv`) that contains tuples
+describing the target environment. The auxiliary vector is intended as a
+lightweight mechanism to pass dynamic configuration information on embedded
+targets, serving as an alternative to compile time constants used during
+hardware initialization. The auxiliary vector API has been repurposed to
+allow retrieval of hardware configuration parameters such as clock
+frequencies and device base addresses for use as a compact alternative to
+(DTB) Device Tree Binary, which is not available on small embedded targets.
+
+libfemto contains the following device drivers:
+
+- HTIF (Host Target Interface)
+- NS16550A UART Console
+- SiFive UART Console
+- SiFive Test Device
+- Semihosting Syscalls
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);
+ }
+}
diff --git a/riscv/riscv-probe/libfemto/drivers/htif.c b/riscv/riscv-probe/libfemto/drivers/htif.c
new file mode 100644
index 0000000..ef6995e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/htif.c
@@ -0,0 +1,91 @@
+// See LICENSE for license details.
+
+#ifdef __riscv
+
+#include "femto.h"
+#include "spinlock.h"
+
+struct { uint32_t arr[2]; } volatile tohost __attribute__((section(".htif")));
+struct { uint32_t arr[2]; } volatile fromhost __attribute__((section(".htif")));
+
+static spinlock_t htif_lock = SPINLOCK_INIT;
+
+static inline void htif_send(uint8_t dev, uint8_t cmd, int64_t data)
+{
+ /* endian neutral encoding with ordered 32-bit writes */
+ union { uint32_t arr[2]; uint64_t val; } encode = {
+ .val = (uint64_t)dev << 56 | (uint64_t)cmd << 48 | data
+ };
+ tohost.arr[0] = encode.arr[0];
+ tohost.arr[1] = encode.arr[1];
+}
+
+static inline void htif_recv(uint8_t *dev, uint8_t *cmd, int64_t *data)
+{
+ /* endian neutral decoding with ordered 32-bit reads */
+ union { uint32_t arr[2]; uint64_t val; } decode;
+ decode.arr[0] = fromhost.arr[0];
+ decode.arr[1] = fromhost.arr[1];
+ *dev = decode.val >> 56;
+ *cmd = (decode.val >> 48) & 0xff;
+ *data = decode.val << 16 >> 16;
+}
+
+static int64_t htif_get_fromhost(uint8_t dev, uint8_t cmd)
+{
+ /* receive data for specified device and command */
+ uint8_t rdev, rcmd;
+ int64_t data;
+ htif_recv(&rdev, &rcmd, &data);
+ return rdev == dev && rcmd == cmd ? data : -1;
+}
+
+static void htif_set_tohost(uint8_t dev, uint8_t cmd, int64_t data)
+{
+ /* send data with specified device and command */
+ while (tohost.arr[0]) {
+ asm volatile ("" : : "r" (fromhost.arr[0]));
+ asm volatile ("" : : "r" (fromhost.arr[1]));
+ }
+ htif_send(dev, cmd, data);
+}
+
+static int htif_getchar()
+{
+ int ch;
+ spinlock_lock(&htif_lock);
+ if ((ch = htif_get_fromhost(1, 0) & 0xff)) {
+ htif_set_tohost(1, 0, 0);
+ }
+ spinlock_unlock(&htif_lock);
+ return ch;
+}
+
+static int htif_putchar(int ch)
+{
+ spinlock_lock(&htif_lock);
+ htif_set_tohost(1, 1, ch & 0xff);
+ spinlock_unlock(&htif_lock);
+ return ch & 0xff;
+}
+
+static void htif_poweroff(int status)
+{
+ for (;;) {
+ htif_set_tohost(0, 0, 1);
+ }
+}
+
+console_device_t console_htif = {
+ NULL,
+ htif_getchar,
+ htif_putchar
+};
+
+
+poweroff_device_t poweroff_htif = {
+ NULL,
+ htif_poweroff
+};
+
+#endif
diff --git a/riscv/riscv-probe/libfemto/drivers/ns16550a.c b/riscv/riscv-probe/libfemto/drivers/ns16550a.c
new file mode 100644
index 0000000..cff5605
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/ns16550a.c
@@ -0,0 +1,65 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ UART_RBR = 0x00, /* Receive Buffer Register */
+ UART_THR = 0x00, /* Transmit Hold Register */
+ UART_IER = 0x01, /* Interrupt Enable Register */
+ UART_DLL = 0x00, /* Divisor LSB (LCR_DLAB) */
+ UART_DLM = 0x01, /* Divisor MSB (LCR_DLAB) */
+ UART_FCR = 0x02, /* FIFO Control Register */
+ UART_LCR = 0x03, /* Line Control Register */
+ UART_MCR = 0x04, /* Modem Control Register */
+ UART_LSR = 0x05, /* Line Status Register */
+ UART_MSR = 0x06, /* Modem Status Register */
+ UART_SCR = 0x07, /* Scratch Register */
+
+ UART_LCR_DLAB = 0x80, /* Divisor Latch Bit */
+ UART_LCR_8BIT = 0x03, /* 8-bit */
+ UART_LCR_PODD = 0x08, /* Parity Odd */
+
+ UART_LSR_DA = 0x01, /* Data Available */
+ UART_LSR_OE = 0x02, /* Overrun Error */
+ UART_LSR_PE = 0x04, /* Parity Error */
+ UART_LSR_FE = 0x08, /* Framing Error */
+ UART_LSR_BI = 0x10, /* Break indicator */
+ UART_LSR_RE = 0x20, /* THR is empty */
+ UART_LSR_RI = 0x40, /* THR is empty and line is idle */
+ UART_LSR_EF = 0x80, /* Erroneous data in FIFO */
+};
+
+static volatile uint8_t *uart;
+
+static void ns16550a_init()
+{
+ uart = (uint8_t *)(void *)getauxval(NS16550A_UART0_CTRL_ADDR);
+ uint32_t uart_freq = getauxval(UART0_CLOCK_FREQ);
+ uint32_t baud_rate = getauxval(UART0_BAUD_RATE);
+ uint32_t divisor = uart_freq / (16 * baud_rate);
+ uart[UART_LCR] = UART_LCR_DLAB;
+ uart[UART_DLL] = divisor & 0xff;
+ uart[UART_DLM] = (divisor >> 8) & 0xff;
+ uart[UART_LCR] = UART_LCR_PODD | UART_LCR_8BIT;
+}
+
+static int ns16550a_getchar()
+{
+ if (uart[UART_LSR] & UART_LSR_DA) {
+ return uart[UART_RBR];
+ } else {
+ return -1;
+ }
+}
+
+static int ns16550a_putchar(int ch)
+{
+ while ((uart[UART_LSR] & UART_LSR_RI) == 0);
+ return uart[UART_THR] = ch & 0xff;
+}
+
+console_device_t console_ns16550a = {
+ ns16550a_init,
+ ns16550a_getchar,
+ ns16550a_putchar
+};
diff --git a/riscv/riscv-probe/libfemto/drivers/semihost.c b/riscv/riscv-probe/libfemto/drivers/semihost.c
new file mode 100644
index 0000000..9f47834
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/semihost.c
@@ -0,0 +1,60 @@
+// See LICENSE for license details.
+
+#ifdef __riscv
+
+#include "femto.h"
+
+enum {
+ SBI_CONSOLE_PUTCHAR = 1,
+ SBI_CONSOLE_GETCHAR = 2,
+ SBI_SHUTDOWN = 8
+};
+
+#define __syscall(...) \
+ asm volatile ("ecall\n\t" : "+r"(a0) : __VA_ARGS__ : "memory")
+
+static inline long semihost_call0(long n)
+{
+ register long a7 __asm__("a7") = n;
+ register long a0 __asm__("a0");
+ __syscall("r"(a7));
+ return a0;
+}
+
+static inline long semihost_call1(long n, long a)
+{
+ register long a7 __asm__("a7") = n;
+ register long a0 __asm__("a0") = a;
+ __syscall("r"(a7), "0"(a0));
+ return a0;
+}
+
+static int semihost_getchar()
+{
+ semihost_call0(SBI_CONSOLE_GETCHAR);
+}
+
+static int semihost_putchar(int ch)
+{
+ semihost_call1(SBI_CONSOLE_PUTCHAR, ch);
+}
+
+static void semihost_poweroff(int status)
+{
+ semihost_call0(SBI_SHUTDOWN);
+ __builtin_unreachable();
+}
+
+console_device_t console_semihost = {
+ NULL,
+ semihost_getchar,
+ semihost_putchar
+};
+
+
+poweroff_device_t poweroff_semihost = {
+ NULL,
+ semihost_poweroff
+};
+
+#endif \ No newline at end of file
diff --git a/riscv/riscv-probe/libfemto/drivers/sifive_test.c b/riscv/riscv-probe/libfemto/drivers/sifive_test.c
new file mode 100644
index 0000000..1bda9e9
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/sifive_test.c
@@ -0,0 +1,28 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ SIFIVE_TEST_FAIL = 0x3333,
+ SIFIVE_TEST_PASS = 0x5555,
+};
+
+static volatile uint32_t *test;
+
+static void sifive_test_init()
+{
+ test = (uint32_t *)(void *)getauxval(SIFIVE_TEST_CTRL_ADDR);
+}
+
+static void sifive_test_poweroff(int status)
+{
+ *test = SIFIVE_TEST_PASS;
+ while (1) {
+ asm volatile("");
+ }
+}
+
+poweroff_device_t poweroff_sifive_test = {
+ sifive_test_init,
+ sifive_test_poweroff
+};
diff --git a/riscv/riscv-probe/libfemto/drivers/sifive_uart.c b/riscv/riscv-probe/libfemto/drivers/sifive_uart.c
new file mode 100644
index 0000000..9be877e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/drivers/sifive_uart.c
@@ -0,0 +1,58 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+enum {
+ /* UART Registers */
+ UART_REG_TXFIFO = 0,
+ UART_REG_RXFIFO = 1,
+ UART_REG_TXCTRL = 2,
+ UART_REG_RXCTRL = 3,
+ UART_REG_IE = 4,
+ UART_REG_IP = 5,
+ UART_REG_DIV = 6,
+
+ /* TXCTRL register */
+ UART_TXEN = 1,
+ UART_TXSTOP = 2,
+
+ /* RXCTRL register */
+ UART_RXEN = 1,
+
+ /* IP register */
+ UART_IP_TXWM = 1,
+ UART_IP_RXWM = 2
+};
+
+static volatile int *uart;
+
+static void sifive_uart_init()
+{
+ uart = (int *)(void *)getauxval(SIFIVE_UART0_CTRL_ADDR);
+ uint32_t uart_freq = getauxval(UART0_CLOCK_FREQ);
+ uint32_t baud_rate = getauxval(UART0_BAUD_RATE);
+ uint32_t divisor = uart_freq / baud_rate - 1;
+ uart[UART_REG_DIV] = divisor;
+ uart[UART_REG_TXCTRL] = UART_TXEN;
+ uart[UART_REG_RXCTRL] = UART_RXEN;
+ uart[UART_REG_IE] = 0;
+}
+
+static int sifive_uart_getchar()
+{
+ int ch = uart[UART_REG_RXFIFO];
+ if (ch < 0) return -1;
+ return ch;
+}
+
+static int sifive_uart_putchar(int ch)
+{
+ while (uart[UART_REG_TXFIFO] < 0);
+ return uart[UART_REG_TXFIFO] = ch & 0xff;
+}
+
+console_device_t console_sifive_uart = {
+ sifive_uart_init,
+ sifive_uart_getchar,
+ sifive_uart_putchar
+};
diff --git a/riscv/riscv-probe/libfemto/include/alloca.h b/riscv/riscv-probe/libfemto/include/alloca.h
new file mode 100644
index 0000000..2ae6a3e
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/alloca.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define alloca(size) __builtin_alloca (size)
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/csr.h b/riscv/riscv-probe/libfemto/include/arch/riscv/csr.h
new file mode 100644
index 0000000..8bbc5f4
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/csr.h
@@ -0,0 +1,81 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int* csr_enum_array();
+const char** csr_name_array();
+long read_csr_enum(int csrenum);
+void write_csr_enum(int csrenum, long value);
+
+enum {
+ csr_none,
+ 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
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/encoding.h b/riscv/riscv-probe/libfemto/include/arch/riscv/encoding.h
new file mode 100644
index 0000000..d592bbc
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/encoding.h
@@ -0,0 +1,197 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MSTATUS_UIE 0x00000001
+#define MSTATUS_SIE 0x00000002
+#define MSTATUS_HIE 0x00000004
+#define MSTATUS_MIE 0x00000008
+#define MSTATUS_UPIE 0x00000010
+#define MSTATUS_SPIE 0x00000020
+#define MSTATUS_HPIE 0x00000040
+#define MSTATUS_MPIE 0x00000080
+#define MSTATUS_SPP 0x00000100
+#define MSTATUS_HPP 0x00000600
+#define MSTATUS_MPP 0x00001800
+#define MSTATUS_FS 0x00006000
+#define MSTATUS_XS 0x00018000
+#define MSTATUS_MPRV 0x00020000
+#define MSTATUS_SUM 0x00040000
+#define MSTATUS_MXR 0x00080000
+#define MSTATUS_TVM 0x00100000
+#define MSTATUS_TW 0x00200000
+#define MSTATUS_TSR 0x00400000
+#define MSTATUS32_SD 0x80000000
+#define MSTATUS64_SD 0x8000000000000000
+
+#define SSTATUS_UIE 0x00000001
+#define SSTATUS_SIE 0x00000002
+#define SSTATUS_UPIE 0x00000010
+#define SSTATUS_SPIE 0x00000020
+#define SSTATUS_SPP 0x00000100
+#define SSTATUS_FS 0x00006000
+#define SSTATUS_XS 0x00018000
+#define SSTATUS_SUM 0x00040000
+#define SSTATUS_MXR 0x00080000
+#define SSTATUS32_SD 0x80000000
+#define SSTATUS64_SD 0x8000000000000000
+
+#define DCSR_XDEBUGVER (3U<<30)
+#define DCSR_NDRESET (1<<29)
+#define DCSR_FULLRESET (1<<28)
+#define DCSR_EBREAKM (1<<15)
+#define DCSR_EBREAKH (1<<14)
+#define DCSR_EBREAKS (1<<13)
+#define DCSR_EBREAKU (1<<12)
+#define DCSR_STOPCYCLE (1<<10)
+#define DCSR_STOPTIME (1<<9)
+#define DCSR_CAUSE (7<<6)
+#define DCSR_DEBUGINT (1<<5)
+#define DCSR_HALT (1<<3)
+#define DCSR_STEP (1<<2)
+#define DCSR_PRV (3<<0)
+
+#define DCSR_CAUSE_NONE 0
+#define DCSR_CAUSE_SWBP 1
+#define DCSR_CAUSE_HWBP 2
+#define DCSR_CAUSE_DEBUGINT 3
+#define DCSR_CAUSE_STEP 4
+#define DCSR_CAUSE_HALT 5
+
+#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4))
+#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5))
+#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
+
+#define MCONTROL_SELECT (1<<19)
+#define MCONTROL_TIMING (1<<18)
+#define MCONTROL_ACTION (0x3f<<12)
+#define MCONTROL_CHAIN (1<<11)
+#define MCONTROL_MATCH (0xf<<7)
+#define MCONTROL_M (1<<6)
+#define MCONTROL_H (1<<5)
+#define MCONTROL_S (1<<4)
+#define MCONTROL_U (1<<3)
+#define MCONTROL_EXECUTE (1<<2)
+#define MCONTROL_STORE (1<<1)
+#define MCONTROL_LOAD (1<<0)
+
+#define MCONTROL_TYPE_NONE 0
+#define MCONTROL_TYPE_MATCH 2
+
+#define MCONTROL_ACTION_DEBUG_EXCEPTION 0
+#define MCONTROL_ACTION_DEBUG_MODE 1
+#define MCONTROL_ACTION_TRACE_START 2
+#define MCONTROL_ACTION_TRACE_STOP 3
+#define MCONTROL_ACTION_TRACE_EMIT 4
+
+#define MCONTROL_MATCH_EQUAL 0
+#define MCONTROL_MATCH_NAPOT 1
+#define MCONTROL_MATCH_GE 2
+#define MCONTROL_MATCH_LT 3
+#define MCONTROL_MATCH_MASK_LOW 4
+#define MCONTROL_MATCH_MASK_HIGH 5
+
+#define MIP_SSIP (1 << IRQ_S_SOFT)
+#define MIP_HSIP (1 << IRQ_H_SOFT)
+#define MIP_MSIP (1 << IRQ_M_SOFT)
+#define MIP_STIP (1 << IRQ_S_TIMER)
+#define MIP_HTIP (1 << IRQ_H_TIMER)
+#define MIP_MTIP (1 << IRQ_M_TIMER)
+#define MIP_SEIP (1 << IRQ_S_EXT)
+#define MIP_HEIP (1 << IRQ_H_EXT)
+#define MIP_MEIP (1 << IRQ_M_EXT)
+
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define SPTBR32_MODE 0x80000000
+#define SPTBR32_ASID 0x7FC00000
+#define SPTBR32_PPN 0x003FFFFF
+#define SPTBR64_MODE 0xF000000000000000
+#define SPTBR64_ASID 0x0FFFF00000000000
+#define SPTBR64_PPN 0x00000FFFFFFFFFFF
+
+#define SPTBR_MODE_OFF 0
+#define SPTBR_MODE_SV32 1
+#define SPTBR_MODE_SV39 8
+#define SPTBR_MODE_SV48 9
+#define SPTBR_MODE_SV57 10
+#define SPTBR_MODE_SV64 11
+
+#define PMP_R 0x01
+#define PMP_W 0x02
+#define PMP_X 0x04
+#define PMP_A 0x18
+#define PMP_L 0x80
+#define PMP_SHIFT 2
+#define PMPCFG_COUNT 4
+#define PMPADDR_COUNT 16
+
+#define PMP_OFF 0x00
+#define PMP_TOR 0x08
+#define PMP_NA4 0x10
+#define PMP_NAPOT 0x18
+
+#define IRQ_S_SOFT 1
+#define IRQ_H_SOFT 2
+#define IRQ_M_SOFT 3
+#define IRQ_S_TIMER 5
+#define IRQ_H_TIMER 6
+#define IRQ_M_TIMER 7
+#define IRQ_S_EXT 9
+#define IRQ_H_EXT 10
+#define IRQ_M_EXT 11
+#define IRQ_COP 12
+#define IRQ_HOST 13
+
+#define DEFAULT_RSTVEC 0x00001000
+#define CLINT_BASE 0x02000000
+#define CLINT_SIZE 0x000c0000
+#define EXT_IO_BASE 0x40000000
+#define DRAM_BASE 0x80000000
+
+// page table entry (PTE) fields
+#define PTE_V 0x001 // Valid
+#define PTE_R 0x002 // Read
+#define PTE_W 0x004 // Write
+#define PTE_X 0x008 // Execute
+#define PTE_U 0x010 // User
+#define PTE_G 0x020 // Global
+#define PTE_A 0x040 // Accessed
+#define PTE_D 0x080 // Dirty
+#define PTE_SOFT 0x300 // Reserved for Software
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#ifdef __riscv
+
+#if __riscv_xlen == 64
+# define MSTATUS_SD MSTATUS64_SD
+# define SSTATUS_SD SSTATUS64_SD
+# define RISCV_PGLEVEL_BITS 9
+# define SPTBR_MODE SPTBR64_MODE
+#else
+# define MSTATUS_SD MSTATUS32_SD
+# define SSTATUS_SD SSTATUS32_SD
+# define RISCV_PGLEVEL_BITS 10
+# define SPTBR_MODE SPTBR32_MODE
+#endif
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/machine.h b/riscv/riscv-probe/libfemto/include/arch/riscv/machine.h
new file mode 100644
index 0000000..077a6c1
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/machine.h
@@ -0,0 +1,183 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+
+void arch_setup();
+void exit(int status) __attribute__((noreturn));
+
+#define die(str, ...) ({ \
+ printf("%s:%d: " str "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(-1); })
+
+#define assert(x) ({ if (!(x)) die("assertion failed: %s", #x); })
+
+#define read_const_csr(reg) ({ unsigned long __tmp; \
+ asm ("csrr %0, " #reg : "=r"(__tmp)); __tmp; })
+
+#define read_csr(reg) ({ unsigned long __tmp; \
+ asm volatile ("csrr %0, " #reg : "=r"(__tmp)); __tmp; })
+
+#define write_csr(reg, val) ({ \
+ asm volatile ("csrw " #reg ", %0" :: "rK"(val)); })
+
+#define swap_csr(reg, val) ({ unsigned long __tmp; \
+ asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); __tmp; })
+
+#define set_csr(reg, bit) ({ unsigned long __tmp; \
+ asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; })
+
+#define clear_csr(reg, bit) ({ unsigned long __tmp; \
+ asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; })
+
+static inline uintptr_t get_field(uintptr_t reg, uintptr_t mask)
+{
+ return ((reg & mask) / (mask & ~(mask << 1)));
+}
+
+static inline uintptr_t set_field(uintptr_t reg, uintptr_t mask, uintptr_t val)
+{
+ return ((reg & ~mask) | ((val * (mask & ~(mask << 1))) & mask));
+}
+
+static inline unsigned long rdtime() { return read_csr(time); }
+static inline unsigned long rdcycle() { return read_csr(cycle); }
+static inline unsigned long rdinstret() { return read_csr(instret); }
+static inline int64_t misa() { return read_const_csr(misa); }
+static inline int has_ext(char ext) { return misa() & (1 << (ext - 'a')); }
+static inline int xlen() { return misa() < 0 ? 64 : 32; }
+static inline void wfi() { asm volatile ("wfi" ::: "memory"); }
+
+__attribute__((noreturn)) static inline void mret()
+{
+ asm volatile ("mret");
+ __builtin_unreachable();
+}
+
+
+/*
+ * Memory
+ *
+ * TODO - improve this API to return a list of memory segments
+ */
+
+typedef struct memory_info
+{
+ uintptr_t start;
+ uintptr_t end;
+} memory_info_t;
+
+
+/*
+ * memory_probe - return memory_info
+ */
+memory_info_t memory_probe();
+
+/*
+ * memory_probe_range - probe a memory address range
+ */
+uintptr_t memory_probe_range(uintptr_t start, uintptr_t end);
+
+
+/*
+ * Physical Memory Protection
+ *
+ * PMP is optional but if implememented, enforcement must be enabled by
+ * default, if no PMP entries are set. This means loads, stores or fetches
+ * from any mode besides M mode, will fail unless explicitly allowed.
+ * PMP must be configured irregardless of whether it is implemented.
+ */
+
+typedef struct pmp_info
+{
+ int width;
+ int granularity;
+ int count;
+} pmp_info_t;
+
+/*
+ * pmp_probe - return pmp_info
+ */
+pmp_info_t pmp_probe();
+
+/*
+ * pmp_entry_granularity - return PMP entry width (physical memory width)
+ */
+int pmp_entry_width();
+
+/*
+ * pmp_entry_granularity - return PMP entry granularity (smallest entry size)
+ */
+int pmp_entry_granularity();
+
+/*
+ * pmp_entry_count - return number of PMP entries
+ */
+int pmp_entry_count();
+
+/*
+ * pmp_clear_all - set PMP to disallow mode != PRV_M physical memory accesses
+ */
+void pmp_clear_all();
+
+/*
+ * pmp_allow_all - set PMP to allow mode != PRV_M physical memory accesses
+ */
+void pmp_allow_all();
+
+/*
+ * pmp_entry_set - set one PMP entry
+ *
+ * - n : pmp entry number
+ * - prot : protection (PMP_R | PMP_W | PMP_X)
+ * - addr : start address
+ * - len : power of two length
+ */
+int pmp_entry_set(unsigned n, uint8_t prot, uint64_t addr, uint64_t len);
+
+
+/*
+ * Privileged modes
+ */
+
+/*
+ * mode_set_and_jump
+ *
+ * Set mstatus.mpp, sets mepc to passed function pointer and then issues mret
+ * Note: the hart will continue running on the same stack
+ */
+static inline void mode_set_and_jump(unsigned mode, void (*fn)(void))
+{
+ assert(mode <= PRV_U);
+ write_csr(mstatus, set_field(read_csr(mstatus), MSTATUS_MPP, mode));
+ write_csr(mepc, fn);
+ mret();
+}
+
+/*
+ * mode_set_and_continue
+ *
+ * Set mstatus.mpp, sets mepc to instruction after mret and then issues mret
+ * Note: the hart will continue running on the same stack
+ */
+static inline void mode_set_and_continue(unsigned mode)
+{
+ assert(mode <= PRV_U);
+ write_csr(mstatus, set_field(read_csr(mstatus), MSTATUS_MPP, mode));
+ asm volatile (
+ "lla t0, 1f\n"
+ "csrw mepc, t0\n"
+ "mret\n"
+ "1:"
+ ::: "t0"
+ );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/pte.h b/riscv/riscv-probe/libfemto/include/arch/riscv/pte.h
new file mode 100644
index 0000000..38d7b8d
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/pte.h
@@ -0,0 +1,121 @@
+#pragma once
+
+enum {
+ PTE_SHIFT_V = 0,
+ PTE_SHIFT_R = 1,
+ PTE_SHIFT_W = 2,
+ PTE_SHIFT_X = 3,
+ PTE_SHIFT_U = 4,
+ PTE_SHIFT_G = 5,
+ PTE_SHIFT_A = 6,
+ PTE_SHIFT_D = 7,
+ PTE_SHIFT_SW1 = 8,
+ PTE_SHIFT_SW2 = 9,
+ PTE_V = 1 << PTE_SHIFT_V,
+ PTE_R = 1 << PTE_SHIFT_R,
+ PTE_W = 1 << PTE_SHIFT_W,
+ PTE_X = 1 << PTE_SHIFT_X,
+ PTE_U = 1 << PTE_SHIFT_U,
+ PTE_G = 1 << PTE_SHIFT_G,
+ PTE_A = 1 << PTE_SHIFT_A,
+ PTE_D = 1 << PTE_SHIFT_D,
+ PTE_SW1 = 1 << PTE_SHIFT_SW1,
+ PTE_SW2 = 1 << PTE_SHIFT_SW2
+};
+
+enum {
+ SV32_LEVELS = 2,
+ SV32_LEVEL_BITS = 10,
+ SV32_PTE_SIZE = 4
+};
+
+union sv32_va {
+ uint32_t val;
+ struct {
+ uint32_t pg_off : 12;
+ uint32_t vpn : 20;
+ } va;
+};
+
+union sv32_pa {
+ uint64_t val;
+ struct {
+ uint64_t pg_off : 12;
+ uint64_t ppn : 22;
+ uint64_t rsrv : 30;
+ } pa;
+};
+
+union sv32_pte {
+ uint32_t val;
+ struct {
+ uint32_t flags : 10;
+ uint32_t ppn : 22;
+ } pte;
+};
+
+enum {
+ SV39_LEVELS = 3,
+ SV39_LEVEL_BITS = 9,
+ SV39_PTE_SIZE = 8
+};
+
+union sv39_va {
+ uint64_t val;
+ struct {
+ uint64_t pg_off : 12;
+ uint64_t vpn : 27;
+ uint64_t rsrv : 25;
+ } va;
+};
+
+union sv39_pa {
+ uint64_t val;
+ struct {
+ uint64_t pg_off : 12;
+ uint64_t ppn : 44;
+ uint64_t rsrv : 8;
+ } pa;
+};
+
+union sv39_pte {
+ uint64_t val;
+ struct {
+ uint64_t flags : 10;
+ uint64_t ppn : 44;
+ uint64_t rsrv : 10;
+ } pte;
+};
+
+enum {
+ SV48_LEVELS = 4,
+ SV48_LEVEL_BITS = 9,
+ SV48_PTE_SIZE = 8
+};
+
+union sv48_va {
+ uint64_t val;
+ struct {
+ uint64_t pg_off : 12;
+ uint64_t vpn : 36;
+ uint64_t rsrv : 16;
+ } va;
+};
+
+union sv48_pa {
+ uint64_t val;
+ struct {
+ uint64_t pg_off : 12;
+ uint64_t ppn : 44;
+ uint64_t rsrv : 8;
+ } pa;
+};
+
+union sv48_pte {
+ uint64_t val;
+ struct {
+ uint64_t flags : 10;
+ uint64_t ppn : 44;
+ uint64_t rsrv : 10;
+ } pte;
+};
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/spinlock.h b/riscv/riscv-probe/libfemto/include/arch/riscv/spinlock.h
new file mode 100644
index 0000000..d747cef
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/spinlock.h
@@ -0,0 +1,192 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Ticket Lock
+ *
+ * Author: Michael Clark <[email protected]>
+ *
+ * Ticket locks are fair spinlocks that order acccess on a first-in,
+ * first-out basis. The lock is composed of a head counter and a tail
+ * counter. The head counter indicates the ticket number of the current
+ * lock owner. The tail counter indicates the last issued ticket number.
+ * To acquire the lock, the acquiring thread atomically increments the
+ * tail counter to assign itself a ticket number. It then waits until
+ * its assigned ticket number is seen in the head counter. If the lock
+ * is not contended, then head will equal the ticket number just assigned.
+ * To release the lock, the lock owner atomically increments head counter
+ * thereby allowing access to the thread holding the next ticket number.
+ *
+ * Initialializtion - the lock is initialized unlocked, so that the
+ * next tail increment returns a ticket that will acquire the lock.
+ *
+ * tail | head
+ * -------|-------
+ * 0x0000 | 0x0001
+ *
+ * Lock - the tail is incremented, and the thread waits unti the tail
+ * is equal to the head before returning control, which is the typical
+ * case if the lock is uncontended.
+ *
+ * tail | head
+ * -------|-------
+ * 0x0001 | 0x0001
+ *
+ * Unlock - the head is incremented, so that it will now equal the value
+ * of the ticket of the next waiter, if any.
+ *
+ * tail | head
+ * -------|-------
+ * 0x0001 | 0x0002
+ *
+ * Waiters - the number of waiters can be calculated by subtracting
+ * head (current ticket) from the tail plus one (next ticket)
+ *
+ * num_waiters = tail + 1 - head
+ */
+
+/**
+ * Ticket lock types
+ */
+
+typedef unsigned short ticket_t;
+typedef unsigned int ticketdata_t;
+
+typedef struct {
+ volatile ticketdata_t data;
+} spinlock_t;
+
+#define TICKET_SHIFT ((sizeof(ticketdata_t) / sizeof(ticket_t)) * 8)
+
+/**
+ * Ticket lock encode
+ */
+static inline ticketdata_t ticketlock_data(ticket_t tail, ticket_t head)
+{
+ return (((ticketdata_t)tail) << TICKET_SHIFT) | (ticketdata_t)head;
+}
+
+/**
+ * Ticket lock tail
+ */
+static inline ticket_t ticketlock_tail(ticketdata_t data) { return data >> 16; }
+
+/**
+ * Ticket lock head
+ */
+static inline ticket_t ticketlock_head(ticketdata_t data) { return data; }
+
+/**
+ * Ticket lock initializer
+ */
+#define SPINLOCK_INIT (spinlock_t){1};
+
+/**
+ * load reserved acquire
+ *
+ * \param pvalue pointer to word to load from
+ * \return loaded value
+ */
+static inline unsigned int _lr_aq_w(volatile unsigned int *pvalue)
+{
+ unsigned int value;
+ asm volatile (
+ "lr.w.aq %0, %1"
+ : "=&r"(value), "+A"(*pvalue));
+ return value;
+}
+
+/**
+ * store conditional release
+ *
+ * \param pvalue pointer to word to store to
+ * \param value word to write
+ * \return true for failure
+ */
+static inline int _sc_rl_w(volatile unsigned int *pvalue, unsigned int value)
+{
+ int result;
+ asm volatile (
+ "sc.w.rl %0, %2, %1"
+ : "=&r"(result), "+A"(*pvalue)
+ : "r"(value)
+ : "memory"
+ );
+ return result;
+}
+
+/**
+ * load acquire
+ *
+ * \param pvalue pointer to word to load from
+ * \return loaded value
+ */
+static inline unsigned int _l_aq_w(volatile unsigned int *pvalue)
+{
+ unsigned int result;
+ asm volatile (
+ "lw %0, %1\n"
+ "fence r,rw"
+ : "=&r"(result), "+A"(*pvalue)
+ );
+ return result;
+}
+
+/**
+ * barrier acquire
+ *
+ * order subsequent modifications after prior lock reads
+ *
+ * <read lock>
+ * _barrier_acquire();
+ */
+static inline void _barrier_acquire() { asm volatile ("fence r,rw"); }
+
+/**
+ * barrier release
+ *
+ * order prior modifications before subsequent lock write
+ *
+ * _barrier_release();
+ * <write lock>
+ */
+static inline void _barrier_release() { asm volatile ("fence rw,w"); }
+
+/**
+ * spinlock lock
+ *
+ * \param lock pointer to lock
+ */
+void spinlock_lock(spinlock_t *lock);
+
+/**
+ * spinlock trylock
+ *
+ * \param lock pointer to lock
+ * \return non zero if lock was successfully taken
+ */
+int spinlock_trylock(spinlock_t *lock);
+
+/**
+ * spinlock unlock
+ *
+ * \param lock pointer to lock
+ */
+void spinlock_unlock(spinlock_t *lock);
+
+/**
+ * spinlock waiters
+ *
+ * \param lock pointer to lock
+ * \return number of waiters
+ */
+int spinlock_waiters(spinlock_t *lock);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/stdatomic.h b/riscv/riscv-probe/libfemto/include/arch/riscv/stdatomic.h
new file mode 100644
index 0000000..8672636
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/stdatomic.h
@@ -0,0 +1,592 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * We use the GCC implementation of __atomic_is_lock_free
+ */
+
+#define atomic_is_lock_free(obj) \
+ __atomic_is_lock_free(sizeof(obj), obj)
+
+/*
+ * GCC Built-in Atomics (not enabled by default, because they are buggy)
+ */
+
+#if defined(LIBFEMTO_USE_GCC_BUILTINS)
+
+#define atomic_flag_test_and_set(obj) \
+ __atomic_test_and_set(obj, __ATOMIC_SEQ_CST)
+#define atomic_flag_test_and_set_explicit(obj, order) \
+ __atomic_test_and_set(obj, order)
+
+#define atomic_flag_clear(obj) \
+ __atomic_test_and_set(obj, __ATOMIC_SEQ_CST)
+#define atomic_flag_clear_explicit(obj, order) \
+ __atomic_test_and_set(obj, order)
+
+#define atomic_thread_fence(order) \
+ __atomic_thread_fence(order)
+
+#define atomic_signal_fence(order) \
+ __atomic_signal_fence(order)
+
+#define atomic_store(obj, desired) \
+ __atomic_store_n(obj, desired, __ATOMIC_SEQ_CST)
+#define atomic_store_explicit(obj, desired, order) \
+ __atomic_store_n(obj, desired, order)
+
+#define atomic_load(obj) \
+ __atomic_load_n(obj, __ATOMIC_SEQ_CST)
+#define atomic_load_explicit(obj,order) \
+ __atomic_load_n(obj,order)
+
+#define atomic_exchange(obj, desired) \
+ __atomic_exchange_n(obj, desired, __ATOMIC_SEQ_CST)
+#define atomic_exchange_explicit(obj, desired, order) \
+ __atomic_exchange_n(obj, desired, order)
+
+#define atomic_compare_exchange_strong(obj, expected, desired) \
+ __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \
+ __atomic_compare_exchange_n(obj, expected, desired, 0, succ, fail)
+
+#define atomic_compare_exchange_weak(obj, expected, desired) \
+ __atomic_compare_exchange_n(obj, expected, desired, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \
+ __atomic_compare_exchange_n(obj, expected, desired, 1, succ, fail)
+
+#define atomic_fetch_add(obj, arg) \
+ __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_add_explicit(obj, arg, order) \
+ __atomic_fetch_add(obj, arg, order)
+
+#define atomic_fetch_sub(obj, arg) \
+ __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_sub_explicit(obj, arg, order) \
+ __atomic_fetch_sub(obj, arg, order)
+
+#define atomic_fetch_or(obj, arg) \
+ __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_or_explicit(obj, arg, order) \
+ __atomic_fetch_or(obj, arg, order)
+
+#define atomic_fetch_xor(obj, arg) \
+ __atomic_fetch_xor(obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_xor_explicit(obj, arg, order) \
+ __atomic_fetch_xor(obj, arg, order)
+
+#define atomic_fetch_and(obj, arg) \
+ __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_and_explicit(obj, arg, order) \
+ __atomic_fetch_and(obj, arg, order)
+#endif
+
+#elif defined(__riscv)
+
+/*
+ * atomic_load
+ */
+
+#define __atomic_load_asm(obj, ASM_BARRIER, ASM_ACQUIRE) \
+__extension__ ({ \
+ __typeof__(obj) __obj = (obj); \
+ __typeof__(*(obj)) __result; \
+ switch (sizeof(__typeof__(*obj))) { \
+ case 1: \
+ __asm__ volatile ( \
+ ASM_BARRIER "\n" "lb %0, %1\n" ASM_ACQUIRE \
+ : "=&r"(__result), "+A"(*__obj) \
+ ); \
+ break; \
+ case 2: \
+ __asm__ volatile ( \
+ ASM_BARRIER "\n" "lh %0, %1\n" ASM_ACQUIRE \
+ : "=&r"(__result), "+A"(*__obj) \
+ ); \
+ break; \
+ case 4: \
+ __asm__ volatile ( \
+ ASM_BARRIER "\n" "lw %0, %1\n" ASM_ACQUIRE \
+ : "=&r"(__result), "+A"(*__obj) \
+ ); \
+ break; \
+ case 8: \
+ if (__riscv_xlen < 64) __builtin_unreachable(); \
+ __asm__ volatile ( \
+ ASM_BARRIER "\n" "ld %0, %1\n" ASM_ACQUIRE \
+ : "=&r"(__result), "+A"(*__obj) \
+ ); \
+ break; \
+ } \
+ __result; \
+})
+
+#define __atomic_load_relaxed(obj) \
+ __atomic_load_asm(obj, "", "")
+
+#define __atomic_load_acquire(obj) \
+ __atomic_load_asm(obj, "", "fence r,rw")
+
+#define __atomic_load_seq_cst(obj) \
+ __atomic_load_asm(obj, "fence rw,rw", "fence r,rw")
+
+#define atomic_load_explicit(obj, order) \
+__extension__ ({ \
+ __typeof__(*(obj)) __result; \
+ switch (order) { \
+ case __ATOMIC_ACQUIRE: \
+ __result = __atomic_load_acquire(obj) ; break; \
+ case __ATOMIC_SEQ_CST: \
+ __result = __atomic_load_seq_cst(obj) ; break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __result = __atomic_load_relaxed(obj) ; break; \
+ } \
+ __result; \
+})
+
+#define atomic_load(obj) \
+ atomic_load_explicit(obj, __ATOMIC_SEQ_CST)
+
+/*
+ * atomic_store
+ */
+
+#define __atomic_store_asm(obj, value, ASM_RELEASE) \
+__extension__ ({ \
+ __typeof__(obj) __obj = (obj); \
+ __typeof__(*(obj)) __value = (value); \
+ switch (sizeof(__typeof__(*obj))) { \
+ case 1: \
+ __asm__ volatile ( \
+ ASM_RELEASE "\n" "sb %1, %0\n" \
+ : "+A"(*__obj) : "r"(__value) : "memory" \
+ ); \
+ break; \
+ case 2: \
+ __asm__ volatile ( \
+ ASM_RELEASE "\n" "sh %1, %0\n" \
+ : "+A"(*__obj) : "r"(__value) : "memory" \
+ ); \
+ break; \
+ case 4: \
+ __asm__ volatile ( \
+ ASM_RELEASE "\n" "sw %1, %0\n" \
+ : "+A"(*__obj) : "r"(__value) : "memory" \
+ ); \
+ break; \
+ case 8: \
+ if (__riscv_xlen < 64) __builtin_unreachable(); \
+ __asm__ volatile ( \
+ ASM_RELEASE "\n" "sd %1, %0\n" \
+ : "+A"(*__obj) : "r"(__value) : "memory" \
+ ); \
+ break; \
+ } \
+})
+
+#define __atomic_store_relaxed(obj, value) \
+ __atomic_store_asm(obj, value, "")
+
+#define __atomic_store_release(obj, value) \
+ __atomic_store_asm(obj, value, "fence rw,w")
+
+#define __atomic_store_seq_cst(obj, value) \
+ __atomic_store_asm(obj, value, "fence rw,w")
+
+
+#define atomic_store_explicit(obj, value, order) \
+__extension__ ({ \
+ switch (order) { \
+ case __ATOMIC_RELEASE: \
+ __atomic_store_release(obj, value) ; break; \
+ case __ATOMIC_SEQ_CST: \
+ __atomic_store_seq_cst(obj, value) ; break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __atomic_store_relaxed(obj, value) ; break; \
+ } \
+})
+
+#define atomic_store(obj, value) \
+ atomic_store_explicit(obj, value, __ATOMIC_SEQ_CST)
+
+/*
+ * atomic_compare_exchange
+ */
+
+#define __atomic_cmpxchg_asm(obj, exp, val, ASM_AQ, ASM_RL) \
+__extension__ ({ \
+ __typeof__(obj) __obj = (obj); \
+ __typeof__(*(obj)) __exp = (exp); \
+ __typeof__(*(obj)) __val = (val); \
+ __typeof__(*(obj)) __result; \
+ register unsigned int __ret; \
+ switch (sizeof(__typeof__(*obj))) { \
+ case 1: \
+ case 2: \
+ __builtin_unreachable(); \
+ break; \
+ case 4: \
+ __asm__ volatile ( \
+ "0: lr.w" ASM_AQ " %0, %2\n" \
+ " bne %0, %z3, 1f\n" \
+ " sc.w" ASM_RL " %1, %z4, %2\n" \
+ " bnez %1, 0b \n" /* always strong */ \
+ "1:\n" \
+ : "=&r"(__result), "=&r" (__ret), "+A"(*__obj) \
+ : "r"(__exp), "r"(__val) \
+ : "memory" \
+ ); \
+ break; \
+ case 8: \
+ if (__riscv_xlen < 64) __builtin_unreachable(); \
+ __asm__ volatile ( \
+ "0: lr.d" ASM_AQ " %0, %2\n" \
+ " bne %0, %z3, 1f\n" \
+ " sc.d" ASM_RL " %1, %z4, %2\n" \
+ " bnez %1, 0b \n" /* always strong */ \
+ "1:\n" \
+ : "=&r"(__result), "=&r" (__ret), "+A"(*__obj) \
+ : "r"(__exp), "r"(__val) \
+ : "memory" \
+ ); \
+ break; \
+ } \
+ __result; \
+})
+
+#define __atomic_cmpxchg_relaxed(obj, exp, val) \
+ __atomic_cmpxchg_asm(obj, exp, val, "", "")
+
+#define __atomic_cmpxchg_acquire(obj, exp, val) \
+ __atomic_cmpxchg_asm(obj, exp, val, ".aq", "")
+
+#define __atomic_cmpxchg_release(obj, exp, val) \
+ __atomic_cmpxchg_asm(obj, exp, val, "", ".rl")
+
+#define __atomic_cmpxchg_acq_rel(obj, exp, val) \
+ __atomic_cmpxchg_asm(obj, exp, val, ".aq", ".rl")
+
+#define __atomic_cmpxchg_seq_cst(obj, exp, val) \
+ __atomic_cmpxchg_asm(obj, exp, val, ".aqrl", ".rl")
+
+#define __atomic_cmpxchg_strong(obj, exp, val, succ, fail) \
+__extension__ ({ \
+ __typeof__(*(obj)) __result; \
+ switch (succ) { \
+ case __ATOMIC_ACQUIRE: \
+ case __ATOMIC_CONSUME: /* promote to acquire for now */ \
+ __result = __atomic_cmpxchg_acquire(obj, exp, val); break; \
+ case __ATOMIC_RELEASE: \
+ __result = __atomic_cmpxchg_release(obj, exp, val); break; \
+ case __ATOMIC_ACQ_REL: \
+ __result = __atomic_cmpxchg_acq_rel(obj, exp, val); break; \
+ case __ATOMIC_SEQ_CST: \
+ __result = __atomic_cmpxchg_seq_cst(obj, exp, val); break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __result = __atomic_cmpxchg_relaxed(obj, exp, val); break; \
+ } \
+ __result; \
+})
+
+#define atomic_compare_exchange_strong(obj, exp, val) \
+ __atomic_cmpxchg_strong(obj, exp, val, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#define atomic_compare_exchange_strong_explicit(obj, exp, val, succ, fail) \
+ __atomic_cmpxchg_strong(obj, exp, val, succ, fail)
+
+#define atomic_compare_exchange_weak(obj, exp, val) \
+ __atomic_cmpxchg_strong(obj, exp, val, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#define atomic_compare_exchange_weak_explicit(obj, exp, val, succ, fail) \
+ __atomic_cmpxchg_strong(obj, exp, val, succ, fail)
+
+/*
+ * atomic_op template
+ */
+
+#define __atomic_op_asm(AMO_OP, obj, arg, ASM_AQRL) \
+__extension__ ({ \
+ __typeof__(obj) __obj = (obj); \
+ __typeof__(*(obj)) __arg = (arg); \
+ __typeof__(*(obj)) __result; \
+ switch (sizeof(__typeof__(*obj))) { \
+ case 1: \
+ case 2: \
+ __builtin_unreachable(); \
+ break; \
+ case 4: \
+ __asm__ volatile ( \
+ AMO_OP ".w" ASM_AQRL " %0, %2, %1\n" \
+ : "=&r"(__result), "+A"(*__obj) \
+ : "r"(__arg) \
+ : "memory" \
+ ); \
+ break; \
+ case 8: \
+ if (__riscv_xlen < 64) __builtin_unreachable(); \
+ __asm__ volatile ( \
+ AMO_OP ".d" ASM_AQRL " %0, %2, %1\n" \
+ : "=&r"(__result), "+A"(*__obj) \
+ : "r"(__arg) \
+ : "memory" \
+ ); \
+ break; \
+ } \
+ __result; \
+})
+
+#define __atomic_op_relaxed(op, obj, arg) \
+ __atomic_op_asm(op, obj, arg, "")
+
+#define __atomic_op_acquire(op, obj, arg) \
+ __atomic_op_asm(op, obj, arg, ".aq")
+
+#define __atomic_op_release(op, obj, arg) \
+ __atomic_op_asm(op, obj, arg, ".rl")
+
+#define __atomic_op_acq_rel(op, obj, arg) \
+ __atomic_op_asm(op, obj, arg, ".aqrl")
+
+#define __atomic_op_seq_cst(op, obj, arg) \
+ __atomic_op_asm(op, obj, arg, ".aqrl")
+
+#define __atomic_op(op, obj, arg, order) \
+__extension__ ({ \
+ __typeof__(*(obj)) __result; \
+ switch (order) { \
+ case __ATOMIC_ACQUIRE: \
+ case __ATOMIC_CONSUME: /* promote to acquire for now */ \
+ __result = __atomic_op_acquire(op, obj, arg); break; \
+ case __ATOMIC_RELEASE: \
+ __result = __atomic_op_release(op, obj, arg); break; \
+ case __ATOMIC_ACQ_REL: \
+ __result = __atomic_op_acq_rel(op, obj, arg); break; \
+ case __ATOMIC_SEQ_CST: \
+ __result = __atomic_op_seq_cst(op, obj, arg); break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __result = __atomic_op_relaxed(op, obj, arg); break; \
+ } \
+ __result; \
+})
+
+/*
+ * atomic_exchange
+ */
+
+#define atomic_exchange(obj, arg) \
+ __atomic_op("amoswap", obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_exchange_explicit(obj, arg, order) \
+ __atomic_op("amoswap", obj, arg, order)
+
+/*
+ * atomic_fetch_add
+ */
+
+#define atomic_fetch_add(obj, arg) \
+ __atomic_op("amoadd", obj, arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_add_explicit(obj, arg, order) \
+ __atomic_op("amoadd", obj, arg, order)
+
+/*
+ * atomic_fetch_sub
+ */
+
+#define atomic_fetch_sub(obj, arg) \
+ __atomic_op("amoadd", obj, -arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_sub_explicit(obj, arg, order) \
+ __atomic_op("amoadd", obj, -arg, order)
+
+/*
+ * atomic_fetch_or
+ */
+
+#define atomic_fetch_or(obj, arg) \
+ __atomic_op("amoor", obj, -arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_or_explicit(obj, arg, order) \
+ __atomic_op("amoor", obj, -arg, order)
+
+/*
+ * atomic_fetch_xor
+ */
+
+#define atomic_fetch_xor(obj, arg) \
+ __atomic_op("amoxor", obj, -arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_xor_explicit(obj, arg, order) \
+ __atomic_op("amoxor", obj, -arg, order)
+
+/*
+ * atomic_fetch_and
+ */
+
+#define atomic_fetch_and(obj, arg) \
+ __atomic_op("amoand", obj, -arg, __ATOMIC_SEQ_CST)
+#define atomic_fetch_and_explicit(obj, arg, order) \
+ __atomic_op("amoand", obj, -arg, order)
+
+
+/*
+ * atomic_flag_test_and_set
+ */
+
+#define __atomic_flag_test_and_set_asm(obj, ASM_AQRL) \
+__extension__ ({ \
+ struct atomic_flag* __obj = (obj); \
+ size_t __shift = (((unsigned long)__obj) & 3) << 3; \
+ uint32_t *__word = (uint32_t *)(((unsigned long)__obj) & ~3); \
+ uint32_t __mask = 1 << __shift; \
+ uint32_t __result; \
+ __asm__ volatile ( \
+ "amoor.w" ASM_AQRL " %0, %2, %1\n" \
+ : "=&r"(__result), "+A"(*__word) \
+ : "r"(__mask) \
+ : "memory" \
+ ); \
+ ((__result >> __shift) & 0xff); \
+})
+
+#define __atomic_flag_test_and_set_relaxed(obj) \
+ __atomic_flag_test_and_set_asm(obj, "")
+
+#define __atomic_flag_test_and_set_acquire(obj) \
+ __atomic_flag_test_and_set_asm(obj, ".aq")
+
+#define __atomic_flag_test_and_set_release(obj) \
+ __atomic_flag_test_and_set_asm(obj, ".rl")
+
+#define __atomic_flag_test_and_set_acq_rel(obj) \
+ __atomic_flag_test_and_set_asm(obj, ".aqrl")
+
+#define __atomic_flag_test_and_set_seq_cst(obj) \
+ __atomic_flag_test_and_set_asm(obj, ".aqrl")
+
+
+#define __atomic_flag_test_and_set(obj, order) \
+__extension__ ({ \
+ _Bool __result; \
+ switch (order) { \
+ case __ATOMIC_ACQUIRE: \
+ case __ATOMIC_CONSUME: /* promote to acquire for now */ \
+ __result = __atomic_flag_test_and_set_acquire(obj); break; \
+ case __ATOMIC_RELEASE: \
+ __result = __atomic_flag_test_and_set_release(obj); break; \
+ case __ATOMIC_ACQ_REL: \
+ __result = __atomic_flag_test_and_set_acq_rel(obj); break; \
+ case __ATOMIC_SEQ_CST: \
+ __result = __atomic_flag_test_and_set_seq_cst(obj); break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __result = __atomic_flag_test_and_set_relaxed(obj); break; \
+ } \
+ __result; \
+})
+
+#define atomic_flag_test_and_set_explicit(obj, order) \
+ __atomic_flag_test_and_set(obj, order)
+#define atomic_flag_test_and_set(obj, order) \
+ __atomic_flag_test_and_set(obj, __ATOMIC_SEQ_CST)
+
+/*
+ * atomic_flag_clear
+ */
+
+#define __atomic_flag_clear_asm(obj, ASM_AQRL) \
+__extension__ ({ \
+ struct atomic_flag* __obj = (obj); \
+ size_t __shift = (((unsigned long)__obj) & 3) << 3; \
+ uint32_t *__word = (uint32_t *)(((unsigned long)__obj) & ~3); \
+ uint32_t __mask = ~(0xff << __shift); \
+ uint32_t __result; \
+ __asm__ volatile ( \
+ "amoand.w" ASM_AQRL " %0, %2, %1\n" \
+ : "=&r"(__result), "+A"(*__word) \
+ : "r"(__mask) \
+ : "memory" \
+ ); \
+ ((__result >> __shift) & 0xff); \
+})
+
+#define __atomic_flag_clear_relaxed(obj) \
+ __atomic_flag_clear_asm(obj, "")
+
+#define __atomic_flag_clear_acquire(obj) \
+ __atomic_flag_clear_asm(obj, ".aq")
+
+#define __atomic_flag_clear_release(obj) \
+ __atomic_flag_clear_asm(obj, ".rl")
+
+#define __atomic_flag_clear_acq_rel(obj) \
+ __atomic_flag_clear_asm(obj, ".aqrl")
+
+#define __atomic_flag_clear_seq_cst(obj) \
+ __atomic_flag_clear_asm(obj, ".aqrl")
+
+
+#define __atomic_flag_clear(obj, order) \
+__extension__ ({ \
+ _Bool __result; \
+ switch (order) { \
+ case __ATOMIC_ACQUIRE: \
+ case __ATOMIC_CONSUME: /* promote to acquire for now */ \
+ __result = __atomic_flag_clear_acquire(obj); break; \
+ case __ATOMIC_RELEASE: \
+ __result = __atomic_flag_clear_release(obj); break; \
+ case __ATOMIC_ACQ_REL: \
+ __result = __atomic_flag_clear_acq_rel(obj); break; \
+ case __ATOMIC_SEQ_CST: \
+ __result = __atomic_flag_clear_seq_cst(obj); break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __result = __atomic_flag_clear_relaxed(obj); break; \
+ } \
+ __result; \
+})
+
+#define atomic_flag_clear_explicit(obj, order) \
+ __atomic_flag_clear(obj, order)
+#define atomic_flag_clear(obj, order) \
+ __atomic_flag_clear(obj, __ATOMIC_SEQ_CST)
+
+/*
+ * atomic_thread_fence
+ */
+
+#define __atomic_thread_fence_asm(order) \
+__extension__ ({ \
+ switch (order) { \
+ case __ATOMIC_ACQUIRE: \
+ case __ATOMIC_CONSUME: /* promote to acquire for now */ \
+ __asm__ volatile ("fence r,rw" ::: "memory"); break; \
+ case __ATOMIC_RELEASE: \
+ __asm__ volatile ("fence rw,w" ::: "memory"); break; \
+ case __ATOMIC_ACQ_REL: /* should be fence.tso */ \
+ __asm__ volatile ("fence rw,rw" ::: "memory"); break; \
+ case __ATOMIC_SEQ_CST: \
+ __asm__ volatile ("fence rw,rw" ::: "memory"); break; \
+ case __ATOMIC_RELAXED: \
+ default: \
+ __asm__ volatile ("" ::: "memory"); break; \
+ } \
+})
+
+#define atomic_thread_fence(order) \
+ __atomic_thread_fence_asm(order)
+
+/*
+ * atomic_signal_fence
+ */
+
+#define atomic_signal_fence(order) \
+ __asm__ volatile ("" ::: "memory")
+
+#endif /* __riscv */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/arch/riscv/trap.h b/riscv/riscv-probe/libfemto/include/arch/riscv/trap.h
new file mode 100644
index 0000000..0b33ec5
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/arch/riscv/trap.h
@@ -0,0 +1,51 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*trap_fn)(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc);
+trap_fn get_trap_fn();
+void set_trap_fn(trap_fn fn);
+
+const char * riscv_excp_names[16];
+const char * riscv_intr_names[16];
+
+enum {
+ cause_misaligned_fetch = 0,
+ cause_fault_fetch = 1,
+ cause_illegal_instruction = 2,
+ cause_breakpoint = 3,
+ cause_misaligned_load = 4,
+ cause_fault_load = 5,
+ cause_misaligned_store = 6,
+ cause_fault_store = 7,
+ cause_user_ecall = 8,
+ cause_supervisor_ecall = 9,
+ cause_hypervisor_ecall = 10,
+ cause_machine_ecall = 11,
+ cause_exec_page_fault = 12,
+ cause_load_page_fault = 13,
+ cause_store_page_fault = 15
+};
+
+enum {
+ intr_u_software = 0,
+ intr_s_software = 1,
+ intr_h_software = 2,
+ intr_m_software = 3,
+ intr_u_timer = 4,
+ intr_s_timer = 5,
+ intr_h_timer = 6,
+ intr_m_timer = 7,
+ intr_u_external = 8,
+ intr_s_external = 9,
+ intr_h_external = 10,
+ intr_m_external = 11,
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/auxval.h b/riscv/riscv-probe/libfemto/include/auxval.h
new file mode 100644
index 0000000..de10824
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/auxval.h
@@ -0,0 +1,34 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ HART0_CLOCK_FREQ = 0x00010000,
+ UART0_CLOCK_FREQ = 0x00011000,
+ UART0_BAUD_RATE = 0x00011100,
+ NS16550A_UART0_CTRL_ADDR = 0x00030000,
+ RISCV_HTIF_BASE_ADDR = 0x00050000,
+ SIFIVE_CLINT_CTRL_ADDR = 0x55550000,
+ SIFIVE_CLIC_CRTL_ADDR = 0x55550001,
+ SIFIVE_TEST_CTRL_ADDR = 0x55550002,
+ SIFIVE_UART0_CTRL_ADDR = 0x55550010,
+ SIFIVE_GPIO0_CTRL_ADDR = 0x55550020,
+ SIFIVE_SPI0_CTRL_ADDR = 0x55550030,
+};
+
+typedef struct auxval {
+ unsigned long key;
+ unsigned long val;
+} auxval_t;
+
+extern auxval_t __auxv[];
+
+unsigned long getauxval(unsigned long key);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/device.h b/riscv/riscv-probe/libfemto/include/device.h
new file mode 100644
index 0000000..822ab8c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/device.h
@@ -0,0 +1,39 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct console_device {
+ void (*init)();
+ int (*getchar)();
+ int (*putchar)(int);
+} console_device_t;
+
+typedef struct poweroff_device {
+ void (*init)();
+ void (*poweroff)(int);
+} poweroff_device_t;
+
+void register_console(console_device_t *dev);
+void register_poweroff(poweroff_device_t *dev);
+
+extern console_device_t *console_dev;
+extern poweroff_device_t *poweroff_dev;
+
+extern console_device_t console_none;
+extern console_device_t console_htif;
+extern console_device_t console_ns16550a;
+extern console_device_t console_sifive_uart;
+extern console_device_t console_semihost;
+
+extern poweroff_device_t poweroff_none;
+extern poweroff_device_t poweroff_htif;
+extern poweroff_device_t poweroff_sifive_test;
+extern poweroff_device_t poweroff_semihost;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/elf.h b/riscv/riscv-probe/libfemto/include/elf.h
new file mode 100644
index 0000000..abfb892
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/elf.h
@@ -0,0 +1,545 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+typedef uint8_t Elf32_Byte;
+
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef uint64_t Elf64_Off;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+typedef uint8_t Elf64_Byte;
+
+enum {
+ AT_NULL = 0,
+ AT_PHDR = 3,
+ AT_PHENT = 4,
+ AT_PHNUM = 5,
+ AT_PAGESZ = 6,
+ AT_BASE = 7,
+ AT_FLAGS = 8,
+ AT_ENTRY = 9,
+ AT_UID = 11,
+ AT_EUID = 12,
+ AT_GID = 13,
+ AT_EGID = 14,
+ AT_CLKTCK = 17,
+ AT_SECURE = 23,
+ AT_RANDOM = 25
+};
+
+typedef struct {
+ Elf32_Word a_type;
+ Elf32_Word a_val;
+} Elf32_auxv;
+
+typedef struct {
+ Elf64_Word a_type;
+ Elf64_Word a_val;
+} Elf64_auxv;
+
+enum {
+ ET_NONE = 0,
+ ET_REL = 1,
+ ET_EXEC = 2,
+ ET_DYN = 3,
+ ET_CORE = 4,
+ ET_LOPROC = 0xff00,
+ ET_HIPROC = 0xffff
+};
+
+enum {
+ EM_NONE = 0,
+ EM_X86_64 = 62,
+ EM_RISCV = 243
+};
+
+enum {
+ EV_NONE = 0,
+ EV_CURRENT = 1
+};
+
+enum {
+ EI_MAG0 = 0,
+ EI_MAG1 = 1,
+ EI_MAG2 = 2,
+ EI_MAG3 = 3,
+ EI_CLASS = 4,
+ EI_DATA = 5,
+ EI_VERSION = 6,
+ EI_OSABI = 7,
+ EI_ABIVERSION = 8,
+ EI_PAD = 9,
+ EI_NIDENT = 16
+};
+
+enum {
+ ELFMAG0 = 0x7f,
+ ELFMAG1 = 'E',
+ ELFMAG2 = 'L',
+ ELFMAG3 = 'F'
+};
+
+enum {
+ ELFCLASSNONE = 0,
+ ELFCLASS32 = 1,
+ ELFCLASS64 = 2,
+ ELFCLASS128 = 3
+};
+
+enum {
+ ELFDATANONE = 0,
+ ELFDATA2LSB = 1,
+ ELFDATA2MSB = 2
+};
+
+enum {
+ ELFOSABI_SYSV = 0,
+ ELFOSABI_LINUX = 3,
+ ELFOSABI_SOLARIS = 6,
+ ELFOSABI_FREEBSD = 9
+};
+
+enum {
+ ELFABIVERSION_NONE = 0
+};
+
+typedef struct {
+ Elf32_Byte e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry;
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct {
+ Elf64_Byte e_ident[EI_NIDENT];
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry;
+ Elf64_Off e_phoff;
+ Elf64_Off e_shoff;
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+enum {
+ PT_NULL = 0,
+ PT_LOAD = 1,
+ PT_DYNAMIC = 2,
+ PT_INTERP = 3,
+ PT_NOTE = 4,
+ PT_SHLIB = 5,
+ PT_PHDR = 6,
+ PT_TLS = 7,
+ PT_LOOS = 0x60000000,
+ PT_GNU_EH_FRAME = 0x6474e550,
+ PT_GNU_STACK = 0x6474e551,
+ PT_GNU_RELRO = 0x6474e552,
+ PT_HIOS = 0x6fffffff,
+ PT_LOPROC = 0x70000000,
+ PT_HIPROC = 0x7fffffff
+};
+
+enum {
+ PF_X = 0x1,
+ PF_W = 0x2,
+ PF_R = 0x4,
+ PF_MASKOS = 0x00FF0000,
+ PF_MASKPROC = 0xFF000000,
+};
+
+typedef struct {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct {
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset;
+ Elf64_Addr p_vaddr;
+ Elf64_Addr p_paddr;
+ Elf64_Xword p_filesz;
+ Elf64_Xword p_memsz;
+ Elf64_Xword p_align;
+} Elf64_Phdr;
+
+enum {
+ SHN_UNDEF = 0,
+ SHN_LOPROC = 0xff00,
+ SHN_HIPROC = 0xff1f,
+ SHN_LOOS = 0xff20,
+ SHN_HIOS = 0xff3f,
+ SHN_ABS = 0xfff1,
+ SHN_COMMON = 0xfff2,
+ SHN_XINDEX = 0xffff,
+ SHN_HIRESERVE = 0xffff,
+};
+
+enum {
+ SHT_NULL = 0,
+ SHT_PROGBITS = 1,
+ SHT_SYMTAB = 2,
+ SHT_STRTAB = 3,
+ SHT_RELA = 4,
+ SHT_HASH = 5,
+ SHT_DYNAMIC = 6,
+ SHT_NOTE = 7,
+ SHT_NOBITS = 8,
+ SHT_REL = 9,
+ SHT_SHLIB = 10,
+ SHT_DYNSYM = 11,
+ SHT_INIT_ARRAY = 14,
+ SHT_FINI_ARRAY = 15,
+ SHT_PREINIT_ARRAY = 16,
+ SHT_GROUP = 17,
+ SHT_SYMTAB_SHNDX = 18,
+ SHT_LOOS = 0x60000000,
+ SHT_GNU_VERDEF = 0x6ffffffd,
+ SHT_GNU_VERNEED = 0x6ffffffe,
+ SHT_GNU_VERSYM = 0x6fffffff,
+ SHT_HIOS = 0x6fffffff,
+ SHT_LOPROC = 0x70000000,
+ SHT_HIPROC = 0x7fffffff,
+ SHT_LOUSER = 0x80000000,
+ SHT_HIUSER = 0xffffffff
+};
+
+enum {
+ SHF_WRITE = 0x1,
+ SHF_ALLOC = 0x2,
+ SHF_EXECINSTR = 0x4,
+ SHF_MERGE = 0x10,
+ SHF_STRINGS = 0x20,
+ SHF_INFO_LINK = 0x40,
+ SHF_LINK_ORDER = 0x80,
+ SHF_GROUP = 0x200,
+ SHF_TLS = 0x400,
+ SHF_MASKOS = 0x0F000000,
+ SHF_MASKPROC = 0xf0000000
+};
+
+enum {
+ GRP_COMDAT = 0x1,
+ GRP_MASKOS = 0x0ff00000,
+ GRP_MASKPROC = 0xf0000000
+};
+
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct {
+ Elf64_Word sh_name;
+ Elf64_Word sh_type;
+ Elf64_Xword sh_flags;
+ Elf64_Addr sh_addr;
+ Elf64_Off sh_offset;
+ Elf64_Xword sh_size;
+ Elf64_Word sh_link;
+ Elf64_Word sh_info;
+ Elf64_Xword sh_addralign;
+ Elf64_Xword sh_entsize;
+} Elf64_Shdr;
+
+
+typedef struct {
+ Elf32_Word n_namesz;
+ Elf32_Word n_descsz;
+ Elf32_Word n_type;
+} Elf32_Nhdr;
+
+typedef struct {
+ Elf64_Word n_namesz;
+ Elf64_Word n_descsz;
+ Elf64_Word n_type;
+} Elf64_Nhdr;
+
+static inline const Elf32_Byte ELF32_ST_BIND(Elf32_Word i) { return i >> 4; }
+static inline const Elf32_Byte ELF32_ST_TYPE(Elf32_Word i) { return i & 0xf; }
+static inline const Elf32_Byte ELF32_ST_INFO(Elf32_Word b, Elf32_Word t) { return (b << 4) | (t & 0xf); }
+
+static inline const Elf32_Byte ELF64_ST_BIND(Elf32_Word i) { return i >> 4; }
+static inline const Elf32_Byte ELF64_ST_TYPE(Elf32_Word i) { return i & 0xf; }
+static inline const Elf32_Byte ELF64_ST_INFO(Elf32_Word b, Elf32_Word t) { return (b << 4) | (t & 0xf); }
+
+enum {
+ STB_LOCAL = 0,
+ STB_GLOBAL = 1,
+ STB_WEAK = 2,
+ STB_LOOS = 10,
+ STB_HIOS = 12,
+ STB_LOPROC = 13,
+ STB_HIPROC = 15
+};
+
+enum {
+ STT_NOTYPE = 0,
+ STT_OBJECT = 1,
+ STT_FUNC = 2,
+ STT_SECTION = 3,
+ STT_FILE = 4,
+ STT_LOOS = 10,
+ STT_HIOS = 12,
+ STT_LOPROC = 13,
+ STT_HIPROC = 15
+};
+
+enum {
+ STV_DEFAULT = 0,
+ STV_INTERNAL = 1,
+ STV_HIDDEN = 2,
+ STV_PROTECTED = 3
+};
+
+typedef struct {
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ Elf32_Byte st_info;
+ Elf32_Byte st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+typedef struct {
+ Elf64_Word st_name;
+ Elf64_Byte st_info;
+ Elf64_Byte st_other;
+ Elf64_Half st_shndx;
+ Elf64_Addr st_value;
+ Elf64_Xword st_size;
+} Elf64_Sym;
+
+static inline const Elf32_Word ELF32_R_SYM(Elf32_Word i) { return i >> 8; }
+static inline const Elf32_Word ELF32_R_TYPE(Elf32_Word i) { return i & 0xff; }
+static inline const Elf32_Word ELF32_R_INFO(Elf32_Word s, Elf32_Word t) { return (s << 8) | (t & 0xff); }
+
+static inline const Elf64_Xword ELF64_R_SYM(Elf64_Xword i) { return i >> 32; }
+static inline const Elf64_Xword ELF64_R_TYPE(Elf64_Xword i) { return i & 0xffffffffUL; }
+static inline const Elf64_Xword ELF64_R_INFO(Elf64_Xword s, Elf64_Xword t) { return (s << 32) | (t & 0xffffffffUL); }
+
+typedef struct {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct
+{
+ Elf64_Addr r_offset;
+ Elf64_Xword r_info;
+} Elf64_Rel;
+
+typedef struct
+{
+ Elf64_Addr r_offset;
+ Elf64_Xword r_info;
+ Elf64_Sxword r_addend;
+} Elf64_Rela;
+
+enum {
+ R_X86_64_NONE = 0,
+ R_X86_64_64 = 1,
+ R_X86_64_PC32 = 2,
+ R_X86_64_GOT32 = 3,
+ R_X86_64_PLT32 = 4,
+ R_X86_64_COPY = 5,
+ R_X86_64_GLOB_DAT = 6,
+ R_X86_64_JUMP_SLOT = 7,
+ R_X86_64_RELATIVE = 8,
+ R_X86_64_GOTPCREL = 9,
+ R_X86_64_32 = 10,
+ R_X86_64_32S = 11,
+ R_X86_64_16 = 12,
+ R_X86_64_PC16 = 13,
+ R_X86_64_8 = 14,
+ R_X86_64_PC8 = 15,
+ R_X86_64_DTPMOD64 = 16,
+ R_X86_64_DTPOFF64 = 17,
+ R_X86_64_TPOFF64 = 18,
+ R_X86_64_TLSGD = 19,
+ R_X86_64_TLSLD = 20,
+ R_X86_64_DTPOFF32 = 21,
+ R_X86_64_GOTTPOFF = 22,
+ R_X86_64_TPOFF32 = 23,
+ R_X86_64_PC64 = 24,
+ R_X86_64_GOTOFF64 = 25,
+ R_X86_64_GOTPC32 = 26,
+ R_X86_64_SIZE32 = 32,
+ R_X86_64_SIZE64 = 33,
+ R_X86_64_GOTPC32_TLSDESC = 34,
+ R_X86_64_TLSDESC_CALL = 35,
+ R_X86_64_TLSDESC = 36,
+ R_X86_64_IRELATIVE = 37,
+ R_X86_64_RELATIVE64 = 38
+};
+
+enum {
+ R_RISCV_NONE = 0,
+ R_RISCV_32 = 1,
+ R_RISCV_64 = 2,
+ R_RISCV_RELATIVE = 3,
+ R_RISCV_COPY = 4,
+ R_RISCV_JUMP_SLOT = 5,
+ R_RISCV_TLS_DTPMOD32 = 6,
+ R_RISCV_TLS_DTPMOD64 = 7,
+ R_RISCV_TLS_DTPREL32 = 8,
+ R_RISCV_TLS_DTPREL64 = 9,
+ R_RISCV_TLS_TPREL32 = 10,
+ R_RISCV_TLS_TPREL64 = 11,
+ R_RISCV_BRANCH = 16,
+ R_RISCV_JAL = 17,
+ R_RISCV_CALL = 18,
+ R_RISCV_CALL_PLT = 19,
+ R_RISCV_GOT_HI20 = 20,
+ R_RISCV_TLS_GOT_HI20 = 21,
+ R_RISCV_TLS_GD_HI20 = 22,
+ R_RISCV_PCREL_HI20 = 23,
+ R_RISCV_PCREL_LO12_I = 24,
+ R_RISCV_PCREL_LO12_S = 25,
+ R_RISCV_HI20 = 26,
+ R_RISCV_LO12_I = 27,
+ R_RISCV_LO12_S = 28,
+ R_RISCV_TPREL_HI20 = 29,
+ R_RISCV_TPREL_LO12_I = 30,
+ R_RISCV_TPREL_LO12_S = 31,
+ R_RISCV_TPREL_ADD = 32,
+ R_RISCV_ADD8 = 33,
+ R_RISCV_ADD16 = 34,
+ R_RISCV_ADD32 = 35,
+ R_RISCV_ADD64 = 36,
+ R_RISCV_SUB8 = 37,
+ R_RISCV_SUB16 = 38,
+ R_RISCV_SUB32 = 39,
+ R_RISCV_SUB64 = 40,
+ R_RISCV_GNU_VTINHERIT = 41,
+ R_RISCV_GNU_VTENTRY = 42,
+ R_RISCV_ALIGN = 43,
+ R_RISCV_RVC_BRANCH = 44,
+ R_RISCV_RVC_JUMP = 45,
+ R_RISCV_RVC_LUI = 46,
+ R_RISCV_GPREL_I = 47,
+ R_RISCV_GPREL_S = 48,
+ R_RISCV_TPREL_I = 49,
+ R_RISCV_TPREL_S = 50,
+ R_RISCV_RELAX = 51,
+ R_RISCV_SUB6 = 52,
+ R_RISCV_SET6 = 53,
+ R_RISCV_SET8 = 54,
+ R_RISCV_SET16 = 55,
+ R_RISCV_SET32 = 56,
+};
+
+enum {
+ EF_RISCV_RVC = 0x1,
+ EF_RISCV_FLOAT_ABI_SINGLE = 0x2,
+ EF_RISCV_FLOAT_ABI_DOUBLE = 0x4,
+ EF_RISCV_FLOAT_ABI_QUAD = 0x6,
+ EF_RISCV_RVE = 0x8
+};
+
+enum {
+ DF_ORIGIN = 1,
+ DF_SYMBOLIC = 2,
+ DF_TEXTREL = 4,
+ DF_BIND_NOW = 8,
+ DF_STATIC_TLS = 16
+};
+
+enum {
+ DT_NULL = 0,
+ DT_NEEDED = 1,
+ DT_PLTRELSZ = 2,
+ DT_PLTGOT = 3,
+ DT_HASH = 4,
+ DT_STRTAB = 5,
+ DT_SYMTAB = 6,
+ DT_RELA = 7,
+ DT_RELASZ = 8,
+ DT_RELAENT = 9,
+ DT_STRSZ = 10,
+ DT_SYMENT = 11,
+ DT_INIT = 12,
+ DT_FINI = 13,
+ DT_SONAME = 14,
+ DT_RPATH = 15,
+ DT_SYMBOLIC = 16,
+ DT_REL = 17,
+ DT_RELSZ = 18,
+ DT_RELENT = 19,
+ DT_PLTREL = 20,
+ DT_DEBUG = 21,
+ DT_TEXTREL = 22,
+ DT_JMPREL = 23,
+ DT_BIND_NOW = 24,
+ DT_INIT_ARRAY = 25,
+ DT_FINI_ARRAY = 26,
+ DT_INIT_ARRAYSZ = 27,
+ DT_FINI_ARRAYSZ = 28,
+ DT_RUNPATH = 29,
+ DT_FLAGS = 30,
+ DT_LOOS = 0x60000000,
+ DT_HIOS = 0x6fffffff,
+ DT_LOPROC = 0x70000000,
+ DT_HIPROC = 0x7fffffff
+};
+
+typedef struct {
+ Elf32_Sword d_tag;
+ union {
+ Elf32_Word d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+ Elf64_Sxword d_tag;
+ union {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ } d_un;
+} Elf64_Dyn;
diff --git a/riscv/riscv-probe/libfemto/include/endian.h b/riscv/riscv-probe/libfemto/include/endian.h
new file mode 100644
index 0000000..8510f29
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/endian.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#define bswap16(x) __builtin_bswap16(x)
+#define bswap32(x) __builtin_bswap32(x)
+#define bswap64(x) __builtin_bswap64(x)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint16_t htobe16(uint16_t x) { return bswap16(x); }
+static inline uint16_t htole16(uint16_t x) { return x; }
+static inline uint16_t be16toh(uint16_t x) { return bswap16(x); }
+static inline uint16_t le16toh(uint16_t x) { return x; }
+
+static inline uint32_t htobe32(uint32_t x) { return bswap32(x); }
+static inline uint32_t htole32(uint32_t x) { return x; }
+static inline uint32_t be32toh(uint32_t x) { return bswap32(x); }
+static inline uint32_t le32toh(uint32_t x) { return x; }
+
+static inline uint64_t htobe64(uint64_t x) { return bswap64(x); }
+static inline uint64_t htole64(uint64_t x) { return x; }
+static inline uint64_t be64toh(uint64_t x) { return bswap64(x); }
+static inline uint64_t le64toh(uint64_t x) { return x; }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint16_t htobe16(uint16_t x) { return x; }
+static inline uint16_t htole16(uint16_t x) { return bswap16(x); }
+static inline uint16_t be16toh(uint16_t x) { return x; }
+static inline uint16_t le16toh(uint16_t x) { return bswap16(x); }
+
+static inline uint32_t htobe32(uint32_t x) { return x; }
+static inline uint32_t htole32(uint32_t x) { return bswap32(x); }
+static inline uint32_t be32toh(uint32_t x) { return x; }
+static inline uint32_t le32toh(uint16_t x) { return bswap64(x); }
+
+static inline uint64_t htobe64(uint64_t x) { return x; }
+static inline uint64_t htole64(uint64_t x) { return bswap64(x); }
+static inline uint64_t be64toh(uint64_t x) { return x; }
+static inline uint64_t le64toh(uint64_t x) { return bswap64(x); }
+#endif
+
+#if __SIZE_WIDTH__ == 32
+#define _htobel htobe32
+#define _beltoh be32toh
+#define _htolel htole32
+#define _leltoh le32toh
+#endif
+
+#if __SIZE_WIDTH__ == 64
+#define _htobel htobe64
+#define _beltoh be64toh
+#define _htolel htole64
+#define _leltoh le64toh
+#endif
+
+#define htobe(X) _Generic((X), \
+ short: htobe16, \
+ unsigned short: htobe16, \
+ int: htobe32, \
+ unsigned int: htobe32, \
+ long: _htobel, \
+ unsigned long: _htobel, \
+ long long: htobe64, \
+ unsigned long long: htobe64 \
+ )(X)
+
+#define betoh(X) _Generic((X), \
+ short: be16toh, \
+ unsigned short: be16toh, \
+ int: be32toh, \
+ unsigned int: be32toh, \
+ long: _beltoh, \
+ unsigned long: _beltoh, \
+ long long: be64toh, \
+ unsigned long long: be64toh \
+ )(X)
+
+#define htole(X) _Generic((X), \
+ short: htole16, \
+ unsigned short: htole16, \
+ int: htole32, \
+ unsigned int: htole32, \
+ long: _htolel, \
+ unsigned long: _htolel, \
+ long long: htole64, \
+ unsigned long long: htole64 \
+ )(X)
+
+#define letoh(X) _Generic((X), \
+ short: le16toh, \
+ unsigned short: le16toh, \
+ int: le32toh, \
+ unsigned int: le32toh, \
+ long: _leltoh, \
+ unsigned long: _leltoh, \
+ long long: le64toh, \
+ unsigned long long: le64toh \
+ )(X)
diff --git a/riscv/riscv-probe/libfemto/include/femto.h b/riscv/riscv-probe/libfemto/include/femto.h
new file mode 100644
index 0000000..aa8b4cf
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/femto.h
@@ -0,0 +1,14 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdbits.h>
+#include <string.h>
+
+#include "auxval.h"
+#include "device.h"
diff --git a/riscv/riscv-probe/libfemto/include/list.h b/riscv/riscv-probe/libfemto/include/list.h
new file mode 100644
index 0000000..5f4cc72
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/list.h
@@ -0,0 +1,495 @@
+/*
+ * Minimal Linux-like double-linked list helper functions
+ *
+ * Copyright (c) 2012-2016, Sven Eckelmann <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+/**
+ * container_of() - Calculate address of object that contains address ptr
+ * @ptr: pointer to member variable
+ * @type: type of the structure containing ptr
+ * @member: name of the member variable in struct @type
+ *
+ * Return: @type pointer of object containing ptr
+ */
+
+#define container_of(ptr, type, member) __extension__ ({ \
+ const __typeof__(((type *)0)->member) *__pmember = (ptr); \
+ (type *)((char *)__pmember - offsetof(type, member)); })
+
+/**
+ * struct list_head - Head and node of a double-linked list
+ * @prev: pointer to the previous node in the list
+ * @next: pointer to the next node in the list
+ *
+ * The simple double-linked list consists of a head and nodes attached to
+ * this head. Both node and head share the same struct type. The list_*
+ * functions and macros can be used to access and modify this data structure.
+ *
+ * The @prev pointer of the list head points to the last list node of the
+ * list and @next points to the first list node of the list. For an empty list,
+ * both member variables point to the head.
+ *
+ * The list nodes are usually embedded in a container structure which holds the
+ * actual data. Such an container object is called entry. The helper list_entry
+ * can be used to calculate the object address from the address of the node.
+ */
+struct list_head {
+ struct list_head *prev;
+ struct list_head *next;
+};
+
+/**
+ * LIST_HEAD - Declare list head and initialize it
+ * @head: name of the new object
+ */
+#define LIST_HEAD(head) \
+ struct list_head head = { &(head), &(head) }
+
+/**
+ * INIT_LIST_HEAD() - Initialize empty list head
+ * @head: pointer to list head
+ *
+ * This can also be used to initialize a unlinked list node.
+ *
+ * A node is usually linked inside a list, will be added to a list in
+ * the near future or the entry containing the node will be free'd soon.
+ *
+ * But an unlinked node may be given to a function which uses list_del(_init)
+ * before it ends up in a previously mentioned state. The list_del(_init) on an
+ * initialized node is well defined and safe. But the result of a
+ * list_del(_init) on an uninitialized node is undefined (unrelated memory is
+ * modified, crashes, ...).
+ */
+static __inline__ void INIT_LIST_HEAD(struct list_head *head)
+{
+ head->next = head;
+ head->prev = head;
+}
+
+/**
+ * list_add() - Add a list node to the beginning of the list
+ * @node: pointer to the new node
+ * @head: pointer to the head of the list
+ */
+static __inline__ void list_add(struct list_head *node,
+ struct list_head *head)
+{
+ struct list_head *next = head->next;
+
+ next->prev = node;
+ node->next = next;
+ node->prev = head;
+ head->next = node;
+}
+
+/**
+ * list_add_tail() - Add a list node to the end of the list
+ * @node: pointer to the new node
+ * @head: pointer to the head of the list
+ */
+static __inline__ void list_add_tail(struct list_head *node,
+ struct list_head *head)
+{
+ struct list_head *prev = head->prev;
+
+ prev->next = node;
+ node->next = head;
+ node->prev = prev;
+ head->prev = node;
+}
+
+/**
+ * list_add_before() - Add a list node before another node to the list
+ * @new_node: pointer to the new node
+ * @node: pointer to the reference node in the list
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_add_before(new_node, node) \
+ list_add_tail(new_node, node)
+
+/**
+ * list_add_behind() - Add a list node behind another node to the list
+ * @new_node: pointer to the new node
+ * @node: pointer to the reference node in the list
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_add_behind(new_node, node) \
+ list_add(new_node, node)
+
+/**
+ * list_del() - Remove a list node from the list
+ * @node: pointer to the node
+ *
+ * The node is only removed from the list. Neither the memory of the removed
+ * node nor the memory of the entry containing the node is free'd. The node
+ * has to be handled like an uninitialized node. Accessing the next or prev
+ * pointer of the node is not safe.
+ *
+ * Unlinked, initialized nodes are also uninitialized after list_del.
+ *
+ * LIST_POISONING can be enabled during build-time to provoke an invalid memory
+ * access when the memory behind the next/prev pointer is used after a list_del.
+ * This only works on systems which prohibit access to the predefined memory
+ * addresses.
+ */
+static __inline__ void list_del(struct list_head *node)
+{
+ struct list_head *next = node->next;
+ struct list_head *prev = node->prev;
+
+ next->prev = prev;
+ prev->next = next;
+
+#ifdef LIST_POISONING
+ node->prev = (struct list_head *)(0x00100100);
+ node->next = (struct list_head *)(0x00200200);
+#endif
+}
+
+/**
+ * list_del_init() - Remove a list node from the list and reinitialize it
+ * @node: pointer to the node
+ *
+ * The removed node will not end up in an uninitialized state like when using
+ * list_del. Instead the node is initialized again to the unlinked state.
+ */
+static __inline__ void list_del_init(struct list_head *node)
+{
+ list_del(node);
+ INIT_LIST_HEAD(node);
+}
+
+/**
+ * list_empty() - Check if list head has no nodes attached
+ * @head: pointer to the head of the list
+ *
+ * Return: 0 - list is not empty !0 - list is empty
+ */
+static __inline__ int list_empty(const struct list_head *head)
+{
+ return (head->next == head);
+}
+
+/**
+ * list_is_singular() - Check if list head has exactly one node attached
+ * @head: pointer to the head of the list
+ *
+ * Return: 0 - list is not singular !0 -list has exactly one entry
+ */
+static __inline__ int list_is_singular(const struct list_head *head)
+{
+ return (!list_empty(head) && head->prev == head->next);
+}
+
+/**
+ * list_splice() - Add list nodes from a list to beginning of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the beginning of the list of @head.
+ * It is similar to list_add but for multiple nodes. The @list head is not
+ * modified and has to be initialized to be used as a valid list head/node
+ * again.
+ */
+static __inline__ void list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *head_first = head->next;
+ struct list_head *list_first = list->next;
+ struct list_head *list_last = list->prev;
+
+ if (list_empty(list))
+ return;
+
+ head->next = list_first;
+ list_first->prev = head;
+
+ list_last->next = head_first;
+ head_first->prev = list_last;
+}
+
+/**
+ * list_splice_tail() - Add list nodes from a list to end of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the end of the list of @head.
+ * It is similar to list_add_tail but for multiple nodes. The @list head is not
+ * modified and has to be initialized to be used as a valid list head/node
+ * again.
+ */
+static __inline__ void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *head_last = head->prev;
+ struct list_head *list_first = list->next;
+ struct list_head *list_last = list->prev;
+
+ if (list_empty(list))
+ return;
+
+ head->prev = list_last;
+ list_last->next = head;
+
+ list_first->prev = head_last;
+ head_last->next = list_first;
+}
+
+/**
+ * list_splice_init() - Move list nodes from a list to beginning of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the beginning of the list of @head.
+ * It is similar to list_add but for multiple nodes.
+ *
+ * The @list head will not end up in an uninitialized state like when using
+ * list_splice. Instead the @list is initialized again to the an empty
+ * list/unlinked state.
+ */
+static __inline__ void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ list_splice(list, head);
+ INIT_LIST_HEAD(list);
+}
+
+/**
+ * list_splice_tail_init() - Move list nodes from a list to end of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the end of the list of @head.
+ * It is similar to list_add_tail but for multiple nodes.
+ *
+ * The @list head will not end up in an uninitialized state like when using
+ * list_splice. Instead the @list is initialized again to the an empty
+ * list/unlinked state.
+ */
+static __inline__ void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ list_splice_tail(list, head);
+ INIT_LIST_HEAD(list);
+}
+
+/**
+ * list_cut_position() - Move beginning of a list to another list
+ * @head_to: pointer to the head of the list which receives nodes
+ * @head_from: pointer to the head of the list
+ * @node: pointer to the node in which defines the cutting point
+ *
+ * All entries from the beginning of the list @head_from to (including) the
+ * @node is moved to @head_from.
+ *
+ * @head_to is replaced when @head_from is not empty. @node must be a real
+ * list node from @head_from or the behavior is undefined.
+ */
+static __inline__ void list_cut_position(struct list_head *head_to,
+ struct list_head *head_from,
+ struct list_head *node)
+{
+ struct list_head *head_from_first = head_from->next;
+
+ if (list_empty(head_from))
+ return;
+
+ if (head_from == node) {
+ INIT_LIST_HEAD(head_to);
+ return;
+ }
+
+ head_from->next = node->next;
+ head_from->next->prev = head_from;
+
+ head_to->prev = node;
+ node->next = head_to;
+ head_to->next = head_from_first;
+ head_to->next->prev = head_to;
+}
+
+/**
+ * list_move() - Move a list node to the beginning of the list
+ * @node: pointer to the node
+ * @head: pointer to the head of the list
+ *
+ * The @node is removed from its old position/node and add to the beginning of
+ * @head
+ */
+static __inline__ void list_move(struct list_head *node, struct list_head *head)
+{
+ list_del(node);
+ list_add(node, head);
+}
+
+/**
+ * list_move_tail() - Move a list node to the end of the list
+ * @node: pointer to the node
+ * @head: pointer to the head of the list
+ *
+ * The @node is removed from its old position/node and add to the end of @head
+ */
+static __inline__ void list_move_tail(struct list_head *node,
+ struct list_head *head)
+{
+ list_del(node);
+ list_add_tail(node, head);
+}
+
+/**
+ * list_entry() - Calculate address of entry that contains list node
+ * @node: pointer to list node
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of entry containing node
+ */
+#define list_entry(node, type, member) container_of(node, type, member)
+
+/**
+ * list_first_entry() - get first entry of the list
+ * @head: pointer to the head of the list
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of first entry in list
+ */
+#define list_first_entry(head, type, member) \
+ list_entry((head)->next, type, member)
+
+/**
+ * list_last_entry() - get last entry of the list
+ * @head: pointer to the head of the list
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of last entry in list
+ */
+#define list_last_entry(head, type, member) \
+ list_entry((head)->prev, type, member)
+
+/**
+ * list_for_each - iterate over list nodes
+ * @node: list_head pointer used as iterator
+ * @head: pointer to the head of the list
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ */
+#define list_for_each(node, head) \
+ for (node = (head)->next; \
+ node != (head); \
+ node = node->next)
+
+/**
+ * list_for_each_entry_t - iterate over list entries
+ * @entry: @type pointer used as iterator
+ * @head: pointer to the head of the list
+ * @type: type of the entries containing the list nodes
+ * @member: name of the list_head member variable in struct @type
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_for_each_entry_t(entry, head, type, member) \
+ for (entry = list_entry((head)->next, type, member); \
+ &entry->member != (head); \
+ entry = list_entry(entry->member.next, type, member))
+
+/**
+ * list_for_each_entry - iterate over list entries
+ * @entry: pointer used as iterator
+ * @head: pointer to the head of the list
+ * @member: name of the list_head member variable in struct type of @entry
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ */
+#define list_for_each_entry(entry, head, member) \
+ list_for_each_entry_t(entry, head, __typeof__(*entry), member)
+
+/**
+ * list_for_each_safe - iterate over list nodes and allow deletes
+ * @node: list_head pointer used as iterator
+ * @safe: list_head pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ */
+#define list_for_each_safe(node, safe, head) \
+ for (node = (head)->next, safe = node->next; \
+ node != (head); \
+ node = safe, safe = node->next)
+
+/**
+ * list_for_each_entry_safe_t - iterate over list entries and allow deletes
+ * @entry: @type pointer used as iterator
+ * @safe: @type pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ * @type: type of the entries containing the list nodes
+ * @member: name of the list_head member variable in struct @type
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_for_each_entry_safe_t(entry, safe, head, type, member) \
+ for (entry = list_entry((head)->next, type, member), \
+ safe = list_entry(entry->member.next, type, member); \
+ &entry->member != (head); \
+ entry = safe, \
+ safe = list_entry(safe->member.next, type, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list entries and allow deletes
+ * @entry: pointer used as iterator
+ * @safe: @type pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ * @member: name of the list_head member variable in struct type of @entry
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ */
+#define list_for_each_entry_safe(entry, safe, head, member) \
+ list_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry), \
+ member)
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/spinlock.h b/riscv/riscv-probe/libfemto/include/spinlock.h
new file mode 100644
index 0000000..c11200f
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/spinlock.h
@@ -0,0 +1,6 @@
+// See LICENSE for license details.
+#pragma once
+
+#ifdef __riscv
+#include <arch/riscv/spinlock.h>
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/stdarg.h b/riscv/riscv-probe/libfemto/include/stdarg.h
new file mode 100644
index 0000000..7383c4a
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdarg.h
@@ -0,0 +1,8 @@
+#pragma once
+
+typedef __builtin_va_list va_list;
+
+#define va_start(v,l) __builtin_va_start(v,l)
+#define va_end(v) __builtin_va_end(v)
+#define va_arg(v,l) __builtin_va_arg(v,l)
+#define va_copy(d,s) __builtin_va_copy(d,s)
diff --git a/riscv/riscv-probe/libfemto/include/stdatomic.h b/riscv/riscv-probe/libfemto/include/stdatomic.h
new file mode 100644
index 0000000..8073868
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdatomic.h
@@ -0,0 +1,61 @@
+// See LICENSE for license details.
+#pragma once
+
+typedef enum memory_order {
+ memory_order_relaxed = __ATOMIC_RELAXED,
+ memory_order_acquire = __ATOMIC_ACQUIRE,
+ memory_order_consume = __ATOMIC_CONSUME,
+ memory_order_release = __ATOMIC_RELEASE,
+ memory_order_acq_rel = __ATOMIC_ACQ_REL,
+ memory_order_seq_cst = __ATOMIC_SEQ_CST
+} memory_order;
+
+#ifdef __cplusplus
+typedef _Atomic(bool) atomic_bool;
+#else
+typedef _Atomic(_Bool) atomic_bool;
+#endif
+typedef _Atomic(char) atomic_char;
+typedef _Atomic(signed char) atomic_schar;
+typedef _Atomic(unsigned char) atomic_uchar;
+typedef _Atomic(short) atomic_short;
+typedef _Atomic(unsigned short) atomic_ushort;
+typedef _Atomic(int) atomic_int;
+typedef _Atomic(unsigned int) atomic_uint;
+typedef _Atomic(long) atomic_long;
+typedef _Atomic(unsigned long) atomic_ulong;
+typedef _Atomic(long long) atomic_llong;
+typedef _Atomic(unsigned long long) atomic_ullong;
+typedef _Atomic(uint_least16_t) atomic_char16_t;
+typedef _Atomic(uint_least32_t) atomic_char32_t;
+typedef _Atomic(wchar_t) atomic_wchar_t;
+typedef _Atomic(int_least8_t) atomic_int_least8_t;
+typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
+typedef _Atomic(int_least16_t) atomic_int_least16_t;
+typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
+typedef _Atomic(int_least32_t) atomic_int_least32_t;
+typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
+typedef _Atomic(int_least64_t) atomic_int_least64_t;
+typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
+typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
+typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
+typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
+typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
+typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
+typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
+typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
+typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
+typedef _Atomic(intptr_t) atomic_intptr_t;
+typedef _Atomic(uintptr_t) atomic_uintptr_t;
+typedef _Atomic(size_t) atomic_size_t;
+typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
+typedef _Atomic(intmax_t) atomic_intmax_t;
+typedef _Atomic(uintmax_t) atomic_uintmax_t;
+
+typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
+
+#define ATOMIC_FLAG_INIT { 0 }
+
+#ifdef __riscv
+#include <arch/riscv/stdatomic.h>
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/stdbits.h b/riscv/riscv-probe/libfemto/include/stdbits.h
new file mode 100644
index 0000000..2985381
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdbits.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define clz(val) ({ \
+ int result; \
+ switch(sizeof(val)) { \
+ case 1: result = clz8(val); break; \
+ case 2: result = clz16(val); break; \
+ case 4: result = clz32(val); break; \
+ case 8: result = clz64(val); break; \
+ } \
+ result; \
+})
+
+#define ctz(val) ({ \
+ int result; \
+ switch(sizeof(val)) { \
+ case 1: result = ctz8(val); break; \
+ case 2: result = ctz16(val); break; \
+ case 4: result = ctz32(val); break; \
+ case 8: result = ctz64(val); break; \
+ } \
+ result; \
+})
+
+int clz8(int8_t val);
+int clz16(int16_t val);
+int clz32(int32_t val);
+int clz64(int64_t val);
+
+int ctz8(int8_t val);
+int ctz16(int16_t val);
+int ctz32(int32_t val);
+int ctz64(int64_t val);
+
+static inline int ispow2(uintptr_t val)
+{
+ return val && !(val & (val-1));
+}
+
+static inline uintptr_t roundpow2(uintptr_t val)
+{
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+#if __SIZE_WIDTH__ == 64
+ val |= val >> 32;
+#endif
+ val++;
+ return val;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/stddef.h b/riscv/riscv-probe/libfemto/include/stddef.h
new file mode 100644
index 0000000..3799f2a
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stddef.h
@@ -0,0 +1,8 @@
+#pragma once
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+#define NULL 0L
+#define offsetof(type, member) __builtin_offsetof(type, member)
diff --git a/riscv/riscv-probe/libfemto/include/stdint.h b/riscv/riscv-probe/libfemto/include/stdint.h
new file mode 100644
index 0000000..bef5c2f
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdint.h
@@ -0,0 +1,52 @@
+#pragma once
+
+typedef __INT8_TYPE__ int8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+
+typedef __INT_FAST8_TYPE__ int_fast8_t;
+typedef __INT_FAST16_TYPE__ int_fast16_t;
+typedef __INT_FAST32_TYPE__ int_fast32_t;
+typedef __INT_FAST64_TYPE__ int_fast64_t;
+
+typedef __INT_LEAST16_TYPE__ int_least16_t;
+typedef __INT_LEAST32_TYPE__ int_least32_t;
+typedef __INT_LEAST64_TYPE__ int_least64_t;
+typedef __INT_LEAST8_TYPE__ int_least8_t;
+
+typedef __INTMAX_TYPE__ intmax_t;
+typedef __INTPTR_TYPE__ intptr_t;
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+typedef __UINT_FAST8_TYPE__ uint_fast8_t;
+typedef __UINT_FAST16_TYPE__ uint_fast16_t;
+typedef __UINT_FAST32_TYPE__ uint_fast32_t;
+typedef __UINT_FAST64_TYPE__ uint_fast64_t;
+
+typedef __UINT_LEAST8_TYPE__ uint_least8_t;
+typedef __UINT_LEAST16_TYPE__ uint_least16_t;
+typedef __UINT_LEAST32_TYPE__ uint_least32_t;
+typedef __UINT_LEAST64_TYPE__ uint_least64_t;
+
+typedef __UINTMAX_TYPE__ uintmax_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+#define INT8_MAX __INT8_MAX__
+#define INT16_MAX __INT16_MAX__
+#define INT32_MAX __INT32_MAX__
+#define INT64_MAX __INT64_MAX__
+
+#define UINT8_MAX __UINT8_MAX__
+#define UINT16_MAX __UINT16_MAX__
+#define UINT32_MAX __UINT32_MAX__
+#define UINT64_MAX __UINT64_MAX__
+
+#define INTPTR_MAX __INTPTR_MAX__
+#define UINTPTR_MAX __UINTPTR_MAX__
+#define PTRDIFF_MAX __PTRDIFF_MAX__
+#define SIZE_MAX __SIZE_MAX__
diff --git a/riscv/riscv-probe/libfemto/include/stdio.h b/riscv/riscv-probe/libfemto/include/stdio.h
new file mode 100644
index 0000000..f97e6e1
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdio.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+
+int getchar(void);
+int printf(const char *, ...);
+int putchar(int);
+int puts(const char *);
+int snprintf(char *, size_t, const char *, ...);
+int vprintf(const char *, va_list);
+int vsnprintf(char *, size_t, const char *, va_list);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/stdlib.h b/riscv/riscv-probe/libfemto/include/stdlib.h
new file mode 100644
index 0000000..7552a1c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/stdlib.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+__attribute__((noreturn)) void abort(void);
+__attribute__((noreturn)) void exit(int status);
+void* malloc(size_t size);
+void free(void* ptr);
+void _malloc_addblock(void* addr, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/include/string.h b/riscv/riscv-probe/libfemto/include/string.h
new file mode 100644
index 0000000..6572d23
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/include/string.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+void *memchr(const void *s, int c, size_t n);
+int memcmp(const void *, const void *, size_t);
+void *memcpy(void *, const void *, size_t);
+void *memset(void *, int, size_t);
+char *strchr(const char *s, int c);
+int strcmp(const char *, const char *);
+size_t strlen(const char *);
+int strncmp(const char *, const char *, size_t);
+char *strncpy(char *, const char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/riscv/riscv-probe/libfemto/std/abort.c b/riscv/riscv-probe/libfemto/std/abort.c
new file mode 100644
index 0000000..d4a7f06
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/abort.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+void abort(void)
+{
+ puts("abort");
+ exit(-1);
+}
diff --git a/riscv/riscv-probe/libfemto/std/clz.c b/riscv/riscv-probe/libfemto/std/clz.c
new file mode 100644
index 0000000..bc94795
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/clz.c
@@ -0,0 +1,22 @@
+// See LICENSE for license details.
+
+#include <stdint.h>
+
+#define GLUE_HELPER(x, y) x##y
+#define GLUE(x, y) GLUE_HELPER(x, y)
+
+#define DEFINE_CLZ(T,bits) \
+int GLUE(clz,bits)(T val) \
+{ \
+ int n = 0; \
+ for (n = 0; n < bits; n++) { \
+ if (val < 0) break; \
+ val <<= 1; \
+ } \
+ return n; \
+}
+
+DEFINE_CLZ(int8_t,8)
+DEFINE_CLZ(int16_t,16)
+DEFINE_CLZ(int32_t,32)
+DEFINE_CLZ(int64_t,64)
diff --git a/riscv/riscv-probe/libfemto/std/ctz.c b/riscv/riscv-probe/libfemto/std/ctz.c
new file mode 100644
index 0000000..098687c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/ctz.c
@@ -0,0 +1,22 @@
+// See LICENSE for license details.
+
+#include <stdint.h>
+
+#define GLUE_HELPER(x, y) x##y
+#define GLUE(x, y) GLUE_HELPER(x, y)
+
+#define DEFINE_CTZ(T,bits) \
+int GLUE(ctz,bits)(T val) \
+{ \
+ int n = 0; \
+ for (n = 0; n < bits; n++) { \
+ if (val & 1) break; \
+ val >>= 1; \
+ } \
+ return n; \
+}
+
+DEFINE_CTZ(int8_t,8)
+DEFINE_CTZ(int16_t,16)
+DEFINE_CTZ(int32_t,32)
+DEFINE_CTZ(int64_t,64)
diff --git a/riscv/riscv-probe/libfemto/std/exit.c b/riscv/riscv-probe/libfemto/std/exit.c
new file mode 100644
index 0000000..2489ef5
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/exit.c
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <device.h>
+
+void exit(int status)
+{
+ poweroff_dev->poweroff(status);
+ asm volatile("1: j 1b");
+ __builtin_unreachable();
+}
diff --git a/riscv/riscv-probe/libfemto/std/getchar.c b/riscv/riscv-probe/libfemto/std/getchar.c
new file mode 100644
index 0000000..35babff
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/getchar.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <device.h>
+
+int getchar()
+{
+ return console_dev->getchar();
+}
diff --git a/riscv/riscv-probe/libfemto/std/malloc.c b/riscv/riscv-probe/libfemto/std/malloc.c
new file mode 100644
index 0000000..3df26f3
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/malloc.c
@@ -0,0 +1,146 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Embedded Artistry
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "list.h"
+
+/* align to nearest power of two */
+#define ALIGN_SIZE(sz, align) (((sz) + ((align)-1)) & ~((align)-1))
+
+/* free list node*/
+typedef struct alloc_node
+{
+ struct list_head node;
+ size_t size;
+ char* block;
+} alloc_node_t;
+
+/* allocation metadata size */
+#define ALLOC_HEADER_SZ __builtin_offsetof(alloc_node_t, block)
+
+/* minimum allocation size of 32 bytes */
+#define MIN_ALLOC_SZ ALLOC_HEADER_SZ + 32
+
+/* free list */
+static LIST_HEAD(free_list);
+
+static void coalesce_free_list(void)
+{
+ alloc_node_t *b, *lb = NULL, *t;
+
+ list_for_each_entry_safe(b, t, &free_list, node)
+ {
+ if (lb)
+ {
+ /* coalesce adjacent blocks */
+ if ((((uintptr_t)&lb->block) + lb->size) == (uintptr_t)b)
+ {
+ lb->size += sizeof(*b) + b->size;
+ list_del(&b->node);
+ continue;
+ }
+ }
+ lb = b;
+ }
+}
+
+void* malloc(size_t size)
+{
+ void* ptr = NULL;
+ alloc_node_t* blk = NULL;
+
+ if (size > 0)
+ {
+ /* Align the pointer */
+ size = ALIGN_SIZE(size, sizeof(void*));
+
+ /* try to find a big enough block */
+ list_for_each_entry(blk, &free_list, node)
+ {
+ if (blk->size >= size)
+ {
+ ptr = &blk->block;
+ break;
+ }
+ }
+
+ if (ptr)
+ {
+ /* split block if possible */
+ if ((blk->size - size) >= MIN_ALLOC_SZ)
+ {
+ alloc_node_t* new_blk;
+ new_blk = (alloc_node_t*)((uintptr_t)(&blk->block) + size);
+ new_blk->size = blk->size - size - ALLOC_HEADER_SZ;
+ blk->size = size;
+ list_add(&new_blk->node, &blk->node);
+ }
+
+ list_del(&blk->node);
+ }
+
+ }
+
+ return ptr;
+}
+
+void free(void* ptr)
+{
+ alloc_node_t *blk, *free_blk;
+
+ if (ptr)
+ {
+ blk = container_of(ptr, alloc_node_t, block);
+
+ /* add block to free list in ascending order by pointer */
+ list_for_each_entry(free_blk, &free_list, node)
+ {
+ if (free_blk > blk)
+ {
+ list_add_tail(&blk->node, &free_blk->node);
+ goto blockadded;
+ }
+ }
+ list_add_tail(&blk->node, &free_list);
+
+ blockadded:
+ coalesce_free_list();
+ }
+}
+
+void _malloc_addblock(void* addr, size_t size)
+{
+ alloc_node_t* blk;
+
+ /* pointer align the block */
+ blk = (alloc_node_t*)ALIGN_SIZE((uintptr_t)addr, sizeof(void*));
+
+ /* calculate usable size */
+ blk->size = (uintptr_t)addr + size - (uintptr_t)blk - ALLOC_HEADER_SZ;
+
+ /* add the block to the free list */
+ list_add(&blk->node, &free_list);
+}
diff --git a/riscv/riscv-probe/libfemto/std/memchr.c b/riscv/riscv-probe/libfemto/std/memchr.c
new file mode 100644
index 0000000..b8287ff
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/memchr.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+void *memchr(const void *s, int c, size_t n)
+{
+ unsigned char *p = (unsigned char *)s;
+ while (n-- > 0) {
+ if (*p == c) return p;
+ p++;
+ }
+ return NULL;
+}
diff --git a/riscv/riscv-probe/libfemto/std/memcmp.c b/riscv/riscv-probe/libfemto/std/memcmp.c
new file mode 100644
index 0000000..73932c9
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/memcmp.c
@@ -0,0 +1,15 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ while (n-- > 0) {
+ char c1 = *(const char*)s1++;
+ char c2 = *(const char*)s2++;
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ return 0;
+}
diff --git a/riscv/riscv-probe/libfemto/std/memcpy.c b/riscv/riscv-probe/libfemto/std/memcpy.c
new file mode 100644
index 0000000..69209e7
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/memcpy.c
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ char *p = dest;
+ while (n-- > 0) {
+ *(char*)dest++ = *(char*)src++;
+ }
+ return p;
+}
diff --git a/riscv/riscv-probe/libfemto/std/memset.c b/riscv/riscv-probe/libfemto/std/memset.c
new file mode 100644
index 0000000..82b1545
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/memset.c
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+void *memset(void *dest, int c, size_t n)
+{
+ char *p = dest;
+ while (n-- > 0) {
+ *(char*)dest++ = c;
+ }
+ return p;
+}
diff --git a/riscv/riscv-probe/libfemto/std/printf.c b/riscv/riscv-probe/libfemto/std/printf.c
new file mode 100644
index 0000000..5d81212
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/printf.c
@@ -0,0 +1,15 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+int printf(const char* s, ...)
+{
+ int res = 0;
+ va_list vl;
+ va_start(vl, s);
+ res = vprintf(s, vl);
+ va_end(vl);
+ return res;
+}
diff --git a/riscv/riscv-probe/libfemto/std/putchar.c b/riscv/riscv-probe/libfemto/std/putchar.c
new file mode 100644
index 0000000..3066a65
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/putchar.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <device.h>
+
+int putchar(int ch)
+{
+ return console_dev->putchar(ch);
+}
diff --git a/riscv/riscv-probe/libfemto/std/puts.c b/riscv/riscv-probe/libfemto/std/puts.c
new file mode 100644
index 0000000..2dd07f7
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/puts.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+
+int puts(const char *s)
+{
+ while (*s) putchar(*s++);
+ putchar('\n');
+}
diff --git a/riscv/riscv-probe/libfemto/std/snprintf.c b/riscv/riscv-probe/libfemto/std/snprintf.c
new file mode 100644
index 0000000..0228195
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/snprintf.c
@@ -0,0 +1,14 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+int snprintf(char* out, size_t n, const char* s, ...)
+{
+ va_list vl;
+ va_start(vl, s);
+ int res = vsnprintf(out, n, s, vl);
+ va_end(vl);
+ return res;
+}
diff --git a/riscv/riscv-probe/libfemto/std/strchr.c b/riscv/riscv-probe/libfemto/std/strchr.c
new file mode 100644
index 0000000..7eef221
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/strchr.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+char *strchr(const char *s, int c)
+{
+ char *p = (char *)s;
+ while (*p) {
+ if (*p == c) return p;
+ p++;
+ }
+ return NULL;
+}
diff --git a/riscv/riscv-probe/libfemto/std/strcmp.c b/riscv/riscv-probe/libfemto/std/strcmp.c
new file mode 100644
index 0000000..73d9d8a
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/strcmp.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+int strcmp(const char *s1, const char *s2)
+{
+ while (*s1 && *s2 && *s1 == *s2) { s1++; s2++; }
+ return *s1 - *s2;
+}
diff --git a/riscv/riscv-probe/libfemto/std/strlen.c b/riscv/riscv-probe/libfemto/std/strlen.c
new file mode 100644
index 0000000..74de6c4
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/strlen.c
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+size_t strlen(const char* s)
+{
+ const char *p = s;
+ for (;; s++) {
+ if (!*s) break;
+ }
+ return s - p;
+}
diff --git a/riscv/riscv-probe/libfemto/std/strncmp.c b/riscv/riscv-probe/libfemto/std/strncmp.c
new file mode 100644
index 0000000..e1b476c
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/strncmp.c
@@ -0,0 +1,9 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ while (n-- > 0 && *s1 && *s2 && *s1 == *s2) { s1++; s2++; }
+ return *s1 - *s2;
+}
diff --git a/riscv/riscv-probe/libfemto/std/strncpy.c b/riscv/riscv-probe/libfemto/std/strncpy.c
new file mode 100644
index 0000000..ccb8fc3
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/strncpy.c
@@ -0,0 +1,15 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+
+char *strncpy(char *dst, const char *src, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n && src[i]; i++) {
+ dst[i] = src[i];
+ }
+ for (; i < n; i++) {
+ dst[i] = 0;
+ }
+ return dst;
+}
diff --git a/riscv/riscv-probe/libfemto/std/vprintf.c b/riscv/riscv-probe/libfemto/std/vprintf.c
new file mode 100644
index 0000000..36c99a3
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/vprintf.c
@@ -0,0 +1,14 @@
+// See LICENSE for license details.
+
+#include <stdio.h>
+#include <alloca.h>
+
+int vprintf(const char* s, va_list vl)
+{
+ char *out;
+ int res = vsnprintf(NULL, -1, s, vl);
+ out = alloca(res + 1);
+ vsnprintf(out, res + 1, s, vl);
+ while (*out) putchar(*out++);
+ return res;
+}
diff --git a/riscv/riscv-probe/libfemto/std/vsnprintf.c b/riscv/riscv-probe/libfemto/std/vsnprintf.c
new file mode 100644
index 0000000..32dcf3f
--- /dev/null
+++ b/riscv/riscv-probe/libfemto/std/vsnprintf.c
@@ -0,0 +1,109 @@
+// See LICENSE for license details.
+
+#include <stddef.h>
+#include <stdarg.h>
+
+int vsnprintf(char * out, size_t n, const char* s, va_list vl)
+{
+ int format = 0;
+ int longarg = 0;
+ size_t pos = 0;
+ for( ; *s; s++) {
+ if (format) {
+ switch(*s) {
+ case 'l': {
+ longarg = 1;
+ break;
+ }
+ case 'p': {
+ longarg = 1;
+ if (out && pos < n) {
+ out[pos] = '0';
+ }
+ pos++;
+ if (out && pos < n) {
+ out[pos] = 'x';
+ }
+ pos++;
+ }
+ case 'x': {
+ long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
+ int hexdigits = 2*(longarg ? sizeof(long) : sizeof(int))-1;
+ for(int i = hexdigits; i >= 0; i--) {
+ int d = (num >> (4*i)) & 0xF;
+ if (out && pos < n) {
+ out[pos] = (d < 10 ? '0'+d : 'a'+d-10);
+ }
+ pos++;
+ }
+ longarg = 0;
+ format = 0;
+ break;
+ }
+ case 'd': {
+ long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
+ if (num < 0) {
+ num = -num;
+ if (out && pos < n) {
+ out[pos] = '-';
+ }
+ pos++;
+ }
+ long digits = 1;
+ for (long nn = num; nn /= 10; digits++)
+ ;
+ for (int i = digits-1; i >= 0; i--) {
+ if (out && pos + i < n) {
+ out[pos + i] = '0' + (num % 10);
+ }
+ num /= 10;
+ }
+ pos += digits;
+ longarg = 0;
+ format = 0;
+ break;
+ }
+ case 's': {
+ const char* s2 = va_arg(vl, const char*);
+ while (*s2) {
+ if (out && pos < n) {
+ out[pos] = *s2;
+ }
+ pos++;
+ s2++;
+ }
+ longarg = 0;
+ format = 0;
+ break;
+ }
+ case 'c': {
+ if (out && pos < n) {
+ out[pos] = (char)va_arg(vl,int);
+ }
+ pos++;
+ longarg = 0;
+ format = 0;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if(*s == '%') {
+ format = 1;
+ }
+ else {
+ if (out && pos < n) {
+ out[pos] = *s;
+ }
+ pos++;
+ }
+ }
+ if (out && pos < n) {
+ out[pos] = 0;
+ }
+ else if (out && n) {
+ out[n-1] = 0;
+ }
+ return pos;
+}
diff --git a/riscv/sections/sections.rv b/riscv/sections/sections.rv
new file mode 100755
index 0000000..9b96da0
--- /dev/null
+++ b/riscv/sections/sections.rv
Binary files differ
diff --git a/riscv/sections/sections.s b/riscv/sections/sections.s
new file mode 100644
index 0000000..8e83ec1
--- /dev/null
+++ b/riscv/sections/sections.s
@@ -0,0 +1,23 @@
+# Messing up with ASM sections
+
+.section .data
+ x: .word 10
+
+.section .text
+ update_x:
+ la t1, x
+ sw a0, (t1)
+ ret
+
+# We can mix sections in different parts of the source
+# The assembler will take care of merging them together in
+# the object file
+
+.section .data
+ y: .word 99
+
+.section .text
+ update_y:
+ la t1, y
+ sw a0, (t1)
+ ret
diff --git a/riscv/test.s b/riscv/test.s
new file mode 100644
index 0000000..fe2b882
--- /dev/null
+++ b/riscv/test.s
@@ -0,0 +1,6 @@
+trunk42:
+ li t1, 42
+ bge t1, a0, done
+ mv a0, t1
+done:
+ jal exit
diff --git a/riscv/unalign.rv b/riscv/unalign.rv
new file mode 100755
index 0000000..097df97
--- /dev/null
+++ b/riscv/unalign.rv
Binary files differ
diff --git a/riscv/unalign.s b/riscv/unalign.s
new file mode 100644
index 0000000..5556349
--- /dev/null
+++ b/riscv/unalign.s
@@ -0,0 +1,23 @@
+.globl _start
+
+.section .data
+.align 1
+i: .half 0xad
+.align 2
+x: .word 0xde
+.align 1
+j: .half 0xf0
+.align 3
+y: .dword 0xbe
+
+.section .text
+_start:
+ .byte 0xaa
+
+exit:
+ .align 2
+ li a0, 5
+ li a7, 93
+ ecall
+
+