Feat: Spike abstract machine.

This commit is contained in:
Yaksis 2024-05-14 11:30:01 +08:00
parent 61657035ac
commit e7f61272cb
No known key found for this signature in database
GPG key ID: F27FB4A20C9CCAC0
13 changed files with 554 additions and 0 deletions

View file

@ -0,0 +1,79 @@
#ifndef AM_H__
#define AM_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Memory protection flags
#define MMAP_NONE 0x00000000 // no access
#define MMAP_READ 0x00000001 // can read
#define MMAP_WRITE 0x00000002 // can write
// Memory area for [@start, @end)
typedef struct {
void *start, *end;
} Area;
// Arch-dependent processor context
typedef struct Context Context;
// An event of type @event, caused by @cause of pointer @ref
typedef struct {
enum {
EVENT_NULL = 0,
EVENT_YIELD, EVENT_SYSCALL, EVENT_PAGEFAULT, EVENT_ERROR,
EVENT_IRQ_TIMER, EVENT_IRQ_IODEV,
} event;
uintptr_t cause, ref;
const char *msg;
} Event;
// A protected address space with user memory @area
// and arch-dependent @ptr
typedef struct {
int pgsize;
Area area;
void *ptr;
} AddrSpace;
#ifdef __cplusplus
extern "C" {
#endif
// ----------------------- TRM: Turing Machine -----------------------
extern Area heap;
void putch (char ch);
void halt (int code) __attribute__((__noreturn__));
// -------------------- IOE: Input/Output Devices --------------------
bool ioe_init (void);
void ioe_read (int reg, void *buf);
void ioe_write (int reg, void *buf);
#include "amdev.h"
// ---------- CTE: Interrupt Handling and Context Switching ----------
bool cte_init (Context *(*handler)(Event ev, Context *ctx));
void yield (void);
bool ienabled (void);
void iset (bool enable);
Context *kcontext (Area kstack, void (*entry)(void *), void *arg);
// ----------------------- VME: Virtual Memory -----------------------
bool vme_init (void *(*pgalloc)(int), void (*pgfree)(void *));
void protect (AddrSpace *as);
void unprotect (AddrSpace *as);
void map (AddrSpace *as, void *vaddr, void *paddr, int prot);
Context *ucontext (AddrSpace *as, Area kstack, void *entry);
// ---------------------- MPE: Multi-Processing ----------------------
bool mpe_init (void (*entry)());
int cpu_count (void);
int cpu_current (void);
int atomic_xchg (int *addr, int newval);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,74 @@
#ifndef __AMDEV_H__
#define __AMDEV_H__
// **MAY SUBJECT TO CHANGE IN THE FUTURE**
#define AM_DEVREG(id, reg, perm, ...) \
enum { AM_##reg = (id) }; \
typedef struct { __VA_ARGS__; } AM_##reg##_T;
AM_DEVREG( 1, UART_CONFIG, RD, bool present);
AM_DEVREG( 2, UART_TX, WR, char data);
AM_DEVREG( 3, UART_RX, RD, char data);
AM_DEVREG( 4, TIMER_CONFIG, RD, bool present, has_rtc);
AM_DEVREG( 5, TIMER_RTC, RD, int year, month, day, hour, minute, second);
AM_DEVREG( 6, TIMER_UPTIME, RD, uint64_t us);
AM_DEVREG( 7, INPUT_CONFIG, RD, bool present);
AM_DEVREG( 8, INPUT_KEYBRD, RD, bool keydown; int keycode);
AM_DEVREG( 9, GPU_CONFIG, RD, bool present, has_accel; int width, height, vmemsz);
AM_DEVREG(10, GPU_STATUS, RD, bool ready);
AM_DEVREG(11, GPU_FBDRAW, WR, int x, y; void *pixels; int w, h; bool sync);
AM_DEVREG(12, GPU_MEMCPY, WR, uint32_t dest; void *src; int size);
AM_DEVREG(13, GPU_RENDER, WR, uint32_t root);
AM_DEVREG(14, AUDIO_CONFIG, RD, bool present; int bufsize);
AM_DEVREG(15, AUDIO_CTRL, WR, int freq, channels, samples);
AM_DEVREG(16, AUDIO_STATUS, RD, int count);
AM_DEVREG(17, AUDIO_PLAY, WR, Area buf);
AM_DEVREG(18, DISK_CONFIG, RD, bool present; int blksz, blkcnt);
AM_DEVREG(19, DISK_STATUS, RD, bool ready);
AM_DEVREG(20, DISK_BLKIO, WR, bool write; void *buf; int blkno, blkcnt);
AM_DEVREG(21, NET_CONFIG, RD, bool present);
AM_DEVREG(22, NET_STATUS, RD, int rx_len, tx_len);
AM_DEVREG(23, NET_TX, WR, Area buf);
AM_DEVREG(24, NET_RX, WR, Area buf);
// Input
#define AM_KEYS(_) \
_(ESCAPE) _(F1) _(F2) _(F3) _(F4) _(F5) _(F6) _(F7) _(F8) _(F9) _(F10) _(F11) _(F12) \
_(GRAVE) _(1) _(2) _(3) _(4) _(5) _(6) _(7) _(8) _(9) _(0) _(MINUS) _(EQUALS) _(BACKSPACE) \
_(TAB) _(Q) _(W) _(E) _(R) _(T) _(Y) _(U) _(I) _(O) _(P) _(LEFTBRACKET) _(RIGHTBRACKET) _(BACKSLASH) \
_(CAPSLOCK) _(A) _(S) _(D) _(F) _(G) _(H) _(J) _(K) _(L) _(SEMICOLON) _(APOSTROPHE) _(RETURN) \
_(LSHIFT) _(Z) _(X) _(C) _(V) _(B) _(N) _(M) _(COMMA) _(PERIOD) _(SLASH) _(RSHIFT) \
_(LCTRL) _(APPLICATION) _(LALT) _(SPACE) _(RALT) _(RCTRL) \
_(UP) _(DOWN) _(LEFT) _(RIGHT) _(INSERT) _(DELETE) _(HOME) _(END) _(PAGEUP) _(PAGEDOWN)
#define AM_KEY_NAMES(key) AM_KEY_##key,
enum {
AM_KEY_NONE = 0,
AM_KEYS(AM_KEY_NAMES)
};
// GPU
#define AM_GPU_TEXTURE 1
#define AM_GPU_SUBTREE 2
#define AM_GPU_NULL 0xffffffff
typedef uint32_t gpuptr_t;
struct gpu_texturedesc {
uint16_t w, h;
gpuptr_t pixels;
} __attribute__((packed));
struct gpu_canvas {
uint16_t type, w, h, x1, y1, w1, h1;
gpuptr_t sibling;
union {
gpuptr_t child;
struct gpu_texturedesc texture;
};
} __attribute__((packed));
#endif

View file

@ -0,0 +1,78 @@
// See LICENSE for license details.
#ifndef _RISCV_ATOMIC_H
#define _RISCV_ATOMIC_H
//#include "config.h"
//#include "encoding.h"
// Currently, interrupts are always disabled in M-mode.
#define disable_irqsave() (0)
#define enable_irqrestore(flags) ((void) (flags))
typedef struct { int lock; } spinlock_t;
#define SPINLOCK_INIT {0}
#define mb() asm volatile ("fence" ::: "memory")
#define atomic_set(ptr, val) (*(volatile typeof(*(ptr)) *)(ptr) = val)
#define atomic_read(ptr) (*(volatile typeof(*(ptr)) *)(ptr))
#ifdef __riscv_atomic
# define atomic_add(ptr, inc) __sync_fetch_and_add(ptr, inc)
# define atomic_or(ptr, inc) __sync_fetch_and_or(ptr, inc)
# define atomic_swap(ptr, swp) __sync_lock_test_and_set(ptr, swp)
# define atomic_cas(ptr, cmp, swp) __sync_val_compare_and_swap(ptr, cmp, swp)
#else
# define atomic_binop(ptr, inc, op) ({ \
long flags = disable_irqsave(); \
typeof(*(ptr)) res = atomic_read(ptr); \
atomic_set(ptr, op); \
enable_irqrestore(flags); \
res; })
# define atomic_add(ptr, inc) atomic_binop(ptr, inc, res + (inc))
# define atomic_or(ptr, inc) atomic_binop(ptr, inc, res | (inc))
# define atomic_swap(ptr, inc) atomic_binop(ptr, inc, (inc))
# define atomic_cas(ptr, cmp, swp) ({ \
long flags = disable_irqsave(); \
typeof(*(ptr)) res = *(volatile typeof(*(ptr)) *)(ptr); \
if (res == (cmp)) *(volatile typeof(ptr))(ptr) = (swp); \
enable_irqrestore(flags); \
res; })
#endif
static inline int spinlock_trylock(spinlock_t* lock)
{
int res = atomic_swap(&lock->lock, -1);
mb();
return res;
}
static inline void spinlock_lock(spinlock_t* lock)
{
do
{
while (atomic_read(&lock->lock))
;
} while (spinlock_trylock(lock));
}
static inline void spinlock_unlock(spinlock_t* lock)
{
mb();
atomic_set(&lock->lock,0);
}
static inline long spinlock_lock_irqsave(spinlock_t* lock)
{
long flags = disable_irqsave();
spinlock_lock(lock);
return flags;
}
static inline void spinlock_unlock_irqrestore(spinlock_t* lock, long flags)
{
spinlock_unlock(lock);
enable_irqrestore(flags);
}
#endif

