├── .gitignore ├── core ├── extension │ └── build.mk ├── riscv │ ├── dispatch_fp32.c │ ├── dispatch_fp64.c │ ├── xlen.h │ ├── dispatch_rv32.c │ ├── dispatch_rv64.c │ ├── rvex.c │ └── riscv.c ├── inc │ └── core │ │ ├── host.h │ │ ├── arch.h │ │ ├── sym.h │ │ ├── types.h │ │ ├── mem.h │ │ ├── riscv.h │ │ ├── ex.h │ │ ├── mapper.h │ │ ├── chrono.h │ │ ├── common.h │ │ ├── worker.h │ │ ├── riscv │ │ ├── dispatch.h │ │ ├── rv.h │ │ ├── trace.h │ │ ├── csr.h │ │ └── inst.h │ │ ├── sem.h │ │ ├── engine.h │ │ ├── lock.h │ │ ├── itrace.h │ │ ├── bus.h │ │ ├── event.h │ │ ├── regview.h │ │ ├── cache.h │ │ ├── irq.h │ │ ├── device.h │ │ └── core.h ├── sym.c ├── host.c ├── build.mk ├── mem.c ├── error.c ├── lock.c ├── ex.c ├── cache.c ├── arch.c ├── sem.c ├── bus.c ├── io.c ├── irq.c ├── list.c ├── device.c ├── regview.c ├── engine.c ├── worker.c └── mapper.c ├── plat └── simple │ ├── build.mk │ └── inc │ └── plat │ └── platform.h ├── app └── sled │ ├── cons.h │ ├── build.mk │ └── cons.c ├── dev ├── build.mk └── sled │ ├── rtc.c │ ├── intc.c │ ├── uart.c │ ├── mpu.c │ └── timer.c ├── include ├── sled │ ├── elf │ │ ├── types.h │ │ ├── elf32.h │ │ └── elf64.h │ ├── chrono.h │ ├── engine.h │ ├── elf.h │ ├── event.h │ ├── machine.h │ ├── regview.h │ ├── list.h │ ├── arch.h │ ├── irq.h │ ├── error.h │ ├── io.h │ ├── types.h │ ├── device.h │ ├── mapper.h │ ├── worker.h │ ├── riscv.h │ └── core.h └── device │ └── sled │ ├── rtc.h │ ├── uart.h │ ├── intc.h │ ├── sled.h │ ├── mpu.h │ └── timer.h ├── LICENSE ├── .clang-format └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | build 4 | 5 | -------------------------------------------------------------------------------- /core/extension/build.mk: -------------------------------------------------------------------------------- 1 | SRCDIR := core/extension 2 | 3 | LIB_CSOURCES += \ 4 | -------------------------------------------------------------------------------- /core/riscv/dispatch_fp32.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #define USING_FP32 1 5 | #include "dispatch_flen.h" 6 | -------------------------------------------------------------------------------- /core/riscv/dispatch_fp64.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #define USING_FP64 1 5 | #include "dispatch_flen.h" 6 | -------------------------------------------------------------------------------- /core/inc/core/host.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | u8 host_get_clock_ns(void); 9 | -------------------------------------------------------------------------------- /plat/simple/build.mk: -------------------------------------------------------------------------------- 1 | PLATPATH := plat/$(PLAT) 2 | 3 | $(PLAT)_INCLUDES := -I$(PLATPATH)/inc 4 | 5 | $(PLAT)_DEVICES := \ 6 | sled_uart \ 7 | sled_rtc \ 8 | sled_intc \ 9 | sled_mpu \ 10 | sled_timer \ 11 | 12 | -------------------------------------------------------------------------------- /app/sled/cons.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | int console_enter(sl_machine_t *m); 9 | 10 | -------------------------------------------------------------------------------- /app/sled/build.mk: -------------------------------------------------------------------------------- 1 | APPPATH := app/$(APP) 2 | 3 | $(APP)_PLATFORM := simple 4 | 5 | $(APP)_INCLUDES += -I$(APPPATH)/inc -I$(BUILDDIR)/app/$(APP) 6 | 7 | $(APP)_CSOURCES := \ 8 | $(APPPATH)/cons.c \ 9 | $(APPPATH)/main.c \ 10 | 11 | $(APP)_CXXSOURCES := \ 12 | -------------------------------------------------------------------------------- /dev/build.mk: -------------------------------------------------------------------------------- 1 | SRCDIR := dev 2 | 3 | sled_intc_CSOURCES := $(SRCDIR)/sled/intc.c 4 | sled_rtc_CSOURCES := $(SRCDIR)/sled/rtc.c 5 | sled_uart_CSOURCES := $(SRCDIR)/sled/uart.c 6 | sled_mpu_CSOURCES := $(SRCDIR)/sled/mpu.c 7 | sled_timer_CSOURCES := $(SRCDIR)/sled/timer.c 8 | 9 | -------------------------------------------------------------------------------- /include/sled/elf/types.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | -------------------------------------------------------------------------------- /core/inc/core/arch.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | typedef struct { 9 | u1 (*reg_index)(u4 reg); 10 | u4 (*reg_for_name)(const char *name); 11 | const char *(*name_for_reg)(u4 reg); 12 | } arch_ops_t; 13 | 14 | const arch_ops_t * arch_get_ops(u1 arch); 15 | -------------------------------------------------------------------------------- /core/sym.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | void sym_free(sl_sym_list_t *list) { 6 | if (list == NULL) return; 7 | if (list->name) free(list->name); 8 | if (list->ent != NULL) { 9 | for (u8 i = 0; i < list->num; i++) { 10 | if (list->ent[i].name != NULL) free(list->ent[i].name); 11 | } 12 | free(list->ent); 13 | } 14 | free(list); 15 | } 16 | -------------------------------------------------------------------------------- /core/inc/core/sym.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct sl_sym_entry { 6 | u8 addr; 7 | u8 size; 8 | u4 flags; 9 | char *name; 10 | }; 11 | 12 | struct sl_sym_list { 13 | sl_sym_list_t *next; 14 | char *name; 15 | u8 num; 16 | sl_sym_entry_t *ent; 17 | }; 18 | 19 | int elf_read_symbols(sl_elf_obj_t *obj, sl_sym_list_t *list); 20 | 21 | void sym_free(sl_sym_list_t *list); 22 | -------------------------------------------------------------------------------- /include/device/sled/rtc.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022 Shac Ron and The Sled Project 3 | 4 | 5 | #pragma once 6 | 7 | #define RTC_REG_DEV_TYPE 0x0 // RO 8 | #define RTC_REG_DEV_VERSION 0x4 // RO 9 | 10 | #define RTC_REG_MONOTONIC64 0x8 // RO 11 | #define RTC_REG_MONOTONIC_LO 0x10 // RO 12 | #define RTC_REG_MONOTONIC_HI 0x14 // RO 13 | 14 | #define RTC_APERTURE_LENGTH 0x18 15 | -------------------------------------------------------------------------------- /core/inc/core/types.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // private types 9 | typedef struct sl_lock sl_lock_t; 10 | typedef struct sl_cond sl_cond_t; 11 | typedef struct rv_core rv_core_t; 12 | typedef struct sl_sem sl_sem_t; 13 | 14 | typedef struct sl_cache sl_cache_t; 15 | typedef struct sl_cache_page sl_cache_page_t; 16 | -------------------------------------------------------------------------------- /include/device/sled/uart.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #define UART_REG_DEV_TYPE 0x0 // RO 7 | #define UART_REG_DEV_VERSION 0x4 // RO 8 | #define UART_REG_CONFIG 0x8 // RW 9 | #define UART_REG_STATUS 0xc // RO 10 | #define UART_REG_FIFO_READ 0x10 // RO 11 | #define UART_REG_FIFO_WRITE 0x14 // WO 12 | 13 | #define UART_APERTURE_LENGTH 0x18 14 | -------------------------------------------------------------------------------- /core/inc/core/mem.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | sl_list_node_t node; 11 | u8 base; 12 | u8 length; 13 | sl_map_ep_t ep; 14 | u1 data[]; 15 | } mem_region_t; 16 | 17 | int mem_region_create(u8 base, u8 length, mem_region_t **m_out); 18 | void mem_region_destroy(mem_region_t *m); 19 | -------------------------------------------------------------------------------- /core/inc/core/riscv.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define RV_HAS_PRIV_LEVEL_USER (1u << 0) 9 | #define RV_HAS_PRIV_LEVEL_SUPERVISOR (1u << 1) 10 | #define RV_HAS_PRIV_LEVEL_HYPERVISOR (1u << 2) 11 | 12 | int sl_riscv_core_create(sl_core_params_t *p, sl_core_t **core_out); 13 | 14 | int riscv_decode_attributes(const char *attrib, u4 *arch_options_out); 15 | -------------------------------------------------------------------------------- /core/host.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #if __APPLE__ 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | u8 host_get_clock_ns(void) { 13 | #if __APPLE__ 14 | return clock_gettime_nsec_np(CLOCK_UPTIME_RAW); 15 | #else 16 | struct timeval tp; 17 | gettimeofday(&tp, NULL); 18 | return (tp.tv_usec * 1000) + (tp.tv_sec * 1000000000); 19 | #endif 20 | } 21 | -------------------------------------------------------------------------------- /core/inc/core/ex.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | typedef enum { 7 | EX_SYSCALL, 8 | EX_BREAKPOINT, 9 | EX_UNDEFINDED, 10 | EX_ABORT_LOAD, 11 | EX_ABORT_LOAD_ALIGN, 12 | EX_ABORT_STORE, 13 | EX_ABORT_STORE_ALIGN, 14 | EX_ABORT_INST, 15 | EX_ABORT_INST_ALIGN, 16 | 17 | EX_MATH_INTEGER, 18 | EX_MATH_FP, 19 | 20 | NUM_EXCPTIONS 21 | } core_ex_t; 22 | 23 | -------------------------------------------------------------------------------- /core/inc/core/mapper.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | typedef struct map_ent map_ent_t; 10 | 11 | struct sl_mapper { 12 | int mode; 13 | u4 num_ents; 14 | map_ent_t **list; 15 | sl_mapper_t *next; 16 | sl_map_ep_t ep; 17 | }; 18 | 19 | void mapper_init(sl_mapper_t *m); 20 | void mapper_shutdown(sl_mapper_t *m); 21 | 22 | int mapper_update(sl_mapper_t *m, sl_event_t *ev); 23 | 24 | void mapper_print_mappings(sl_mapper_t *m); 25 | -------------------------------------------------------------------------------- /core/inc/core/chrono.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct sl_chrono { 12 | const char *name; 13 | u8 next_id; 14 | sl_lock_t lock; 15 | sl_cond_t cond; 16 | sl_list_t active_timers; 17 | sl_list_t unused_timers; 18 | pthread_t thread; 19 | u1 state; 20 | }; 21 | 22 | int sl_chrono_init(sl_chrono_t *c, const char *name); 23 | void sl_chrono_shutdown(sl_chrono_t *c); 24 | -------------------------------------------------------------------------------- /core/riscv/xlen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if USING_RV32 7 | #define XLEN 32 8 | u4 typedef uxlen_t; 9 | i4 typedef sxlen_t; 10 | u8 typedef ux2len_t; 11 | i8 typedef sx2len_t; 12 | result32_t typedef resultxlen_t; 13 | #define XLEN_PREFIX(name) rv32_ ## name 14 | #define PRIXLENx PRIx32 15 | #else 16 | #define XLEN 64 17 | u8 typedef uxlen_t; 18 | i8 typedef sxlen_t; 19 | __uint128_t typedef ux2len_t; 20 | __int128_t typedef sx2len_t; 21 | result64_t typedef resultxlen_t; 22 | #define XLEN_PREFIX(name) rv64_ ## name 23 | #define PRIXLENx PRIx64 24 | #endif 25 | -------------------------------------------------------------------------------- /core/inc/core/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define countof(p) (sizeof(p) / sizeof(p[0])) 9 | #define containerof(p, t, m) (t *)((uintptr_t)p - offsetof(t, m)) 10 | 11 | #define likely(x) __builtin_expect((x),1) 12 | #define unlikely(x) __builtin_expect((x),0) 13 | 14 | #define MIN(a,b) ((a) <= (b) ? (a) : (b)) 15 | #define MAX(a,b) ((a) >= (b) ? (a) : (b)) 16 | 17 | #define ROUND_UP(a, sz) ((((a) + (sz - 1)) / (sz)) * (sz)) 18 | #define ROUND_DOWN(a, sz) (((a) / (sz)) * (sz)) 19 | -------------------------------------------------------------------------------- /include/device/sled/intc.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #define INTC_REG_DEV_TYPE 0x0 // RO 7 | #define INTC_REG_DEV_VERSION 0x4 // RO 8 | 9 | // interrupts that are currently asserted (including masked) 10 | // Writing to a bit clears that asserted bit, a 0 has no effect. 11 | #define INTC_REG_ASSERTED 0x8 // RW 12 | 13 | // interrupts that are masked. Masked interrupt will be asserted 14 | // but will not trigger an IRQ 15 | // Default value: 0xffffffff 16 | #define INTC_REG_MASK 0xc // RW 17 | 18 | #define INTC_APERTURE_LENGTH 0x10 19 | -------------------------------------------------------------------------------- /core/inc/core/worker.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define SL_WORKER_MAX_EPS 64 9 | 10 | struct sl_worker { 11 | const char *name; 12 | 13 | sl_lock_t lock; 14 | sl_cond_t has_event; 15 | sl_list_t ev_list; 16 | 17 | u4 state; 18 | sl_engine_t *engine; 19 | 20 | sl_event_ep_t *endpoint[SL_WORKER_MAX_EPS]; 21 | 22 | pthread_t thread; 23 | int thread_status; 24 | bool thread_running; 25 | }; 26 | 27 | int sl_worker_init(sl_worker_t *w, const char *name); 28 | void sl_worker_shutdown(sl_worker_t *w); 29 | -------------------------------------------------------------------------------- /core/inc/core/riscv/dispatch.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | int rv_exec_atomic(rv_core_t *c, rv_inst_t inst); 10 | int rv_exec_ebreak(rv_core_t *c); 11 | int rv_exec_mem(rv_core_t *c, rv_inst_t inst); 12 | int rv_exec_system(rv_core_t *c, rv_inst_t inst); 13 | 14 | int rv32_dispatch(rv_core_t *c, rv_inst_t inst); 15 | int rv64_dispatch(rv_core_t *c, rv_inst_t inst); 16 | 17 | static inline int rv_undef(rv_core_t *c, rv_inst_t inst) { 18 | return sl_core_synchronous_exception(&c->core, EX_UNDEFINDED, inst.raw, 0); 19 | } 20 | -------------------------------------------------------------------------------- /core/inc/core/sem.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #if __APPLE__ 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | #include 13 | 14 | struct sl_sem { 15 | #if __APPLE__ 16 | dispatch_semaphore_t dsem; 17 | #else 18 | sem_t psem; 19 | #endif 20 | }; 21 | 22 | int sl_sem_init(sl_sem_t *sem, unsigned int value); 23 | int sl_sem_post(sl_sem_t *sem); 24 | int sl_sem_wait(sl_sem_t *sem); 25 | int sl_sem_trywait(sl_sem_t *sem); 26 | int sl_sem_timedwait(sl_sem_t *restrict sem, const struct timespec *restrict abs_timeout); 27 | int sl_sem_destroy(sl_sem_t *sem); 28 | 29 | -------------------------------------------------------------------------------- /plat/simple/inc/plat/platform.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #define PLAT_MEM_BASE 0x10000 7 | #define PLAT_MEM_SIZE (5 * 1024 * 1024) 8 | 9 | #define PLAT_CORE_ARCH SL_ARCH_RISCV 10 | #define PLAT_CORE_SUBARCH SL_SUBARCH_RV32 11 | #define PLAT_ARCH_OPTIONS 0 12 | 13 | // map of interrupt vectors to devices in intc 14 | #define PLAT_INTC_TIMER_IRQ_BIT 0 15 | #define PLAT_INTC_UART_IRQ_BIT 1 16 | 17 | #define WITH_UART 1 18 | #define PLAT_UART_BASE 0x5000000 19 | #define PLAT_INTC_BASE 0x5010000 20 | #define PLAT_RTC_BASE 0x5020000 21 | #define PLAT_MPU_BASE 0x5030000 22 | #define PLAT_TIMER_BASE 0x5040000 23 | -------------------------------------------------------------------------------- /core/inc/core/engine.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct sl_engine { 12 | const char *name; 13 | u4 state; // current running state 14 | sl_irq_ep_t irq_ep; 15 | sl_worker_t *worker; 16 | u4 epid; 17 | sl_event_ep_t event_ep; 18 | sl_engine_ops_t ops; 19 | void *context; 20 | }; 21 | 22 | int sl_engine_init(sl_engine_t *e, const char *name, const sl_engine_ops_t *ops); 23 | void sl_engine_shutdown(sl_engine_t *e); 24 | 25 | int sl_engine_handle_interrupts(sl_engine_t *e); 26 | int sl_engine_wait_for_interrupt(sl_engine_t *e); 27 | -------------------------------------------------------------------------------- /core/inc/core/lock.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | 10 | struct sl_lock { 11 | pthread_mutex_t mu; 12 | }; 13 | 14 | struct sl_cond { 15 | pthread_cond_t cond; 16 | }; 17 | 18 | void sl_lock_init(sl_lock_t *l); 19 | void sl_lock_lock(sl_lock_t *l); 20 | void sl_lock_unlock(sl_lock_t *l); 21 | void sl_lock_destroy(sl_lock_t *l); 22 | 23 | void sl_cond_init(sl_cond_t *c); 24 | void sl_cond_wait(sl_cond_t *c, sl_lock_t *l); 25 | int sl_cond_timed_wait_abs(sl_cond_t *c, sl_lock_t *l, u8 utime); 26 | void sl_cond_signal_one(sl_cond_t *c); 27 | void sl_cond_signal_all(sl_cond_t *c); 28 | void sl_cond_destroy(sl_cond_t *c); 29 | -------------------------------------------------------------------------------- /include/device/sled/sled.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // common headers for sled generic devices 13 | // todo: break this up 14 | 15 | // uart 16 | #define UART_IO_NULL 0 17 | #define UART_IO_CONS 1 18 | #define UART_IO_FILE 2 19 | #define UART_IO_PORT 3 20 | int sled_uart_set_channel(sl_dev_t *d, int io, int fd_in, int fd_out); 21 | 22 | // intc 23 | // ------------- 24 | sl_irq_ep_t * sled_intc_get_irq_ep(sl_dev_t *d); 25 | // map interrupts generated by 'src' to intc bit 'num' 26 | int sled_intc_set_input(sl_dev_t *intc, sl_dev_t *src, u4 num); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /core/inc/core/itrace.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define TRACE_STR_LEN 64 9 | 10 | #define ITRACE_OPT_INST_STORE (1u << 0) 11 | #define ITRACE_OPT_INST16 (1u << 1) 12 | #define ITRACE_OPT_SYSREG (1u << 2) 13 | #define ITRACE_OPT_FLOAT (1u << 3) 14 | #define ITRACE_OPT_DOUBLE (1u << 4) 15 | 16 | typedef struct { 17 | u8 pc; 18 | u8 sp; 19 | u4 opcode; 20 | u2 rd; 21 | u2 options; 22 | u1 pl; 23 | u8 rd_value; 24 | u8 addr; 25 | union { 26 | u8 aux_value; 27 | float f_value; 28 | double d_value; 29 | }; 30 | u4 cur; 31 | char opstr[TRACE_STR_LEN]; 32 | } itrace_t; 33 | -------------------------------------------------------------------------------- /core/inc/core/bus.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct sl_bus { 11 | sl_dev_t dev; 12 | sl_mapper_t mapper; 13 | sl_list_t mem_list; 14 | }; 15 | 16 | int sl_bus_create(const char *name, sl_dev_config_t *cfg, sl_bus_t **bus_out); 17 | void sl_bus_destroy(sl_bus_t *b); 18 | 19 | int sl_bus_init(sl_bus_t *b, const char *name, sl_dev_config_t *cfg); 20 | void sl_bus_shutdown(sl_bus_t *b); 21 | 22 | int bus_add_mem_region(sl_bus_t *b, mem_region_t *r); 23 | int bus_add_device(sl_bus_t *b, sl_dev_t *dev, u8 base); 24 | sl_dev_t * bus_get_device_for_name(sl_bus_t *b, const char *name); 25 | sl_mapper_t * bus_get_mapper(sl_bus_t *b); 26 | -------------------------------------------------------------------------------- /include/sled/chrono.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // sl_chrono_t provides a core timer implementation that can be used to implement asynchronous timer devices 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | // setup functions 15 | int sl_chrono_create(const char *name, sl_chrono_t **c_out); 16 | void sl_chrono_destroy(sl_chrono_t *c); 17 | 18 | int sl_chrono_run(sl_chrono_t *c); 19 | int sl_chrono_pause(sl_chrono_t *c); 20 | int sl_chrono_stop(sl_chrono_t *c); 21 | 22 | int sl_chrono_timer_set(sl_chrono_t *c, u8 us, int (*callback)(void *context, int err), void *context, u8 *id_out); 23 | int sl_chrono_timer_get_remaining(sl_chrono_t *c, u8 id, u8 *remain_out); 24 | int sl_chrono_timer_cancel(sl_chrono_t *c, u8 id); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /core/build.mk: -------------------------------------------------------------------------------- 1 | SRCDIR := core 2 | 3 | LIB_CSOURCES += \ 4 | $(SRCDIR)/arch.c \ 5 | $(SRCDIR)/bus.c \ 6 | $(SRCDIR)/cache.c \ 7 | $(SRCDIR)/chrono.c \ 8 | $(SRCDIR)/core.c \ 9 | $(SRCDIR)/device.c \ 10 | $(SRCDIR)/elf.c \ 11 | $(SRCDIR)/engine.c \ 12 | $(SRCDIR)/error.c \ 13 | $(SRCDIR)/ex.c \ 14 | $(SRCDIR)/host.c \ 15 | $(SRCDIR)/io.c \ 16 | $(SRCDIR)/irq.c \ 17 | $(SRCDIR)/list.c \ 18 | $(SRCDIR)/lock.c \ 19 | $(SRCDIR)/machine.c \ 20 | $(SRCDIR)/mapper.c \ 21 | $(SRCDIR)/mem.c \ 22 | $(SRCDIR)/regview.c \ 23 | $(SRCDIR)/riscv/csr.c \ 24 | $(SRCDIR)/riscv/dispatch.c \ 25 | $(SRCDIR)/riscv/dispatch_fp32.c \ 26 | $(SRCDIR)/riscv/dispatch_fp64.c \ 27 | $(SRCDIR)/riscv/dispatch_rv32.c \ 28 | $(SRCDIR)/riscv/dispatch_rv64.c \ 29 | $(SRCDIR)/riscv/regnames.c \ 30 | $(SRCDIR)/riscv/riscv.c \ 31 | $(SRCDIR)/riscv/rvex.c \ 32 | $(SRCDIR)/sem.c \ 33 | $(SRCDIR)/sym.c \ 34 | $(SRCDIR)/worker.c \ 35 | 36 | -------------------------------------------------------------------------------- /include/sled/engine.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | struct sl_engine_ops { 13 | int (*step)(sl_engine_t *e, u8 num); 14 | int (*run)(sl_engine_t *e); 15 | int (*interrupt)(sl_engine_t *e); 16 | }; 17 | 18 | int sl_engine_create(const char *name, const sl_engine_ops_t *ops, sl_engine_t **e_out); 19 | void sl_engine_destory(sl_engine_t *e); 20 | 21 | void sl_engine_set_context(sl_engine_t *e, void *ctx); 22 | void * sl_engine_get_context(sl_engine_t *e); 23 | 24 | int sl_engine_run(sl_engine_t *e); 25 | int sl_engine_step(sl_engine_t *e, u8 num); 26 | void sl_engine_interrupt_set(sl_engine_t *e, bool enable); 27 | 28 | int sl_engine_async_command(sl_engine_t *e, u4 cmd, bool wait); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /core/inc/core/event.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct sl_event_queue sl_event_queue_t; 11 | 12 | struct sl_event_queue { 13 | sl_lock_t lock; 14 | sl_cond_t available; 15 | sl_list_t list; 16 | }; 17 | 18 | int ev_queue_init(sl_event_queue_t *q); 19 | 20 | void ev_queue_add(sl_event_queue_t *q, sl_event_t *ev); 21 | sl_event_t * ev_queue_remove(sl_event_queue_t *q, bool wait); 22 | sl_list_node_t * ev_queue_remove_all(sl_event_queue_t *q, bool wait); 23 | 24 | void ev_queue_wait(sl_event_queue_t *q); 25 | 26 | // ev_queue_maybe_has_entries does not take the lock, it suitable for polling where 27 | // eventual consistency is sufficient. 28 | bool ev_queue_maybe_has_entries(sl_event_queue_t *q); 29 | 30 | void ev_queue_shutdown(sl_event_queue_t *q); 31 | -------------------------------------------------------------------------------- /core/inc/core/regview.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define HASH_ENTS 256 9 | #define MAX_DEVS 40 10 | 11 | typedef struct { 12 | u4 index; 13 | u4 value; 14 | sl_dev_t *dev; // todo: optimize storage 15 | } hash_item_t; 16 | 17 | typedef struct { 18 | u4 offset; 19 | u4 count; 20 | } hash_ent_t; 21 | 22 | typedef struct { 23 | u4 count; 24 | hash_item_t *items; 25 | hash_ent_t ent[HASH_ENTS]; 26 | } hash_t; 27 | 28 | struct sl_reg_view { 29 | sl_dev_t dev; 30 | const char *name; 31 | hash_t hash; 32 | sl_dev_ops_t ops; 33 | u4 dev_count; 34 | u4 dev_view_offset[MAX_DEVS]; 35 | sl_dev_t *dev_list[MAX_DEVS]; 36 | }; 37 | 38 | int sl_reg_view_init(sl_reg_view_t *rv, const char *name, sl_dev_config_t *cfg); 39 | void sl_reg_view_shutdown(sl_reg_view_t *rv); 40 | -------------------------------------------------------------------------------- /core/inc/core/cache.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2024-2025 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #define SL_CACHE_ENTS 64 10 | 11 | struct sl_cache_page { 12 | u8 base; 13 | void *buf; 14 | }; 15 | 16 | struct sl_cache { 17 | u1 page_shift; 18 | u8 miss_addr; 19 | u8 hash_hit; 20 | u8 hash_miss; 21 | sl_cache_page_t page[SL_CACHE_ENTS]; 22 | }; 23 | 24 | void sl_cache_init(sl_cache_t *c); 25 | void sl_cache_shutdown(sl_cache_t *c); 26 | 27 | int sl_cache_rw_single(sl_cache_t *c, u8 addr, usize size, void *buf, bool read); 28 | int sl_cache_read(sl_cache_t *c, u8 addr, usize size, void *buf); 29 | int sl_cache_write(sl_cache_t *c, u8 addr, usize size, void *buf); 30 | 31 | void sl_cache_set_page(sl_cache_t *c, u8 base, void *buf); 32 | 33 | void sl_cache_invalidate_page(sl_cache_t *c, u8 addr); 34 | void sl_cache_invalidate_all(sl_cache_t *c); 35 | -------------------------------------------------------------------------------- /core/inc/core/irq.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | struct sl_irq_mux { 9 | sl_irq_ep_t *client; // client irq link 10 | u4 client_num; // interrupt number to use in client invocation 11 | u4 enabled; // inverse mask 12 | u4 active; // irq bits processed into output 13 | }; 14 | 15 | struct sl_irq_ep { 16 | u4 asserted; // input irq line state 17 | u4 retained; // sticky version of asserted bits 18 | sl_irq_mux_t mux; // output irq to client 19 | 20 | // the supplied assert function, if any, should do the proper locking to 21 | // protect this structure from possible concurrency 22 | int (*assert)(sl_irq_ep_t *ep, u4 num, bool high); 23 | void *context; 24 | }; 25 | 26 | int sl_irq_ep_init(sl_irq_ep_t *ep); 27 | void sl_irq_ep_shutdown(sl_irq_ep_t *ep); 28 | -------------------------------------------------------------------------------- /include/sled/elf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | typedef struct sl_elf_obj sl_elf_obj_t; 16 | 17 | int sl_elf_open(const char *filename, sl_elf_obj_t **obj_out); 18 | int sl_elf_arch(sl_elf_obj_t *obj); 19 | int sl_elf_subarch(sl_elf_obj_t *obj); 20 | u4 sl_elf_arch_options(sl_elf_obj_t *obj); 21 | bool sl_elf_is_64bit(sl_elf_obj_t *obj); 22 | u8 sl_elf_get_entry(sl_elf_obj_t *obj); 23 | ssize_t sl_elf_symbol_length(sl_elf_obj_t *obj, const char *name); 24 | ssize_t sl_elf_read_symbol(sl_elf_obj_t *obj, const char *name, void *buf, usize buflen); 25 | void *sl_elf_get_program_header(sl_elf_obj_t *obj, u4 index); 26 | void *sl_elf_pointer_for_offset(sl_elf_obj_t *obj, u8 offset); 27 | void sl_elf_close(sl_elf_obj_t *obj); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shac Ron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/sled/event.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #define SL_EV_EP_CALLBACK 0xffffffff 14 | 15 | #define SL_EV_FLAG_FREE (1u << 0) // free event pointer when done 16 | #define SL_EV_FLAG_SIGNAL (1u << 1) // wait on call completion before returning 17 | 18 | struct sl_event_ep { 19 | int (*handle)(sl_event_ep_t *ep, sl_event_t *ev); 20 | }; 21 | 22 | struct sl_event { 23 | sl_list_node_t node; // internal, should be zero 24 | u4 epid; // endpoint id 25 | u4 type; // user-defined 26 | u4 flags; // combination of zero or more S_EV_FLAGs 27 | u4 option; // user-defined 28 | u8 arg[4]; // user-defined 29 | uintptr_t signal; // internal, should be zero 30 | int (*callback)(sl_event_t *ev); // NULL unless epid is SL_EV_EP_CALLBACK 31 | void *cookie; // user-defined, may be NULL 32 | int err; // returned status from handler. Only valid if SL_EV_FLAG_SIGNAL is set. 33 | }; 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /core/riscv/dispatch_rv32.c: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Shac Ron 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // SPDX-License-Identifier: MIT License 24 | 25 | #define USING_RV32 1 26 | #include "dispatch_xlen.h" 27 | -------------------------------------------------------------------------------- /core/riscv/dispatch_rv64.c: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2022 Shac Ron 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // SPDX-License-Identifier: MIT License 24 | 25 | #define USING_RV64 1 26 | #include "dispatch_xlen.h" 27 | -------------------------------------------------------------------------------- /core/mem.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2025 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static int mem_io(sl_map_ep_t *ep, sl_io_op_t *op) { 13 | mem_region_t *m = containerof(ep, mem_region_t, ep); 14 | void *data = m->data + op->addr; 15 | 16 | switch (op->op) { 17 | case IO_OP_IN: 18 | memcpy(op->buf, data, op->count * op->size); 19 | return 0; 20 | case IO_OP_OUT: 21 | memcpy(data, op->buf, op->count * op->size); 22 | return 0; 23 | case IO_OP_RESOLVE: 24 | op->arg[0] = (uptr)data; 25 | op->arg[1] = m->length - op->addr; 26 | return 0; 27 | default: 28 | return sl_io_for_data(data, op); 29 | } 30 | } 31 | 32 | int mem_region_create(u8 base, u8 length, mem_region_t **m_out) { 33 | const usize size = length + sizeof(mem_region_t); 34 | mem_region_t *m = calloc(1, size); 35 | if (m == NULL) return SL_ERR_MEM; 36 | 37 | m->base = base; 38 | m->length = length; 39 | m->ep.io = mem_io; 40 | *m_out = m; 41 | return 0; 42 | } 43 | 44 | void mem_region_destroy(mem_region_t *m) { 45 | free(m); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /include/sled/machine.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | int sl_machine_create(sl_machine_t **m_out); 14 | 15 | int sl_machine_add_core(sl_machine_t *m, sl_core_params_t *opts); 16 | int sl_machine_add_device(sl_machine_t *m, u4 type, u8 base, const char *name); 17 | int sl_machine_add_device_prefab(sl_machine_t *m, u8 base, sl_dev_t *d); 18 | int sl_machine_add_mem(sl_machine_t *m, u8 base, u8 size); 19 | 20 | // Create a device without adding it to the machine 21 | int sl_machine_create_device(sl_machine_t *m, u4 type, const char *name, sl_dev_t **d_out); 22 | 23 | int sl_machine_load_core(sl_machine_t *m, u4 id, sl_elf_obj_t *obj, bool configure); 24 | int sl_machine_load_core_raw(sl_machine_t *m, u4 id, u8 addr, void *buf, u8 size); 25 | 26 | sl_dev_t * sl_machine_get_device_for_name(sl_machine_t *m, const char *name); 27 | sl_core_t * sl_machine_get_core(sl_machine_t *m, u4 id); 28 | int sl_machine_set_interrupt(sl_machine_t *m, u4 irq, bool high); 29 | sl_chrono_t * sl_machine_get_chrono(sl_machine_t *m); 30 | 31 | void sl_machine_destroy(sl_machine_t *m); 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /include/sled/regview.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // reg_view is a wrapper for peripheral address ranges 13 | 14 | // This is intended for: 15 | // 1. platforms with messy MMIO spaces with overlapping device register addresses. 16 | // 2. devices whose register layouts change per instance or per platform. 17 | 18 | // Once allocated, new mappings can be added to the reg_view. The mappings are 19 | // a static list of 'view addresses', the addresses visible in the MMIO space, 20 | // and 'device addresses', the addresses presented to the device's IO functions. 21 | // Multiple mappings can be added as needed, though they should not conflict for the 22 | // same view address. 23 | // The mapped devices receive a uniform address range, allowing the same code to be 24 | // used without needing be aware of the variable user-facing addresses. 25 | 26 | int sl_reg_view_create(const char *name, sl_dev_config_t *cfg, sl_reg_view_t **rv_out); 27 | void sl_reg_view_destroy(sl_reg_view_t *rv); 28 | 29 | sl_dev_t * sl_reg_view_get_dev(sl_reg_view_t *rv); 30 | 31 | int sl_reg_view_add_mapping(sl_reg_view_t *rv, sl_dev_t *dev, u4 view_offset, const u4 *view_addr, const u4 *dev_addr, u4 addr_count); 32 | 33 | void sl_reg_view_print_mappings(sl_dev_t *d, u8 base); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /core/error.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #define ESTR(e) [-(e)] = #e 8 | 9 | static const char *err_str[] = { 10 | [SL_OK] = "SL_OK", 11 | ESTR(SL_ERR), 12 | ESTR(SL_ERR_ARG), 13 | ESTR(SL_ERR_MEM), 14 | ESTR(SL_ERR_UNSUPPORTED), 15 | ESTR(SL_ERR_UNIMPLEMENTED), 16 | ESTR(SL_ERR_FULL), 17 | ESTR(SL_ERR_RANGE), 18 | ESTR(SL_ERR_STATE), 19 | ESTR(SL_ERR_TIMEOUT), 20 | ESTR(SL_ERR_BUSY), 21 | ESTR(SL_ERR_EXITED), 22 | ESTR(SL_ERR_NEXT), 23 | ESTR(SL_ERR_NOT_FOUND), 24 | ESTR(SL_ERR_SYSTEM), 25 | ESTR(SL_ERR_RESTART), 26 | 27 | ESTR(SL_ERR_UNDEF), 28 | ESTR(SL_ERR_ABORT), 29 | ESTR(SL_ERR_SYSCALL), 30 | ESTR(SL_ERR_BREAKPOINT), 31 | 32 | // ESTR(SL_ERR_BUS), 33 | ESTR(SL_ERR_IO_NODEV), 34 | ESTR(SL_ERR_IO_ALIGN), 35 | ESTR(SL_ERR_IO_SIZE), 36 | ESTR(SL_ERR_IO_COUNT), 37 | ESTR(SL_ERR_IO_PERM), 38 | ESTR(SL_ERR_IO_NOWR), 39 | ESTR(SL_ERR_IO_NORD), 40 | ESTR(SL_ERR_IO_INVALID), 41 | ESTR(SL_ERR_IO_NOMAP), 42 | ESTR(SL_ERR_IO_NOATOMIC), 43 | ESTR(SL_ERR_IO_NOCACHE), 44 | }; 45 | 46 | const char *st_err(int err) { 47 | if (err > 0) return "UNDEFINED"; 48 | err = -err; 49 | if (err > countof(err_str)) return "UNDEFINED"; 50 | const char *e = err_str[err]; 51 | if (!e) e = "ST_ERR_UNKNOWN"; 52 | return e; 53 | } 54 | -------------------------------------------------------------------------------- /core/inc/core/device.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define DEV_FLAG_EMBEDDED (1u << 0) 17 | 18 | struct sl_dev { 19 | sl_list_node_t node; 20 | 21 | u4 magic; 22 | u8 base; 23 | 24 | const sl_dev_ops_t *ops; 25 | const char *name; 26 | 27 | sl_lock_t lock; 28 | sl_irq_mux_t irq_mux; // outgoing interrupts 29 | sl_map_ep_t map_ep; // incoming io from external mapper 30 | void *context; // context of owner object 31 | u4 aperture; // size of memory region used by device registers 32 | sl_event_ep_t event_ep; // async event endpoint 33 | sl_mapper_t *mapper; // held for owner object, todo: remove 34 | sl_worker_t *worker; // worker thread and event loop 35 | u4 worker_epid; // id to use for event loop direct events 36 | }; 37 | 38 | // device API 39 | static inline void dev_lock(sl_dev_t *d) { sl_lock_lock(&d->lock); } 40 | static inline void dev_unlock(sl_dev_t *d) { sl_lock_unlock(&d->lock); } 41 | 42 | // internal device calls 43 | int sl_device_init(sl_dev_t *d, sl_dev_config_t *cfg); 44 | void sl_device_shutdown(sl_dev_t *d); 45 | 46 | sl_dev_t * device_get_for_ep(sl_map_ep_t *ep); 47 | -------------------------------------------------------------------------------- /core/lock.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define LOCK_DEBUG 1 10 | 11 | #if LOCK_DEBUG 12 | #include 13 | #include 14 | #define LOCK_ASSERT_OK(x) assert((x) == 0) 15 | #else 16 | #define LOCK_ASSERT_OK(x) x 17 | #endif 18 | 19 | void sl_lock_init(sl_lock_t *l) { 20 | LOCK_ASSERT_OK(pthread_mutex_init(&l->mu, NULL)); 21 | } 22 | 23 | void sl_lock_lock(sl_lock_t *l) { 24 | LOCK_ASSERT_OK(pthread_mutex_lock(&l->mu)); 25 | } 26 | 27 | void sl_lock_unlock(sl_lock_t *l) { 28 | LOCK_ASSERT_OK(pthread_mutex_unlock(&l->mu)); 29 | } 30 | 31 | void sl_lock_destroy(sl_lock_t *l) { 32 | LOCK_ASSERT_OK(pthread_mutex_destroy(&l->mu)); 33 | } 34 | 35 | void sl_cond_init(sl_cond_t *c) { 36 | LOCK_ASSERT_OK(pthread_cond_init(&c->cond, NULL)); 37 | } 38 | 39 | void sl_cond_wait(sl_cond_t *c, sl_lock_t *l) { 40 | LOCK_ASSERT_OK(pthread_cond_wait(&c->cond, &l->mu)); 41 | } 42 | 43 | int sl_cond_timed_wait_abs(sl_cond_t *c, sl_lock_t *l, u8 utime) { 44 | struct timespec ts; 45 | ts.tv_sec = utime / 1000000; 46 | ts.tv_nsec = (utime % 1000000) * 1000; 47 | int err = pthread_cond_timedwait(&c->cond, &l->mu, &ts); 48 | if (err == 0) return 0; 49 | #if LOCK_DEBUG 50 | assert(err == ETIMEDOUT); 51 | #endif 52 | return SL_ERR_TIMEOUT; 53 | } 54 | 55 | void sl_cond_signal_one(sl_cond_t *c) { 56 | LOCK_ASSERT_OK(pthread_cond_signal(&c->cond)); 57 | } 58 | 59 | void sl_cond_signal_all(sl_cond_t *c) { 60 | LOCK_ASSERT_OK(pthread_cond_broadcast(&c->cond)); 61 | } 62 | 63 | void sl_cond_destroy(sl_cond_t *c) { 64 | LOCK_ASSERT_OK(pthread_cond_destroy(&c->cond)); 65 | } 66 | -------------------------------------------------------------------------------- /include/sled/list.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | struct sl_list_node { 13 | struct sl_list_node *next; 14 | }; 15 | 16 | struct sl_list { 17 | sl_list_node_t *first; 18 | sl_list_node_t *last; 19 | }; 20 | 21 | struct sl_list_iterator { 22 | sl_list_t *list; 23 | sl_list_node_t *current; 24 | sl_list_node_t *previous; 25 | }; 26 | 27 | void sl_list_init(sl_list_t *list); // this is equivalent to zeroing the list struct 28 | void sl_list_add_last(sl_list_t *list, sl_list_node_t *n); 29 | void sl_list_add_first(sl_list_t *list, sl_list_node_t *n); 30 | sl_list_node_t * sl_list_remove_first(sl_list_t *list); 31 | sl_list_node_t * sl_list_remove_all(sl_list_t *list); 32 | static inline sl_list_node_t * sl_list_peek_first(sl_list_t *list) { return list->first; } 33 | static inline sl_list_node_t * sl_list_peek_last(sl_list_t *list) { return list->last; } 34 | static inline bool sl_list_is_empty(sl_list_t *list) { return list->first == NULL; } 35 | 36 | void sl_list_remove_node(sl_list_t *list, sl_list_node_t *n, sl_list_node_t *prev); 37 | 38 | // finding requires traversing the list 39 | int sl_list_find_and_remove(sl_list_t *list, sl_list_node_t *n); 40 | 41 | void sl_list_insert_sorted(sl_list_t *list, int(*compare)(const void *, const void *), sl_list_node_t *n); 42 | 43 | void sl_list_interator_begin(sl_list_iterator_t *iter, sl_list_t *list); 44 | sl_list_node_t * sl_list_interator_next(sl_list_iterator_t *iter); 45 | sl_list_node_t * sl_list_iterator_get_current(sl_list_iterator_t *iter); 46 | void sl_list_iterator_remove_current(sl_list_iterator_t *iter); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | -------------------------------------------------------------------------------- /dev/sled/rtc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define RTC_TYPE 'rtcs' 12 | #define RTC_VERSION 0 13 | 14 | static int rtc_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 15 | if (count != 1) return SL_ERR_IO_COUNT; 16 | 17 | u4 *val = buf; 18 | switch (addr) { 19 | case RTC_REG_DEV_TYPE: 20 | if (size != 4) return SL_ERR_IO_SIZE; 21 | *val = RTC_TYPE; 22 | return 0; 23 | 24 | case RTC_REG_DEV_VERSION: 25 | if (size != 4) return SL_ERR_IO_SIZE; 26 | *val = RTC_VERSION; 27 | return 0; 28 | 29 | default: 30 | break; 31 | } 32 | 33 | struct timeval tp; 34 | gettimeofday(&tp, NULL); 35 | u8 us = tp.tv_usec + (tp.tv_sec * 1000000); 36 | 37 | switch (addr) { 38 | case RTC_REG_MONOTONIC64: 39 | if (size != 8) return SL_ERR_IO_SIZE; 40 | *(u8 *)buf = us; 41 | return 0; 42 | 43 | case RTC_REG_MONOTONIC_LO: 44 | if (size != 4) return SL_ERR_IO_SIZE; 45 | *val = (u4)us; 46 | return 0; 47 | 48 | case RTC_REG_MONOTONIC_HI: 49 | if (size != 4) return SL_ERR_IO_SIZE; 50 | *val = (u4)(us >> 32); 51 | return 0; 52 | 53 | default: 54 | return SL_ERR_IO_INVALID; 55 | } 56 | } 57 | 58 | static int sled_rtc_create(sl_dev_t *d, sl_dev_config_t *cfg) { 59 | cfg->aperture = RTC_APERTURE_LENGTH; 60 | return 0; 61 | } 62 | 63 | static const sl_dev_ops_t rtc_ops = { 64 | .type = SL_DEV_SLED_RTC, 65 | .read = rtc_read, 66 | .create = sled_rtc_create, 67 | }; 68 | 69 | DECLARE_DEVICE(sled_rtc, SL_DEV_RTC, &rtc_ops); 70 | -------------------------------------------------------------------------------- /include/sled/arch.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // Top level architecture 13 | #define SL_ARCH_MIPS 0 14 | #define SL_ARCH_ARM 1 15 | #define SL_ARCH_RISCV 2 16 | #define SL_ARCH_NUM 3 17 | #define SL_ARCH_UNKNOWN 0xff 18 | 19 | // MIPS subarch 20 | #define SL_SUBARCH_MIPS 0 21 | #define SL_SUBARCH_MIPS64 1 22 | 23 | // ARM subarch 24 | #define SL_SUBARCH_ARM 0 25 | #define SL_SUBARCH_ARM64 1 26 | 27 | // RISCV subarch 28 | #define SL_SUBARCH_RV32 0 29 | #define SL_SUBARCH_RV64 1 30 | 31 | // RISCV extensions 32 | #define SL_RISCV_EXT_M (1u << 0) // multiply/divide 33 | #define SL_RISCV_EXT_A (1u << 1) // atomic 34 | #define SL_RISCV_EXT_F (1u << 2) // float 35 | #define SL_RISCV_EXT_D (1u << 3) // double 36 | #define SL_RISCV_EXT_Q (1u << 4) // quad 37 | #define SL_RISCV_EXT_L (1u << 5) // decimal float 38 | #define SL_RISCV_EXT_C (1u << 6) // compressed 39 | #define SL_RISCV_EXT_B (1u << 7) // bit manipulation 40 | #define SL_RISCV_EXT_J (1u << 8) // dynamically translated 41 | #define SL_RISCV_EXT_T (1u << 9) // transactional memory 42 | #define SL_RISCV_EXT_P (1u << 10) // packed simd 43 | #define SL_RISCV_EXT_V (1u << 11) // vector 44 | #define SL_RISCV_EXT_N (1u << 12) // user-level interrupts 45 | #define SL_RISCV_EXT_ZICSR (1u << 13) // csrs implemented, implicit in floating point 46 | 47 | 48 | const char *sl_arch_name(u1 arch); 49 | u4 sl_arch_reg_for_name(u1 arch, const char *name); 50 | u4 sl_arch_get_reg_count(u1 arch, u1 subarch, int type); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /core/inc/core/riscv/rv.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // #define RV_TRACE 1 12 | 13 | #define RV_MODE_RV32 0 14 | #define RV_MODE_RV64 1 15 | 16 | #define RV_OP_MRET RV_PL_MACHINE 17 | #define RV_OP_SRET RV_PL_SUPERVISOR 18 | 19 | #define RV_NAN_S 0x7fc00000 20 | // #define RV_NAN_D 21 | 22 | typedef struct { 23 | u8 scratch; 24 | u8 epc; 25 | u8 cause; 26 | u8 tval; 27 | u8 ip; 28 | u8 isa; 29 | u8 edeleg; 30 | u8 ideleg; 31 | u8 ie; 32 | u8 tvec; 33 | u8 counteren; 34 | } rv_sr_pl_t; 35 | 36 | typedef struct { 37 | result64_t (*csr_op)(rv_core_t *c, int op, u4 csr, u8 value); 38 | const char *(*name_for_sysreg)(rv_core_t *c, u2 num); 39 | void (*destroy)(void *ext_private); 40 | } rv_isa_extension_t; 41 | 42 | struct rv_core { 43 | sl_core_t core; 44 | 45 | u8 status; 46 | 47 | // system registers 48 | rv_sr_pl_t sr_pl[3]; 49 | u8 mvendorid; 50 | u8 marchid; 51 | u8 mimpid; 52 | u8 mhartid; 53 | u8 mconfigptr; 54 | 55 | u8 stap; 56 | 57 | // offsets for calculating these from running counters 58 | i8 mcycle_offset; 59 | i8 minstret_offset; 60 | 61 | u4 pmpcfg[16]; 62 | u8 pmpaddr[64]; 63 | u8 mhpmcounter[29]; 64 | u8 mhpevent[29]; // mhpevent3-mhpevent31 65 | 66 | rv_isa_extension_t ext; // isa extension 67 | void *ext_private; 68 | }; 69 | 70 | int rv_dispatch(rv_core_t *c, u4 instruction); 71 | int rv_exception_return(rv_core_t *c, u1 op); 72 | 73 | rv_sr_pl_t* rv_get_pl_csrs(rv_core_t *c, u1 pl); 74 | 75 | u1 rv_reg_index(u4 reg); 76 | const char *rv_name_for_reg(u4 reg); 77 | u4 rv_reg_for_name(const char *name); 78 | -------------------------------------------------------------------------------- /include/device/sled/mpu.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #define MPU_MAX_MAPPINGS 64 7 | 8 | #define MPU_REG_DEV_TYPE 0x0 // RO 9 | #define MPU_REG_DEV_VERSION 0x4 // RO 10 | #define MPU_REG_CONFIG 0x8 // RW 11 | #define MPU_REG_STATUS 0xc // RO 12 | #define MPU_REG_MAP_ENTS 0x10 // RO 13 | #define MPU_REG_MAP_VA_BASE_LO(i) (0x100 + (8 * i)) // RW 14 | #define MPU_REG_MAP_VA_BASE_HI(i) (0x104 + (8 * i)) // RW 15 | #define MPU_REG_MAP_PA_BASE_LO(i) (0x300 + (8 * i)) // RW 16 | #define MPU_REG_MAP_PA_BASE_HI(i) (0x304 + (8 * i)) // RW 17 | #define MPU_REG_MAP_LEN(i) (0x500 + (4 * i)) // RW 18 | 19 | #define MPU_APERTURE_LENGTH 0x600 20 | 21 | 22 | // MPU_REG_CONFIG 23 | 24 | // enable mapping using the last applied mapping values 25 | #define MPU_CONFIG_ENABLE (1u << 0) 26 | 27 | // Apply the current mapping registers for mapping. 28 | #define MPU_CONFIG_APPLY (1u << 1) 29 | 30 | // Clear all mapping registers. This does not affect applied mappings. 31 | // If CLEAR and APPLY are both set, current values in the map are applied and then 32 | // the mapping registers are cleared. 33 | #define MPU_CONFIG_CLEAR (1u << 2) 34 | 35 | // MPU_REG_STATUS 36 | 37 | // Indicates whether mapping is enabled. 38 | #define MPU_STATUS_ENABLED MPU_CONFIG_ENABLE 39 | 40 | 41 | // MPU_REG_MAP_ENTS 42 | // This register holds the maximum number of entries for each MPU_REG_MAP* register. 43 | // Currently equivalent to MPU_MAX_MAPPINGS 44 | 45 | 46 | // MPU_REG_MAP_VA_BASE 47 | // Virtual base address of region to be mapped 48 | 49 | // MPU_REG_MAP_PA_BASE 50 | // Corresponding physical base address to return 51 | 52 | // MPU_REG_MAP_LEN 53 | // Length in bytes of the mapped region. 54 | -------------------------------------------------------------------------------- /include/sled/irq.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define SL_IRQ_VEC_ALL 0xffffffff 9 | 10 | // interrupt mux 11 | // Interrupt multiplexer is a simple array of interrupt bits and a target interrupt endpoint. 12 | // When one or more bits are active, the multiplexer's output signal is set to high 13 | // Otherwise it is set to low. 14 | // One use for this is a device that can issue multiple interrupt events, all of which should 15 | // trigger the same interrupt request number on the system's interrupt controller. 16 | // Interrupt mux is present in the sl_dev_t structure for this purpose. 17 | 18 | void sl_irq_mux_set_active(sl_irq_mux_t *m, u4 vec); 19 | int sl_irq_mux_set_active_bit(sl_irq_mux_t *m, u4 index, bool high); 20 | u4 sl_irq_mux_get_active(sl_irq_mux_t *m); 21 | void sl_irq_mux_set_enabled(sl_irq_mux_t *m, u4 vec); 22 | u4 sl_irq_mux_get_enabled(sl_irq_mux_t *m); 23 | 24 | // irq endpoint 25 | // Interrupt request endpoint provides a framework for receiving interrupts. This is 26 | // intended for implementing interrupt controller devices. 27 | 28 | // input irq functions 29 | int sl_irq_endpoint_assert(sl_irq_ep_t *ep, u4 num, bool high); 30 | 31 | // user functions 32 | int sl_irq_endpoint_set_enabled(sl_irq_ep_t *ep, u4 vec); 33 | int sl_irq_endpoint_clear(sl_irq_ep_t *ep, u4 vec); 34 | u4 sl_irq_endpoint_get_enabled(sl_irq_ep_t *ep); 35 | u4 sl_irq_endpoint_get_asserted(sl_irq_ep_t *ep); 36 | u4 sl_irq_endpoint_get_active(sl_irq_ep_t *ep); 37 | 38 | // setup functions 39 | int sl_irq_ep_create(sl_irq_ep_t **ep_out); 40 | void sl_irq_ep_destroy(sl_irq_ep_t *ep); 41 | 42 | int sl_irq_mux_set_client(sl_irq_mux_t *m, sl_irq_ep_t *ep, u4 num); 43 | int sl_irq_endpoint_set_client(sl_irq_ep_t *ep, sl_irq_ep_t *client, u4 num); 44 | void sl_irq_endpoint_set_handler(sl_irq_ep_t *ep, int (*assert)(sl_irq_ep_t *ep, u4 num, bool high)); 45 | 46 | void sl_irq_endpoint_set_context(sl_irq_ep_t *ep, void *context); 47 | void * sl_irq_endpoint_get_context(sl_irq_ep_t *ep); 48 | 49 | -------------------------------------------------------------------------------- /include/sled/error.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define SL_OK 0 11 | #define SL_ERR -1 // generic error 12 | 13 | // user errors 14 | #define SL_ERR_ARG -2 // invalid argument 15 | #define SL_ERR_MEM -3 // no memory 16 | #define SL_ERR_UNSUPPORTED -4 // operation not supported 17 | #define SL_ERR_UNIMPLEMENTED -5 // operation not implemented 18 | #define SL_ERR_FULL -6 // queue is full 19 | #define SL_ERR_RANGE -7 // request out of range 20 | #define SL_ERR_STATE -8 // bad state 21 | #define SL_ERR_TIMEOUT -9 // timeout 22 | #define SL_ERR_BUSY -10 // busy 23 | #define SL_ERR_EXITED -11 // exited cleanly 24 | #define SL_ERR_NEXT -12 // go to next handler 25 | #define SL_ERR_NOT_FOUND -13 // entry not found 26 | #define SL_ERR_SYSTEM -14 // non-specific system error 27 | #define SL_ERR_RESTART -15 // restart operation 28 | 29 | // instruction execution errors 30 | #define SL_ERR_UNDEF -16 // undefined (illegal) instruction 31 | #define SL_ERR_ABORT -17 // instruction or data load/store failure 32 | #define SL_ERR_SYSCALL -18 // system call 33 | #define SL_ERR_BREAKPOINT -19 // breakpoint encountered 34 | 35 | // IO errors 36 | // #define SL_ERR_BUS -32 37 | #define SL_ERR_IO_NODEV -33 // device not found 38 | #define SL_ERR_IO_ALIGN -34 // invalid address alignment 39 | #define SL_ERR_IO_SIZE -35 // invalid io size 40 | #define SL_ERR_IO_COUNT -36 // invalid io count 41 | #define SL_ERR_IO_PERM -37 // no permission 42 | #define SL_ERR_IO_NOWR -38 // no write allowed 43 | #define SL_ERR_IO_NORD -39 // no read allowed 44 | #define SL_ERR_IO_INVALID -40 // invalid io operation 45 | #define SL_ERR_IO_NOMAP -41 // no valid mapping 46 | #define SL_ERR_IO_NOATOMIC -42 // atomic ops not supported 47 | #define SL_ERR_IO_NOCACHE -43 // caching not allowed (mmio) 48 | 49 | 50 | const char *st_err(int err); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /include/sled/io.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2025 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define IO_OP_IN 0 9 | #define IO_OP_OUT 1 10 | 11 | #define IO_OP_RESOLVE 2 12 | 13 | // Atomic Ops. These use arg[] instead of buf and order instead of count 14 | #define IO_OP_ATOMIC_SWAP 3 15 | #define IO_OP_ATOMIC_CAS 4 // compare and swap 16 | #define IO_OP_ATOMIC_ADD 5 17 | #define IO_OP_ATOMIC_SUB 6 18 | #define IO_OP_ATOMIC_AND 7 19 | #define IO_OP_ATOMIC_OR 8 20 | #define IO_OP_ATOMIC_XOR 9 21 | #define IO_OP_ATOMIC_SMAX 10 22 | #define IO_OP_ATOMIC_SMIN 11 23 | #define IO_OP_ATOMIC_UMAX 12 24 | #define IO_OP_ATOMIC_UMIN 13 25 | 26 | #define IO_IS_ATOMIC(op) (op >= IO_OP_ATOMIC_SWAP) 27 | 28 | typedef struct sl_io_op sl_io_op_t; 29 | typedef struct sl_io_port sl_io_port_t; 30 | 31 | // sl_io_op: an io operation 32 | // When called with op IO_OP_RESOLVE 33 | // 'size' and 'count' should be 1, 'buf' is unused 34 | // On successful return: 35 | // arg[0] will contain the host machine pointer to the data 36 | // arg[1] will contain the length of the data 37 | 38 | struct sl_io_op { 39 | u8 addr; // bus address of target data 40 | u2 size; // size of a single entry in bytes. should be power of 2. 41 | u1 op; // IO_OP 42 | u1 align; // enforce alignment - boolean value, always true for atomics 43 | union { 44 | u4 count; // number of 'size' entries - used for IN, OUT 45 | struct { 46 | u1 order; // memory order - used for all atomics 47 | u1 order_fail; // only used for IO_OP_ATOMIC_CAS 48 | }; 49 | }; 50 | union { 51 | void *buf; // io buffer, used for IN, OUT 52 | u8 arg[2]; // arg[0] used for all atomics, arg[1] for IO_OP_ATOMIC_CAS 53 | }; 54 | void *agent; // io source, used for permission checking and attribution 55 | }; 56 | 57 | int sl_io_for_data(void *data, sl_io_op_t *op); 58 | -------------------------------------------------------------------------------- /include/sled/types.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2025 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | // public types 15 | 16 | typedef int8_t i1; 17 | typedef int16_t i2; 18 | typedef int32_t i4; 19 | typedef int64_t i8; 20 | 21 | typedef uint8_t u1; 22 | typedef uint16_t u2; 23 | typedef uint32_t u4; 24 | typedef uint64_t u8; 25 | 26 | // typedef ssize_t ssize; 27 | typedef size_t usize; 28 | 29 | typedef intptr_t iptr; 30 | typedef uintptr_t uptr; 31 | 32 | typedef struct sl_bus sl_bus_t; 33 | typedef struct sl_core sl_core_t; 34 | typedef struct sl_core_params sl_core_params_t; 35 | typedef struct sl_dev sl_dev_t; 36 | typedef struct sl_dev_config sl_dev_config_t; 37 | typedef struct sl_dev_ops sl_dev_ops_t; 38 | typedef struct sl_elf_obj sl_elf_obj_t; 39 | typedef struct sl_engine sl_engine_t; 40 | typedef struct sl_engine_ops sl_engine_ops_t; 41 | typedef struct sl_event sl_event_t; 42 | typedef struct sl_event_ep sl_event_ep_t; 43 | typedef struct sl_io_op sl_io_op_t; 44 | typedef struct sl_irq_ep sl_irq_ep_t; 45 | typedef struct sl_irq_mux sl_irq_mux_t; 46 | typedef struct sl_list sl_list_t; 47 | typedef struct sl_list_node sl_list_node_t; 48 | typedef struct sl_list_iterator sl_list_iterator_t; 49 | typedef struct sl_machine sl_machine_t; 50 | typedef struct sl_map_ep sl_map_ep_t; 51 | typedef struct sl_mapper sl_mapper_t; 52 | typedef struct sl_mapping sl_mapping_t; 53 | typedef struct sl_reg_view sl_reg_view_t; 54 | typedef struct sl_sym_entry sl_sym_entry_t; 55 | typedef struct sl_sym_list sl_sym_list_t; 56 | typedef struct sl_chrono sl_chrono_t; 57 | typedef struct sl_worker sl_worker_t; 58 | 59 | typedef struct { 60 | u4 value; 61 | int err; 62 | } result32_t; 63 | 64 | typedef struct { 65 | u8 value; 66 | int err; 67 | } result64_t; 68 | 69 | typedef struct { 70 | void *value; 71 | int err; 72 | } resultptr_t; 73 | 74 | static inline result64_t result64_with_error(int err) { return (result64_t){ .err = err }; } 75 | static inline result64_t result64_with_value(u8 value) { return (result64_t){ .err = 0, .value = value }; } 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /include/sled/device.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // sled core devices 13 | #define SL_DEV_NONE 0 14 | #define SL_DEV_BUS 1 15 | #define SL_DEV_REG_VIEW 2 16 | 17 | // sled devices 18 | #define SL_DEV_SLED_UART 128 19 | #define SL_DEV_SLED_RTC 129 20 | #define SL_DEV_SLED_INTC 130 21 | #define SL_DEV_SLED_MPU 131 22 | #define SL_DEV_SLED_TIMER 132 23 | 24 | // user-defined devices 25 | #define SL_DEV_RESERVED 1024 26 | 27 | #define DECLARE_DEVICE(_name, _type, _ops) \ 28 | const void * const _sl_device_dyn_ops_##_name = _ops; 29 | 30 | struct sl_dev_config { 31 | const sl_dev_ops_t *ops; 32 | const char *name; 33 | u4 aperture; 34 | 35 | sl_machine_t *machine; 36 | // todo: 37 | // power domains 38 | // clock domains 39 | // interrupt ep? 40 | // device-specific config 41 | }; 42 | 43 | struct sl_dev_ops { 44 | u4 type; 45 | // mmio read transaction 46 | int (*read)(void *ctx, u8 addr, u4 size, u4 count, void *buf); 47 | // mmio write transaction 48 | int (*write)(void *ctx, u8 addr, u4 size, u4 count, void *buf); 49 | // create driver-specific context 50 | int (*create)(sl_dev_t *d, sl_dev_config_t *cfg); 51 | // destroy driver-specific context 52 | void (*destroy)(sl_dev_t *d); 53 | }; 54 | 55 | // allocate and call ops->create() 56 | int sl_device_create(sl_dev_config_t *cfg, sl_dev_t **dev_out); 57 | // free device and call ops->destroy() 58 | void sl_device_destroy(sl_dev_t *d); 59 | 60 | 61 | void sl_device_set_context(sl_dev_t *d, void *ctx); 62 | void * sl_device_get_context(sl_dev_t *d); 63 | 64 | void sl_device_lock(sl_dev_t *d); 65 | void sl_device_unlock(sl_dev_t *d); 66 | 67 | void sl_device_set_worker(sl_dev_t *d, sl_worker_t *w, u4 epid); 68 | 69 | sl_irq_mux_t * sl_device_get_irq_mux(sl_dev_t *d); 70 | 71 | sl_mapper_t * sl_device_get_mapper(sl_dev_t *d); 72 | void sl_device_set_mapper(sl_dev_t *d, sl_mapper_t *m); 73 | 74 | // async device ops 75 | 76 | int sl_device_send_event_async(sl_dev_t *d, sl_event_t *ev); 77 | int sl_device_update_mapper_async(sl_dev_t *d, u4 ops, u4 count, sl_mapping_t *ent_list); 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | -------------------------------------------------------------------------------- /include/sled/mapper.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | /* 9 | A mapper is an abstract address translation device. 10 | 11 | Examples of translators: 12 | Bus - map access from bus address to the target device 13 | MPU - memory protection unit performs static region mapping and protection 14 | MMU - memory manager unit performs dynamic page mapping and protection 15 | 16 | The core mapper is implemented in sled-core and used by the generic bus. 17 | Other implementations exist as device drivers, called map drivers. 18 | 19 | A mapper instance operates in the context of the mapping client, typically 20 | the associated CPU core or a chained mapper. Map drivers operate in device context. 21 | Device context may or may not be the same as the mapping client. To synchronize 22 | changes from the map driver to the mapper, the mapping client's async command queue 23 | must be used. The map driver must not block the mapping client from completing its work. 24 | */ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #define SL_MAP_EV_TYPE_UPDATE 0x1000 31 | 32 | #define SL_MAP_OP_MODE_BLOCK (0u) 33 | #define SL_MAP_OP_MODE_PASSTHROUGH (1u) 34 | #define SL_MAP_OP_MODE_TRANSLATE (2u) 35 | #define SL_MAP_OP_MODE_MASK (3u) 36 | 37 | #define SL_MAP_TYPE_UNKNOWN 0 38 | #define SL_MAP_TYPE_MEMORY 1 39 | #define SL_MAP_TYPE_DEVICE 2 40 | #define SL_MAP_TYPE_MAPPER 3 41 | 42 | #define SL_MAP_OP_REPLACE (1u << 2) 43 | 44 | struct sl_mapping { 45 | u8 input_base; 46 | u8 length; 47 | u8 output_base; 48 | u4 domain; 49 | u2 permissions; 50 | u1 type; 51 | sl_map_ep_t *ep; 52 | }; 53 | 54 | struct sl_map_ep { 55 | int (*io)(sl_map_ep_t *ep, sl_io_op_t *op); 56 | }; 57 | 58 | // setup 59 | 60 | int sl_mapper_create(sl_mapper_t **map_out); 61 | void sl_mapper_destroy(sl_mapper_t *m); 62 | 63 | void sl_mapper_set_mode(sl_mapper_t *m, int mode); 64 | int sl_mappper_add_mapping(sl_mapper_t *m, sl_mapping_t *ent); 65 | int sl_mapper_io(void *ctx, sl_io_op_t *op); 66 | sl_mapper_t * sl_mapper_get_next(sl_mapper_t *m); 67 | sl_map_ep_t * sl_mapper_get_ep(sl_mapper_t *m); 68 | 69 | resultptr_t sl_mapper_resolve(sl_mapper_t *m, u8 addr, u8 *len_out); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: '0' 3 | AlignAfterOpenBracket: DontAlign 4 | AlignConsecutiveMacros: 'true' 5 | AlignConsecutiveAssignments: 'false' 6 | AlignConsecutiveDeclarations: 'false' 7 | AlignEscapedNewlines: DontAlign 8 | AlignOperands: 'true' 9 | AlignTrailingComments: 'true' 10 | AllowAllArgumentsOnNextLine: 'false' 11 | AllowAllConstructorInitializersOnNextLine: 'true' 12 | AllowAllParametersOfDeclarationOnNextLine: 'false' 13 | AllowShortBlocksOnASingleLine: Empty 14 | AllowShortCaseLabelsOnASingleLine: 'true' 15 | AllowShortFunctionsOnASingleLine: Inline 16 | AllowShortIfStatementsOnASingleLine: AllIfsAndElse 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortLoopsOnASingleLine: 'true' 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: 'false' 21 | AlwaysBreakTemplateDeclarations: MultiLine 22 | BinPackArguments: 'true' 23 | BinPackParameters: 'true' 24 | BreakBeforeBinaryOperators: None 25 | BreakBeforeBraces: Attach 26 | BreakBeforeTernaryOperators: 'true' 27 | BreakConstructorInitializers: AfterColon 28 | BreakInheritanceList: AfterColon 29 | BreakStringLiterals: 'false' 30 | ColumnLimit: '120' 31 | CompactNamespaces: 'true' 32 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 33 | ContinuationIndentWidth: '4' 34 | Cpp11BracedListStyle: 'true' 35 | DerivePointerAlignment: 'false' 36 | DisableFormat: 'false' 37 | ExperimentalAutoDetectBinPacking: 'false' 38 | FixNamespaceComments: 'true' 39 | IncludeBlocks: Preserve 40 | IndentCaseLabels: 'false' 41 | IndentPPDirectives: None 42 | IndentWidth: '4' 43 | IndentWrappedFunctionNames: 'false' 44 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 45 | Language: Cpp 46 | NamespaceIndentation: None 47 | PointerAlignment: Right 48 | ReflowComments: 'true' 49 | SortIncludes: 'true' 50 | SpaceAfterCStyleCast: 'false' 51 | SpaceAfterLogicalNot: 'false' 52 | SpaceAfterTemplateKeyword: 'false' 53 | SpaceBeforeAssignmentOperators: 'true' 54 | SpaceBeforeCpp11BracedList: 'false' 55 | SpaceBeforeCtorInitializerColon: 'false' 56 | SpaceBeforeInheritanceColon: 'false' 57 | SpaceBeforeParens: ControlStatements 58 | SpaceBeforeRangeBasedForLoopColon: 'false' 59 | SpaceInEmptyParentheses: 'false' 60 | SpacesInAngles: 'false' 61 | SpacesInCStyleCastParentheses: 'false' 62 | SpacesInContainerLiterals: 'false' 63 | SpacesInParentheses: 'false' 64 | SpacesInSquareBrackets: 'false' 65 | Standard: Cpp11 66 | TabWidth: '4' 67 | UseTab: Never 68 | 69 | ... 70 | -------------------------------------------------------------------------------- /core/ex.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int sl_core_synchronous_exception(sl_core_t *c, u8 ex, u8 value, u4 status) { 12 | u4 inst; 13 | 14 | // todo: check if nested exception 15 | 16 | switch (ex) { 17 | case EX_SYSCALL: 18 | if (c->options & SL_CORE_OPT_TRAP_SYSCALL) return SL_ERR_SYSCALL; 19 | return c->exception_enter(c, ex, value); 20 | 21 | case EX_UNDEFINDED: 22 | if (c->options & SL_CORE_OPT_TRAP_UNDEF) { 23 | inst = (u4)value; 24 | printf("UNDEFINED instruction %08x at pc=%" PRIx64 "\n", inst, c->pc); 25 | sl_core_dump_state(c); 26 | return SL_ERR_UNDEF; 27 | } else { 28 | return c->exception_enter(c, ex, value); 29 | } 30 | 31 | case EX_ABORT_LOAD: 32 | if (c->options & SL_CORE_OPT_TRAP_ABORT) { 33 | printf("LOAD FAULT (rd) at addr=%" PRIx64 ", pc=%" PRIx64 ", err=%s\n", value, c->pc, st_err(status)); 34 | sl_core_dump_state(c); 35 | return status; 36 | } else { 37 | if (status == SL_ERR_IO_ALIGN) ex = EX_ABORT_LOAD_ALIGN; 38 | return c->exception_enter(c, ex, value); 39 | } 40 | 41 | case EX_ABORT_STORE: 42 | if (c->options & SL_CORE_OPT_TRAP_ABORT) { 43 | printf("STORE FAULT at addr=%" PRIx64 ", pc=%" PRIx64 ", err=%s\n", value, c->pc, st_err(status)); 44 | sl_core_dump_state(c); 45 | return status; 46 | } else { 47 | if (status == SL_ERR_IO_ALIGN) ex = EX_ABORT_STORE_ALIGN; 48 | return c->exception_enter(c, ex, value); 49 | } 50 | 51 | case EX_ABORT_INST: 52 | if (c->options & SL_CORE_OPT_TRAP_PREFETCH_ABORT) { 53 | printf("PREFETCH FAULT at addr=%" PRIx64 ", pc=%" PRIx64 ", err=%s\n", value, c->pc, st_err(status)); 54 | sl_core_dump_state(c); 55 | return status; 56 | } else { 57 | if (status == SL_ERR_IO_ALIGN) ex = EX_ABORT_INST_ALIGN; 58 | return c->exception_enter(c, ex, value); 59 | } 60 | 61 | default: 62 | printf("\nUNHANDLED EXCEPTION type %" PRIx64 "\n", ex); 63 | return SL_ERR_UNIMPLEMENTED; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/cache.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2024-2025 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #define DEFAULT_CACHE_SHIFT 12 // 4096 12 | 13 | static inline u1 base_hash(u8 base) { 14 | return base & (SL_CACHE_ENTS - 1); 15 | } 16 | 17 | int sl_cache_rw_single(sl_cache_t *c, u8 addr, usize size, void *buf, bool read) { 18 | const u1 shift = c->page_shift; 19 | const u8 base = addr >> shift; 20 | const u8 offset = addr - (base << shift); 21 | 22 | // is current page? 23 | const u1 hash = base_hash(base); 24 | if (base == c->page[hash].base) { 25 | c->hash_hit++; 26 | goto current_page; 27 | } 28 | 29 | c->miss_addr = addr; 30 | c->hash_miss++; 31 | return SL_ERR_NOT_FOUND; 32 | 33 | current_page: 34 | if (read) 35 | memcpy(buf, c->page[hash].buf + offset, size); 36 | else 37 | memcpy(c->page[hash].buf + offset, buf, size); 38 | return 0; 39 | } 40 | 41 | static int cache_rw(sl_cache_t *c, u8 addr, usize size, void *buf, bool read) { 42 | const u8 pg_size = (1u << c->page_shift); 43 | usize remain = pg_size - (addr & (pg_size - 1)); 44 | if (remain >= size) 45 | return sl_cache_rw_single(c, addr, size, buf, read); 46 | 47 | while (size) { 48 | int err = sl_cache_rw_single(c, addr, remain, buf, read); 49 | if (err) 50 | return err; 51 | addr += remain; 52 | size -= remain; 53 | buf += remain; 54 | remain = pg_size; 55 | if (size < pg_size) 56 | remain = size; 57 | } 58 | return 0; 59 | } 60 | 61 | int sl_cache_read(sl_cache_t *c, u8 addr, usize size, void *buf) { 62 | return cache_rw(c, addr, size, buf, true); 63 | } 64 | 65 | int sl_cache_write(sl_cache_t *c, u8 addr, usize size, void *buf) { 66 | return cache_rw(c, addr, size, buf, false); 67 | } 68 | 69 | void sl_cache_set_page(sl_cache_t *c, u8 addr, void *buf) { 70 | const u1 shift = c->page_shift; 71 | const u8 base = addr >> shift; 72 | const u1 hash = base_hash(base); 73 | c->page[hash].base = base; 74 | c->page[hash].buf = buf; 75 | } 76 | 77 | void sl_cache_init(sl_cache_t *c) { 78 | c->page_shift = DEFAULT_CACHE_SHIFT; 79 | for (int i = 0; i < SL_CACHE_ENTS; i++) 80 | c->page[i].base = ~((u8)0); 81 | c->hash_hit = 0; 82 | c->hash_miss = 0; 83 | } 84 | 85 | void sl_cache_shutdown(sl_cache_t *c) {} 86 | -------------------------------------------------------------------------------- /core/inc/core/riscv/trace.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #if RV_TRACE 7 | 8 | #define RV_TRACE_BRANCH_ABSOLUTE 1 9 | 10 | #include 11 | 12 | #define RV_TRACE_DECL_OPSTR const char *opstr 13 | #define RV_TRACE_OPSTR(s) do { opstr = s; } while (0) 14 | #define RV_TRACE_RD(c, r, v) do { c->core.trace->rd = r; c->core.trace->rd_value = v; } while (0) 15 | #define RV_TRACE_RDF(c, r, v) \ 16 | do { \ 17 | c->core.trace->rd = r; c->core.trace->f_value = v; \ 18 | c->core.trace->options |= ITRACE_OPT_FLOAT; \ 19 | } while (0) 20 | #define RV_TRACE_RDD(c, r, v) \ 21 | do { \ 22 | c->core.trace->rd = r; c->core.trace->d_value = v; \ 23 | c->core.trace->options |= ITRACE_OPT_DOUBLE; \ 24 | } while (0) 25 | 26 | #define RV_TRACE_STORE(c, a, r, v) \ 27 | do { \ 28 | c->core.trace->options |= ITRACE_OPT_INST_STORE; \ 29 | c->core.trace->addr = a; c->core.trace->rd = r; c->core.trace->rd_value = v; \ 30 | } while (0) 31 | 32 | #define RV_TRACE_STORE_F(c, a, r, v) \ 33 | do { \ 34 | c->core.trace->options |= ITRACE_OPT_INST_STORE | ITRACE_OPT_FLOAT; \ 35 | c->core.trace->addr = a; c->core.trace->rd = r; c->core.trace->f_value = v; \ 36 | } while (0) 37 | 38 | #define RV_TRACE_STORE_D(c, a, r, v) \ 39 | do { \ 40 | c->core.trace->options |= ITRACE_OPT_INST_STORE | ITRACE_OPT_DOUBLE; \ 41 | c->core.trace->addr = a; c->core.trace->rd = r; c->core.trace->d_value = v; \ 42 | } while (0) 43 | 44 | #define RV_TRACE_OPT(c, o) do { c->core.trace->options |= o; } while (0) 45 | #define RV_TRACE_PRINT(c, ...) \ 46 | do { \ 47 | itrace_t *_tr = c->core.trace; \ 48 | int _len = snprintf(_tr->opstr + _tr->cur, TRACE_STR_LEN - _tr->cur, __VA_ARGS__); \ 49 | _tr->cur += _len; \ 50 | } while (0) 51 | 52 | #if RV_TRACE_BRANCH_ABSOLUTE 53 | #define RV_BR_TARGET(dest, imm) (dest) 54 | #else 55 | #define RV_BR_TARGET(dest, imm) (imm) 56 | #endif // RV_TRACE_BRANCH_ABSOLUTE 57 | 58 | #else 59 | #define RV_TRACE_DECL_OPSTR do {} while (0) 60 | #define RV_TRACE_OPSTR(s) do {} while (0) 61 | #define RV_TRACE_RD(c, r, v) do {} while (0) 62 | #define RV_TRACE_RDF(c, r, v) do {} while (0) 63 | #define RV_TRACE_RDD(c, r, v) do {} while (0) 64 | #define RV_TRACE_STORE(c, a, r, v) do {} while (0) 65 | #define RV_TRACE_STORE_F(c, a, r, v) do {} while (0) 66 | #define RV_TRACE_STORE_D(c, a, r, v) do {} while (0) 67 | #define RV_TRACE_OPT(c, o) do {} while (0) 68 | #define RV_TRACE_PRINT(rc, format, ...) do {} while (0) 69 | #endif 70 | -------------------------------------------------------------------------------- /core/arch.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct { 14 | u2 value; 15 | const char *name; 16 | } reg_name_t; 17 | 18 | static const char * arch_name_map[] = { 19 | [SL_ARCH_MIPS] = "mips", 20 | [SL_ARCH_ARM] = "arm", 21 | [SL_ARCH_RISCV] = "riscv", 22 | }; 23 | 24 | static const reg_name_t reg_common[] = { 25 | { SL_CORE_REG_PC, "pc" }, 26 | { SL_CORE_REG_SP, "sp" }, 27 | { SL_CORE_REG_LR, "lr" }, 28 | }; 29 | 30 | static const arch_ops_t arch_ops[] = { 31 | [SL_ARCH_MIPS] = { }, 32 | [SL_ARCH_ARM] = { }, 33 | [SL_ARCH_RISCV] = { 34 | .reg_index = rv_reg_index, 35 | .reg_for_name = rv_reg_for_name, 36 | .name_for_reg = rv_name_for_reg, 37 | }, 38 | }; 39 | 40 | typedef struct { 41 | u1 arch; 42 | u1 subarch; 43 | const char *arch_name; 44 | const char *subarch_name; 45 | u4 int_reg_count; 46 | } arch_info_t; 47 | 48 | static const arch_info_t arch_info[] = { 49 | { 50 | .arch = SL_ARCH_RISCV, 51 | .subarch = SL_SUBARCH_RV32, 52 | .subarch_name = "rv32", 53 | .int_reg_count = 32, 54 | }, 55 | { 56 | .arch = SL_ARCH_RISCV, 57 | .subarch = SL_SUBARCH_RV64, 58 | .subarch_name = "rv64", 59 | .int_reg_count = 32, 60 | }, 61 | }; 62 | 63 | const char * sl_arch_name(u1 arch) { 64 | if (arch > SL_ARCH_NUM) return NULL; 65 | return arch_name_map[arch]; 66 | } 67 | 68 | const arch_ops_t * arch_get_ops(u1 arch) { 69 | if (arch > SL_ARCH_NUM) return NULL; 70 | return &arch_ops[arch]; 71 | } 72 | 73 | 74 | u4 sl_arch_get_reg_count(u1 arch, u1 subarch, int type) { 75 | if (type != SL_CORE_REG_TYPE_INT) return 0; 76 | 77 | for (int i = 0; i < countof(arch_info); i++) { 78 | const arch_info_t *a = &arch_info[i]; 79 | if ((a->arch == arch) && (a->subarch == subarch)) { 80 | return a->int_reg_count; 81 | } 82 | } 83 | return 0; 84 | } 85 | 86 | u4 sl_arch_reg_for_name(u1 arch, const char *name) { 87 | for (int i = 0; i < countof(reg_common); i++) { 88 | const reg_name_t *r = ®_common[i]; 89 | if (!strcmp(name, r->name)) return r->value; 90 | } 91 | 92 | const arch_ops_t *ops = &arch_ops[arch]; 93 | if (ops->reg_for_name != NULL) 94 | return ops->reg_for_name(name); 95 | 96 | return SL_CORE_REG_INVALID; 97 | } 98 | -------------------------------------------------------------------------------- /include/device/sled/timer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | // Sled timer 7 | 8 | // This device contains a set of asynchronous countdown timers. 9 | // Each timer can run independently in either one shot or continuous modes. 10 | 11 | // Timers are in host wall-clock microseconds. A scaler register is available 12 | // to adjust the ratio of real time to simulated time. 13 | 14 | #define TIMER_REG_DEV_TYPE 0x0 // RO 15 | #define TIMER_REG_DEV_VERSION 0x4 // RO 16 | #define TIMER_REG_CONFIG 0x8 // RW 17 | #define TIMER_REG_STATUS 0xc // RO 18 | // IRQ mask bitfield for each timer unit 19 | // Writing 1 masks interrupt. 0 enables interrupt to proceed. 20 | // Default value: all masked 21 | #define TIMER_IRQ_MASK 0x10 // RW 22 | // IRQ status bitfield for each timer unit. Writing 1 clears a set bit, 0 retains it. 23 | #define TIMER_IRQ_STATUS 0x14 // RW 24 | 25 | // scale the frequency of timers by multiple of real world clock 26 | #define TIMER_REG_RT_SCALER_US 0x18 // RW 27 | 28 | // number individual timer units 29 | #define TIMER_REG_NUM_UNITS 0x1c // RO 30 | 31 | // Configuration register for timer i 32 | #define TIMER_REG_UNIT_CONFIG(i) (0x20 + (0x20 * i)) 33 | 34 | // Initial starting value of timer, in microseconds 35 | #define TIMER_REG_UNIT_RESET_VAL_LO(i) (0x24 + (0x20 * i)) 36 | #define TIMER_REG_UNIT_RESET_VAL_HI(i) (0x28 + (0x20 * i)) 37 | 38 | // Current value of timer, in microseconds 39 | #define TIMER_REG_UNIT_CURRENT_VAL_LO(i) (0x2c + (0x20 * i)) 40 | #define TIMER_REG_UNIT_CURRENT_VAL_HI(i) (0x30 + (0x20 * i)) 41 | 42 | // Start timer. 43 | // Setting this bit starts the timer. If the timer was already 44 | // running, no effect is observed. 45 | // Clearing this bit stops the timer. 46 | #define TIMER_UNIT_CONFIG_RUN (1u << 0) 47 | 48 | // Continuous timer. 49 | // If set to 1, the timer will restart immediately when it reaches zero. 50 | // This mode guarantees no timer drift. 51 | // If set to 0, the timer will stop and the run bit will be cleared when 52 | // zero is reached (one shot mode). 53 | // Interrupts will be generated every time zero is reached, whether one 54 | // shot or continuous. 55 | // The looped bit will be set each time zero is reached. 56 | #define TIMER_UNIT_CONFIG_CONTINUOUS (1u << 1) 57 | 58 | // Timer loop/end indicator. 59 | // Set to 1 when timer has expired or looped. 60 | // Cleared when a timer is started. 61 | // Write 1 to clear, 0 to retain current value. 62 | #define TIMER_UNIT_CONFIG_LOOPED (1u << 2) 63 | 64 | 65 | #define TIMER_APERTURE_LENGTH(num) (0x20 * (num * 0x20)) 66 | -------------------------------------------------------------------------------- /core/sem.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #if __APPLE__ 8 | 9 | int sl_sem_init(sl_sem_t *sem, unsigned int value) { 10 | sem->dsem = dispatch_semaphore_create(value); 11 | if (sem->dsem == NULL) return SL_ERR_MEM; 12 | return 0; 13 | } 14 | 15 | int sl_sem_post(sl_sem_t *sem) { 16 | dispatch_semaphore_signal(sem->dsem); 17 | return 0; 18 | } 19 | 20 | int sl_sem_wait(sl_sem_t *sem) { 21 | dispatch_semaphore_wait(sem->dsem, DISPATCH_TIME_FOREVER); 22 | return 0; 23 | } 24 | 25 | int sl_sem_trywait(sl_sem_t *sem) { 26 | if (dispatch_semaphore_wait(sem->dsem, DISPATCH_TIME_NOW) < 0) 27 | return SL_ERR_BUSY; 28 | return 0; 29 | } 30 | 31 | int sl_sem_timedwait(sl_sem_t *restrict sem, const struct timespec *restrict abs_timeout) { 32 | dispatch_time_t dt = dispatch_walltime(abs_timeout, 0); 33 | if (dispatch_semaphore_wait(sem->dsem, dt) < 0) 34 | return SL_ERR_TIMEOUT; 35 | return 0; 36 | } 37 | 38 | int sl_sem_destroy(sl_sem_t *sem) { 39 | dispatch_release(sem->dsem); 40 | return 0; 41 | } 42 | 43 | #else 44 | 45 | #include 46 | 47 | int sl_sem_init(sl_sem_t *sem, unsigned int value) { 48 | if (sem_init(&sem->psem, 0, value)) return SL_ERR_ARG; // errno = EINVAL 49 | return 0; 50 | } 51 | 52 | int sl_sem_post(sl_sem_t *sem) { 53 | if (sem_post(&sem->psem)) { 54 | if (errno == EINVAL) return SL_ERR_ARG; 55 | if (errno == EOVERFLOW) return SL_ERR_FULL; 56 | return SL_ERR; 57 | } 58 | return 0; 59 | } 60 | 61 | int sl_sem_wait(sl_sem_t *sem) { 62 | retry: 63 | if (sem_wait(&sem->psem)) { 64 | if (errno == EINTR) goto retry; 65 | if (errno == EINVAL) return SL_ERR_ARG; 66 | return SL_ERR; 67 | } 68 | return 0; 69 | } 70 | 71 | int sl_sem_trywait(sl_sem_t *sem) { 72 | retry: 73 | if (sem_trywait(&sem->psem)) { 74 | switch (errno) { 75 | case EINTR: goto retry; 76 | case EINVAL: return SL_ERR_ARG; 77 | case EAGAIN: return SL_ERR_BUSY; 78 | default: return SL_ERR; 79 | } 80 | } 81 | return 0; 82 | } 83 | 84 | int sl_sem_timedwait(sl_sem_t *restrict sem, const struct timespec *restrict abs_timeout) { 85 | retry: 86 | if (sem_timedwait(&sem->psem, abs_timeout)) { 87 | switch (errno) { 88 | case EINTR: goto retry; 89 | case EINVAL: return SL_ERR_ARG; 90 | case ETIMEDOUT: return SL_ERR_TIMEOUT; 91 | default: return SL_ERR; 92 | } 93 | } 94 | return 0; 95 | } 96 | 97 | int sl_sem_destroy(sl_sem_t *sem) { 98 | if (sem_destroy(&sem->psem)) { 99 | if (errno == EINVAL) return SL_ERR_ARG; 100 | return SL_ERR; 101 | } 102 | return 0; 103 | } 104 | 105 | #endif // __APPLE__ 106 | 107 | -------------------------------------------------------------------------------- /include/sled/worker.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // Worker is a container that executes one or more engines, as well as handling async events for them. 13 | // A worker is intended as a context for a workloop thread. 14 | // The worker calls the engine's step() function repeatedly as long as that engine is runnable. 15 | // In between steps it checks for events. If no engines are runnable, the thread blocks until more events 16 | // are enqueued. 17 | 18 | 19 | // ---------------------------------------------------------------------------- 20 | // Synchronous functions 21 | // ---------------------------------------------------------------------------- 22 | 23 | // Synchronous functions must only be called when the work loop is not running. 24 | 25 | int sl_worker_create(const char *name, sl_worker_t **w_out); 26 | void sl_worker_destroy(sl_worker_t *w); 27 | 28 | int sl_worker_add_engine(sl_worker_t *w, sl_engine_t *e, u4 *id_out); 29 | 30 | // Add a event handler that will receive events in the context of the work queue thread. 31 | // This is guaranteed to be between engine steps. 32 | int sl_worker_add_event_endpoint(sl_worker_t *w, sl_event_ep_t *ep, u4 *id_out); 33 | 34 | // Handle events incoming to the worker. 35 | // May block if wait-for-event status is enabled. 36 | int sl_worker_handle_events(sl_worker_t *w); 37 | 38 | // Runs 'num' iterations of the work loop on current thread 39 | int sl_worker_step(sl_worker_t *w, u8 num); 40 | 41 | // Run work loop on current thread 42 | // This will not return until the worker has exited. 43 | int sl_worker_run(sl_worker_t *w); 44 | 45 | // Run work loop on a new thread. This call returns immediately. 46 | int sl_worker_thread_run(sl_worker_t *w); 47 | 48 | // Sets the current state of the engine to runnable. Runnable engines are stepped by the work loop. 49 | // Note: special use case: 50 | // This function is safe to call from the context of the work loop thread. For example, an engine may 51 | // set its run state in response to an event. 52 | void sl_worker_set_engine_runnable(sl_worker_t *w, bool runnable); 53 | 54 | 55 | // ---------------------------------------------------------------------------- 56 | // Synchronizing functions 57 | // ---------------------------------------------------------------------------- 58 | 59 | // Synchronizing functions must only be called when the workloop thread is running. 60 | 61 | // Join the thread created by sl_worker_thread_run(). 62 | // This call does not command the work loop to exit. The worker must be notified that it needs 63 | // to exit via an async event if it does not exit on its own. 64 | int sl_worker_thread_join(sl_worker_t *w); 65 | 66 | 67 | // ---------------------------------------------------------------------------- 68 | // Async functions 69 | // ---------------------------------------------------------------------------- 70 | 71 | // Send an aynchronous event to the work queue. The call returns after the event has been enqueued. 72 | // The event will only be processed if the work loop thread is running. 73 | int sl_worker_event_enqueue_async(sl_worker_t *w, sl_event_t *ev); 74 | 75 | 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /core/riscv/rvex.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | rv_sr_pl_t* rv_get_pl_csrs(rv_core_t *c, u1 pl) { 14 | assert(c->core.el != 0); 15 | return &c->sr_pl[pl - 1]; 16 | } 17 | 18 | int riscv_core_exception_enter(sl_core_t *core, u8 cause, u8 addr) { 19 | rv_core_t *c = (rv_core_t *)core; 20 | 21 | // todo: check medeleg / mideleg for priv level dispatching 22 | 23 | u4 rv_fault = 0; 24 | switch ((u4)cause) { 25 | case EX_SYSCALL: 26 | rv_fault = RV_EX_CALL_FROM_U + c->core.el; 27 | break; 28 | case EX_UNDEFINDED: rv_fault = RV_EX_INST_ILLEGAL; break; 29 | case EX_ABORT_LOAD: rv_fault = RV_EX_LOAD_FAULT; break; 30 | case EX_ABORT_LOAD_ALIGN: rv_fault = RV_EX_LOAD_ALIGN; break; 31 | case EX_ABORT_STORE: rv_fault = RV_EX_STORE_FAULT; break; 32 | case EX_ABORT_STORE_ALIGN: rv_fault = RV_EX_STORE_ALIGN; break; 33 | case EX_ABORT_INST: rv_fault = RV_EX_INST_FAULT; break; 34 | case EX_ABORT_INST_ALIGN: rv_fault = RV_EX_INST_ALIGN; break; 35 | default: 36 | assert(false); 37 | return SL_ERR_UNIMPLEMENTED; 38 | } 39 | cause = (cause & RV_CAUSE64_INT) | rv_fault; 40 | 41 | rv_sr_pl_t *r = rv_get_pl_csrs(c, SL_CORE_EL_MONITOR); 42 | r->cause = cause; 43 | r->epc = c->core.pc; 44 | r->tval = addr; 45 | 46 | // update status register 47 | csr_status_t s; 48 | s.raw = c->status; 49 | s.m_mpie = s.m_mie; // previous interrupt state 50 | s.m_mie = 0; // disable interrupts 51 | s.m_mpp = c->core.el; // previous priv level 52 | c->status = s.raw; 53 | 54 | c->core.el = SL_CORE_EL_MONITOR; // todo: fix me 55 | 56 | u8 tvec = r->tvec; 57 | if (cause & RV_CAUSE64_INT) { 58 | const u8 ci = cause & ~RV_CAUSE64_INT; 59 | // m->ip = 1ull << ci; 60 | if (tvec & 1) tvec += (ci << 2); 61 | } 62 | c->core.pc = tvec; 63 | c->core.branch_taken = true; 64 | sl_core_interrupt_set(&c->core, false); 65 | return 0; 66 | } 67 | 68 | int rv_exception_return(rv_core_t *c, u1 op) { 69 | bool int_enabled = true; 70 | u1 dest_pl; 71 | csr_status_t s; 72 | s.raw = c->status; 73 | 74 | if (op == RV_OP_MRET) { 75 | // mret 76 | dest_pl = s.m_mpp; 77 | s.m_mie = s.m_mpie; 78 | s.m_mpie = 1; 79 | s.m_mpp = 0; 80 | int_enabled = s.m_mie; 81 | } else if (op == RV_OP_SRET) { 82 | if (s.m_tsr) return SL_ERR_UNDEF; // trap sret 83 | dest_pl = s.spp; 84 | s.sie = s.spie; 85 | s.spp = 0; 86 | int_enabled = s.sie; 87 | } else { 88 | return SL_ERR_UNIMPLEMENTED; 89 | } 90 | c->status = s.raw; 91 | 92 | rv_sr_pl_t *r = rv_get_pl_csrs(c, c->core.el); 93 | c->core.pc = r->epc; 94 | c->core.el = dest_pl; 95 | c->core.branch_taken = true; 96 | 97 | sl_core_interrupt_set(&c->core, int_enabled); 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /include/sled/riscv.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | // Standard RISCV defines 7 | 8 | // ------------------------------------------------------------------ 9 | // registers 10 | // ------------------------------------------------------------------ 11 | 12 | #define RV_ZERO 0 // zero 13 | #define RV_RA 1 // return address 14 | #define RV_SP 2 // stack pointer 15 | #define RV_GP 3 // global pointer 16 | #define RV_TP 4 // thread pointer 17 | #define RV_T0 5 // Temporary/alternate link register 18 | #define RV_T1 6 // Temporary 19 | #define RV_T2 7 // Temporary 20 | #define RV_S0 8 // Saved register 21 | #define RV_FP RV_S0 // Frame pointer 22 | #define RV_S1 9 // Saved register 23 | #define RV_A0 10 // Function arguments/return values 24 | #define RV_A1 11 // Function arguments/return values 25 | #define RV_A2 12 // Function argument 26 | #define RV_A3 13 // Function argument 27 | #define RV_A4 14 // Function argument 28 | #define RV_A5 15 // Function argument 29 | #define RV_A6 16 // Function argument 30 | #define RV_A7 17 // Function argument 31 | #define RV_S2 18 // Saved register 32 | #define RV_S3 19 // Saved register 33 | #define RV_S4 20 // Saved register 34 | #define RV_S5 21 // Saved register 35 | #define RV_S6 22 // Saved register 36 | #define RV_S7 23 // Saved register 37 | #define RV_S8 24 // Saved register 38 | #define RV_S9 25 // Saved register 39 | #define RV_S10 26 // Saved register 40 | #define RV_S11 27 // Saved register 41 | #define RV_T3 28 // Temporary 42 | #define RV_T4 29 // Temporary 43 | #define RV_T5 30 // Temporary 44 | #define RV_T6 31 // Temporary 45 | 46 | // ------------------------------------------------------------------ 47 | // privilege levels 48 | // ------------------------------------------------------------------ 49 | #define RV_PL_USER 0 50 | #define RV_PL_SUPERVISOR 1 51 | #define RV_PL_HYPERVISOR 2 52 | #define RV_PL_MACHINE 3 53 | 54 | // ------------------------------------------------------------------ 55 | // exceptions 56 | // ------------------------------------------------------------------ 57 | // High bit is set if cause is interrupt 58 | #define RV_CAUSE64_INT (1ul << 63) 59 | #define RV_CAUSE32_INT (1ul << 31) 60 | 61 | // cause when interrupt bit is set 62 | #define RV_INT_SW_S 1 // software interrupt from supervisor mode 63 | #define RV_INT_SW_M 3 // software interrupt from machine mode 64 | #define RV_INT_TIMER_S 5 // timer interrupt from supervisor mode 65 | #define RV_INT_TIMER_M 7 // timer interrupt from machine mode 66 | #define RV_INT_EXTERNAL_S 9 // external interrupt from supervisor mode 67 | #define RV_INT_EXTERNAL_M 11 // external interrupt from machine mode 68 | 69 | // cause when interrupt bit is not set 70 | #define RV_EX_INST_ALIGN 0 71 | #define RV_EX_INST_FAULT 1 72 | #define RV_EX_INST_ILLEGAL 2 73 | #define RV_EX_BREAK 3 74 | #define RV_EX_LOAD_ALIGN 4 75 | #define RV_EX_LOAD_FAULT 5 76 | #define RV_EX_STORE_ALIGN 6 77 | #define RV_EX_STORE_FAULT 7 78 | #define RV_EX_CALL_FROM_U 8 79 | #define RV_EX_CALL_FROM_S 9 80 | #define RV_EX_CALL_FROM_H 10 81 | #define RV_EX_CALL_FROM_M 11 82 | #define RV_EX_INST_PAGE_FAULT 12 83 | #define RV_EX_LOAD_PAGE_FAULT 13 84 | #define RV_EX_STORE_PAGE_FAULT 15 85 | -------------------------------------------------------------------------------- /core/bus.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | // #define BUS_TRACE 1 16 | 17 | #ifdef BUS_TRACE 18 | #define TRACE(...) printf(__VA_ARGS__) 19 | #else 20 | #define TRACE(format, ...) do {} while(0) 21 | #endif 22 | 23 | static int bus_op_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 24 | sl_bus_t *b = ctx; 25 | 26 | sl_io_op_t op = {}; 27 | op.addr = addr; 28 | op.count = count; 29 | op.size = size; 30 | op.op = IO_OP_IN; 31 | op.align = 0; 32 | op.buf = buf; 33 | op.agent = b; 34 | return sl_mapper_io(&b->mapper, &op); 35 | } 36 | 37 | static int bus_op_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 38 | sl_bus_t *b = ctx; 39 | 40 | TRACE("[[ bus: %#" PRIx64 "(%u@%u) ]]\n", addr, count, size); 41 | 42 | sl_io_op_t op = {}; 43 | op.addr = addr; 44 | op.count = count; 45 | op.size = size; 46 | op.op = IO_OP_OUT; 47 | op.align = 0; 48 | op.buf = buf; 49 | op.agent = b; 50 | return sl_mapper_io(&b->mapper, &op); 51 | } 52 | 53 | int bus_add_mem_region(sl_bus_t *b, mem_region_t *r) { 54 | sl_mapping_t m = {}; 55 | m.input_base = r->base; 56 | m.length = r->length; 57 | m.output_base = 0; 58 | m.type = SL_MAP_TYPE_MEMORY; 59 | m.ep = &r->ep; 60 | int err = sl_mappper_add_mapping(&b->mapper, &m); 61 | if (err) return err; 62 | sl_list_add_last(&b->mem_list, &r->node); 63 | return 0; 64 | } 65 | 66 | int bus_add_device(sl_bus_t *b, sl_dev_t *dev, u8 base) { 67 | dev->base = base; 68 | sl_mapping_t m = {}; 69 | m.input_base = dev->base; 70 | m.length = dev->aperture; 71 | m.output_base = 0; 72 | m.type = SL_MAP_TYPE_DEVICE; 73 | m.ep = &dev->map_ep; 74 | return sl_mappper_add_mapping(&b->mapper, &m); 75 | } 76 | 77 | sl_mapper_t * bus_get_mapper(sl_bus_t *b) { return &b->mapper; } 78 | 79 | static const sl_dev_ops_t bus_ops = { 80 | .type = SL_DEV_BUS, 81 | .read = bus_op_read, 82 | .write = bus_op_write, 83 | }; 84 | 85 | int sl_bus_init(sl_bus_t *b, const char *name, sl_dev_config_t *cfg) { 86 | cfg->name = name; 87 | int err = sl_device_init(&b->dev, cfg); 88 | if (err) return err; 89 | mapper_init(&b->mapper); 90 | sl_mapper_set_mode(&b->mapper, SL_MAP_OP_MODE_TRANSLATE); 91 | sl_device_set_context(&b->dev, b); 92 | sl_device_set_mapper(&b->dev, &b->mapper); 93 | sl_list_init(&b->mem_list); 94 | return 0; 95 | } 96 | 97 | int sl_bus_create(const char *name, sl_dev_config_t *cfg, sl_bus_t **bus_out) { 98 | sl_bus_t *b = calloc(1, sizeof(*b)); 99 | if (b == NULL) return SL_ERR_MEM; 100 | cfg->ops = &bus_ops; 101 | int err = sl_bus_init(b, name, cfg); 102 | if (err) { 103 | free(b); 104 | return err; 105 | } 106 | *bus_out = b; 107 | return 0; 108 | } 109 | 110 | void sl_bus_shutdown(sl_bus_t *b) { 111 | mapper_shutdown(&b->mapper); 112 | sl_list_node_t *c; 113 | while ((c = sl_list_remove_first(&b->mem_list)) != NULL) 114 | mem_region_destroy((mem_region_t *)c); 115 | sl_device_shutdown(&b->dev); 116 | } 117 | 118 | void sl_bus_destroy(sl_bus_t *b) { 119 | if (b == NULL) return; 120 | sl_bus_shutdown(b); 121 | free(b); 122 | } 123 | -------------------------------------------------------------------------------- /core/io.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define ATOMIC_CAS(d, expected, desired, o, of) atomic_compare_exchange_strong_explicit(d, &expected, desired, o, of) 12 | 13 | // always perform the exchange even when it is seemingly pointless to synchronize the observable 14 | // value with the memory barriers 15 | 16 | #define ATOMIC_OP(op, data, utype, stype) { \ 17 | const utype v = op->arg[0]; \ 18 | utype e = op->arg[1]; \ 19 | _Atomic utype *d = data; \ 20 | _Atomic stype *sd = data; \ 21 | utype result; \ 22 | switch (op->op) { \ 23 | case IO_OP_ATOMIC_SWAP: result = atomic_exchange_explicit(d, v, op->order); break; \ 24 | case IO_OP_ATOMIC_CAS: result = (ATOMIC_CAS(d, e, v, op->order, op->order_fail) ? 0 : 1); break; \ 25 | case IO_OP_ATOMIC_ADD: result = atomic_fetch_add_explicit(d, v, op->order); break; \ 26 | case IO_OP_ATOMIC_SUB: result = atomic_fetch_sub_explicit(d, v, op->order); break; \ 27 | case IO_OP_ATOMIC_AND: result = atomic_fetch_and_explicit(d, v, op->order); break; \ 28 | case IO_OP_ATOMIC_OR: result = atomic_fetch_or_explicit(d, v, op->order); break; \ 29 | case IO_OP_ATOMIC_XOR: result = atomic_fetch_xor_explicit(d, v, op->order); break; \ 30 | case IO_OP_ATOMIC_SMAX: \ 31 | for (bool success = false; !success; ) { \ 32 | stype cur = *(stype *)data; \ 33 | const stype max = MAX((stype)v, cur); \ 34 | success = ATOMIC_CAS((_Atomic stype *)data, cur, max, op->order, memory_order_relaxed); \ 35 | result = max; \ 36 | } \ 37 | break; \ 38 | case IO_OP_ATOMIC_SMIN: \ 39 | for (bool success = false; !success; ) { \ 40 | stype cur = *(stype *)data; \ 41 | const stype min = MIN((stype)v, cur); \ 42 | success = ATOMIC_CAS(sd, cur, min, op->order, memory_order_relaxed); \ 43 | result = min; \ 44 | } \ 45 | break; \ 46 | case IO_OP_ATOMIC_UMAX: \ 47 | for (bool success = false; !success; ) { \ 48 | utype cur = *(utype *)data; \ 49 | const utype max = MAX(v, cur); \ 50 | success = ATOMIC_CAS(d, cur, max, op->order, memory_order_relaxed); \ 51 | result = max; \ 52 | } \ 53 | break; \ 54 | case IO_OP_ATOMIC_UMIN: \ 55 | for (bool success = false; !success; ) { \ 56 | utype cur = *(utype *)data; \ 57 | const utype min = MIN(v, cur); \ 58 | success = ATOMIC_CAS(d, cur, min, op->order, memory_order_relaxed); \ 59 | result = min; \ 60 | } \ 61 | break; \ 62 | default: \ 63 | return SL_ERR_IO_INVALID; \ 64 | } \ 65 | op->arg[0] = result; } 66 | 67 | 68 | int sl_io_for_data(void *data, sl_io_op_t *op) { 69 | if (op->op == IO_OP_IN) { 70 | memcpy(op->buf, data, op->count * op->size); 71 | return 0; 72 | } 73 | if (op->op == IO_OP_OUT) { 74 | memcpy(data, op->buf, op->count * op->size); 75 | return 0; 76 | } 77 | 78 | if ((uptr)data & (sizeof(op->size) - 1)) return SL_ERR_IO_ALIGN; 79 | 80 | switch (op->size) { 81 | case 1: ATOMIC_OP(op, data, u1, i1); break; 82 | case 2: ATOMIC_OP(op, data, u2, i2); break; 83 | case 4: ATOMIC_OP(op, data, u4, i4); break; 84 | case 8: ATOMIC_OP(op, data, u8, i8); break; 85 | default: 86 | return SL_ERR_IO_SIZE; 87 | } 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /core/irq.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | u4 sl_irq_endpoint_get_enabled(sl_irq_ep_t *ep) { return ep->mux.enabled; } 12 | u4 sl_irq_endpoint_get_asserted(sl_irq_ep_t *ep) { return ep->retained; } 13 | u4 sl_irq_endpoint_get_active(sl_irq_ep_t *ep) { return ep->mux.active; } 14 | void * sl_irq_endpoint_get_context(sl_irq_ep_t *ep) { return ep->context; } 15 | void sl_irq_endpoint_set_context(sl_irq_ep_t *ep, void *context) { ep->context = context; } 16 | void sl_irq_endpoint_set_handler(sl_irq_ep_t *ep, int (*assert)(sl_irq_ep_t *ep, u4 num, bool high)) { ep->assert = assert; } 17 | 18 | u4 sl_irq_mux_get_active(sl_irq_mux_t *m) { return m->active; } 19 | u4 sl_irq_mux_get_enabled(sl_irq_mux_t *m) { return m->enabled; } 20 | 21 | void sl_irq_mux_set_active(sl_irq_mux_t *o, u4 vec) { 22 | const bool was_high = (o->active & o->enabled) > 0; 23 | o->active = vec; 24 | if (o->client != NULL) { 25 | const bool is_high = (vec & o->enabled) > 0; 26 | if (is_high != was_high) 27 | o->client->assert(o->client, o->client_num, is_high); 28 | } 29 | } 30 | 31 | int sl_irq_mux_set_active_bit(sl_irq_mux_t *o, u4 index, bool high) { 32 | if (index > 31) return SL_ERR_ARG; 33 | const u4 bit = 1u << index; 34 | u4 act = o->active; 35 | if (high) act |= bit; 36 | else act &= ~bit; 37 | if (act != o->active) sl_irq_mux_set_active(o, act); 38 | return 0; 39 | } 40 | 41 | void sl_irq_mux_set_enabled(sl_irq_mux_t *m, u4 vec) { 42 | m->enabled = vec; 43 | sl_irq_mux_set_active(m, m->active); 44 | } 45 | 46 | static inline void irq_endpoint_set_active(sl_irq_ep_t *ep) { 47 | sl_irq_mux_set_active(&ep->mux, ep->retained & ep->mux.enabled); 48 | } 49 | 50 | int sl_irq_endpoint_assert(sl_irq_ep_t *ep, u4 num, bool high) { 51 | if (num > 31) return SL_ERR_ARG; 52 | const u4 bit = 1u << num; 53 | if (high) { 54 | if (ep->asserted & bit) return 0; 55 | ep->asserted |= bit; 56 | ep->retained |= bit; 57 | } else { 58 | if ((ep->asserted & bit) == 0) return 0; 59 | ep->asserted &= ~bit; 60 | } 61 | irq_endpoint_set_active(ep); 62 | return 0; 63 | } 64 | 65 | int sl_irq_endpoint_set_enabled(sl_irq_ep_t *ep, u4 vec) { 66 | ep->mux.enabled = vec; 67 | irq_endpoint_set_active(ep); 68 | return 0; 69 | } 70 | 71 | int sl_irq_endpoint_clear(sl_irq_ep_t *ep, u4 vec) { 72 | ep->retained &= ~vec; // clear set bits 73 | ep->retained |= ep->asserted; // asserted irqs can't be cleared 74 | irq_endpoint_set_active(ep); 75 | return 0; 76 | } 77 | 78 | int sl_irq_mux_set_client(sl_irq_mux_t *m, sl_irq_ep_t *ep, u4 num) { 79 | if (num > 31) return SL_ERR_ARG; 80 | m->client = ep; 81 | m->client_num = num; 82 | return 0; 83 | } 84 | 85 | int sl_irq_endpoint_set_client(sl_irq_ep_t *ep, sl_irq_ep_t *client, u4 num) { 86 | if (num > 31) return SL_ERR_ARG; 87 | ep->mux.client = client; 88 | ep->mux.client_num = num; 89 | irq_endpoint_set_active(ep); 90 | return 0; 91 | } 92 | 93 | int sl_irq_ep_init(sl_irq_ep_t *ep) { 94 | memset(ep, 0, sizeof(*ep)); 95 | return 0; 96 | } 97 | 98 | int sl_irq_ep_create(sl_irq_ep_t **ep_out) { 99 | sl_irq_ep_t *ep = calloc(1, sizeof(*ep)); 100 | if (ep == NULL) return SL_ERR_MEM; 101 | *ep_out = ep; 102 | return 0; 103 | } 104 | 105 | void sl_irq_ep_shutdown(sl_irq_ep_t *ep) {} // nop 106 | 107 | void sl_irq_ep_destroy(sl_irq_ep_t *ep) { 108 | if (ep == NULL) return; 109 | free(ep); 110 | } 111 | 112 | 113 | -------------------------------------------------------------------------------- /core/list.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | 6 | #include 7 | 8 | void sl_list_init(sl_list_t *list) { 9 | list->first = NULL; 10 | list->last = NULL; 11 | } 12 | 13 | void sl_list_add_last(sl_list_t *list, sl_list_node_t *n) { 14 | n->next = NULL; 15 | if (list->last == NULL) list->first = n; 16 | else list->last->next = n; 17 | list->last = n; 18 | } 19 | 20 | void sl_list_add_first(sl_list_t *list, sl_list_node_t *n) { 21 | n->next = list->first; 22 | list->first = n; 23 | if (list->last == NULL) list->last = n; 24 | } 25 | 26 | sl_list_node_t * sl_list_remove_first(sl_list_t *list) { 27 | sl_list_node_t *n = list->first; 28 | if (n == NULL) return NULL; 29 | list->first = n->next; 30 | if (n->next == NULL) list->last = NULL; 31 | n->next = NULL; 32 | return n; 33 | } 34 | 35 | sl_list_node_t * sl_list_remove_all(sl_list_t *list) { 36 | sl_list_node_t *n = list->first; 37 | list->first = NULL; 38 | list->last = NULL; 39 | return n; 40 | } 41 | 42 | void sl_list_insert_sorted(sl_list_t *list, int(*compare)(const void *, const void *), sl_list_node_t *n) { 43 | if (list->first == NULL) { 44 | sl_list_add_first(list, n); 45 | return; 46 | } 47 | 48 | if (compare(n, list->first) <= 0) { 49 | n->next = list->first; 50 | list->first = n; 51 | return; 52 | } 53 | 54 | sl_list_node_t *cur = list->first->next; 55 | sl_list_node_t *prev = list->first; 56 | for ( ; cur != NULL; cur = cur->next) { 57 | if (compare(n, cur) <= 0) { 58 | n->next = cur; 59 | prev->next = n; 60 | return; 61 | } 62 | prev = cur; 63 | } 64 | sl_list_add_first(list, n); 65 | } 66 | 67 | void sl_list_remove_node(sl_list_t *list, sl_list_node_t *n, sl_list_node_t *prev) { 68 | if (list->first == NULL) return; 69 | sl_list_node_t *next; 70 | if (prev == NULL) { 71 | assert(n == list->first); 72 | next = list->first->next; 73 | list->first = next; 74 | } else { 75 | next = n->next; 76 | prev->next = next; 77 | } 78 | if (next == NULL) list->last = prev; 79 | n->next = NULL; 80 | } 81 | 82 | int sl_list_find_and_remove(sl_list_t *list, sl_list_node_t *n) { 83 | sl_list_node_t *prev = NULL; 84 | 85 | for (sl_list_node_t *cur = list->first; cur != NULL; cur = cur->next) { 86 | if (cur == n) { 87 | if (prev == NULL) list->first = n->next; 88 | else prev->next = n->next; 89 | if (n->next == NULL) list->last = prev; 90 | return 0; 91 | } 92 | prev = cur; 93 | } 94 | return -1; 95 | } 96 | 97 | void sl_list_interator_begin(sl_list_iterator_t *iter, sl_list_t *list) { 98 | iter->list = list; 99 | iter->current = list->first; 100 | iter->previous = NULL; 101 | } 102 | 103 | sl_list_node_t * sl_list_interator_next(sl_list_iterator_t *iter) { 104 | if (iter->current == NULL) return NULL; 105 | iter->previous = iter->current; 106 | iter->current = iter->current->next; 107 | return iter->current; 108 | } 109 | 110 | sl_list_node_t * sl_list_iterator_get_current(sl_list_iterator_t *iter) { 111 | return iter->current; 112 | } 113 | 114 | void sl_list_iterator_remove_current(sl_list_iterator_t *iter) { 115 | if (iter->current == NULL) return; 116 | if (iter->previous == NULL) { 117 | sl_list_remove_first(iter->list); 118 | iter->current = iter->list->first; 119 | return; 120 | } 121 | sl_list_node_t *next = iter->current->next; 122 | iter->current = next; 123 | iter->previous->next = next; 124 | if (next == NULL) iter->list->last = iter->previous; 125 | } 126 | -------------------------------------------------------------------------------- /dev/sled/intc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define INTC_TYPE 'intc' 14 | #define INTC_VERSION 0 15 | 16 | #define INTC_NUM_SUPPORTED 32 17 | 18 | typedef struct { 19 | sl_dev_t *dev; 20 | sl_irq_ep_t *irq_ep; 21 | } sled_intc_t; 22 | 23 | sl_irq_ep_t * sled_intc_get_irq_ep(sl_dev_t *d) { 24 | sled_intc_t *ic = sl_device_get_context(d); 25 | return ic->irq_ep; 26 | } 27 | 28 | int sled_intc_set_input(sl_dev_t *intc, sl_dev_t *src, u4 num) { 29 | if (num >= INTC_NUM_SUPPORTED) return SL_ERR_RANGE; 30 | sled_intc_t *ic = sl_device_get_context(intc); 31 | return sl_irq_mux_set_client(sl_device_get_irq_mux(src), ic->irq_ep, num); 32 | } 33 | 34 | static int intc_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 35 | if (size != 4) return SL_ERR_IO_SIZE; 36 | if (count != 1) return SL_ERR_IO_COUNT; 37 | 38 | u4 *val = buf; 39 | int err = 0; 40 | 41 | sled_intc_t *ic = ctx; 42 | sl_device_lock(ic->dev); 43 | switch (addr) { 44 | case INTC_REG_DEV_TYPE: *val = INTC_TYPE; break; 45 | case INTC_REG_DEV_VERSION: *val = INTC_VERSION; break; 46 | case INTC_REG_ASSERTED: *val = sl_irq_endpoint_get_asserted(ic->irq_ep); break; 47 | case INTC_REG_MASK: *val = ~sl_irq_endpoint_get_enabled(ic->irq_ep); break; 48 | default: err = SL_ERR_IO_INVALID; break; 49 | } 50 | sl_device_unlock(ic->dev); 51 | return err; 52 | } 53 | 54 | static int intc_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 55 | if (size != 4) return SL_ERR_IO_SIZE; 56 | if (count != 1) return SL_ERR_IO_COUNT; 57 | 58 | u4 val = *(u4 *)buf; 59 | int err = 0; 60 | 61 | sled_intc_t *ic = ctx; 62 | sl_device_lock(ic->dev); 63 | switch (addr) { 64 | case INTC_REG_DEV_TYPE: err = SL_ERR_IO_NOWR; break; 65 | case INTC_REG_DEV_VERSION: err = SL_ERR_IO_NOWR; break; 66 | case INTC_REG_ASSERTED: 67 | err = sl_irq_endpoint_clear(ic->irq_ep, val); 68 | break; 69 | 70 | case INTC_REG_MASK: 71 | err = sl_irq_endpoint_set_enabled(ic->irq_ep, ~val); 72 | break; 73 | 74 | default: err = SL_ERR_IO_INVALID; break; 75 | } 76 | sl_device_unlock(ic->dev); 77 | return err; 78 | } 79 | 80 | static int sled_intc_assert(sl_irq_ep_t *ep, u4 num, bool high) { 81 | if (num >= INTC_NUM_SUPPORTED) return SL_ERR_ARG; 82 | sled_intc_t *ic = sl_irq_endpoint_get_context(ep); 83 | 84 | sl_device_lock(ic->dev); 85 | int err = sl_irq_endpoint_assert(ep, num, high); 86 | sl_device_unlock(ic->dev); 87 | return err; 88 | } 89 | 90 | static void sled_intc_destroy(sl_dev_t *d) { 91 | sled_intc_t *ic = sl_device_get_context(d); 92 | sl_irq_ep_destroy(ic->irq_ep); 93 | free(ic); 94 | } 95 | 96 | static int sled_intc_create(sl_dev_t *d, sl_dev_config_t *cfg) { 97 | sled_intc_t *ic = calloc(1, sizeof(*ic)); 98 | if (ic == NULL) return SL_ERR_MEM; 99 | ic->dev = d; 100 | cfg->aperture = INTC_APERTURE_LENGTH; 101 | sl_device_set_context(ic->dev, ic); 102 | 103 | int err; 104 | if ((err = sl_irq_ep_create(&ic->irq_ep))) { 105 | free(ic); 106 | return err; 107 | } 108 | sl_irq_endpoint_set_context(ic->irq_ep, ic); 109 | sl_irq_endpoint_set_handler(ic->irq_ep, sled_intc_assert); 110 | return 0; 111 | } 112 | 113 | static const sl_dev_ops_t intc_ops = { 114 | .type = SL_DEV_SLED_INTC, 115 | .read = intc_read, 116 | .write = intc_write, 117 | .create = sled_intc_create, 118 | .destroy = sled_intc_destroy, 119 | }; 120 | 121 | DECLARE_DEVICE(sled_intc, SL_DEV_INTC, &intc_ops); 122 | -------------------------------------------------------------------------------- /dev/sled/uart.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define UART_TYPE 'rxtx' 14 | #define UART_VERSION 0 15 | 16 | #define BUFLEN 255 17 | 18 | typedef struct { 19 | sl_dev_t *dev; 20 | int io_type; 21 | int fd_in; 22 | int fd_out; 23 | 24 | // registers 25 | u4 config; 26 | u4 status; 27 | u4 buf_pos; 28 | 29 | u1 buf[BUFLEN + 1]; 30 | } sled_uart_t; 31 | 32 | static void uart_flush(sled_uart_t *u) { 33 | u->buf[u->buf_pos] = '\0'; 34 | if (u->fd_out >= 0) dprintf(u->fd_out, "%s", u->buf); 35 | u->buf_pos = 0; 36 | } 37 | 38 | static int uart_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 39 | if (size != 4) return SL_ERR_IO_SIZE; 40 | if (count != 1) return SL_ERR_IO_COUNT; 41 | 42 | sled_uart_t *u = ctx; 43 | u4 *val = buf; 44 | int err = 0; 45 | 46 | sl_device_lock(u->dev); 47 | switch (addr) { 48 | case UART_REG_DEV_TYPE: *val = UART_TYPE; break; 49 | case UART_REG_DEV_VERSION: *val = UART_VERSION; break; 50 | case UART_REG_CONFIG: *val = u->config; break; 51 | case UART_REG_STATUS: *val = u->status; break; 52 | case UART_REG_FIFO_READ: *val = 0; break; 53 | case UART_REG_FIFO_WRITE: err = SL_ERR_IO_NORD; break; 54 | default: err = SL_ERR_IO_INVALID; break; 55 | } 56 | sl_device_unlock(u->dev); 57 | return err; 58 | } 59 | 60 | static int uart_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 61 | if (size != 4) return SL_ERR_IO_SIZE; 62 | if (count != 1) return SL_ERR_IO_COUNT; 63 | 64 | sled_uart_t *u = ctx; 65 | u4 val = *(u4 *)buf; 66 | u1 c; 67 | int err = 0; 68 | 69 | sl_device_lock(u->dev); 70 | switch (addr) { 71 | case UART_REG_CONFIG: u->config = val; break; 72 | 73 | case UART_REG_FIFO_WRITE: 74 | c = (u1)val; 75 | u->buf[u->buf_pos++] = c; 76 | if ((c == '\n') || (u->buf_pos == BUFLEN)) uart_flush(u); 77 | break; 78 | 79 | case UART_REG_DEV_TYPE: 80 | case UART_REG_DEV_VERSION: 81 | case UART_REG_STATUS: 82 | case UART_REG_FIFO_READ: 83 | err = SL_ERR_IO_NOWR; 84 | break; 85 | 86 | default: err = SL_ERR_IO_INVALID; break; 87 | } 88 | sl_device_unlock(u->dev); 89 | return err; 90 | } 91 | 92 | int sled_uart_set_channel(sl_dev_t *dev, int io, int fd_in, int fd_out) { 93 | sled_uart_t *u = sl_device_get_context(dev); 94 | switch (io) { 95 | case UART_IO_NULL: 96 | u->fd_in = u->fd_out = -1; 97 | break; 98 | 99 | case UART_IO_FILE: 100 | u->fd_in = -1; 101 | u->fd_out = fd_out; 102 | break; 103 | 104 | case UART_IO_CONS: 105 | case UART_IO_PORT: 106 | u->fd_in = fd_in; 107 | u->fd_out = fd_out; 108 | break; 109 | 110 | default: 111 | return -1; 112 | } 113 | u->io_type = io; 114 | return 0; 115 | } 116 | 117 | static void sled_uart_destroy(sl_dev_t *d) { 118 | sled_uart_t *u = sl_device_get_context(d); 119 | if (u->buf_pos > 0) uart_flush(u); 120 | free(u); 121 | } 122 | 123 | static int sled_uart_create(sl_dev_t *d, sl_dev_config_t *cfg) { 124 | sled_uart_t *u = calloc(1, sizeof(sled_uart_t)); 125 | if (u == NULL) return SL_ERR_MEM; 126 | u->dev = d; 127 | sl_device_set_context(d, u); 128 | cfg->aperture = UART_APERTURE_LENGTH; 129 | u->fd_in = STDIN_FILENO; 130 | u->fd_out = STDOUT_FILENO; 131 | return 0; 132 | } 133 | 134 | static const sl_dev_ops_t uart_ops = { 135 | .type = SL_DEV_SLED_UART, 136 | .read = uart_read, 137 | .write = uart_write, 138 | .create = sled_uart_create, 139 | .destroy = sled_uart_destroy, 140 | }; 141 | 142 | DECLARE_DEVICE(sled_uart, SL_DEV_UART, &uart_ops); 143 | -------------------------------------------------------------------------------- /core/device.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define DEV_MAGIC 0x91919192 16 | 17 | int device_mapper_ep_io(sl_map_ep_t *ep, sl_io_op_t *op) { 18 | sl_dev_t *d = containerof(ep, sl_dev_t, map_ep); 19 | switch (op->op) { 20 | case IO_OP_IN: 21 | if (d->ops->read == NULL) 22 | return SL_ERR_IO_NORD; 23 | return d->ops->read(d->context, op->addr, op->size, op->count, op->buf); 24 | case IO_OP_OUT: 25 | if (d->ops->write == NULL) 26 | return SL_ERR_IO_NOWR; 27 | return d->ops->write(d->context, op->addr, op->size, op->count, op->buf); 28 | case IO_OP_RESOLVE: 29 | return SL_ERR_IO_NOCACHE; 30 | default: 31 | return SL_ERR_IO_NOATOMIC; 32 | } 33 | } 34 | 35 | void sl_device_set_context(sl_dev_t *d, void *ctx) { d->context = ctx; } 36 | void * sl_device_get_context(sl_dev_t *d) { return d->context; } 37 | 38 | void sl_device_lock(sl_dev_t *d) { sl_lock_lock(&d->lock); } 39 | void sl_device_unlock(sl_dev_t *d) { sl_lock_unlock(&d->lock); } 40 | sl_irq_mux_t * sl_device_get_irq_mux(sl_dev_t *d) { return &d->irq_mux; } 41 | sl_mapper_t * sl_device_get_mapper(sl_dev_t *d) { return d->mapper; } 42 | void sl_device_set_mapper(sl_dev_t *d, sl_mapper_t *m) { d->mapper = m; } 43 | 44 | sl_dev_t * device_get_for_ep(sl_map_ep_t *ep) { 45 | return containerof(ep, sl_dev_t, map_ep); 46 | } 47 | 48 | void sl_device_set_worker(sl_dev_t *d, sl_worker_t *w, u4 epid) { 49 | d->worker = w; 50 | d->worker_epid = epid; 51 | } 52 | 53 | int sl_device_send_event_async(sl_dev_t *d, sl_event_t *ev) { 54 | if (d->worker == NULL) return SL_ERR_UNSUPPORTED; 55 | return sl_worker_event_enqueue_async(d->worker, ev); 56 | } 57 | 58 | static int device_ep_handle_event(sl_event_ep_t *ep, sl_event_t *ev) { 59 | if (ev->type != SL_MAP_EV_TYPE_UPDATE) { 60 | ev->err = SL_ERR_ARG; 61 | return 0; 62 | } 63 | sl_dev_t *d = containerof(ep, sl_dev_t, event_ep); 64 | return mapper_update(d->mapper, ev); 65 | } 66 | 67 | int sl_device_update_mapper_async(sl_dev_t *d, u4 ops, u4 count, sl_mapping_t *ent_list) { 68 | if (d->worker == NULL) return SL_ERR_UNSUPPORTED; 69 | if (d->mapper == NULL) return SL_ERR_UNSUPPORTED; 70 | 71 | sl_event_t *ev = calloc(1, sizeof(*ev)); 72 | if (ev == NULL) return SL_ERR_MEM; 73 | 74 | ev->epid = d->worker_epid; 75 | ev->flags = SL_EV_FLAG_FREE; 76 | ev->type = SL_MAP_EV_TYPE_UPDATE; 77 | ev->arg[0] = ops; 78 | ev->arg[1] = count; 79 | ev->arg[2] = (uintptr_t)ent_list; 80 | int err = sl_worker_event_enqueue_async(d->worker, ev); 81 | if (err) free(ev); 82 | return err; 83 | } 84 | 85 | int sl_device_init(sl_dev_t *d, sl_dev_config_t *cfg) { 86 | d->magic = DEV_MAGIC; 87 | d->ops = cfg->ops; 88 | d->name = cfg->name; 89 | d->aperture = cfg->aperture; 90 | d->map_ep.io = device_mapper_ep_io; 91 | d->event_ep.handle = device_ep_handle_event; 92 | sl_lock_init(&d->lock); 93 | return 0; 94 | } 95 | 96 | int sl_device_create(sl_dev_config_t *cfg, sl_dev_t **dev_out) { 97 | sl_dev_t *d = calloc(1, sizeof(*d)); 98 | if (d == NULL) return SL_ERR_MEM; 99 | int err = sl_device_init(d, cfg); 100 | if (err) { 101 | free(d); 102 | return err; 103 | } 104 | if (d->ops->create != NULL) { 105 | err = d->ops->create(d, cfg); 106 | if (err) { 107 | sl_device_destroy(d); 108 | return err; 109 | } 110 | } 111 | d->aperture = cfg->aperture; 112 | *dev_out = d; 113 | return 0; 114 | } 115 | 116 | void sl_device_shutdown(sl_dev_t *d) { 117 | assert(d->magic == DEV_MAGIC); 118 | if (d->ops->destroy != NULL) d->ops->destroy(d); 119 | sl_lock_destroy(&d->lock); 120 | } 121 | 122 | void sl_device_destroy(sl_dev_t *d) { 123 | if (d == NULL) return; 124 | assert(d->magic == DEV_MAGIC); 125 | sl_device_shutdown(d); 126 | free(d); 127 | } 128 | -------------------------------------------------------------------------------- /core/inc/core/riscv/csr.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #define RV_CSR_OP_READ 0b000 10 | #define RV_CSR_OP_SWAP 0b001 // CSRRW/CSRRWI 11 | #define RV_CSR_OP_READ_SET 0b010 // CSRRS/CSRRSI 12 | #define RV_CSR_OP_READ_CLEAR 0b011 // CSRRC/CSRRCI 13 | #define RV_CSR_OP_WRITE 0b100 14 | 15 | // offsets in rv_sr_trap_t 16 | // These match the CSR num field (lower 4 bits) 17 | #define RV_SR_SCRATCH 0 18 | #define RV_SR_EPC 1 19 | #define RV_SR_CAUSE 2 20 | #define RV_SR_TVAL 3 21 | #define RV_SR_IP 4 22 | // These don't match the num field. They are machine mode only 23 | #define RV_SR_TINST 5 24 | #define RV_SR_TVAL2 6 25 | 26 | // offsets in sr_minfo + 1 27 | // These match the CSR num field 28 | #define RV_SR_MVENDORID 1 29 | #define RV_SR_MARCHID 2 30 | #define RV_SR_MIMPID 3 31 | #define RV_SR_HARTID 4 32 | #define RV_SR_MCONFIGPTR 5 33 | 34 | 35 | // fields starting with m_ are only available in M mode, otherwise wpri 36 | 37 | typedef union { 38 | u4 raw; 39 | struct { 40 | u4 _wpri0:1; // write preserve, read ignore 41 | u4 sie:1; // s-mode interrupts enabled 42 | u4 _wpri1:1; // ------------- 43 | u4 m_mie:1; // m-mode interrupts enabled 44 | u4 _wpri2:1; // ------------- 45 | u4 spie:1; // s-mode interrupt state prior to exception 46 | u4 ube:1; // big endian 47 | u4 m_mpie:1; // m-mode interrupt state prior to exception 48 | u4 spp:1; // s-mode prior privilege 49 | u4 vs:2; // vector state 50 | u4 m_mpp:2; // m-mode prior privilege 51 | u4 fs:2; // floating point state 52 | u4 xs:2; // user mode extension state 53 | u4 m_mprv:1; // effective memory privilege mode 54 | u4 sum:1; // supervisor user memory access 55 | u4 mxr:1; // make executable readable 56 | u4 m_tvm:1; // trap virtual memory 57 | u4 m_tw:1; // timeout wait 58 | u4 m_tsr:1; // trap SRET 59 | u4 _wpri3:8; // ------------- 60 | u4 sd:1; // (32-bit only) something dirty (fs/vs/xs) 61 | }; 62 | } csr_status_t; 63 | 64 | typedef struct { 65 | u4 _wpri0:4; // write preserve, read ignore 66 | u4 sbe:1; // s-mode big endian 67 | u4 m_mbe:1; // m-mode big endian 68 | u4 _wpri1:26; // ------------- 69 | } csr_statush_t; 70 | 71 | typedef union { 72 | u8 raw; 73 | struct { 74 | u8 _wpri0:1; // write preserve, read ignore 75 | u8 sie:1; // s-mode interrupts enabled 76 | u8 _wpri1:1; // ------------- 77 | u8 m_mie:1; // m-mode interrupts enabled 78 | u8 _wpri2:1; // ------------- 79 | u8 spie:1; // s-mode interrupt state prior to exception 80 | u8 ube:1; // big endian 81 | u8 m_mpie:1; // m-mode interrupt state prior to exception 82 | u8 spp:1; // s-mode prior privilege 83 | u8 vs:2; // vector state 84 | u8 m_mpp:2; // m-mode prior privilege 85 | u8 fs:2; // floating point state 86 | u8 xs:2; // user mode extension state 87 | u8 m_mprv:1; // effective memory privilege mode 88 | u8 sum:1; // supervisor user memory access 89 | u8 mxr:1; // make executable readable 90 | u8 m_tvm:1; // trap virtual memory 91 | u8 m_tw:1; // timeout wait 92 | u8 m_tsr:1; // trap SRET 93 | u8 _wpri3:9; // ------------- 94 | u8 uxl:2; // 64-bit only: u-mode xlen 95 | u8 sxl:2; // 64-bit only: s-mode xlen 96 | u8 sbe:1; // s-mode big endian 97 | u8 m_mbe:1; // m-mode big endian 98 | u8 _wpri4:25; // ------------- 99 | u8 sd:1; // something dirty (fs/vs/xs) 100 | }; 101 | } csr_status64_t; 102 | 103 | typedef struct { 104 | u4 cy:1; 105 | u4 tm:1; 106 | u4 ir:1; 107 | u4 hpm:29; // hpm3 ... hpm31 108 | } csr_counteren_t; 109 | 110 | 111 | typedef union { 112 | u4 raw; 113 | struct { 114 | u4 num:4; 115 | u4 func:4; 116 | u4 level:2; 117 | u4 type:2; 118 | u4 _unused:20; 119 | } f; 120 | } csr_addr_t; 121 | 122 | result64_t rv_csr_op(rv_core_t *c, int op, u4 csr, u8 value); 123 | result64_t rv_csr_update(rv_core_t *c, int op, u8 *reg, u8 update_value); 124 | 125 | const char *rv_name_for_sysreg(rv_core_t *c, u2 num); 126 | -------------------------------------------------------------------------------- /core/inc/core/core.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define MAX_PHYS_MEM_REGIONS 4 17 | 18 | #define BARRIER_LOAD (1u << 0) 19 | #define BARRIER_STORE (1u << 1) 20 | #define BARRIER_SYSTEM (1u << 2) 21 | #define BARRIER_SYNC (1u << 3) 22 | 23 | #define MONITOR_UNARMED 0xf 24 | #define MONITOR_ARMED1 0 25 | #define MONITOR_ARMED2 1 26 | #define MONITOR_ARMED4 2 27 | #define MONITOR_ARMED8 3 28 | #define MONITOR_ARMED16 4 29 | 30 | #define CORE_INT_ENABLED(s) (s & SL_CORE_STATE_INTERRUPTS_EN) 31 | #define CORE_IS_WFI(s) (s & SL_CORE_STATE_WFI) 32 | 33 | #define CORE_EV_IRQ 1 34 | #define CORE_EV_RUNMODE 2 35 | 36 | typedef union { 37 | u4 u4; 38 | float f; 39 | u8 u8; 40 | double d; 41 | } sl_fp_reg_t; 42 | 43 | struct sl_core { 44 | 45 | // ---------------------------------------------------------------------------- 46 | // synchronous functions 47 | // ---------------------------------------------------------------------------- 48 | int (*exception_enter)(sl_core_t *c, u8 ex, u8 value); 49 | int (*breakpoint)(sl_core_t *c); 50 | 51 | void (*set_reg)(sl_core_t *c, u4 reg, u8 value); 52 | u8 (*get_reg)(sl_core_t *c, u4 reg); 53 | void (*shutdown)(sl_core_t *c); 54 | void (*destroy)(sl_core_t *c); 55 | 56 | u1 el; // exception level 57 | u1 mode; // execution mode (register length) 58 | u1 prev_len; // length of last instruction 59 | bool branch_taken; // was the last instruction a branch 60 | 61 | u4 state; // core state 62 | 63 | u8 pc; 64 | u8 r[32]; 65 | 66 | fexcept_t fexc; // host cumulative fp exception flags 67 | u1 frm; // floating point rounding mode 68 | sl_fp_reg_t f[32]; 69 | 70 | u8 monitor_addr; 71 | u8 monitor_value; 72 | u1 monitor_status; 73 | 74 | u8 ticks; 75 | sl_mapper_t *mapper; 76 | sl_bus_t *bus; 77 | sl_cache_t icache; // instruction cache 78 | sl_cache_t dcache; // data cache 79 | 80 | sl_engine_t engine; 81 | 82 | itrace_t *trace; 83 | const arch_ops_t *arch_ops; 84 | 85 | u1 arch; 86 | u1 subarch; 87 | u1 id; // numerical id of this core instance 88 | u4 options; 89 | u4 arch_options; 90 | const char *name; 91 | #if WITH_SYMBOLS 92 | sl_sym_list_t *symbols; 93 | #endif 94 | }; 95 | 96 | // ---------------------------------------------------------------------------- 97 | // setup functions 98 | // ---------------------------------------------------------------------------- 99 | 100 | // Setup functions may only be called when the core dispatch loop is not 101 | // running. 102 | 103 | int sl_core_init(sl_core_t *c, sl_core_params_t *p, sl_mapper_t *m); 104 | void sl_core_shutdown(sl_core_t *c); 105 | void sl_core_destroy(sl_core_t *c); 106 | 107 | void sl_core_config_get(sl_core_t *c, sl_core_params_t *p); 108 | int sl_core_config_set(sl_core_t *c, sl_core_params_t *p); 109 | 110 | void sl_core_add_symbols(sl_core_t *c, sl_sym_list_t *list); 111 | 112 | // ---------------------------------------------------------------------------- 113 | // async functions 114 | // ---------------------------------------------------------------------------- 115 | 116 | // Core Events are a way to send events to the core's dispatch loop. 117 | // Events are a means to synchronize asynchronous inputs to the core. 118 | // Events can be interrupts or other changes initiated from outside the 119 | // dispatch loop. Events will be handled before the next instruction is 120 | // dispatched. 121 | 122 | // int sl_core_event_send_async(sl_core_t *c, sl_event_t *ev); 123 | 124 | // ---------------------------------------------------------------------------- 125 | // synchronous functions 126 | // ---------------------------------------------------------------------------- 127 | 128 | // These functions may only be invoked by the core dispatch loop 129 | 130 | void sl_core_interrupt_set(sl_core_t *c, bool enable); 131 | int sl_core_endian_set(sl_core_t *c, bool big); 132 | void sl_core_instruction_barrier(sl_core_t *c); 133 | void sl_core_memory_barrier(sl_core_t *c, u4 type); 134 | 135 | void sl_core_next_pc(sl_core_t *c); 136 | int sl_core_load_pc(sl_core_t *c, u4 *inst); 137 | 138 | int sl_core_synchronous_exception(sl_core_t *c, u8 ex, u8 value, u4 status); 139 | 140 | // ---------------------------------------------------------------------------- 141 | // Misc 142 | // ---------------------------------------------------------------------------- 143 | 144 | // safe to call in any context as long as the core is not shut down. 145 | sl_sym_entry_t *sl_core_get_sym_for_addr(sl_core_t *c, u8 addr); 146 | -------------------------------------------------------------------------------- /include/sled/core.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // Public core interface 13 | 14 | // exception / privilege level 15 | #define SL_CORE_EL_USER 0 16 | #define SL_CORE_EL_SUPERVISOR 1 17 | #define SL_CORE_EL_HYPERVISOR 2 18 | #define SL_CORE_EL_MONITOR 3 19 | 20 | // execution mode (register width in bytes) 21 | #define SL_CORE_MODE_1 0 22 | #define SL_CORE_MODE_2 1 23 | #define SL_CORE_MODE_4 2 24 | #define SL_CORE_MODE_8 3 25 | #define SL_CORE_MODE_16 4 26 | 27 | #define SL_CORE_OPT_TRAP_SYSCALL (1u << 0) 28 | #define SL_CORE_OPT_TRAP_BREAKPOINT (1u << 1) 29 | #define SL_CORE_OPT_TRAP_ABORT (1u << 2) 30 | #define SL_CORE_OPT_TRAP_UNDEF (1u << 3) 31 | #define SL_CORE_OPT_TRAP_PREFETCH_ABORT (1u << 4) 32 | #define SL_CORE_OPT_ENDIAN_LITTLE (1u << 30) 33 | #define SL_CORE_OPT_ENDIAN_BIG (1u << 31) 34 | 35 | struct sl_core_params { 36 | u1 arch; 37 | u1 subarch; 38 | u1 id; 39 | u4 options; 40 | u4 arch_options; 41 | const char *name; 42 | sl_bus_t *bus; 43 | }; 44 | 45 | // Special register defines to pass to set/get_reg() 46 | #define SL_CORE_REG_PC 0xffff 47 | #define SL_CORE_REG_SP 0xfffe 48 | #define SL_CORE_REG_LR 0xfffd 49 | #define SL_CORE_REG_ARG0 0xfffc 50 | #define SL_CORE_REG_ARG1 0xfffb 51 | 52 | #define SL_CORE_REG_INVALID 0xffffffff 53 | 54 | // Architecture-specific registers definitions 55 | // RISCV - add reg base to CSR address 56 | #define SL_RV_CORE_REG_BASE 0x80000000 57 | #define SL_RV_CORE_REG(csr) (SL_RV_CORE_REG_BASE + (csr)) 58 | 59 | // Query register types 60 | #define SL_CORE_REG_TYPE_INT 0 61 | #define SL_CORE_REG_TYPE_FLOAT 1 62 | #define SL_CORE_REG_TYPE_VECTOR 2 63 | #define SL_CORE_REG_TYPE_MATRIX 3 64 | 65 | // Core state args (bitfield) 66 | #define SL_CORE_STATE_INTERRUPTS_EN (1u << 0) 67 | #define SL_CORE_STATE_WFI (1u << 1) 68 | #define SL_CORE_STATE_ENDIAN_BIG (1u << 2) 69 | 70 | /* 71 | There are two ways to run a core 72 | Synchronous execution - this is accomplished by iterated calls to 73 | sl_core_step(). Between steps, all core accessor functions are safe to call. 74 | Note that these functions are NOT generally thread safe and thus sl_core_t must 75 | be protected by an external lock if accessed from multiple threads. 76 | 77 | Asynchronous execution - the involves a call to sl_core_dispatch_loop(). The 78 | dispatch loop is a blocking call and should be done on a separate thread. 79 | Control of the core is accomplished via async events. No other core accessor 80 | functions may be called unless specified while the dispatch loop is running. 81 | */ 82 | 83 | // Execute a number of instructions on current thread 84 | int sl_core_step(sl_core_t *c, u8 num); 85 | 86 | // Run dispatch loop on current thread - enter async execution mode 87 | int sl_core_run(sl_core_t *c); 88 | 89 | // ---------------------------------------------------------------------------- 90 | // Synchronous accessor functions 91 | // ---------------------------------------------------------------------------- 92 | 93 | void sl_core_set_reg(sl_core_t *c, u4 reg, u8 value); 94 | u8 sl_core_get_reg(sl_core_t *c, u4 reg); 95 | int sl_core_mem_read_single(sl_core_t *c, u8 addr, u1 size, void *buf); 96 | int sl_core_mem_write_single(sl_core_t *c, u8 addr, u1 size, void *buf); 97 | int sl_core_mem_read(sl_core_t *c, u8 addr, u4 size, u4 count, void *buf); 98 | int sl_core_mem_write(sl_core_t *c, u8 addr, u4 size, u4 count, void *buf); 99 | int sl_core_mem_atomic(sl_core_t *c, u8 addr, u4 size, u1 aop, u8 arg0, u8 arg1, u8 *result, u1 ord, u1 ord_fail); 100 | u8 sl_core_get_cycles(sl_core_t *c); 101 | int sl_core_set_mapper(sl_core_t *c, sl_dev_t *d); 102 | void sl_core_dump_state(sl_core_t *c); 103 | void sl_core_set_mode(sl_core_t *c, u1 mode); 104 | 105 | void sl_core_print_cache_stats(sl_core_t *c); 106 | 107 | // ---------------------------------------------------------------------------- 108 | // Async control functions 109 | // ---------------------------------------------------------------------------- 110 | #define SL_CORE_CMD_RUN 0 111 | #define SL_CORE_CMD_HALT 1 112 | #define SL_CORE_CMD_EXIT 2 113 | 114 | int sl_core_async_command(sl_core_t *c, u4 cmd, bool wait); 115 | 116 | // ---------------------------------------------------------------------------- 117 | // Always safe to call on a valid core 118 | // ---------------------------------------------------------------------------- 119 | u1 sl_core_get_arch(sl_core_t *c); 120 | u4 sl_core_get_reg_count(sl_core_t *c, int type); 121 | 122 | // debug feature: print the bus topology as seen by this core 123 | // using the current translation regime. 124 | void sl_core_print_bus_topology(sl_core_t *c); 125 | void sl_core_print_config(sl_core_t *c); 126 | 127 | #ifdef __cplusplus 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /core/regview.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 -2024Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | sl_dev_t * sl_reg_view_get_dev(sl_reg_view_t *rv) { return &rv->dev; }; 14 | 15 | static inline u4 hash_bits(u4 val) { 16 | return (val >> 2) & 0xff; 17 | } 18 | 19 | static hash_item_t * hash_lookup(hash_t *h, u4 i) { 20 | const u4 bits = hash_bits(i); 21 | hash_ent_t *e = &h->ent[bits]; 22 | hash_item_t *it = &h->items[e->offset]; 23 | for (u4 c = 0; c < e->count; c++) { 24 | if (it[c].index == i) return &it[c]; 25 | } 26 | return NULL; 27 | } 28 | 29 | static void hash_insert_item(hash_t *h, u4 index, u4 value, sl_dev_t *dev) { 30 | const u4 bits = hash_bits(index); 31 | hash_ent_t *e = &h->ent[bits]; 32 | hash_item_t *it = &h->items[e->offset]; 33 | it[e->count].index = index; 34 | it[e->count].value = value; 35 | it[e->count].dev = dev; 36 | e->count++; 37 | h->count++; 38 | } 39 | 40 | int sl_reg_view_add_mapping(sl_reg_view_t *rv, sl_dev_t *dev, u4 view_offset, const u4 *view_addr, const u4 *dev_addr, u4 addr_count) { 41 | if (addr_count == 0) return SL_ERR_ARG; 42 | 43 | if (rv->dev_count == MAX_DEVS) return SL_ERR_FULL; 44 | 45 | // todo: verify there are no conflicts 46 | 47 | hash_t *h = &rv->hash; 48 | hash_item_t *prev_items = h->items; 49 | u4 prev_count = h->count; 50 | 51 | h->items = malloc((addr_count + prev_count) * sizeof(hash_item_t)); 52 | if (h->items == NULL) { 53 | h->items = prev_items; 54 | return SL_ERR_MEM; 55 | } 56 | 57 | for (int i = 0; i < addr_count; i++) { 58 | const u4 bits = hash_bits(view_addr[i] + view_offset); 59 | h->ent[bits].count++; 60 | } 61 | 62 | u4 offset = 0; 63 | for (int i = 0; i < HASH_ENTS; i++) { 64 | h->ent[i].offset = offset; 65 | offset += h->ent[i].count; 66 | h->ent[i].count = 0; 67 | } 68 | 69 | h->count = 0; 70 | 71 | // copy existing entries 72 | for (int i = 0; i < prev_count; i++) 73 | hash_insert_item(h, prev_items[i].index, prev_items[i].value, prev_items[i].dev); 74 | free(prev_items); 75 | 76 | u4 max_vaddr = 0; 77 | for (int i = 0; i < addr_count; i++) { 78 | u4 addr; 79 | if (dev_addr == NULL) addr = i; // linear addressing 80 | else addr = dev_addr[i]; 81 | const u4 vaddr = view_addr[i] + view_offset; 82 | hash_insert_item(h, vaddr, addr, dev); 83 | if (vaddr > max_vaddr) max_vaddr = vaddr; 84 | } 85 | rv->dev.aperture = max_vaddr + 4; 86 | rv->dev_view_offset[rv->dev_count] = view_offset; 87 | rv->dev_list[rv->dev_count] = dev; 88 | rv->dev_count++; 89 | return 0; 90 | } 91 | 92 | static int reg_view_device_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 93 | if (size != 4) return SL_ERR_IO_SIZE; 94 | if (count != 1) return SL_ERR_IO_COUNT; 95 | if (addr & 3) return SL_ERR_IO_ALIGN; 96 | 97 | sl_reg_view_t *rv = ctx; 98 | hash_item_t *it = hash_lookup(&rv->hash, addr); 99 | if (it == NULL) return SL_ERR_IO_INVALID; 100 | sl_dev_t *d = it->dev; 101 | return d->ops->read(d->context, it->value << 2, size, count, buf); 102 | } 103 | 104 | static int reg_view_device_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 105 | if (size != 4) return SL_ERR_IO_SIZE; 106 | if (count != 1) return SL_ERR_IO_COUNT; 107 | if (addr & 3) return SL_ERR_IO_ALIGN; 108 | 109 | sl_reg_view_t *rv = ctx; 110 | hash_item_t *it = hash_lookup(&rv->hash, addr); 111 | if (it == NULL) return SL_ERR_IO_INVALID; 112 | sl_dev_t *d = it->dev; 113 | return d->ops->write(d->context, it->value << 2, size, count, buf); 114 | } 115 | 116 | static const sl_dev_ops_t reg_view_ops = { 117 | .type = SL_DEV_REG_VIEW, 118 | .read = reg_view_device_read, 119 | .write = reg_view_device_write, 120 | }; 121 | 122 | int sl_reg_view_init(sl_reg_view_t *rv, const char *name, sl_dev_config_t *cfg) { 123 | rv->name = name; 124 | cfg->ops = ®_view_ops; 125 | cfg->name = name; 126 | int err = sl_device_init(&rv->dev, cfg); 127 | if (err) return err; 128 | sl_device_set_context(&rv->dev, rv); 129 | return 0; 130 | } 131 | 132 | int sl_reg_view_create(const char *name, sl_dev_config_t *cfg, sl_reg_view_t **rv_out) { 133 | sl_reg_view_t *rv = calloc(1, sizeof(*rv)); 134 | if (rv == NULL) return SL_ERR_MEM; 135 | 136 | int err = sl_reg_view_init(rv, name, cfg); 137 | if (err) { 138 | free(rv); 139 | return err; 140 | } 141 | *rv_out = rv; 142 | return 0; 143 | } 144 | 145 | void sl_reg_view_shutdown(sl_reg_view_t *rv) { 146 | free(rv->hash.items); 147 | } 148 | 149 | void sl_reg_view_destroy(sl_reg_view_t *rv) { 150 | sl_reg_view_shutdown(rv); 151 | free(rv); 152 | } 153 | 154 | void sl_reg_view_print_mappings(sl_dev_t *d, u8 base) { 155 | sl_reg_view_t *rv = containerof(d, sl_reg_view_t, dev); 156 | for (u4 i = 0; i < rv->dev_count; i++) { 157 | printf(" > %#20" PRIx64 " %s\n", base + rv->dev_view_offset[i], rv->dev_list[i]->name); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /core/engine.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // Called in device context 23 | // Send a message to dispatch loop to handle interrupt change 24 | static int engine_irq_transition_async(sl_irq_ep_t *ep, u4 num, bool high) { 25 | sl_event_t *ev = calloc(1, sizeof(*ev)); 26 | if (ev == NULL) return SL_ERR_MEM; 27 | 28 | sl_engine_t *e = containerof(ep, sl_engine_t, irq_ep); 29 | 30 | ev->epid = e->epid; 31 | ev->flags |= SL_EV_FLAG_FREE; 32 | ev->type = CORE_EV_IRQ; 33 | ev->option = 0; 34 | ev->arg[0] = num; 35 | ev->arg[1] = high; 36 | 37 | sl_worker_event_enqueue_async(e->worker, ev); 38 | return 0; 39 | } 40 | 41 | static void engine_set_wfi(sl_engine_t *e, bool enable) { 42 | if (enable) e->state |= SL_CORE_STATE_WFI; 43 | else e->state &= ~SL_CORE_STATE_WFI; 44 | sl_worker_set_engine_runnable(e->worker, !enable); 45 | } 46 | 47 | static int engine_handle_irq_event(sl_engine_t *e, sl_event_t *ev) { 48 | sl_irq_ep_t *ep = &e->irq_ep; 49 | u4 num = ev->arg[0]; 50 | bool high = ev->arg[1]; 51 | int err = sl_irq_endpoint_assert(ep, num, high); 52 | return err; 53 | } 54 | 55 | void sl_engine_set_context(sl_engine_t *e, void *ctx) { e->context = ctx; } 56 | void * sl_engine_get_context(sl_engine_t *e) { return e->context; } 57 | 58 | int sl_engine_async_command(sl_engine_t *e, u4 cmd, bool wait) { 59 | if (e->worker == NULL) return SL_ERR_STATE; 60 | sl_event_t *ev = calloc(1, sizeof(*ev)); 61 | if (ev == NULL) return SL_ERR_MEM; 62 | ev->epid = e->epid; 63 | ev->type = CORE_EV_RUNMODE; 64 | ev->option = cmd; 65 | ev->flags = SL_EV_FLAG_FREE; 66 | if (wait) ev->flags |= SL_EV_FLAG_SIGNAL; 67 | sl_worker_event_enqueue_async(e->worker, ev); 68 | return 0; 69 | } 70 | 71 | int sl_engine_wait_for_interrupt(sl_engine_t *e) { 72 | sl_irq_ep_t *ep = &e->irq_ep; 73 | if (ep->asserted) return 0; 74 | engine_set_wfi(e, true); 75 | return 0; 76 | } 77 | 78 | const sl_engine_ops_t * engine_get_ops(sl_engine_t *e) { 79 | return &e->ops; 80 | } 81 | 82 | void sl_engine_interrupt_set(sl_engine_t *e, bool enable) { 83 | if (enable) e->state |= SL_CORE_STATE_INTERRUPTS_EN; 84 | else e->state &= ~SL_CORE_STATE_INTERRUPTS_EN; 85 | } 86 | 87 | static int engine_handle_runmode_event(sl_engine_t *e, sl_event_t *ev) { 88 | int err = 0; 89 | switch(ev->option) { 90 | case SL_CORE_CMD_RUN: engine_set_wfi(e, false); break; 91 | case SL_CORE_CMD_HALT: engine_set_wfi(e, true); break; 92 | case SL_CORE_CMD_EXIT: err = SL_ERR_EXITED; break; 93 | default: 94 | printf("unknown engine cmd option %u\n", ev->option); 95 | err = SL_ERR_ARG; 96 | break; 97 | } 98 | if (ev->flags & SL_EV_FLAG_SIGNAL) { 99 | sl_sem_t *sem = (sl_sem_t *)ev->signal; 100 | sl_sem_post(sem); 101 | } 102 | return err; 103 | } 104 | 105 | static int engine_event_handle(sl_event_ep_t *ep, sl_event_t *ev) { 106 | sl_engine_t *e = containerof(ep, sl_engine_t, event_ep); 107 | int err = 0; 108 | 109 | switch (ev->type) { 110 | case CORE_EV_IRQ: 111 | if ((err = engine_handle_irq_event(e, ev))) break; 112 | if (e->state & SL_CORE_STATE_INTERRUPTS_EN) err = sl_engine_handle_interrupts(e); 113 | break; 114 | 115 | case CORE_EV_RUNMODE: err = engine_handle_runmode_event(e, ev); break; 116 | default: 117 | ev->err = SL_ERR_ARG; 118 | printf("unknown event type %u\n", ev->type); 119 | break; 120 | } 121 | return err; 122 | } 123 | 124 | int sl_engine_handle_interrupts(sl_engine_t *e) { 125 | sl_irq_ep_t *ep = &e->irq_ep; 126 | if (ep->asserted == 0) return 0; 127 | engine_set_wfi(e, false); 128 | return e->ops.interrupt(e); 129 | } 130 | 131 | int sl_engine_step(sl_engine_t *e, u8 num) { 132 | return e->ops.step(e, num); 133 | } 134 | 135 | int sl_engine_run(sl_engine_t *e) { 136 | return e->ops.run(e); 137 | } 138 | 139 | int sl_engine_init(sl_engine_t *e, const char *name, const sl_engine_ops_t *ops) { 140 | e->name = name; 141 | e->worker = NULL; 142 | e->event_ep.handle = engine_event_handle; 143 | if (ops != NULL) e->ops = *ops; 144 | int err = sl_irq_ep_init(&e->irq_ep); 145 | if (err) return err; 146 | e->irq_ep.assert = engine_irq_transition_async; 147 | return 0; 148 | } 149 | 150 | int sl_engine_create(const char *name, const sl_engine_ops_t *ops, sl_engine_t **e_out) { 151 | sl_engine_t *e = calloc(1, sizeof(*e)); 152 | if (e == NULL) return SL_ERR_MEM; 153 | int err = sl_engine_init(e, name, ops); 154 | if (err) { 155 | free(e); 156 | return err; 157 | } 158 | *e_out = e; 159 | return 0; 160 | } 161 | 162 | void sl_engine_shutdown(sl_engine_t *e) { 163 | sl_irq_ep_shutdown(&e->irq_ep); 164 | } 165 | 166 | void sl_engine_destory(sl_engine_t *e) { 167 | if (e == NULL) return; 168 | sl_engine_shutdown(e); 169 | free(e); 170 | } 171 | -------------------------------------------------------------------------------- /core/worker.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define SL_WORKER_STATE_ENGINE_RUNNABLE (1u << 0) 16 | 17 | static void queue_add(sl_worker_t *w, sl_event_t *ev) { 18 | sl_lock_lock(&w->lock); 19 | sl_list_add_last(&w->ev_list, &ev->node); 20 | sl_cond_signal_all(&w->has_event); 21 | sl_lock_unlock(&w->lock); 22 | } 23 | 24 | static int handle_events(sl_worker_t *w, bool wait) { 25 | sl_lock_lock(&w->lock); 26 | if (wait) { 27 | while (sl_list_peek_first(&w->ev_list) == NULL) { 28 | sl_cond_wait(&w->has_event, &w->lock); 29 | } 30 | 31 | } 32 | sl_list_node_t *ev_list = sl_list_remove_all(&w->ev_list); 33 | sl_lock_unlock(&w->lock); 34 | 35 | int err = 0; 36 | while (err == 0) { 37 | sl_event_t *ev = (sl_event_t *)ev_list; 38 | if (ev == NULL) break; 39 | ev_list = ev->node.next; 40 | const u4 id = ev->epid; 41 | if (id == SL_EV_EP_CALLBACK) { 42 | err = ev->callback(ev); 43 | } else { 44 | if ((id > SL_WORKER_MAX_EPS) || (w->endpoint[id] == NULL)) { 45 | ev->err = SL_ERR_ARG; 46 | } else { 47 | err = w->endpoint[id]->handle(w->endpoint[id], ev); 48 | } 49 | } 50 | if (ev->flags & SL_EV_FLAG_SIGNAL) sl_sem_post((sl_sem_t *)ev->signal); 51 | else if (ev->flags & SL_EV_FLAG_FREE) free(ev); 52 | } 53 | return err; 54 | } 55 | 56 | void sl_worker_set_engine_runnable(sl_worker_t *w, bool runnable) { 57 | if (runnable) w->state |= SL_WORKER_STATE_ENGINE_RUNNABLE; 58 | else w->state &= ~SL_WORKER_STATE_ENGINE_RUNNABLE; 59 | } 60 | 61 | int sl_worker_add_engine(sl_worker_t *w, sl_engine_t *e, u4 *id_out) { 62 | int err = sl_worker_add_event_endpoint(w, &e->event_ep, id_out); 63 | if (err) return err; 64 | e->worker = w; 65 | w->engine = e; 66 | return 0; 67 | } 68 | 69 | int sl_worker_add_event_endpoint(sl_worker_t *w, sl_event_ep_t *ep, u4 *id_out) { 70 | for (u4 i = 0; i < SL_WORKER_MAX_EPS; i++) { 71 | if (w->endpoint[i] == NULL) { 72 | w->endpoint[i] = ep; 73 | *id_out = i; 74 | return 0; 75 | } 76 | } 77 | return SL_ERR_FULL; 78 | } 79 | 80 | int sl_worker_event_enqueue_async(sl_worker_t *w, sl_event_t *ev) { 81 | int err = 0; 82 | sl_sem_t sem; 83 | 84 | u4 flags = ev->flags; 85 | if (flags & SL_EV_FLAG_SIGNAL) { 86 | if ((err = sl_sem_init(&sem, 0))) return err; 87 | ev->signal = (uintptr_t)&sem; 88 | } 89 | 90 | queue_add(w, ev); 91 | 92 | if (flags & SL_EV_FLAG_SIGNAL) { 93 | sl_sem_wait(&sem); 94 | sl_sem_destroy(&sem); 95 | if (flags & SL_EV_FLAG_FREE) free(ev); 96 | } 97 | return 0; 98 | } 99 | 100 | int sl_worker_handle_events(sl_worker_t *w) { 101 | int err = 0; 102 | 103 | if (w->state & SL_WORKER_STATE_ENGINE_RUNNABLE) { 104 | void *n = atomic_load_explicit((_Atomic(sl_list_node_t *)*)&w->ev_list.first, memory_order_relaxed); 105 | if (n != NULL) { 106 | if ((err = handle_events(w, false))) return err; 107 | } 108 | } 109 | 110 | while ((w->state & SL_WORKER_STATE_ENGINE_RUNNABLE) == 0) { 111 | if ((err = handle_events(w, true))) return err; 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | int sl_worker_step(sl_worker_t *w, u8 num) { 118 | return sl_engine_step(w->engine, num); 119 | } 120 | 121 | int sl_worker_run(sl_worker_t *w) { 122 | assert(!w->thread_running); 123 | return sl_engine_run(w->engine); 124 | } 125 | 126 | static void * workloop_thread(void *arg) { 127 | sl_worker_t *w = arg; 128 | w->thread_status = sl_engine_run(w->engine); 129 | return NULL; 130 | } 131 | 132 | int sl_worker_thread_run(sl_worker_t *w) { 133 | assert(!w->thread_running); 134 | w->thread_running = true; 135 | int err = pthread_create(&w->thread, NULL, workloop_thread, w); 136 | if (err) { 137 | w->thread_running = false; 138 | return SL_ERR_STATE; 139 | } 140 | return 0; 141 | } 142 | 143 | int sl_worker_thread_join(sl_worker_t *w) { 144 | assert(w->thread_running); 145 | int err = pthread_join(w->thread, NULL); 146 | switch (err) { 147 | case 0: 148 | w->thread_running = false; 149 | return 0; 150 | 151 | case EINVAL: 152 | case ESRCH: 153 | case EDEADLK: 154 | return SL_ERR_STATE; 155 | 156 | default: return SL_ERR; 157 | } 158 | } 159 | 160 | int sl_worker_init(sl_worker_t *w, const char *name) { 161 | w->name = name; 162 | w->thread_running = false; 163 | sl_lock_init(&w->lock); 164 | sl_cond_init(&w->has_event); 165 | return 0; 166 | } 167 | 168 | int sl_worker_create(const char *name, sl_worker_t **w_out) { 169 | sl_worker_t *w = calloc(1, sizeof(*w)); 170 | if (w == NULL) return SL_ERR_MEM; 171 | 172 | int err = sl_worker_init(w, name); 173 | if (err) { 174 | free(w); 175 | return err; 176 | } 177 | *w_out = w; 178 | return 0; 179 | } 180 | 181 | void sl_worker_shutdown(sl_worker_t *w) { 182 | assert(!w->thread_running); 183 | sl_lock_destroy(&w->lock); 184 | sl_cond_destroy(&w->has_event); 185 | } 186 | 187 | void sl_worker_destroy(sl_worker_t *w) { 188 | if (w == NULL) return; 189 | sl_worker_shutdown(w); 190 | free(w); 191 | } 192 | 193 | -------------------------------------------------------------------------------- /dev/sled/mpu.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // sled MPU device 16 | 17 | #define MPU_TYPE 'mpux' 18 | #define MPU_VERSION 0 19 | 20 | typedef struct { 21 | sl_dev_t *dev; 22 | sl_mapper_t *mapper; 23 | 24 | bool enabled; 25 | u1 config; 26 | u4 map_len[MPU_MAX_MAPPINGS]; 27 | u8 va_base[MPU_MAX_MAPPINGS]; 28 | u8 pa_base[MPU_MAX_MAPPINGS]; 29 | } sled_mpu_t; 30 | 31 | static int mpu_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 32 | if (size != 4) return SL_ERR_IO_SIZE; 33 | if (count != 1) return SL_ERR_IO_COUNT; 34 | if (addr & 3) return SL_ERR_IO_ALIGN; 35 | 36 | int err = 0; 37 | sled_mpu_t *m = ctx; 38 | 39 | sl_device_lock(m->dev); 40 | u4 *val = buf; 41 | switch (addr) { 42 | case MPU_REG_DEV_TYPE: *val = MPU_TYPE; goto out; 43 | case MPU_REG_DEV_VERSION: *val = MPU_VERSION; goto out; 44 | case MPU_REG_CONFIG: *val = m->config; goto out; 45 | case MPU_REG_STATUS: *val = m->config & MPU_CONFIG_ENABLE; goto out; 46 | case MPU_REG_MAP_ENTS: *val = MPU_MAX_MAPPINGS; goto out; 47 | default: break; 48 | } 49 | if ((addr >= MPU_REG_MAP_VA_BASE_LO(0)) && (addr < MPU_REG_MAP_VA_BASE_LO(MPU_MAX_MAPPINGS))) { 50 | const u4 index = (addr - MPU_REG_MAP_VA_BASE_LO(0)) >> 2; 51 | u4 *p = (u4 *)m->va_base; 52 | *val = p[index]; 53 | goto out; 54 | } 55 | if ((addr >= MPU_REG_MAP_PA_BASE_LO(0)) && (addr < MPU_REG_MAP_PA_BASE_LO(MPU_MAX_MAPPINGS))) { 56 | const u4 index = (addr - MPU_REG_MAP_PA_BASE_LO(0)) >> 2; 57 | u4 *p = (u4 *)m->pa_base; 58 | *val = p[index]; 59 | goto out; 60 | } 61 | if ((addr >= MPU_REG_MAP_LEN(0)) && (addr < MPU_REG_MAP_LEN(MPU_MAX_MAPPINGS))) { 62 | const u4 index = (addr - MPU_REG_MAP_LEN(0)) >> 2; 63 | *val = m->map_len[index]; 64 | goto out; 65 | } 66 | err = SL_ERR_IO_INVALID; 67 | out: 68 | sl_device_unlock(m->dev); 69 | return err; 70 | } 71 | 72 | static void clear_entries(sled_mpu_t *m) { 73 | const usize size = (sizeof(u8) * 2 * MPU_MAX_MAPPINGS) + (sizeof(u4) * MPU_MAX_MAPPINGS); 74 | memset(m->map_len, 0, size); 75 | } 76 | 77 | static int update_config(sled_mpu_t *m, u4 val) { 78 | int err = 0; 79 | u4 config = m->config; 80 | u4 ops = 0; 81 | u4 ent_count = 0; 82 | sl_mapping_t *ent_list = NULL; 83 | 84 | if (val & MPU_CONFIG_ENABLE) 85 | config |= MPU_CONFIG_ENABLE; 86 | else 87 | config &= ~MPU_CONFIG_ENABLE; 88 | 89 | if (val & MPU_CONFIG_APPLY) { 90 | ent_list = calloc(MPU_MAX_MAPPINGS, sizeof(sl_mapping_t)); 91 | if (ent_list == NULL) return SL_ERR_MEM; 92 | sl_mapper_t *next = sl_mapper_get_next(m->mapper); 93 | for (int i = 0; i < MPU_MAX_MAPPINGS; i++) { 94 | if (m->map_len[i] == 0) continue; 95 | sl_mapping_t *ent = &ent_list[ent_count]; 96 | ent->input_base = m->va_base[i]; 97 | ent->length = m->map_len[i]; 98 | ent->output_base = m->pa_base[i]; 99 | ent->type = SL_MAP_TYPE_MAPPER; 100 | ent->ep = sl_mapper_get_ep(next); 101 | ent_count++; 102 | } 103 | if (ent_count > 0) ops |= SL_MAP_OP_REPLACE; 104 | } 105 | 106 | if (config & MPU_CONFIG_ENABLE) ops |= SL_MAP_OP_MODE_TRANSLATE; 107 | else ops |= SL_MAP_OP_MODE_PASSTHROUGH; 108 | 109 | // todo: add completion callback to update_async to delay updating config 110 | if ((err = sl_device_update_mapper_async(m->dev, ops, ent_count, ent_list))) goto out_err; 111 | 112 | if (val & MPU_CONFIG_CLEAR) clear_entries(m); 113 | m->config = config; 114 | return 0; 115 | 116 | out_err: 117 | free(ent_list); 118 | return err; 119 | } 120 | 121 | static int mpu_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 122 | if (size != 4) return SL_ERR_IO_SIZE; 123 | if (count != 1) return SL_ERR_IO_COUNT; 124 | if (addr & 3) return SL_ERR_IO_ALIGN; 125 | 126 | int err = 0; 127 | sled_mpu_t *m = ctx; 128 | u4 val = *(u4 *)buf; 129 | 130 | sl_device_lock(m->dev); 131 | switch (addr) { 132 | case MPU_REG_CONFIG: 133 | err = update_config(m, val); 134 | goto out; 135 | 136 | case MPU_REG_DEV_TYPE: 137 | case MPU_REG_DEV_VERSION: 138 | case MPU_REG_STATUS: 139 | case MPU_REG_MAP_ENTS: 140 | err = SL_ERR_IO_NOWR; 141 | goto out; 142 | 143 | default: 144 | break; 145 | } 146 | if ((addr >= MPU_REG_MAP_VA_BASE_LO(0)) && (addr < MPU_REG_MAP_VA_BASE_LO(MPU_MAX_MAPPINGS))) { 147 | const u4 index = (addr - MPU_REG_MAP_VA_BASE_LO(0)) >> 2; 148 | u4 *p = (u4 *)m->va_base; 149 | p[index] = val; 150 | goto out; 151 | } 152 | if ((addr >= MPU_REG_MAP_PA_BASE_LO(0)) && (addr < MPU_REG_MAP_PA_BASE_LO(MPU_MAX_MAPPINGS))) { 153 | const u4 index = (addr - MPU_REG_MAP_PA_BASE_LO(0)) >> 2; 154 | u4 *p = (u4 *)m->pa_base; 155 | p[index] = val; 156 | goto out; 157 | } 158 | if ((addr >= MPU_REG_MAP_LEN(0)) && (addr < MPU_REG_MAP_LEN(MPU_MAX_MAPPINGS))) { 159 | const u4 index = (addr - MPU_REG_MAP_LEN(0)) >> 2; 160 | m->map_len[index] = val; 161 | goto out; 162 | } 163 | err = SL_ERR_IO_INVALID; 164 | out: 165 | sl_device_unlock(m->dev); 166 | return err; 167 | } 168 | 169 | static void sled_mpu_destroy(sl_dev_t *d) { 170 | sled_mpu_t *m = sl_device_get_context(d); 171 | sl_mapper_destroy(m->mapper); 172 | free(m); 173 | } 174 | 175 | static int sled_mpu_create(sl_dev_t *d, sl_dev_config_t *cfg) { 176 | int err = 0; 177 | 178 | sled_mpu_t *m = calloc(1, sizeof(sled_mpu_t)); 179 | if (m == NULL) return SL_ERR_MEM; 180 | if ((err = sl_mapper_create(&m->mapper))) goto out_err; 181 | 182 | m->dev = d; 183 | cfg->aperture = MPU_APERTURE_LENGTH; 184 | sl_device_set_context(m->dev, m); 185 | sl_mapper_set_mode(m->mapper, SL_MAP_OP_MODE_PASSTHROUGH); 186 | sl_device_set_mapper(m->dev, m->mapper); 187 | return 0; 188 | 189 | out_err: 190 | free(m); 191 | return err; 192 | } 193 | 194 | static const sl_dev_ops_t mpu_ops = { 195 | .type = SL_DEV_SLED_MPU, 196 | .read = mpu_read, 197 | .write = mpu_write, 198 | .create = sled_mpu_create, 199 | .destroy = sled_mpu_destroy, 200 | }; 201 | 202 | DECLARE_DEVICE(sled_mpu, SL_DEV_MPU, &mpu_ops); 203 | -------------------------------------------------------------------------------- /core/inc/core/riscv/inst.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2023 Shac Ron and The Sled Project 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // opcodes 9 | #define OP_ALU 0b0110011 // ALU REG 10 | #define OP_ALU32 0b0111011 // rv64 ALU 32 11 | #define OP_IMM 0b0010011 // ALU IMM 12 | #define OP_IMM32 0b0011011 // rv64 ALU IMM 32 13 | #define OP_LUI 0b0110111 14 | #define OP_AUIPC 0b0010111 15 | #define OP_JAL 0b1101111 16 | #define OP_JALR 0b1100111 17 | #define OP_BRANCH 0b1100011 18 | #define OP_LOAD 0b0000011 19 | #define OP_STORE 0b0100011 20 | #define OP_MISC_MEM 0b0001111 // fence 21 | #define OP_SYSTEM 0b1110011 // csr ecall ebreak 22 | 23 | #define OP_AMO 0b0101111 // RV32A / RV64A extension 24 | #define OP_FP 0b1010011 // FP32/64 25 | #define OP_FP_LOAD 0b0000111 // FP32/64 load 26 | #define OP_FP_STORE 0b0100111 // FP32/64 store 27 | 28 | #define OP_FMADD_S 0b1000011 // FMADD.S 29 | #define OP_FMSUB_S 0b1000111 // FMSUB.S 30 | #define OP_FNMSUB_S 0b1001011 // FNMSUB.S 31 | #define OP_FNMADD_S 0b1001111 // FNMADD.S 32 | 33 | typedef union { 34 | u4 raw; 35 | struct { 36 | u4 opcode : 7; 37 | u4 imm3 : 1; 38 | u4 imm1 : 4; 39 | u4 funct3 : 3; 40 | u4 rs1 : 5; 41 | u4 rs2 : 5; 42 | u4 imm2 : 6; 43 | u4 imm4 : 1; 44 | } b; 45 | struct { 46 | u4 opcode : 7; 47 | u4 rd : 5; 48 | u4 funct3 : 3; 49 | u4 rs1 : 5; 50 | u4 imm : 12; 51 | } i; 52 | struct { 53 | u4 opcode : 7; 54 | u4 rd : 5; 55 | u4 imm3 : 8; 56 | u4 imm2 : 1; 57 | u4 imm1 : 10; 58 | u4 imm4 : 1; 59 | } j; 60 | struct { 61 | u4 opcode : 7; 62 | u4 rd : 5; 63 | u4 funct3 : 3; 64 | u4 rs1 : 5; 65 | u4 rs2 : 5; 66 | u4 funct7 : 7; 67 | } r; 68 | struct { 69 | u4 opcode : 7; 70 | u4 imm1 : 5; 71 | u4 funct3 : 3; 72 | u4 rs1 : 5; 73 | u4 rs2 : 5; 74 | u4 imm2 : 7; 75 | } s; 76 | struct { 77 | u4 opcode : 7; 78 | u4 rd : 5; 79 | u4 imm : 20; 80 | } u; 81 | struct { 82 | u4 opcode : 7; 83 | u4 rd : 5; 84 | u4 rm : 3; 85 | u4 rs1 : 5; 86 | u4 rs2 : 5; 87 | u4 fmt : 2; 88 | u4 funct5 : 5; // rs3 89 | } r4; 90 | } rv_inst_t; 91 | 92 | typedef union { 93 | u2 raw; 94 | struct { 95 | u2 opcode : 2; 96 | u2 rs2 : 5; 97 | u2 rsd : 5; 98 | u2 funct4 : 1; // this is different from the spec definition of funct4 99 | u2 funct3 : 3; 100 | } cr; 101 | struct { 102 | u2 opcode : 2; 103 | u2 imm0 : 5; 104 | u2 rsd : 5; 105 | u2 imm1 : 1; 106 | u2 funct3 : 3; 107 | } ci; 108 | struct { 109 | u2 opcode : 2; 110 | u2 off_6 : 2; 111 | u2 off_2 : 3; 112 | u2 rd : 5; 113 | u2 off_5 : 1; 114 | u2 funct3 : 3; 115 | } ci4; 116 | struct { 117 | u2 opcode : 2; 118 | u2 off_6 : 3; 119 | u2 off_3 : 2; 120 | u2 rd : 5; 121 | u2 off_5 : 1; 122 | u2 funct3 : 3; 123 | } ci8; 124 | struct { 125 | u2 opcode : 2; 126 | u2 rs2 : 5; 127 | u2 imm : 6; 128 | u2 funct3 : 3; 129 | } css; 130 | struct { 131 | u2 opcode : 2; 132 | u2 rd : 3; 133 | u2 off_3 : 1; 134 | u2 off_2 : 1; 135 | u2 off_6 : 4; 136 | u2 off_4 : 2; 137 | u2 funct3 : 3; 138 | } ciw; 139 | struct { 140 | u2 opcode : 2; 141 | u2 rd : 3; 142 | u2 imm0 : 2; 143 | u2 rs : 3; 144 | u2 imm1 : 3; 145 | u2 funct3 : 3; 146 | } cl; 147 | struct { 148 | u2 opcode : 2; 149 | u2 rs2 : 3; 150 | u2 imm0 : 2; 151 | u2 rs1 : 3; 152 | u2 imm1 : 3; 153 | u2 funct3 : 3; 154 | } cs; 155 | struct { 156 | u2 opcode : 2; 157 | u2 off_5 : 1; 158 | u2 off_1 : 2; 159 | u2 off_6 : 2; 160 | u2 rs : 3; 161 | u2 off_3 : 2; 162 | u2 off_8 : 1; 163 | u2 funct3 : 3; 164 | } cb; 165 | struct { 166 | u2 opcode : 2; 167 | u2 imm0 : 5; 168 | u2 rsd : 3; 169 | u2 funct2 : 2; 170 | u2 imm1 : 1; 171 | u2 funct3 : 3; 172 | } cba; // CB-ALU format 173 | struct { 174 | u2 opcode : 2; 175 | u2 off_5 : 1; 176 | u2 off_1 : 3; 177 | u2 off_7 : 1; 178 | u2 off_6 : 1; 179 | u2 off_10 : 1; 180 | u2 off_8 : 2; 181 | u2 off_4 : 1; 182 | u2 off_11 : 1; 183 | u2 funct3 : 3; 184 | } cj; 185 | } rv_cinst_t; 186 | 187 | // The CI, CS, and CSS immediate format varies depending on the instruction 188 | // This is an attempt to make extracting them more sane. 189 | 190 | // ci format: imm1: imm[5], imm0: imm[4:0] 191 | #define CI_IMM(ci) (ci.ci.imm0 | (ci.ci.imm1 << 5)) 192 | 193 | // ci format: imm1: imm[9], imm0: imm[4|6|8:7|5] 194 | #define CI_ADDI16SP_IMM(ci) (((ci.ci.imm0 & 1) << 5) | ((ci.ci.imm0 & 6) << 6) | \ 195 | ((ci.ci.imm0 & 8) << 3) | (ci.ci.imm0 & 0x10) | (ci.ci.imm1 << 9)) 196 | 197 | // ci format: imm1: offset[5], imm0: offset[4:2|7:6] 198 | #define CI_IMM_SCALED_4(ci) ((ci.ci4.off_6 << 6) | (ci.ci4.off_2 << 2) | (ci.ci4.off_5 << 5)) 199 | 200 | // ci format: imm1: offset[5], imm0: offset[4:3|8:6] 201 | #define CI_IMM_SCALED_8(ci) ((ci.ci8.off_6 << 6) | (ci.ci8.off_3 << 3) | (ci.ci8.off_5 << 5)) 202 | 203 | #define CIW_IMM(ci) ((ci.ciw.off_2 << 2) | (ci.ciw.off_3 << 3) | (ci.ciw.off_4 << 4) | (ci.ciw.off_6 << 6)) 204 | 205 | #define CJ_IMM(ci) ((ci.cj.off_5 << 5) | (ci.cj.off_1 << 1) | (ci.cj.off_7 << 7) | \ 206 | (ci.cj.off_6 << 6) | (ci.cj.off_10 << 10) | (ci.cj.off_8 << 8) | \ 207 | (ci.cj.off_4 << 4) | (ci.cj.off_11 << 11)) 208 | 209 | #define CB_IMM(ci) ((ci.cb.off_5 << 5) | (ci.cb.off_1 << 1) | (ci.cb.off_6 << 6) | \ 210 | (ci.cb.off_3 << 3) | (ci.cb.off_8 << 8)) 211 | 212 | // same as CI_IMM 213 | #define CBA_IMM(ci) (ci.cba.imm0 | (ci.cba.imm1 << 5)) 214 | 215 | #define CS_IMM_SCALED_4(ci) (((ci.cl.imm0 & 1) << 6) | ((ci.cl.imm0 & 2) << 1) | ((ci.cl.imm1) << 3)) 216 | #define CS_IMM_SCALED_8(ci) ((ci.cl.imm0 << 6) | (ci.cl.imm1 << 3)) 217 | 218 | #define CSS_IMM_SCALED_4(ci) (((ci.css.imm & 3) << 6) | (ci.css.imm & ~3)) 219 | #define CSS_IMM_SCALED_8(ci) (((ci.css.imm & 7) << 6) | (ci.css.imm & ~7)) 220 | -------------------------------------------------------------------------------- /dev/sled/timer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define TIMER_TYPE 'timr' 18 | #define TIMER_VERSION 0 19 | #define TIMER_MAX_UNITS 8 20 | 21 | typedef struct sled_timer sled_timer_t; 22 | 23 | typedef struct { 24 | sled_timer_t *timer; 25 | u4 config; 26 | u4 status; 27 | u8 reset_val; 28 | u8 tid; // timer id for chrono 29 | u8 count; 30 | } sled_timer_unit_t; 31 | 32 | struct sled_timer { 33 | sl_dev_t *dev; 34 | sl_chrono_t *chrono; 35 | 36 | // registers 37 | u4 config; 38 | u4 status; 39 | u4 scalar; 40 | u4 num_units; 41 | sled_timer_unit_t unit[TIMER_MAX_UNITS]; 42 | }; 43 | 44 | static u4 index_for_pointer(sled_timer_t *t, sled_timer_unit_t *u) { 45 | return ((uptr)u - (uptr)&t->unit[0]) / sizeof(sled_timer_unit_t); 46 | } 47 | 48 | static sled_timer_unit_t * addr_to_unit(sled_timer_t *t, u8 addr, u4 *reg) { 49 | addr -= 0x20; 50 | u4 u = addr / 0x20; 51 | *reg = (addr & (0x20 - 1)) >> 2; 52 | return &t->unit[u]; 53 | } 54 | 55 | static int timer_read(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 56 | if (size != 4) return SL_ERR_IO_SIZE; 57 | if (count != 1) return SL_ERR_IO_COUNT; 58 | if (addr & 3) return SL_ERR_IO_ALIGN; 59 | 60 | sled_timer_t *t = ctx; 61 | u4 *val = buf; 62 | int err = 0; 63 | sl_irq_mux_t *m; 64 | 65 | sl_device_lock(t->dev); 66 | switch (addr) { 67 | case TIMER_REG_DEV_TYPE: *val = TIMER_TYPE; goto out; 68 | case TIMER_REG_DEV_VERSION: *val = TIMER_VERSION; goto out; 69 | case TIMER_REG_CONFIG: *val = t->config; goto out; 70 | case TIMER_REG_STATUS: *val = t->status; goto out; 71 | case TIMER_REG_RT_SCALER_US: *val = t->scalar; goto out; 72 | case TIMER_REG_NUM_UNITS: *val = t->num_units; goto out; 73 | 74 | case TIMER_IRQ_MASK: 75 | m = sl_device_get_irq_mux(t->dev); 76 | *val = ~sl_irq_mux_get_enabled(m); 77 | goto out; 78 | 79 | case TIMER_IRQ_STATUS: 80 | m = sl_device_get_irq_mux(t->dev); 81 | *val = sl_irq_mux_get_active(m); 82 | goto out; 83 | 84 | default: break; 85 | } 86 | if ((addr < 0x20) || (addr >= TIMER_APERTURE_LENGTH(t->num_units))) { 87 | err = SL_ERR_IO_INVALID; 88 | goto out; 89 | } 90 | 91 | u4 reg; 92 | sled_timer_unit_t *u = addr_to_unit(t, addr, ®); 93 | switch (reg) { 94 | case 0: *val = u->config; break; 95 | case 1: *val = (u4)u->reset_val; break; 96 | case 2: *val = (u4)(u->reset_val >> 32); break; 97 | // case 3: 98 | // case 4: *val = (u4)unit->reset_val; break; 99 | } 100 | 101 | out: 102 | sl_device_unlock(t->dev); 103 | return err; 104 | } 105 | 106 | static int timer_callback(void *context, int err) { 107 | if (err) { 108 | printf("timer stopped\n"); 109 | return 0; 110 | } 111 | 112 | sled_timer_unit_t *u = context; 113 | sled_timer_t *t = u->timer; 114 | int ret = 0; 115 | u8 count; 116 | u4 index = index_for_pointer(t, u); 117 | sl_irq_mux_t *m = sl_device_get_irq_mux(t->dev); 118 | 119 | sl_device_lock(t->dev); 120 | 121 | if (u->config & TIMER_UNIT_CONFIG_CONTINUOUS) ret = SL_ERR_RESTART; 122 | else u->config &= ~TIMER_UNIT_CONFIG_RUN; 123 | u->config |= TIMER_UNIT_CONFIG_LOOPED; 124 | u->count++; 125 | count = u->count; 126 | sl_irq_mux_set_active_bit(m, index, true); 127 | sl_device_unlock(t->dev); 128 | 129 | printf("timer fired %" PRIu64 "\n", count); 130 | return ret; 131 | } 132 | 133 | static void timer_set_unit_config_locked(sled_timer_t *t, sled_timer_unit_t *u, u4 val) { 134 | u4 config = u->config; 135 | 136 | config &= ~TIMER_UNIT_CONFIG_CONTINUOUS; 137 | config |= (val & TIMER_UNIT_CONFIG_CONTINUOUS); 138 | // clear looped on write 139 | if (val & TIMER_UNIT_CONFIG_LOOPED) config &= ~TIMER_UNIT_CONFIG_LOOPED; 140 | 141 | if (config & TIMER_UNIT_CONFIG_RUN) { 142 | // currently running 143 | if ((val & TIMER_UNIT_CONFIG_RUN) == 0) { 144 | // stop timer 145 | config &= ~TIMER_UNIT_CONFIG_RUN; 146 | sl_chrono_timer_cancel(t->chrono, u->tid); 147 | } 148 | } else { 149 | if (val & TIMER_UNIT_CONFIG_RUN) { 150 | // start timer 151 | config &= ~TIMER_UNIT_CONFIG_LOOPED; 152 | config |= TIMER_UNIT_CONFIG_RUN; 153 | int err = sl_chrono_timer_set(t->chrono, u->reset_val, timer_callback, u, &u->tid); 154 | if (err) { 155 | fprintf(stderr, "timer_set_unit_config_locked: failed to allocate memory for system timer, device is in broken state\n"); 156 | } 157 | } 158 | } 159 | u->config = config; 160 | } 161 | 162 | static int timer_write(void *ctx, u8 addr, u4 size, u4 count, void *buf) { 163 | if (size != 4) return SL_ERR_IO_SIZE; 164 | if (count != 1) return SL_ERR_IO_COUNT; 165 | if (addr & 3) return SL_ERR_IO_ALIGN; 166 | 167 | sled_timer_t *t = ctx; 168 | u4 val = *(u4 *)buf; 169 | int err = 0; 170 | sl_irq_mux_t *m; 171 | 172 | sl_device_lock(t->dev); 173 | switch (addr) { 174 | case TIMER_REG_CONFIG: t->config = val; goto out; 175 | case TIMER_REG_RT_SCALER_US: t->scalar = val; goto out; 176 | 177 | case TIMER_IRQ_MASK: 178 | m = sl_device_get_irq_mux(t->dev); 179 | sl_irq_mux_set_enabled(m, ~val); 180 | goto out; 181 | 182 | case TIMER_IRQ_STATUS: { 183 | m = sl_device_get_irq_mux(t->dev); 184 | u4 vec = sl_irq_mux_get_active(m); 185 | vec &= ~val; 186 | sl_irq_mux_set_active(m, vec); 187 | goto out; 188 | } 189 | 190 | case TIMER_REG_DEV_TYPE: 191 | case TIMER_REG_DEV_VERSION: 192 | case TIMER_REG_STATUS: 193 | case TIMER_REG_NUM_UNITS: 194 | err = SL_ERR_IO_NOWR; 195 | goto out; 196 | 197 | // default: err = SL_ERR_IO_INVALID; goto out; 198 | default: break; 199 | } 200 | if (addr >= TIMER_APERTURE_LENGTH(t->num_units)) { 201 | err = SL_ERR_IO_INVALID; 202 | goto out; 203 | } 204 | u4 reg; 205 | sled_timer_unit_t *u = addr_to_unit(t, addr, ®); 206 | switch (reg) { 207 | case 0: // TIMER_REG_UNIT_CONFIG 208 | timer_set_unit_config_locked(t, u, val); break; 209 | 210 | case 1: // TIMER_REG_UNIT_RESET_VAL_LO 211 | u->reset_val = (u->reset_val & 0xffffffff00000000) | val; break; 212 | 213 | case 2: // TIMER_REG_UNIT_RESET_VAL_HI 214 | u->reset_val = (u->reset_val & 0xffffffff) | ((u8)val << 32); break; 215 | 216 | case 3: // TIMER_REG_UNIT_CURRENT_VAL_LO 217 | case 4: // TIMER_REG_UNIT_CURRENT_VAL_HI 218 | err = SL_ERR_IO_NOWR; break; 219 | 220 | default: 221 | err = SL_ERR_IO_INVALID; break; 222 | } 223 | 224 | out: 225 | sl_device_unlock(t->dev); 226 | return err; 227 | } 228 | 229 | static void sled_timer_destroy(sl_dev_t *d) { 230 | sled_timer_t *t = sl_device_get_context(d); 231 | free(t); 232 | } 233 | 234 | static int sled_timer_create(sl_dev_t *d, sl_dev_config_t *cfg) { 235 | sled_timer_t *t = calloc(1, sizeof(sled_timer_t)); 236 | if (t == NULL) return SL_ERR_MEM; 237 | 238 | t->dev = d; 239 | cfg->aperture = TIMER_APERTURE_LENGTH(TIMER_MAX_UNITS); 240 | sl_device_set_context(d, t); 241 | t->chrono = sl_machine_get_chrono(cfg->machine); 242 | t->num_units = 1; 243 | t->scalar = 1; 244 | for (int i = 0; i < TIMER_MAX_UNITS; i++) t->unit[i].timer = t; 245 | return 0; 246 | } 247 | 248 | static const sl_dev_ops_t timer_ops = { 249 | .type = SL_DEV_SLED_TIMER, 250 | .read = timer_read, 251 | .write = timer_write, 252 | .create = sled_timer_create, 253 | .destroy = sled_timer_destroy, 254 | }; 255 | 256 | DECLARE_DEVICE(sled_timer, SL_DEV_TIMER, &timer_ops); 257 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SDKDIR ?= ../sdk 2 | BLD_BASEDIR ?= build 3 | APPS ?= sled 4 | 5 | BLD_HOST_OBJDIR ?= $(BLD_BASEDIR)/obj 6 | BLD_HOST_BINDIR ?= $(BLD_BASEDIR) 7 | BLD_HOST_LIBDIR ?= $(BLD_BASEDIR)/lib 8 | BLD_HOST_INCDIR ?= $(SDKDIR)/include 9 | BLD_HOST_OS ?= $(shell uname -s) 10 | BLD_HOST_UNIVERSAL ?= 0 11 | BLD_HOST_USE_SANITIZERS ?= 1 12 | BLD_HOST_LTO ?= 0 13 | 14 | SILENT ?= @ 15 | 16 | ############################################################################## 17 | # C build environment 18 | ############################################################################## 19 | 20 | # don't delete some intermediate files 21 | .SECONDARY: 22 | 23 | BLD_HOST_CC ?= clang 24 | BLD_HOST_CXX ?= clang++ 25 | BLD_HOST_AS ?= clang 26 | BLD_HOST_LD := clang++ 27 | ifeq ($(BLD_HOST_OS),Darwin) 28 | # libtool required for fat archives 29 | BLD_HOST_AR := libtool 30 | BLD_HOST_ARFLAGS := -static -o 31 | else 32 | BLD_HOST_AR ?= ar 33 | BLD_HOST_ARFLAGS ?= -c -q 34 | endif 35 | 36 | CFLAGS := -Wall -MMD -Wint-conversion -Wincompatible-pointer-types 37 | DEFINES := 38 | LDFLAGS := 39 | 40 | ############################################################################## 41 | # build type 42 | ############################################################################## 43 | 44 | # Select by choosing BUILD on the command line 45 | BUILD ?= release 46 | 47 | ifeq ($(RV_TRACE),1) 48 | DEFINES += -DRV_TRACE=1 -DWITH_SYMBOLS=1 49 | endif 50 | 51 | ifeq ($(BUILD),release) 52 | CFLAGS += -g -O3 53 | else 54 | CFLAGS += -g3 -O0 55 | DEFINES += -DBUILD_DEBUG=1 56 | BLD_HOST_LTO := 0 57 | endif 58 | 59 | ifeq ($(BLD_HOST_USE_SANITIZERS),1) 60 | CFLAGS += -fsanitize=address,nullability,undefined -ftrivial-auto-var-init=pattern 61 | endif 62 | 63 | ifeq ($(BLD_HOST_UNIVERSAL),1) 64 | CFLAGS += -arch arm64 -arch x86_64 65 | endif 66 | 67 | ifeq ($(BLD_HOST_LTO),1) 68 | CFLAGS += -flto=thin 69 | LDFLAGS += -flto=thin 70 | endif 71 | 72 | CXXFLAGS := $(CFLAGS) -std=c++17 73 | LIBTARGETS := 74 | INCLUDES := -Iinclude 75 | CSOURCES := 76 | CLIBFLAGS := -fvisibility=hidden -fPIC 77 | DOBJS := 78 | PLATFORMS := 79 | DEVICES := 80 | 81 | ############################################################################## 82 | # include app defines 83 | ############################################################################## 84 | 85 | define include_app 86 | 87 | APP := $(1) 88 | $(1)_LINK_LIBS := 89 | $(1)_CSOURCES := 90 | $(1)_CXXSOURCES := 91 | $(1)_DEFINES := $(DEFINES) 92 | $(1)_INCLUDES := $(INCLUDES) 93 | 94 | include app/$(1)/build.mk 95 | 96 | LIBTARGETS += $$($(1)_LIBS) 97 | INCLUDES += $$($(1)_INCLUDES) 98 | $(1)_OBJS := $$($(1)_CSOURCES:%.c=$(BLD_HOST_OBJDIR)/%.c.o) $$($(1)_CXXSOURCES:%.cpp=$(BLD_HOST_OBJDIR)/%.cpp.o) 99 | DOBJS += $$($(1)_OBJS) 100 | PLATFORMS += $$($(1)_PLATFORM) 101 | 102 | endef 103 | 104 | $(foreach app,$(APPS),$(eval $(call include_app,$(app)))) 105 | 106 | ############################################################################## 107 | # include platform defines 108 | ############################################################################## 109 | 110 | define include_plat 111 | 112 | PLAT := $(1) 113 | $(1)_INCLUDES := 114 | $(1)_DEVICES := 115 | 116 | include plat/$(1)/build.mk 117 | 118 | DEVICES += $$($(1)_DEVICES) 119 | 120 | endef 121 | 122 | $(foreach plat,$(PLATFORMS),$(eval $(call include_plat,$(plat)))) 123 | 124 | ############################################################################## 125 | # top level build rules 126 | ############################################################################## 127 | 128 | .PHONY: all 129 | all: apps 130 | 131 | 132 | $(BLD_HOST_OBJDIR)/core/%.c.o: core/%.c 133 | $(SILENT) mkdir -p $(dir $@) 134 | @echo " [cc]" $< 135 | $(SILENT) $(BLD_HOST_CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -Icore/inc -c -o $@ $< 136 | 137 | $(BLD_HOST_OBJDIR)/core/%.cpp.o: core/%.cpp 138 | $(SILENT) mkdir -p $(dir $@) 139 | @echo " [c++]" $< 140 | $(SILENT) $(BLD_HOST_CXX) $(CXXFLAGS) $(INCLUDES) $(DEFINES) -Icore/inc -c -o $@ $< 141 | 142 | $(BLD_HOST_OBJDIR)/dev/%.c.o: dev/%.c 143 | $(SILENT) mkdir -p $(dir $@) 144 | @echo " [cc]" $< 145 | $(SILENT) $(BLD_HOST_CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -Icore/inc -c -o $@ $< 146 | 147 | ############################################################################## 148 | # device build rules 149 | ############################################################################## 150 | 151 | include dev/build.mk 152 | 153 | DEVICES := $(sort $(DEVICES)) 154 | 155 | define build_dev 156 | 157 | $(1): $(BLD_HOST_LIBDIR)/dev/$(1).a 158 | 159 | $(1)_OBJS := $$($(1)_CSOURCES:%.c=$(BLD_HOST_OBJDIR)/%.c.o) 160 | 161 | $(BLD_HOST_LIBDIR)/dev/$(1).a: $$($(1)_OBJS) 162 | $(SILENT) mkdir -p $$(dir $$@) 163 | @echo " [ar]" $$(notdir $$@) 164 | $(SILENT) rm -f $$@ 165 | $(SILENT) $(BLD_HOST_AR) $(BLD_HOST_ARFLAGS) $$@ $$^ 166 | 167 | endef 168 | 169 | $(foreach dev,$(DEVICES),$(eval $(call build_dev,$(dev)))) 170 | 171 | devices: $(DEVICES:%=$(BLD_HOST_LIBDIR)/dev/%) 172 | 173 | ############################################################################## 174 | # app build rules 175 | ############################################################################## 176 | 177 | define build_app 178 | 179 | $(1)_INCLUDES += $$($$($(1)_PLATFORM)_INCLUDES) 180 | $(1)_DEVICE_LIBS := $$($$($(1)_PLATFORM)_DEVICES:%=$(BLD_HOST_LIBDIR)/dev/%.a) 181 | 182 | $(1): $(BLD_HOST_BINDIR)/$(1) 183 | 184 | $(BLD_HOST_BINDIR)/$(1): $($(1)_OBJS) $(BLD_HOST_LIBDIR)/libsled.a $(BLD_HOST_OBJDIR)/app/$(1)/dyn_dev_list.o $$($(1)_DEVICE_LIBS) 185 | $(SILENT) mkdir -p $$(dir $$@) 186 | @echo " [ld]" $$(notdir $$@) 187 | $(SILENT) $(BLD_HOST_LD) $(CFLAGS) $(LDFLAGS) -o $$@ $$^ 188 | 189 | $(BLD_HOST_OBJDIR)/app/%.c.o: app/%.c 190 | $(SILENT) mkdir -p $$(dir $$@) 191 | @echo " [cc]" $$< 192 | $(SILENT) $(BLD_HOST_CC) $(CFLAGS) $$($(1)_INCLUDES) $($(1)_DEFINES) -c -o $$@ $$< 193 | 194 | $(BLD_HOST_OBJDIR)/app/%.cpp.o: app/%.cpp 195 | $(SILENT) mkdir -p $$(dir $$@) 196 | @echo " [c++]" $$< 197 | $(SILENT) $(BLD_HOST_CXX) $(CXXFLAGS) $$($(1)_INCLUDES) $($(1)_DEFINES) -c -o $$@ $$< 198 | 199 | # device dynamic list generation 200 | $(BLD_HOST_OBJDIR)/app/$(1)/dyn_dev_list.o: $(BLD_HOST_OBJDIR)/app/$(1)/dyn_dev_list.c 201 | @echo " [cc]" $$(notdir $$<) 202 | $(SILENT) $(BLD_HOST_CC) $(CFLAGS) $$($(1)_INCLUDES) $($(1)_DEFINES) -c -o $$@ $$< 203 | 204 | $(BLD_HOST_OBJDIR)/app/$(1)/dyn_dev_list.c: $$($(1)_DEVICE_LIBS) 205 | @echo " [dyndev]" $$(notdir $$@) 206 | @nm --defined-only $$^ | sed -n 's/.* [_]*\(_sl_device_dyn_ops_.*\)/extern const void * \1;/p' > $$@ 207 | @echo "const void * dyn_dev_ops_list[] = {" >> $$@ 208 | @nm --defined-only $$^ | sed -n 's/.* [_]*\(_sl_device_dyn_ops_.*\)/\&\1,/p' >> $$@ 209 | @echo "(void *)0 };" >> $$@ 210 | 211 | endef 212 | 213 | $(foreach app,$(APPS),$(eval $(call build_app,$(app)))) 214 | 215 | apps: $(APPS:%=$(BLD_HOST_BINDIR)/%) 216 | 217 | ############################################################################## 218 | # install rules 219 | ############################################################################## 220 | 221 | $(BLD_HOST_INCDIR)/%.h: include/%.h 222 | $(SILENT) mkdir -p $(dir $@) 223 | $(SILENT) cp $^ $@ 224 | 225 | ifeq ($(MAKECMDGOALS),$(filter $(MAKECMDGOALS),install install_headers)) 226 | PUB_HEADERS := $(shell find include -name '*.h') 227 | PUB_HEADERS := $(PUB_HEADERS:include/%=$(BLD_HOST_INCDIR)/%) 228 | endif 229 | 230 | install_headers: $(PUB_HEADERS) 231 | 232 | install: apps install_headers 233 | 234 | 235 | ############################################################################## 236 | # libsled build rules 237 | ############################################################################## 238 | 239 | LIB_CSOURCES := 240 | LIB_CXXSOURCES := 241 | 242 | include core/build.mk 243 | include core/extension/build.mk 244 | 245 | LIB_OBJS := $(LIB_CSOURCES:%.c=$(BLD_HOST_OBJDIR)/%.c.o) $(LIB_CXXSOURCES:%.cpp=$(BLD_HOST_OBJDIR)/%.cpp.o) 246 | 247 | DOBJS += $(LIB_OBJS) 248 | 249 | lib: $(BLD_HOST_LIBDIR)/libsled.a 250 | 251 | $(BLD_HOST_LIBDIR)/libsled.a: $(LIB_OBJS) 252 | @echo " [ar]" $(notdir $@) 253 | $(SILENT) mkdir -p $(dir $@) 254 | $(SILENT) rm -f $@ 255 | $(SILENT) $(BLD_HOST_AR) $(BLD_HOST_ARFLAGS) $@ $^ 256 | 257 | 258 | ############################################################################## 259 | # others 260 | ############################################################################## 261 | 262 | .PHONY: clean 263 | clean: 264 | $(SILENT) rm -rf $(BLD_BASEDIR) 265 | 266 | 267 | ifneq ($(MAKECMDGOALS),clean) 268 | -include $(DOBJS:.o=.d) 269 | endif -------------------------------------------------------------------------------- /core/mapper.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define MAP_ALLOC_INCREMENT 256 18 | 19 | struct map_ent { 20 | u8 va_base; 21 | u8 va_end; 22 | u8 pa_base; 23 | u4 domain; 24 | u2 permissions; 25 | u1 type; 26 | sl_map_ep_t *ep; 27 | }; 28 | 29 | static int ent_compare(const void *v0, const void *v1) { 30 | map_ent_t *a = *(map_ent_t **)v0; 31 | map_ent_t *b = *(map_ent_t **)v1; 32 | if (a->va_base < b->va_base) return -1; 33 | if (a->va_base > b->va_base) return 1; 34 | return 0; 35 | } 36 | 37 | static map_ent_t * create_map_ent(sl_mapping_t *m) { 38 | map_ent_t *n = malloc(sizeof(*n)); 39 | if (n == NULL) return NULL; 40 | n->va_base = m->input_base; 41 | n->va_end = m->input_base + m->length; 42 | n->pa_base = m->output_base; 43 | n->domain = m->domain; 44 | n->permissions = m->permissions; 45 | n->type = m->type; 46 | n->ep = m->ep; 47 | return n; 48 | } 49 | 50 | static void finalize_mappings(sl_mapper_t *m) { 51 | qsort(m->list, m->num_ents, sizeof(map_ent_t *), ent_compare); 52 | } 53 | 54 | int sl_mappper_add_mapping(sl_mapper_t *m, sl_mapping_t *ent) { 55 | map_ent_t *n = create_map_ent(ent); 56 | if (n == NULL) return SL_ERR_MEM; 57 | 58 | if ((m->num_ents % MAP_ALLOC_INCREMENT) == 0) { 59 | void * p = realloc(m->list, (m->num_ents + MAP_ALLOC_INCREMENT) * sizeof(void *)); 60 | if (p == NULL) { 61 | free(n); 62 | return SL_ERR_MEM; 63 | } 64 | m->list = p; 65 | } 66 | 67 | m->list[m->num_ents] = n; 68 | m->num_ents++; 69 | finalize_mappings(m); 70 | return 0; 71 | } 72 | 73 | static map_ent_t * ent_for_address(sl_mapper_t *m, u8 addr) { 74 | if (m->num_ents == 0) return NULL; 75 | 76 | u4 start, end, cur; 77 | start = 0; 78 | end = m->num_ents; 79 | 80 | do { 81 | cur = (start + end) / 2; 82 | map_ent_t *ent = m->list[cur]; 83 | if (ent->va_base > addr) { 84 | end = cur; 85 | } else { 86 | if (ent->va_end > addr) return ent; // found 87 | if (start == cur) break; 88 | start = cur; 89 | } 90 | } while (start != end); 91 | return NULL; 92 | } 93 | 94 | void sl_mapper_set_mode(sl_mapper_t *m, int mode) { m->mode = mode; } 95 | sl_mapper_t * sl_mapper_get_next(sl_mapper_t *m) { return m->next; } 96 | sl_map_ep_t * sl_mapper_get_ep(sl_mapper_t *m) { return &m->ep; } 97 | 98 | static int mapper_ep_io(sl_map_ep_t *ep, sl_io_op_t *op) { 99 | sl_mapper_t *m = containerof(ep, sl_mapper_t, ep); 100 | for ( ; ; ) { 101 | if (m->mode == SL_MAP_OP_MODE_BLOCK) 102 | return SL_ERR_IO_NOMAP; 103 | if (m->mode != SL_MAP_OP_MODE_PASSTHROUGH) 104 | break; 105 | m = m->next; 106 | } 107 | 108 | if ((op->op == IO_OP_RESOLVE) || IO_IS_ATOMIC(op->op)) { 109 | map_ent_t *e = ent_for_address(m, op->addr); 110 | if (e == NULL) 111 | return SL_ERR_IO_NOMAP; 112 | const u8 offset = op->addr - e->va_base; 113 | op->addr = e->pa_base + offset; 114 | return e->ep->io(e->ep, op); 115 | } 116 | 117 | int err = 0; 118 | u8 addr = op->addr; 119 | const u2 size = op->size; 120 | u8 len = size * op->count; 121 | sl_io_op_t subop = *op; 122 | while (len) { 123 | // todo: check alignment 124 | 125 | map_ent_t *e = ent_for_address(m, addr); 126 | if (e == NULL) 127 | return SL_ERR_IO_NOMAP; 128 | 129 | u8 offset = addr - e->va_base; 130 | u8 avail = e->va_end - e->va_base - offset; 131 | if (avail > len) avail = len; 132 | 133 | // todo: verify we are not lopping off trailing bytes 134 | 135 | subop.addr = e->pa_base + offset; 136 | subop.count = avail / size; 137 | 138 | if ((err = e->ep->io(e->ep, &subop))) 139 | return err; 140 | len -= avail; 141 | addr += avail; 142 | subop.buf += avail; 143 | } 144 | return 0; 145 | } 146 | 147 | int sl_mapper_io(void *ctx, sl_io_op_t *op) { 148 | sl_mapper_t *m = ctx; 149 | return mapper_ep_io(&m->ep, op); 150 | } 151 | 152 | resultptr_t sl_mapper_resolve(sl_mapper_t *m, u8 addr, u8 *len_out) { 153 | resultptr_t res; 154 | sl_io_op_t op; 155 | 156 | op.addr = addr; 157 | op.size = 1; 158 | op.op = IO_OP_RESOLVE; 159 | op.align = 1; 160 | op.count = 1; 161 | res.err = mapper_ep_io(&m->ep, &op); 162 | if (res.err == 0) { 163 | *len_out = op.arg[1]; 164 | res.value = (void *)op.arg[0]; 165 | } 166 | return res; 167 | } 168 | 169 | int mapper_update(sl_mapper_t *m, sl_event_t *ev) { 170 | if (ev->type != SL_MAP_EV_TYPE_UPDATE) return SL_ERR_ARG; 171 | int err = 0; 172 | 173 | u4 op = ev->arg[0]; 174 | if (op & SL_MAP_OP_REPLACE) { 175 | u4 count = ev->arg[1]; 176 | sl_mapping_t *ent_list = (sl_mapping_t *)(ev->arg[2]); 177 | 178 | u4 size = ((count + MAP_ALLOC_INCREMENT - 1) / MAP_ALLOC_INCREMENT) * MAP_ALLOC_INCREMENT; 179 | map_ent_t **list = calloc(size, sizeof(map_ent_t *)); 180 | if (list == NULL) return SL_ERR_MEM; 181 | 182 | for (u4 i = 0; i < count; i++) { 183 | list[i] = create_map_ent(ent_list + i); 184 | if (list[i] == NULL) { 185 | err = SL_ERR_MEM; 186 | break; 187 | } 188 | } 189 | 190 | u4 fnum = m->num_ents; 191 | map_ent_t **flist = m->list; 192 | if (err) { 193 | fnum = count; 194 | flist = list; 195 | } 196 | for (u4 i = 0; i < fnum; i++) { 197 | if (flist[i] != NULL) free(flist[i]); 198 | } 199 | free(flist); 200 | free(ent_list); 201 | if (err) return err; 202 | 203 | m->num_ents = count; 204 | m->list = list; 205 | finalize_mappings(m); 206 | } 207 | m->mode = op & SL_MAP_OP_MODE_MASK; 208 | return 0; 209 | } 210 | 211 | void mapper_init(sl_mapper_t *m) { 212 | memset(m, 0, sizeof(*m)); 213 | m->ep.io = mapper_ep_io; 214 | } 215 | 216 | int sl_mapper_create(sl_mapper_t **map_out) { 217 | sl_mapper_t *m = calloc(1, sizeof(*m)); 218 | if (m == NULL) return SL_ERR_MEM; 219 | m->ep.io = mapper_ep_io; 220 | *map_out = m; 221 | return 0; 222 | } 223 | 224 | void mapper_shutdown(sl_mapper_t *m) { 225 | for (u4 i = 0; i < m->num_ents; i++) { 226 | free(m->list[i]); 227 | } 228 | free(m->list); 229 | } 230 | 231 | void sl_mapper_destroy(sl_mapper_t *m) { 232 | if (m == NULL) return; 233 | mapper_shutdown(m); 234 | free(m); 235 | } 236 | 237 | void mapper_print_mappings(sl_mapper_t *m) { 238 | printf("mapper\n"); 239 | bool has_next = false; 240 | switch (m->mode) { 241 | case SL_MAP_OP_MODE_PASSTHROUGH: 242 | printf(" passthrough\n"); 243 | has_next = true; 244 | break; 245 | 246 | case SL_MAP_OP_MODE_BLOCK: 247 | printf(" blocked\n"); 248 | break; 249 | 250 | case SL_MAP_OP_MODE_TRANSLATE: 251 | for (u4 i = 0; i < m->num_ents; i++) { 252 | sl_dev_t *d; 253 | map_ent_t *ent = m->list[i]; 254 | printf(" %#20" PRIx64 " %#20" PRIx64 " %#20" PRIx64 "", ent->pa_base, ent->va_base, ent->va_end - ent->va_base); 255 | 256 | switch (ent->type) { 257 | case SL_MAP_TYPE_MEMORY: 258 | printf(" memory\n"); 259 | break; 260 | 261 | case SL_MAP_TYPE_DEVICE: 262 | d = device_get_for_ep(ent->ep); 263 | printf(" device: %s\n", d->name); 264 | if (d->ops->type == SL_DEV_REG_VIEW) { 265 | // printf("reg view mapping:\n"); 266 | sl_reg_view_print_mappings(d, ent->va_base); 267 | } 268 | break; 269 | 270 | case SL_MAP_TYPE_MAPPER: 271 | printf(" mapper\n"); 272 | has_next = true; 273 | break; 274 | 275 | default: 276 | printf(" \n"); 277 | break; 278 | } 279 | } 280 | break; 281 | 282 | default: 283 | printf(" unhandled mode\n"); 284 | break; 285 | } 286 | 287 | if (has_next) mapper_print_mappings(m->next); 288 | } 289 | -------------------------------------------------------------------------------- /include/sled/elf/elf32.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 1996-1998 John D. Polstra. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | * 28 | * $FreeBSD$ 29 | */ 30 | 31 | #ifndef _SYS_ELF32_H_ 32 | #define _SYS_ELF32_H_ 1 33 | 34 | #include 35 | 36 | /* 37 | * ELF definitions common to all 32-bit architectures. 38 | */ 39 | 40 | typedef u4 Elf32_Addr; 41 | typedef u2 Elf32_Half; 42 | typedef u4 Elf32_Off; 43 | typedef i4 Elf32_Sword; 44 | typedef u4 Elf32_Word; 45 | typedef u8 Elf32_Lword; 46 | 47 | typedef Elf32_Word Elf32_Hashelt; 48 | 49 | /* Non-standard class-dependent datatype used for abstraction. */ 50 | typedef Elf32_Word Elf32_Size; 51 | typedef Elf32_Sword Elf32_Ssize; 52 | 53 | /* 54 | * ELF header. 55 | */ 56 | 57 | typedef struct { 58 | unsigned char e_ident[EI_NIDENT]; /* File identification. */ 59 | Elf32_Half e_type; /* File type. */ 60 | Elf32_Half e_machine; /* Machine architecture. */ 61 | Elf32_Word e_version; /* ELF format version. */ 62 | Elf32_Addr e_entry; /* Entry point. */ 63 | Elf32_Off e_phoff; /* Program header file offset. */ 64 | Elf32_Off e_shoff; /* Section header file offset. */ 65 | Elf32_Word e_flags; /* Architecture-specific flags. */ 66 | Elf32_Half e_ehsize; /* Size of ELF header in bytes. */ 67 | Elf32_Half e_phentsize; /* Size of program header entry. */ 68 | Elf32_Half e_phnum; /* Number of program header entries. */ 69 | Elf32_Half e_shentsize; /* Size of section header entry. */ 70 | Elf32_Half e_shnum; /* Number of section header entries. */ 71 | Elf32_Half e_shstrndx; /* Section name strings section. */ 72 | } Elf32_Ehdr; 73 | 74 | /* 75 | * Shared object information, found in SHT_MIPS_LIBLIST. 76 | */ 77 | 78 | typedef struct { 79 | Elf32_Word l_name; /* The name of a shared object. */ 80 | Elf32_Word l_time_stamp; /* 32-bit timestamp. */ 81 | Elf32_Word l_checksum; /* Checksum of visible symbols, sizes. */ 82 | Elf32_Word l_version; /* Interface version string index. */ 83 | Elf32_Word l_flags; /* Flags (LL_*). */ 84 | } Elf32_Lib; 85 | 86 | /* 87 | * Section header. 88 | */ 89 | 90 | typedef struct { 91 | Elf32_Word sh_name; /* Section name (index into the 92 | section header string table). */ 93 | Elf32_Word sh_type; /* Section type. */ 94 | Elf32_Word sh_flags; /* Section flags. */ 95 | Elf32_Addr sh_addr; /* Address in memory image. */ 96 | Elf32_Off sh_offset; /* Offset in file. */ 97 | Elf32_Word sh_size; /* Size in bytes. */ 98 | Elf32_Word sh_link; /* Index of a related section. */ 99 | Elf32_Word sh_info; /* Depends on section type. */ 100 | Elf32_Word sh_addralign; /* Alignment in bytes. */ 101 | Elf32_Word sh_entsize; /* Size of each entry in section. */ 102 | } Elf32_Shdr; 103 | 104 | /* 105 | * Program header. 106 | */ 107 | 108 | typedef struct { 109 | Elf32_Word p_type; /* Entry type. */ 110 | Elf32_Off p_offset; /* File offset of contents. */ 111 | Elf32_Addr p_vaddr; /* Virtual address in memory image. */ 112 | Elf32_Addr p_paddr; /* Physical address (not used). */ 113 | Elf32_Word p_filesz; /* Size of contents in file. */ 114 | Elf32_Word p_memsz; /* Size of contents in memory. */ 115 | Elf32_Word p_flags; /* Access permission flags. */ 116 | Elf32_Word p_align; /* Alignment in memory and file. */ 117 | } Elf32_Phdr; 118 | 119 | /* 120 | * Dynamic structure. The ".dynamic" section contains an array of them. 121 | */ 122 | 123 | typedef struct { 124 | Elf32_Sword d_tag; /* Entry type. */ 125 | union { 126 | Elf32_Word d_val; /* Integer value. */ 127 | Elf32_Addr d_ptr; /* Address value. */ 128 | } d_un; 129 | } Elf32_Dyn; 130 | 131 | /* 132 | * Relocation entries. 133 | */ 134 | 135 | /* Relocations that don't need an addend field. */ 136 | typedef struct { 137 | Elf32_Addr r_offset; /* Location to be relocated. */ 138 | Elf32_Word r_info; /* Relocation type and symbol index. */ 139 | } Elf32_Rel; 140 | 141 | /* Relocations that need an addend field. */ 142 | typedef struct { 143 | Elf32_Addr r_offset; /* Location to be relocated. */ 144 | Elf32_Word r_info; /* Relocation type and symbol index. */ 145 | Elf32_Sword r_addend; /* Addend. */ 146 | } Elf32_Rela; 147 | 148 | /* Macros for accessing the fields of r_info. */ 149 | #define ELF32_R_SYM(info) ((info) >> 8) 150 | #define ELF32_R_TYPE(info) ((unsigned char)(info)) 151 | 152 | /* Macro for constructing r_info from field values. */ 153 | #define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type)) 154 | 155 | typedef Elf32_Word Elf32_Relr; 156 | 157 | /* 158 | * Note entry header 159 | */ 160 | typedef Elf_Note Elf32_Nhdr; 161 | 162 | /* 163 | * Move entry 164 | */ 165 | typedef struct { 166 | Elf32_Lword m_value; /* symbol value */ 167 | Elf32_Word m_info; /* size + index */ 168 | Elf32_Word m_poffset; /* symbol offset */ 169 | Elf32_Half m_repeat; /* repeat count */ 170 | Elf32_Half m_stride; /* stride info */ 171 | } Elf32_Move; 172 | 173 | /* 174 | * The macros compose and decompose values for Move.r_info 175 | * 176 | * sym = ELF32_M_SYM(M.m_info) 177 | * size = ELF32_M_SIZE(M.m_info) 178 | * M.m_info = ELF32_M_INFO(sym, size) 179 | */ 180 | #define ELF32_M_SYM(info) ((info)>>8) 181 | #define ELF32_M_SIZE(info) ((unsigned char)(info)) 182 | #define ELF32_M_INFO(sym, size) (((sym)<<8)+(unsigned char)(size)) 183 | 184 | /* 185 | * Hardware/Software capabilities entry 186 | */ 187 | typedef struct { 188 | Elf32_Word c_tag; /* how to interpret value */ 189 | union { 190 | Elf32_Word c_val; 191 | Elf32_Addr c_ptr; 192 | } c_un; 193 | } Elf32_Cap; 194 | 195 | /* 196 | * Symbol table entries. 197 | */ 198 | 199 | typedef struct { 200 | Elf32_Word st_name; /* String table index of name. */ 201 | Elf32_Addr st_value; /* Symbol value. */ 202 | Elf32_Word st_size; /* Size of associated object. */ 203 | unsigned char st_info; /* Type and binding information. */ 204 | unsigned char st_other; /* Reserved (not used). */ 205 | Elf32_Half st_shndx; /* Section index of symbol. */ 206 | } Elf32_Sym; 207 | 208 | /* Macros for accessing the fields of st_info. */ 209 | #define ELF32_ST_BIND(info) ((info) >> 4) 210 | #define ELF32_ST_TYPE(info) ((info) & 0xf) 211 | 212 | /* Macro for constructing st_info from field values. */ 213 | #define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 214 | 215 | /* Macro for accessing the fields of st_other. */ 216 | #define ELF32_ST_VISIBILITY(oth) ((oth) & 0x3) 217 | 218 | /* Structures used by Sun & GNU symbol versioning. */ 219 | typedef struct 220 | { 221 | Elf32_Half vd_version; 222 | Elf32_Half vd_flags; 223 | Elf32_Half vd_ndx; 224 | Elf32_Half vd_cnt; 225 | Elf32_Word vd_hash; 226 | Elf32_Word vd_aux; 227 | Elf32_Word vd_next; 228 | } Elf32_Verdef; 229 | 230 | typedef struct 231 | { 232 | Elf32_Word vda_name; 233 | Elf32_Word vda_next; 234 | } Elf32_Verdaux; 235 | 236 | typedef struct 237 | { 238 | Elf32_Half vn_version; 239 | Elf32_Half vn_cnt; 240 | Elf32_Word vn_file; 241 | Elf32_Word vn_aux; 242 | Elf32_Word vn_next; 243 | } Elf32_Verneed; 244 | 245 | typedef struct 246 | { 247 | Elf32_Word vna_hash; 248 | Elf32_Half vna_flags; 249 | Elf32_Half vna_other; 250 | Elf32_Word vna_name; 251 | Elf32_Word vna_next; 252 | } Elf32_Vernaux; 253 | 254 | typedef Elf32_Half Elf32_Versym; 255 | 256 | typedef struct { 257 | Elf32_Half si_boundto; /* direct bindings - symbol bound to */ 258 | Elf32_Half si_flags; /* per symbol flags */ 259 | } Elf32_Syminfo; 260 | 261 | typedef struct { 262 | Elf32_Word ch_type; 263 | Elf32_Word ch_size; 264 | Elf32_Word ch_addralign; 265 | } Elf32_Chdr; 266 | 267 | #endif /* !_SYS_ELF32_H_ */ -------------------------------------------------------------------------------- /core/riscv/riscv.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2022-2024 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | int riscv_core_exception_enter(sl_core_t *core, u8 cause, u8 addr); 24 | static void riscv_core_shutdown(sl_core_t *c); 25 | static void riscv_core_destroy(sl_core_t *c); 26 | 27 | static void riscv_core_set_reg(sl_core_t *c, u4 reg, u8 value) { 28 | rv_core_t *rc = (rv_core_t *)c; 29 | 30 | if (reg == 0) return; // always zero 31 | if (reg < 32) { 32 | rc->core.r[reg] = value; 33 | return; 34 | } 35 | 36 | rv_sr_pl_t *sr = NULL; 37 | if (reg >= SL_RV_CORE_REG_BASE) sr = rv_get_pl_csrs(rc, RV_PL_MACHINE); 38 | 39 | switch (reg) { 40 | case SL_CORE_REG_PC: rc->core.pc = value; break; 41 | case SL_CORE_REG_SP: rc->core.r[RV_SP] = value; break; 42 | case SL_CORE_REG_LR: rc->core.r[RV_RA] = value; break; 43 | case SL_RV_CORE_REG(RV_CSR_MTVEC): sr->tvec = value; break; 44 | case SL_RV_CORE_REG(RV_CSR_MSCRATCH): sr->scratch = value; break; 45 | case SL_RV_CORE_REG(RV_CSR_MEPC): sr->epc = value; break; 46 | case SL_RV_CORE_REG(RV_CSR_MCAUSE): sr->cause = value; break; 47 | case SL_RV_CORE_REG(RV_CSR_MTVAL): sr->tval = value; break; 48 | case SL_RV_CORE_REG(RV_CSR_MIP): sr->ip = value; break; 49 | default: 50 | assert(false); 51 | break; 52 | } 53 | } 54 | 55 | static u8 riscv_core_get_reg(sl_core_t *c, u4 reg) { 56 | rv_core_t *rc = (rv_core_t *)c; 57 | 58 | if (reg == 0) return 0; // always zero 59 | if (reg < 32) 60 | return rc->core.r[reg]; 61 | 62 | switch (reg) { 63 | case SL_CORE_REG_PC: return rc->core.pc; 64 | case SL_CORE_REG_SP: return rc->core.r[RV_SP]; 65 | case SL_CORE_REG_LR: return rc->core.r[RV_RA]; 66 | case SL_CORE_REG_ARG0: return rc->core.r[RV_A0]; 67 | case SL_CORE_REG_ARG1: return rc->core.r[RV_A1]; 68 | default: 69 | assert(false); 70 | return 0xbaddbaddbaddbadd; 71 | } 72 | } 73 | 74 | // Synchronous irq handler - invokes an exception before the next instruction is dispatched. 75 | static int riscv_interrupt(sl_engine_t *e) { 76 | sl_irq_ep_t *ep = &e->irq_ep; 77 | rv_core_t *rc = containerof(e, rv_core_t, core.engine); 78 | 79 | static const u1 irq_pri[] = { 80 | RV_INT_EXTERNAL_M, RV_INT_TIMER_M, RV_INT_SW_M, RV_INT_EXTERNAL_S, RV_INT_TIMER_S, RV_INT_SW_S 81 | }; 82 | 83 | for (int i = 0; i < 6; i++) { 84 | const u1 bit = irq_pri[i]; 85 | const u4 num = (1u << bit); 86 | if (ep->asserted & num) 87 | return riscv_core_exception_enter(&rc->core, bit | RV_CAUSE64_INT, 0); 88 | } 89 | return SL_ERR_STATE; 90 | } 91 | 92 | int sl_riscv_core_create(sl_core_params_t *p, sl_core_t **core_out) { 93 | rv_core_t *rc = calloc(1, sizeof(*rc)); 94 | if (rc == NULL) return SL_ERR_MEM; 95 | int err = sl_core_init(&rc->core, p, NULL); 96 | if (err) { 97 | free(rc); 98 | return err; 99 | } 100 | *core_out = &rc->core; 101 | rc->core.exception_enter = riscv_core_exception_enter; 102 | rc->core.set_reg = riscv_core_set_reg; 103 | rc->core.get_reg = riscv_core_get_reg; 104 | rc->core.shutdown = riscv_core_shutdown; 105 | rc->core.destroy = riscv_core_destroy; 106 | 107 | rc->core.options |= SL_CORE_OPT_ENDIAN_LITTLE; 108 | rc->mhartid = p->id; 109 | rc->core.engine.ops.interrupt = riscv_interrupt; 110 | rc->mimpid = 'sled'; 111 | rc->ext.name_for_sysreg = rv_name_for_sysreg; 112 | return 0; 113 | } 114 | 115 | static void riscv_core_shutdown(sl_core_t *c) { 116 | rv_core_t *rc = (rv_core_t *)c; 117 | if (rc->ext.destroy != NULL) rc->ext.destroy(rc->ext_private); 118 | } 119 | 120 | static void riscv_core_destroy(sl_core_t *c) { 121 | if (c == NULL) return; 122 | sl_core_shutdown(c); 123 | rv_core_t *rc = (rv_core_t *)c; 124 | free(rc); 125 | } 126 | 127 | 128 | typedef struct { 129 | char *name; 130 | u4 ext; 131 | u2 version; 132 | u2 patch; 133 | } rv_extension_t; 134 | 135 | typedef struct { 136 | const char *name; 137 | u4 option; 138 | } rv_extension_map_t; 139 | 140 | static const rv_extension_map_t ext_map[] = { 141 | { "m", SL_RISCV_EXT_M }, 142 | { "a", SL_RISCV_EXT_A }, 143 | { "f", SL_RISCV_EXT_F | SL_RISCV_EXT_ZICSR }, 144 | { "d", SL_RISCV_EXT_D | SL_RISCV_EXT_F | SL_RISCV_EXT_ZICSR }, 145 | { "c", SL_RISCV_EXT_C }, 146 | { "zicsr", SL_RISCV_EXT_ZICSR }, 147 | 148 | // todo: split extensions into finer-grained sets to support the dumb partical extensions below 149 | 150 | { "zmmul", SL_RISCV_EXT_M }, // zmmull is M without divide instructions. For now treat as M. 151 | 152 | // compressed instructions. 153 | { "zca", SL_RISCV_EXT_C }, // Zca - instructions in the C extension that do not include the floating-point loads and stores. 154 | { "zcf", SL_RISCV_EXT_C | SL_RISCV_EXT_F }, // Zcf - the existing set of compressed single precision floating point loads and stores: c.flw, c.flwsp, c.fsw, c.fswsp. 155 | { "zcd", SL_RISCV_EXT_C | SL_RISCV_EXT_F | SL_RISCV_EXT_D }, // Zcd - existing set of compressed double precision floating point loads and stores: c.fld, c.fldsp, c.fsd, c.fsdsp. 156 | { "zcb", SL_RISCV_EXT_C }, // Zcb - simple code-size saving instructions which are easy to implement on all CPUs 157 | // Zcmp - a set of instructions which may be executed as a series of existing 32-bit RISC-V instructions (push/pop and double move) 158 | // Zcmt - adds the table jump instructions and also adds the JVT CSR 159 | 160 | // atomic instructions 161 | { "zaamo", SL_RISCV_EXT_A }, // Zaamo - Atomic Memory Operations Extension 162 | { "zalrsc", SL_RISCV_EXT_A }, // Zalrsc - Load-Reserved/Store-conditional Extension 163 | }; 164 | 165 | static int parse_attribute(char *s, rv_extension_t *ex) { 166 | int len = strlen(s); 167 | if (len == 0) goto malformed; 168 | char *p = strrchr(s, 'p'); 169 | if ((p == NULL) || (p == s)) goto malformed; 170 | char *endp; 171 | u4 patch = strtoul(p + 1, &endp, 10); 172 | if (endp != s + len) goto malformed; 173 | *p = '\0'; 174 | char *v = p - 1; 175 | for ( ; v != s; v--) { 176 | if (!isdigit(*v)) break; 177 | } 178 | if (v == p - 1) goto malformed; 179 | u4 version = strtoul(v + 1, &endp, 10); 180 | if (endp != p) goto malformed; 181 | v[1] = '\0'; 182 | 183 | ex->name = s; 184 | ex->version = version; 185 | ex->patch = patch; 186 | return 0; 187 | 188 | malformed: 189 | return SL_ERR_ARG; 190 | } 191 | 192 | int riscv_decode_attributes(const char *attrib, u4 *arch_options_out) { 193 | int err = 0; 194 | char *at = strdup(attrib); 195 | if (at == NULL) return SL_ERR_MEM; 196 | u4 options = 0; 197 | 198 | printf("RISCV attributes: %s\n", at); 199 | 200 | const char *sep = "_"; 201 | char *lasts; 202 | char *s = strtok_r(at, sep, &lasts); 203 | if (s == NULL) { 204 | printf("invalid format string\n"); 205 | err = SL_ERR_ARG; 206 | goto out; 207 | } 208 | 209 | rv_extension_t ex; 210 | if ((err = parse_attribute(s, &ex))) { 211 | printf("invalid attribute string segment '%s'\n", s); 212 | goto out; 213 | } 214 | 215 | if (!strcmp(ex.name, "rv64i")) { 216 | options = SL_CORE_MODE_8; 217 | } else { 218 | if (strcmp(ex.name, "rv32i")) { 219 | printf("unexpected arch mode: %s\n", ex.name); 220 | err = SL_ERR_ARG; 221 | goto out; 222 | } 223 | options = SL_CORE_MODE_4; 224 | } 225 | 226 | err = SL_ERR_ARG; 227 | while ((s = strtok_r(NULL, sep, &lasts))) { 228 | if (s[0] == '\0') continue; 229 | rv_extension_t ex = {}; 230 | if ((err = parse_attribute(s, &ex))) { 231 | printf("invalid attribute string segment '%s'\n", s); 232 | goto out; 233 | } 234 | 235 | bool found = false; 236 | for (int i = 0; i < countof(ext_map); i++) { 237 | if (!strcmp(ex.name, ext_map[i].name)) { 238 | // printf("attribute %s (%up%u)\n", ex.name, ex.version, ex.patch); 239 | options |= ext_map[i].option; 240 | found = true; 241 | break; 242 | } 243 | } 244 | 245 | if (!found) { 246 | printf("unhandled extension: %s\n", ex.name); 247 | err = SL_ERR_UNSUPPORTED; 248 | goto out; 249 | } 250 | } 251 | err = 0; 252 | *arch_options_out = options; 253 | 254 | out: 255 | free(at); 256 | return err; 257 | } 258 | -------------------------------------------------------------------------------- /include/sled/elf/elf64.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 1996-1998 John D. Polstra. 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | * 28 | * $FreeBSD$ 29 | */ 30 | 31 | #ifndef _SYS_ELF64_H_ 32 | #define _SYS_ELF64_H_ 1 33 | 34 | #include 35 | 36 | /* 37 | * ELF definitions common to all 64-bit architectures. 38 | */ 39 | 40 | typedef u8 Elf64_Addr; 41 | typedef u2 Elf64_Half; 42 | typedef u8 Elf64_Off; 43 | typedef i4 Elf64_Sword; 44 | typedef i8 Elf64_Sxword; 45 | typedef u4 Elf64_Word; 46 | typedef u8 Elf64_Lword; 47 | typedef u8 Elf64_Xword; 48 | 49 | /* 50 | * Types of dynamic symbol hash table bucket and chain elements. 51 | * 52 | * This is inconsistent among 64 bit architectures, so a machine dependent 53 | * typedef is required. 54 | */ 55 | 56 | typedef Elf64_Word Elf64_Hashelt; 57 | 58 | /* Non-standard class-dependent datatype used for abstraction. */ 59 | typedef Elf64_Xword Elf64_Size; 60 | typedef Elf64_Sxword Elf64_Ssize; 61 | 62 | /* 63 | * ELF header. 64 | */ 65 | 66 | typedef struct { 67 | unsigned char e_ident[EI_NIDENT]; /* File identification. */ 68 | Elf64_Half e_type; /* File type. */ 69 | Elf64_Half e_machine; /* Machine architecture. */ 70 | Elf64_Word e_version; /* ELF format version. */ 71 | Elf64_Addr e_entry; /* Entry point. */ 72 | Elf64_Off e_phoff; /* Program header file offset. */ 73 | Elf64_Off e_shoff; /* Section header file offset. */ 74 | Elf64_Word e_flags; /* Architecture-specific flags. */ 75 | Elf64_Half e_ehsize; /* Size of ELF header in bytes. */ 76 | Elf64_Half e_phentsize; /* Size of program header entry. */ 77 | Elf64_Half e_phnum; /* Number of program header entries. */ 78 | Elf64_Half e_shentsize; /* Size of section header entry. */ 79 | Elf64_Half e_shnum; /* Number of section header entries. */ 80 | Elf64_Half e_shstrndx; /* Section name strings section. */ 81 | } Elf64_Ehdr; 82 | 83 | /* 84 | * Shared object information, found in SHT_MIPS_LIBLIST. 85 | */ 86 | 87 | typedef struct { 88 | Elf64_Word l_name; /* The name of a shared object. */ 89 | Elf64_Word l_time_stamp; /* 64-bit timestamp. */ 90 | Elf64_Word l_checksum; /* Checksum of visible symbols, sizes. */ 91 | Elf64_Word l_version; /* Interface version string index. */ 92 | Elf64_Word l_flags; /* Flags (LL_*). */ 93 | } Elf64_Lib; 94 | 95 | /* 96 | * Section header. 97 | */ 98 | 99 | typedef struct { 100 | Elf64_Word sh_name; /* Section name (index into the 101 | section header string table). */ 102 | Elf64_Word sh_type; /* Section type. */ 103 | Elf64_Xword sh_flags; /* Section flags. */ 104 | Elf64_Addr sh_addr; /* Address in memory image. */ 105 | Elf64_Off sh_offset; /* Offset in file. */ 106 | Elf64_Xword sh_size; /* Size in bytes. */ 107 | Elf64_Word sh_link; /* Index of a related section. */ 108 | Elf64_Word sh_info; /* Depends on section type. */ 109 | Elf64_Xword sh_addralign; /* Alignment in bytes. */ 110 | Elf64_Xword sh_entsize; /* Size of each entry in section. */ 111 | } Elf64_Shdr; 112 | 113 | /* 114 | * Program header. 115 | */ 116 | 117 | typedef struct { 118 | Elf64_Word p_type; /* Entry type. */ 119 | Elf64_Word p_flags; /* Access permission flags. */ 120 | Elf64_Off p_offset; /* File offset of contents. */ 121 | Elf64_Addr p_vaddr; /* Virtual address in memory image. */ 122 | Elf64_Addr p_paddr; /* Physical address (not used). */ 123 | Elf64_Xword p_filesz; /* Size of contents in file. */ 124 | Elf64_Xword p_memsz; /* Size of contents in memory. */ 125 | Elf64_Xword p_align; /* Alignment in memory and file. */ 126 | } Elf64_Phdr; 127 | 128 | /* 129 | * Dynamic structure. The ".dynamic" section contains an array of them. 130 | */ 131 | 132 | typedef struct { 133 | Elf64_Sxword d_tag; /* Entry type. */ 134 | union { 135 | Elf64_Xword d_val; /* Integer value. */ 136 | Elf64_Addr d_ptr; /* Address value. */ 137 | } d_un; 138 | } Elf64_Dyn; 139 | 140 | /* 141 | * Relocation entries. 142 | */ 143 | 144 | /* Relocations that don't need an addend field. */ 145 | typedef struct { 146 | Elf64_Addr r_offset; /* Location to be relocated. */ 147 | Elf64_Xword r_info; /* Relocation type and symbol index. */ 148 | } Elf64_Rel; 149 | 150 | /* Relocations that need an addend field. */ 151 | typedef struct { 152 | Elf64_Addr r_offset; /* Location to be relocated. */ 153 | Elf64_Xword r_info; /* Relocation type and symbol index. */ 154 | Elf64_Sxword r_addend; /* Addend. */ 155 | } Elf64_Rela; 156 | 157 | /* Macros for accessing the fields of r_info. */ 158 | #define ELF64_R_SYM(info) ((info) >> 32) 159 | #define ELF64_R_TYPE(info) ((info) & 0xffffffffL) 160 | 161 | /* Macro for constructing r_info from field values. */ 162 | #define ELF64_R_INFO(sym, type) (((sym) << 32) + ((type) & 0xffffffffL)) 163 | 164 | #define ELF64_R_TYPE_DATA(info) (((Elf64_Xword)(info)<<32)>>40) 165 | #define ELF64_R_TYPE_ID(info) (((Elf64_Xword)(info)<<56)>>56) 166 | #define ELF64_R_TYPE_INFO(data, type) \ 167 | (((Elf64_Xword)(data)<<8)+(Elf64_Xword)(type)) 168 | 169 | typedef Elf64_Xword Elf64_Relr; 170 | 171 | /* 172 | * Note entry header 173 | */ 174 | typedef Elf_Note Elf64_Nhdr; 175 | 176 | /* 177 | * Move entry 178 | */ 179 | typedef struct { 180 | Elf64_Lword m_value; /* symbol value */ 181 | Elf64_Xword m_info; /* size + index */ 182 | Elf64_Xword m_poffset; /* symbol offset */ 183 | Elf64_Half m_repeat; /* repeat count */ 184 | Elf64_Half m_stride; /* stride info */ 185 | } Elf64_Move; 186 | 187 | #define ELF64_M_SYM(info) ((info)>>8) 188 | #define ELF64_M_SIZE(info) ((unsigned char)(info)) 189 | #define ELF64_M_INFO(sym, size) (((sym)<<8)+(unsigned char)(size)) 190 | 191 | /* 192 | * Hardware/Software capabilities entry 193 | */ 194 | typedef struct { 195 | Elf64_Xword c_tag; /* how to interpret value */ 196 | union { 197 | Elf64_Xword c_val; 198 | Elf64_Addr c_ptr; 199 | } c_un; 200 | } Elf64_Cap; 201 | 202 | /* 203 | * Symbol table entries. 204 | */ 205 | 206 | typedef struct { 207 | Elf64_Word st_name; /* String table index of name. */ 208 | unsigned char st_info; /* Type and binding information. */ 209 | unsigned char st_other; /* Reserved (not used). */ 210 | Elf64_Half st_shndx; /* Section index of symbol. */ 211 | Elf64_Addr st_value; /* Symbol value. */ 212 | Elf64_Xword st_size; /* Size of associated object. */ 213 | } Elf64_Sym; 214 | 215 | /* Macros for accessing the fields of st_info. */ 216 | #define ELF64_ST_BIND(info) ((info) >> 4) 217 | #define ELF64_ST_TYPE(info) ((info) & 0xf) 218 | 219 | /* Macro for constructing st_info from field values. */ 220 | #define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 221 | 222 | /* Macro for accessing the fields of st_other. */ 223 | #define ELF64_ST_VISIBILITY(oth) ((oth) & 0x3) 224 | 225 | /* Structures used by Sun & GNU-style symbol versioning. */ 226 | typedef struct { 227 | Elf64_Half vd_version; 228 | Elf64_Half vd_flags; 229 | Elf64_Half vd_ndx; 230 | Elf64_Half vd_cnt; 231 | Elf64_Word vd_hash; 232 | Elf64_Word vd_aux; 233 | Elf64_Word vd_next; 234 | } Elf64_Verdef; 235 | 236 | typedef struct { 237 | Elf64_Word vda_name; 238 | Elf64_Word vda_next; 239 | } Elf64_Verdaux; 240 | 241 | typedef struct { 242 | Elf64_Half vn_version; 243 | Elf64_Half vn_cnt; 244 | Elf64_Word vn_file; 245 | Elf64_Word vn_aux; 246 | Elf64_Word vn_next; 247 | } Elf64_Verneed; 248 | 249 | typedef struct { 250 | Elf64_Word vna_hash; 251 | Elf64_Half vna_flags; 252 | Elf64_Half vna_other; 253 | Elf64_Word vna_name; 254 | Elf64_Word vna_next; 255 | } Elf64_Vernaux; 256 | 257 | typedef Elf64_Half Elf64_Versym; 258 | 259 | typedef struct { 260 | Elf64_Half si_boundto; /* direct bindings - symbol bound to */ 261 | Elf64_Half si_flags; /* per symbol flags */ 262 | } Elf64_Syminfo; 263 | 264 | typedef struct { 265 | Elf64_Word ch_type; 266 | Elf64_Word ch_reserved; 267 | Elf64_Xword ch_size; 268 | Elf64_Xword ch_addralign; 269 | } Elf64_Chdr; 270 | 271 | #endif /* !_SYS_ELF64_H_ */ -------------------------------------------------------------------------------- /app/sled/cons.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT License 2 | // Copyright (c) 2023 Shac Ron and The Sled Project 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "cons.h" 15 | 16 | #define countof(p) (sizeof(p) / sizeof(p[0])) 17 | 18 | #define MAX_LINE 4096 19 | #define MAX_ARGS 20 20 | 21 | typedef struct { 22 | sl_machine_t *machine; 23 | sl_core_t *core; 24 | 25 | u4 len; 26 | char *line; 27 | bool done; 28 | } console_t; 29 | 30 | typedef struct { 31 | char sname; 32 | const char *lname; 33 | int (*handler)(console_t *, char *, int, char **); 34 | const char *help; 35 | } cons_command_t; 36 | 37 | static int help_handler(console_t *c, char *cmd, int argc, char **argv); 38 | 39 | static int quit_handler(console_t *c, char *cmd, int argc, char **argv) { 40 | c->done = true; 41 | return 0; 42 | } 43 | 44 | static int reg_handler(console_t *c, char *cmd, int argc, char **argv) { 45 | int arch = sl_core_get_arch(c->core); 46 | u8 val; 47 | 48 | if (argc == 0) { 49 | val = sl_core_get_reg(c->core, SL_CORE_REG_PC); 50 | printf("pc : %" PRIx64 "\n", val); 51 | u4 count = sl_core_get_reg_count(c->core, SL_CORE_REG_TYPE_INT); 52 | for (u4 a = 0; a < count; a += 4) { 53 | u8 r[4]; 54 | for (int b = 0; b < 4; b++) { 55 | if ((a + b) > count) break; 56 | r[b] = sl_core_get_reg(c->core, a + b); 57 | } 58 | printf("r%2u: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", a, r[0], r[1], r[2], r[3]); 59 | } 60 | return 0; 61 | } 62 | 63 | char *rname = argv[0]; 64 | 65 | u4 r = sl_arch_reg_for_name(arch, rname); 66 | if (r == SL_CORE_REG_INVALID) { 67 | printf("invalid register name %s for architecture %s\n", rname, sl_arch_name(arch)); 68 | return 0; 69 | } 70 | 71 | if (argc == 1) { 72 | // read reg 73 | val = sl_core_get_reg(c->core, r); 74 | printf("%#" PRIx64 "\n", val); 75 | return 0; 76 | } 77 | 78 | val = strtoull(argv[1], NULL, 0); 79 | sl_core_set_reg(c->core, r, val); 80 | printf("%s = %#" PRIx64 "\n", rname, val); 81 | return 0; 82 | } 83 | 84 | #define LINE_LEN 100 85 | 86 | static int mem_handler(console_t *c, char *cmd, int argc, char **argv) { 87 | int err = 0; 88 | 89 | if (argc < 2) { 90 | printf("usage:\n" 91 | " mem r \n" 92 | " read memory at address\n" 93 | " mem w \n" 94 | " write value of size wsize to memory\n"); 95 | return 0; 96 | } 97 | 98 | u4 size = 0; 99 | u4 num = 20; 100 | // u8 value = 0; 101 | const u4 line_len = LINE_LEN; 102 | char line[LINE_LEN]; 103 | 104 | const char *op = argv[0]; 105 | if ((op[0] == 'r') || (op[0] == 'w')) { 106 | switch (op[1]) { 107 | case '1': size = 1; break; 108 | case '2': size = 2; break; 109 | case '4': size = 4; break; 110 | case '8': 111 | case '\0': size = 8; break; 112 | default: 113 | printf("invalid mem size. Should be 1, 2, 4, or 8.\n"); 114 | return 0; 115 | } 116 | } else { 117 | printf("invalid mem op %s\n", op); 118 | return 0; 119 | } 120 | 121 | u8 addr = strtoull(argv[1], NULL, 0); 122 | 123 | if (argc == 2) goto do_op; 124 | 125 | // const char *sval = NULL; 126 | const char *snum = argv[2]; 127 | // if (op[0] == 'w') { 128 | // sval = argv[2]; 129 | // if (argc >= 4) snum = argv[3]; 130 | // else snum = NULL; 131 | // } 132 | // if (sval != NULL) value = strtoull(sval, NULL, 0); 133 | if (snum != NULL) num = strtoull(snum, NULL, 0); 134 | 135 | do_op: 136 | if (op[0] == 'r') { 137 | for (u4 i = 0; i < num; ) { 138 | line[0] = '\0'; 139 | int cur = snprintf(line, line_len, "%"PRIx64":", addr); 140 | u4 j; 141 | for (j = i; j < num; j++) { 142 | u8 d; 143 | if ((err = sl_core_mem_read(c->core, addr, size, 1, &d))) { 144 | printf("failed to read memory at %#"PRIx64": %s\n", addr, st_err(err)); 145 | return 0; 146 | } 147 | addr += size; 148 | switch (size) { 149 | case 1: 150 | cur += snprintf(line + cur, line_len - cur, " %02x", (u1)d); 151 | break; 152 | 153 | case 2: 154 | cur += snprintf(line + cur, line_len - cur, " %04x", (u2)d); 155 | break; 156 | 157 | case 4: 158 | cur += snprintf(line + cur, line_len - cur, " %08x", (u4)d); 159 | break; 160 | 161 | default: 162 | cur += snprintf(line + cur, line_len - cur, " %016"PRIx64, d); 163 | break; 164 | } 165 | if ((line_len - cur) < ((2 * size) + 2)) break; 166 | } 167 | puts(line); 168 | i = j; 169 | } 170 | } else { 171 | printf("memory writing not yet implemented\n"); 172 | } 173 | return 0; 174 | } 175 | 176 | static int step_handler(console_t *c, char *cmd, int argc, char **argv) { 177 | u4 step = 1; 178 | if (argc > 0) { 179 | step = strtoul(argv[0], NULL, 0); 180 | } 181 | int err = sl_core_step(c->core, step); 182 | u8 pc = sl_core_get_reg(c->core, SL_CORE_REG_PC); 183 | if (err) { 184 | printf("instruction failed at pc=%#" PRIx64 ": %s\n", pc, st_err(err)); 185 | } else { 186 | // todo: pretty-print instruction disassembly when decoder is done 187 | printf("pc = %#" PRIx64 "\n", pc); 188 | } 189 | return 0; 190 | } 191 | 192 | static const cons_command_t command_list[] = { 193 | { 194 | .sname = 's', 195 | .lname = "step", 196 | .handler = step_handler, 197 | .help = "step one or more instructions", 198 | }, 199 | { 200 | .sname = 'r', 201 | .lname = "reg", 202 | .handler = reg_handler, 203 | .help = "reg read/write", 204 | }, 205 | { 206 | .sname = 'm', 207 | .lname = "mem", 208 | .handler = mem_handler, 209 | .help = "mem read/write", 210 | }, 211 | 212 | { 213 | .sname = '?', 214 | .lname = "help", 215 | .handler = help_handler, 216 | .help = "print out the help screen", 217 | }, 218 | { 219 | .sname = 'q', 220 | .lname = "quit", 221 | .handler = quit_handler, 222 | .help = "exit console", 223 | }, 224 | }; 225 | 226 | static int help_handler(console_t *c, char *cmd, int argc, char **argv) { 227 | for (size_t i = 0; i < countof(command_list); i ++) { 228 | const cons_command_t *cc = &command_list[i]; 229 | printf("%c %s\n %s\n\n", cc->sname, cc->lname, cc->help); 230 | } 231 | return 0; 232 | } 233 | 234 | static int parse_command(console_t *c) { 235 | const char *sep = " \t"; 236 | char *word, *br; 237 | 238 | char *args[MAX_ARGS]; 239 | int err = 0, argc = 0; 240 | 241 | char *cmd = strtok_r(c->line, sep, &br); 242 | if (cmd == NULL) return 0; 243 | 244 | for (word = strtok_r(NULL, sep, &br); word; word = strtok_r(NULL, sep, &br)) { 245 | args[argc] = word; 246 | argc++; 247 | if (argc == MAX_ARGS) break; 248 | } 249 | 250 | bool handled = false; 251 | for (size_t i = 0; i < countof(command_list); i ++) { 252 | const cons_command_t *cc = &command_list[i]; 253 | if ((cmd[0] == cc->sname && cmd[1] == '\0') || !strcmp(cmd, cc->lname)) { 254 | err = cc->handler(c, cmd, argc, args); 255 | if (err) return err; 256 | handled = true; 257 | break; 258 | } 259 | } 260 | 261 | if (!handled) { 262 | printf("unknown command: %s\n", cmd); 263 | } 264 | return 0; 265 | } 266 | 267 | static int read_line(console_t *c) { 268 | int pos = 0; 269 | for ( ; pos < MAX_LINE - 1; pos++) { 270 | int x = getchar(); 271 | if (x == EOF) { 272 | c->len = 0; 273 | c->done = true; 274 | printf("\n"); 275 | return 0; 276 | } 277 | if (x < 0) { 278 | perror("getchar"); 279 | return -1; 280 | } 281 | if (x == '\n') break; 282 | c->line[pos] = x; 283 | } 284 | c->line[pos] = '\0'; 285 | c->len = pos; 286 | return 0; 287 | } 288 | 289 | int console_enter(sl_machine_t *m) { 290 | int err = 0; 291 | console_t c; 292 | c.machine = m; 293 | c.core = sl_machine_get_core(m, 0); 294 | 295 | c.line = malloc(MAX_LINE); 296 | if (c.line == NULL) { 297 | perror("malloc"); 298 | return SL_ERR_MEM; 299 | } 300 | 301 | for (c.done = false; !c.done; ) { 302 | printf("sled> "); 303 | if ((err = read_line(&c))) break; 304 | if (c.len == 0) continue; 305 | if ((err = parse_command(&c))) break; 306 | } 307 | free(c.line); 308 | return err; 309 | } 310 | 311 | --------------------------------------------------------------------------------