diff options
Diffstat (limited to 'riscv/riscv-probe/libfemto/include')
23 files changed, 2898 insertions, 0 deletions
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 |