19
src/common/am/src/cte.c Normal file
View file

@ -0,0 +1,19 @@
#include <am.h>
bool cte_init(Context*(*handler)(Event, Context*)) {
return false;
}
Context *kcontext(Area kstack, void (*entry)(void *), void *arg) {
return NULL;
}
void yield() {
}
bool ienabled() {
return false;
}
void iset(bool enable) {
}

111
src/common/am/src/htif.c Normal file
View file

@ -0,0 +1,111 @@
// See LICENSE for license details.
#include "htif.h"
#include "atomic.h"
#include <klib.h>
extern uint64_t __htif_base;
volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));
volatile int htif_console_buf;
static spinlock_t htif_lock = SPINLOCK_INIT;
#define TOHOST(base_int) (uint64_t *)(base_int + TOHOST_OFFSET)
#define FROMHOST(base_int) (uint64_t *)(base_int + FROMHOST_OFFSET)
#define TOHOST_OFFSET ((uintptr_t)tohost - (uintptr_t)__htif_base)
#define FROMHOST_OFFSET ((uintptr_t)fromhost - (uintptr_t)__htif_base)
static void __check_fromhost()
{
uint64_t fh = fromhost;
if (!fh)
return;
fromhost = 0;
// this should be from the console
assert(FROMHOST_DEV(fh) == 1);
switch (FROMHOST_CMD(fh)) {
case 0:
htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh);
break;
case 1:
break;
default:
assert(0);
}
}
static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
while (tohost)
__check_fromhost();
tohost = TOHOST_CMD(dev, cmd, data);
}
int htif_console_getchar()
{
#if __riscv_xlen == 32
// HTIF devices are not supported on RV32
return -1;
#endif
spinlock_lock(&htif_lock);
__check_fromhost();
int ch = htif_console_buf;
if (ch >= 0) {
htif_console_buf = -1;
__set_tohost(1, 0, 0);
}
spinlock_unlock(&htif_lock);
return ch - 1;
}
static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
spinlock_lock(&htif_lock);
__set_tohost(dev, cmd, data);
while (1) {
uint64_t fh = fromhost;
if (fh) {
if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
fromhost = 0;
break;
}
__check_fromhost();
}
}
spinlock_unlock(&htif_lock);
}
void htif_syscall(uintptr_t arg)
{
do_tohost_fromhost(0, 0, arg);
}
void htif_console_putchar(uint8_t ch)
{
#if __riscv_xlen == 32
// HTIF devices are not supported on RV32, so proxy a write system call
volatile uint64_t magic_mem[8];
magic_mem[0] = SYS_write;
magic_mem[1] = 1;
magic_mem[2] = (uintptr_t)&ch;
magic_mem[3] = 1;
do_tohost_fromhost(0, 0, (uintptr_t)magic_mem);
#else
spinlock_lock(&htif_lock);
__set_tohost(1, 1, ch);
spinlock_unlock(&htif_lock);
#endif
}
void htif_poweroff()
{
while (1) {
fromhost = 0;
tohost = 1;
}
}

24
src/common/am/src/htif.h Normal file
View file

@ -0,0 +1,24 @@
// See LICENSE for license details.
#ifndef _RISCV_HTIF_H
#define _RISCV_HTIF_H
#include <stdint.h>
#if __riscv_xlen == 64
# define TOHOST_CMD(dev, cmd, payload) \
(((uint64_t)(dev) << 56) | ((uint64_t)(cmd) << 48) | (uint64_t)(payload))
#else
# define TOHOST_CMD(dev, cmd, payload) ({ \
if ((dev) || (cmd)) __builtin_trap(); \
(payload); })
#endif
#define FROMHOST_DEV(fromhost_value) ((uint64_t)(fromhost_value) >> 56)
#define FROMHOST_CMD(fromhost_value) ((uint64_t)(fromhost_value) << 8 >> 56)
#define FROMHOST_DATA(fromhost_value) ((uint64_t)(fromhost_value) << 16 >> 16)
void htif_console_putchar(uint8_t);
int htif_console_getchar();
void htif_poweroff() __attribute__((noreturn));
#endif

27
src/common/am/src/ioe.c Normal file
View file

@ -0,0 +1,27 @@
#include <am.h>
#include <klib-macros.h>
void __am_timer_init();
void __am_timer_rtc(AM_TIMER_RTC_T *);
void __am_timer_uptime(AM_TIMER_UPTIME_T *);
static void __am_timer_config(AM_TIMER_CONFIG_T *cfg) { cfg->present = true; cfg->has_rtc = true; }
typedef void (*handler_t)(void *buf);
static void *lut[128] = {
[AM_TIMER_CONFIG] = __am_timer_config,
[AM_TIMER_RTC ] = __am_timer_rtc,
[AM_TIMER_UPTIME] = __am_timer_uptime,
};
static void fail(void *buf) { panic("access nonexist register"); }
bool ioe_init() {
for (int i = 0; i < LENGTH(lut); i++)
if (!lut[i]) lut[i] = fail;
__am_timer_init();
return true;
}
void ioe_read (int reg, void *buf) { ((handler_t)lut[reg])(buf); }
void ioe_write(int reg, void *buf) { ((handler_t)lut[reg])(buf); }

View file

@ -0,0 +1,35 @@
ENTRY(_start)
SECTIONS {
. = 0x80000000;
.text : {
*(entry)
*(.text*)
}
etext = .;
_etext = .;
.rodata : {
*(.rodata*)
}
.htif : {
PROVIDE(__htif_base = . );
*(.htif)
}
.data : {
*(.data)
}
edata = .;
_data = .;
.bss : {
_bss_start = .;
*(.bss*)
*(.sbss*)
*(.scommon)
}
_stack_top = ALIGN(0x1000);
. = _stack_top + 0x20000;
_stack_pointer = .;
end = .;
_end = .;
_heap_start = ALIGN(0x1000);
}

17
src/common/am/src/mpe.c Normal file
View file

@ -0,0 +1,17 @@
#include <am.h>
bool mpe_init(void (*entry)()) {
return false;
}
int cpu_count() {
return 1;
}
int cpu_current() {
return 0;
}
int atomic_xchg(int *addr, int newval) {
return 0;
}

View file

@ -0,0 +1,8 @@
.section entry, "ax"
.globl _start
.type _start, @function
_start:
mv s0, zero
la sp, _stack_pointer
jal _trm_init

30
src/common/am/src/timer.c Normal file
View file

@ -0,0 +1,30 @@
#include <am.h>
static uint64_t boot_time = 0;
#define CLINT_MMIO 0x2000000ul
#define TIME_BASE 0xbff8
static uint64_t read_time() {
uint32_t lo = *(volatile uint32_t *)(CLINT_MMIO + TIME_BASE + 0);
uint32_t hi = *(volatile uint32_t *)(CLINT_MMIO + TIME_BASE + 4);
uint64_t time = ((uint64_t)hi << 32) | lo;
return time / 10;
}
void __am_timer_uptime(AM_TIMER_UPTIME_T *uptime) {
uptime->us = read_time() - boot_time;
}
void __am_timer_init() {
boot_time = read_time();
}
void __am_timer_rtc(AM_TIMER_RTC_T *rtc) {
rtc->second = 0;
rtc->minute = 0;
rtc->hour = 0;
rtc->day = 0;
rtc->month = 0;
rtc->year = 1900;
}

34
src/common/am/src/trm.c Normal file
View file

@ -0,0 +1,34 @@
#include <am.h>
#include <klib.h>
#include <klib-macros.h>
#include "htif.h"
extern char _heap_start;
int main(const char *args);
extern char _pmem_start;
#define PMEM_SIZE (128 * 1024 * 1024)
#define PMEM_END ((uintptr_t)0x80000000 + PMEM_SIZE)
Area heap = RANGE(&_heap_start, PMEM_END);
#ifndef MAINARGS
#define MAINARGS ""
#endif
static const char mainargs[] = MAINARGS;
void putch(char ch) {
htif_console_putchar(ch);
}
void halt(int code) {
printf("Exit with code = %d\n", code);
htif_poweroff();
// should not reach here
while (1);
}
void _trm_init() {
int ret = main(mainargs);
halt(ret);
}

18
src/common/am/src/vme.c Normal file
View file

@ -0,0 +1,18 @@
#include <am.h>
bool vme_init(void* (*pgalloc_f)(int), void (*pgfree_f)(void*)) {
return false;
}
void protect(AddrSpace *as) {
}
void unprotect(AddrSpace *as) {
}
void map(AddrSpace *as, void *va, void *pa, int prot) {
}
Context *ucontext(AddrSpace *as, Area kstack, void *entry) {
return NULL;
}