From 8a8776cab83bf852c477fce2b4a4276efe1734e9 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 8 Sep 2021 10:31:37 +0800 Subject: [PATCH] add ch10 --- .DS_Store | Bin 0 -> 6148 bytes 10-SystemCall/.DS_Store | Bin 0 -> 6148 bytes 10-SystemCall/Makefile | 54 ++++++ 10-SystemCall/README.md | 83 ++++++++ 10-SystemCall/gdbinit | 4 + 10-SystemCall/include/.DS_Store | Bin 0 -> 6148 bytes 10-SystemCall/include/lib.h | 28 +++ 10-SystemCall/include/os.h | 42 +++++ 10-SystemCall/include/riscv.h | 183 ++++++++++++++++++ 10-SystemCall/include/string.h | 10 + 10-SystemCall/include/sys.h | 9 + 10-SystemCall/include/task.h | 16 ++ 10-SystemCall/include/timer.h | 12 ++ 10-SystemCall/include/types.h | 13 ++ 10-SystemCall/include/user_api.h | 7 + 10-SystemCall/include/virtio.h | 129 +++++++++++++ 10-SystemCall/os.ld | 49 +++++ 10-SystemCall/src/.DS_Store | Bin 0 -> 6148 bytes 10-SystemCall/src/alloc.c | 215 +++++++++++++++++++++ 10-SystemCall/src/lib.c | 247 ++++++++++++++++++++++++ 10-SystemCall/src/lock.c | 32 ++++ 10-SystemCall/src/mem.s | 30 +++ 10-SystemCall/src/os.c | 52 ++++++ 10-SystemCall/src/plic.c | 48 +++++ 10-SystemCall/src/start.s | 34 ++++ 10-SystemCall/src/string.c | 40 ++++ 10-SystemCall/src/sys.s | 185 ++++++++++++++++++ 10-SystemCall/src/syscall.c | 30 +++ 10-SystemCall/src/task.c | 35 ++++ 10-SystemCall/src/timer.c | 39 ++++ 10-SystemCall/src/trap.c | 101 ++++++++++ 10-SystemCall/src/user.c | 91 +++++++++ 10-SystemCall/src/usys.s | 6 + 10-SystemCall/src/virtio.c | 312 +++++++++++++++++++++++++++++++ README.md | 3 +- 35 files changed, 2138 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 10-SystemCall/.DS_Store create mode 100644 10-SystemCall/Makefile create mode 100644 10-SystemCall/README.md create mode 100644 10-SystemCall/gdbinit create mode 100644 10-SystemCall/include/.DS_Store create mode 100644 10-SystemCall/include/lib.h create mode 100644 10-SystemCall/include/os.h create mode 100644 10-SystemCall/include/riscv.h create mode 100644 10-SystemCall/include/string.h create mode 100644 10-SystemCall/include/sys.h create mode 100644 10-SystemCall/include/task.h create mode 100644 10-SystemCall/include/timer.h create mode 100644 10-SystemCall/include/types.h create mode 100644 10-SystemCall/include/user_api.h create mode 100644 10-SystemCall/include/virtio.h create mode 100644 10-SystemCall/os.ld create mode 100644 10-SystemCall/src/.DS_Store create mode 100644 10-SystemCall/src/alloc.c create mode 100644 10-SystemCall/src/lib.c create mode 100644 10-SystemCall/src/lock.c create mode 100644 10-SystemCall/src/mem.s create mode 100644 10-SystemCall/src/os.c create mode 100644 10-SystemCall/src/plic.c create mode 100644 10-SystemCall/src/start.s create mode 100644 10-SystemCall/src/string.c create mode 100644 10-SystemCall/src/sys.s create mode 100644 10-SystemCall/src/syscall.c create mode 100644 10-SystemCall/src/task.c create mode 100644 10-SystemCall/src/timer.c create mode 100644 10-SystemCall/src/trap.c create mode 100644 10-SystemCall/src/user.c create mode 100644 10-SystemCall/src/usys.s create mode 100644 10-SystemCall/src/virtio.c diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3f09ccce5f834be2ed38463ec2a69181a63ec871 GIT binary patch literal 6148 zcmeHKJ4ysm5Ukb>7Gz;V!*>C}y}_`%o?tGZpqPv+_yePVQ&81eP$<>F(RF3yyF=!xWfZ>v+Cms=dQ59B`(lU z_%|3Zy!B6q;qh>vHyl4gSt%d|q<|EV0#e`?3V83OEzc4arGONW0^bVw_o2}pyTUOs zJ{=4(0uUEWhjAUV1hIL7*cFb6%+M^U#H3n{7?yPATh(=iV`9=_HGEjzY&D@++|Kh` zl*772MJXT!rV5{=tfH@Zd#$Kw$w5l}%7Q@7<5_Yy22b`lizmb{9Mu zW6Vr4eckSxY5Ur2w*Xv!ot*<+0ByQpqr+l{$$RmdHDb##(S;mKJmC4t=aaFov8~}R zDj;ijggG*dxprIYmyh{Iw#5a#_Zf^eVc(OTp+|qhLRWjkeyg!1>jWNa@~36CJNusf zGFmNtzMPZA{l!gqjXNwb#f)S1wQR4IY=_7>!XEh^vr5k&74AXgdSqp0Lwetye+M@h zQk4NmGi3J1TEu%LqRCOLFe&G4UhZa9=1h{ADPRhi0za;Rd{b%d6Gbac0aL&f*eD?1 zhlDPeMl2M=r-Mbf0uWnl4#v9tG)hboF^yO#@(s;tC{aUQyka;FXL}NPX~aTN!{OrP z!^NImyrH5txc~lN4zedxz!dmb3b@u_H0X0m@ocS4 vPR`ndeoGe^J/dev/null || exit + @echo "Press Ctrl-A and then X to exit QEMU" + $(QEMU) $(QFLAGS) -kernel os.elf + +clean: + rm -f *.elf *.img + +hdd.dsk: + dd if=/dev/urandom of=hdd.dsk bs=1048576 count=32 + +.PHONY : debug +debug: clean os.elf hdd.dsk + @echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU" + @echo "-------------------------------------------------------" + @${QEMU} ${QFLAGS} -kernel os.elf -s -S & + @${GDB} os.elf -q -x ./gdbinit diff --git a/10-SystemCall/README.md b/10-SystemCall/README.md new file mode 100644 index 0000000..0faef0a --- /dev/null +++ b/10-SystemCall/README.md @@ -0,0 +1,83 @@ +# 09-MemoryAllocator + +## Build & Run + +```sh +IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/09-MemoryAllocator (feat/memoryAlloc) +$ make all +rm -f *.elf *.img +riscv64-unknown-elf-gcc -I./include -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -w -T os.ld -o os.elf src/start.s src/sys.s src/mem.s src/lib.c src/timer.c src/task.c src/os.c src/user.c src/trap.c src/lock.c src/plic.c src/virtio.c src/string.c src/alloc.c +Press Ctrl-A and then X to exit QEMU +qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -drive if=none,format=raw,file=hdd.dsk,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -kernel os.elf +HEAP_START = 8001100c, HEAP_SIZE = 07feeff4, num of pages = 521967 +TEXT: 0x80000000 -> 0x8000ac78 +RODATA: 0x8000ac78 -> 0x8000b09f +DATA: 0x8000c000 -> 0x8000c004 +BSS: 0x8000d000 -> 0x8001100c +HEAP: 0x80091100 -> 0x88000000 +OS start +Disk init work is success! +buffer init... +block read... +Virtio IRQ +000000fd +000000af +000000f8 +000000ab +00000088 +00000042 +000000cc +00000017 +00000022 +0000008e + +p = 0x80091700 +p2 = 0x80091300 +p3 = 0x80091100 +OS: Activate next task +Task0: Created! +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +QEMU: Terminated +``` + +## Debug mode + +```sh +make debug +riscv64-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c +Press Ctrl-C and then input 'quit' to exit GDB and QEMU +------------------------------------------------------- +Reading symbols from os.elf... +Breakpoint 1 at 0x80000000: file start.s, line 7. +0x00001000 in ?? () +=> 0x00001000: 97 02 00 00 auipc t0,0x0 + +Thread 1 hit Breakpoint 1, _start () at start.s:7 +7 csrr t0, mhartid # read current hart id +=> 0x80000000 <_start+0>: f3 22 40 f1 csrr t0,mhartid +(gdb) +``` + +### set the breakpoint + +You can set the breakpoint in any c file: + +```sh +(gdb) b trap.c:27 +Breakpoint 2 at 0x80008f78: file trap.c, line 27. +(gdb) +``` + +As the example above, when process running on trap.c, line 27 (Timer Interrupt). +The process will be suspended automatically until you press the key `c` (continue) or `s` (step). diff --git a/10-SystemCall/gdbinit b/10-SystemCall/gdbinit new file mode 100644 index 0000000..7ca6884 --- /dev/null +++ b/10-SystemCall/gdbinit @@ -0,0 +1,4 @@ +set disassemble-next-line on +b _start +target remote : 1234 +c \ No newline at end of file diff --git a/10-SystemCall/include/.DS_Store b/10-SystemCall/include/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1f5b04624bb7108ac51f550ea4f13ac77d1fdd3f GIT binary patch literal 6148 zcmeHKK~BRk5ZrB2n!=?=E{MFK5G&FoY&1f;@ZUD zR6w3xpK7XTPGkD|{N$B5gpWU*x$URa(6WQ~67U9ehm1=q=`m*WUck=4ymt{dbkV_o z3iy5SS6%zy_kcN6W=S*LV;$%2y#*0l*vSl0S1~s^3b+GsE4rq0>^;&hdF^1Y=oah_ z1=Ib2ok6(@d1Zwr&YnI8d<@M@0aL&f_^|@0*<#s_M{7+1Q@|8hDmmLtlOzB_>xa6kCtD0%IQv^r5Ur4EEue&y`mwwjO;ru^v8HS7tq-uv;DZ zb4MpGJX&iCm;##$Bz`!O`hWcW`F}IWMy7x%@UIl$bb30S;Fh$uMmHz5Hiw_V#e~;- lT!*0JN-?-nig)48FrP~YSSYq0F$1$70VjhsrogW%@CkLaimw0w literal 0 HcmV?d00001 diff --git a/10-SystemCall/include/lib.h b/10-SystemCall/include/lib.h new file mode 100644 index 0000000..be9b9fd --- /dev/null +++ b/10-SystemCall/include/lib.h @@ -0,0 +1,28 @@ +#ifndef __LIB_H__ +#define __LIB_H__ + +#include "riscv.h" +#include +#include + +#define lib_error(...) \ + { \ + lib_printf(__VA_ARGS__); \ + while (1) \ + { \ + } \ + } \ + } + +extern char *lib_gets(char *); +extern void uart_init(); +extern void lib_isr(void); +extern int lib_getc(void); +extern void lib_delay(volatile int count); +extern int lib_putc(char ch); +extern void lib_puts(char *s); +extern int lib_printf(const char *s, ...); +extern int lib_vprintf(const char *s, va_list vl); +extern int lib_vsnprintf(char *out, size_t n, const char *s, va_list vl); + +#endif diff --git a/10-SystemCall/include/os.h b/10-SystemCall/include/os.h new file mode 100644 index 0000000..37cf21c --- /dev/null +++ b/10-SystemCall/include/os.h @@ -0,0 +1,42 @@ +#ifndef __OS_H__ +#define __OS_H__ + +#include "riscv.h" +#include "lib.h" +#include "task.h" +#include "timer.h" +#include "string.h" + +extern void panic(char *); +extern void user_init(); +extern void os_kernel(); +extern int os_main(void); + +// PLIC +extern void plic_init(); +extern int plic_claim(); +extern void plic_complete(int); + +// lock +extern void basic_lock(); +extern void basic_unlock(); + +typedef struct lock +{ + volatile int locked; +} lock_t; + +extern int atomic_swap(lock_t *); + +extern void lock_init(lock_t *lock); + +extern void lock_acquire(lock_t *lock); + +extern void lock_free(lock_t *lock); + +// memory allocator + +extern void *malloc(size_t size); +extern void free(void *p); + +#endif diff --git a/10-SystemCall/include/riscv.h b/10-SystemCall/include/riscv.h new file mode 100644 index 0000000..0717d5c --- /dev/null +++ b/10-SystemCall/include/riscv.h @@ -0,0 +1,183 @@ +#ifndef __RISCV_H__ +#define __RISCV_H__ + +#include + +#define reg_t uint32_t // RISCV32: register is 32bits +// define reg_t as uint64_t // RISCV64: register is 64bits + +#define PGSIZE 4096 // bytes per page + +// ref: https://www.activexperts.com/serial-port-component/tutorials/uart/ +#define UART 0x10000000L +#define UART_THR (volatile uint8_t *)(UART + 0x00) // THR:transmitter holding register +#define UART_RHR (volatile uint8_t *)(UART + 0x00) // RHR:Receive holding register +#define UART_DLL (volatile uint8_t *)(UART + 0x00) // LSB of Divisor Latch (write mode) +#define UART_DLM (volatile uint8_t *)(UART + 0x01) // MSB of Divisor Latch (write mode) +#define UART_IER (volatile uint8_t *)(UART + 0x01) // Interrupt Enable Register +#define UART_LCR (volatile uint8_t *)(UART + 0x03) // Line Control Register +#define UART_LSR (volatile uint8_t *)(UART + 0x05) // LSR:line status register +#define UART_LSR_EMPTY_MASK 0x40 // LSR Bit 6: Transmitter empty; both the THR and LSR are empty + +#define UART_REGR(reg) (*(reg)) +#define UART_REGW(reg, v) ((*reg) = (v)) + +// ref: https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h +// enum { +// UART0_IRQ = 10, +// RTC_IRQ = 11, +// VIRTIO_IRQ = 1, /* 1 to 8 */ +// VIRTIO_COUNT = 8, +// PCIE_IRQ = 0x20, /* 32 to 35 */ +// VIRTIO_NDEV = 0x35 /* Arbitrary maximum number of interrupts */ +// }; +#define UART0_IRQ 10 +#define VIRTIO_IRQ 1 + +// Saved registers for kernel context switches. +struct context +{ + /* ignore x0 */ + reg_t ra; + reg_t sp; + reg_t gp; + reg_t tp; + reg_t t0; + reg_t t1; + reg_t t2; + reg_t s0; + reg_t s1; + reg_t a0; + reg_t a1; + reg_t a2; + reg_t a3; + reg_t a4; + reg_t a5; + reg_t a6; + reg_t a7; + reg_t s2; + reg_t s3; + reg_t s4; + reg_t s5; + reg_t s6; + reg_t s7; + reg_t s8; + reg_t s9; + reg_t s10; + reg_t s11; + reg_t t3; + reg_t t4; + reg_t t5; + reg_t t6; + // upon is trap frame + + // save the pc to run in next schedule cycle + // reg_t pc; // offset: 31 *4 = 124 +}; + +// ref: https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h +// +// local interrupt controller, which contains the timer. +// ================== Timer Interrput ==================== + +#define NCPU 8 // maximum number of CPUs +#define CLINT 0x2000000 +#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 4 * (hartid)) +#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. + +static inline reg_t r_tp() +{ + reg_t x; + asm volatile("mv %0, tp" + : "=r"(x)); + return x; +} + +// which hart (core) is this? + +static inline reg_t r_mhartid() +{ + reg_t x; + asm volatile("csrr %0, mhartid" + : "=r"(x)); + return x; +} + +/* Machine Status Register, mstatus */ +#define MSTATUS_MPP (3 << 11) +#define MSTATUS_SPP (1 << 8) + +#define MSTATUS_MPIE (1 << 7) +#define MSTATUS_SPIE (1 << 5) +#define MSTATUS_UPIE (1 << 4) + +#define MSTATUS_MIE (1 << 3) +#define MSTATUS_SIE (1 << 1) +#define MSTATUS_UIE (1 << 0) + +static inline reg_t r_mstatus() +{ + reg_t x; + asm volatile("csrr %0, mstatus" : "=r" (x) ); + return x; +} + +static inline void w_mstatus(reg_t x) +{ + asm volatile("csrw mstatus, %0" : : "r" (x)); +} + +/* + * machine exception program counter, holds the + * instruction address to which a return from + * exception will go. + */ +static inline void w_mepc(reg_t x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +static inline reg_t r_mepc() +{ + reg_t x; + asm volatile("csrr %0, mepc" : "=r" (x)); + return x; +} + +/* Machine Scratch register, for early trap handler */ +static inline void w_mscratch(reg_t x) +{ + asm volatile("csrw mscratch, %0" : : "r" (x)); +} + +/* Machine-mode interrupt vector */ +static inline void w_mtvec(reg_t x) +{ + asm volatile("csrw mtvec, %0" : : "r" (x)); +} + +/* Machine-mode Interrupt Enable */ +#define MIE_MEIE (1 << 11) // external +#define MIE_MTIE (1 << 7) // timer +#define MIE_MSIE (1 << 3) // software + +static inline reg_t r_mie() +{ + reg_t x; + asm volatile("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void w_mie(reg_t x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +static inline reg_t r_mcause() +{ + reg_t x; + asm volatile("csrr %0, mcause" : "=r" (x) ); + return x; +} + +#endif /* __RISCV_H__ */ diff --git a/10-SystemCall/include/string.h b/10-SystemCall/include/string.h new file mode 100644 index 0000000..71bc3d2 --- /dev/null +++ b/10-SystemCall/include/string.h @@ -0,0 +1,10 @@ +#ifndef __STRING_H__ +#define __STRING_H__ + +void *memset(void *, int, unsigned int); +void * +memcpy(void *dst, const void *src, unsigned int n); +void * +memmove(void *dst, const void *src, unsigned int n); + +#endif \ No newline at end of file diff --git a/10-SystemCall/include/sys.h b/10-SystemCall/include/sys.h new file mode 100644 index 0000000..d8270d1 --- /dev/null +++ b/10-SystemCall/include/sys.h @@ -0,0 +1,9 @@ +#ifndef __SYS_H__ +#define __SYS_H__ + +#include "riscv.h" +extern void sys_timer(); +extern void sys_switch(struct context *ctx_old, struct context *ctx_new); +extern void switch_to(struct context *ctx_next); + +#endif diff --git a/10-SystemCall/include/task.h b/10-SystemCall/include/task.h new file mode 100644 index 0000000..1ac7e39 --- /dev/null +++ b/10-SystemCall/include/task.h @@ -0,0 +1,16 @@ +#ifndef __TASK_H__ +#define __TASK_H__ + +#include "riscv.h" +#include "sys.h" + +#define MAX_TASK 10 +#define STACK_SIZE 1024 + +extern int taskTop; + +extern int task_create(void (*task)(void)); +extern void task_go(int i); +extern void task_os(); + +#endif diff --git a/10-SystemCall/include/timer.h b/10-SystemCall/include/timer.h new file mode 100644 index 0000000..b971c37 --- /dev/null +++ b/10-SystemCall/include/timer.h @@ -0,0 +1,12 @@ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include "riscv.h" +#include "sys.h" +#include "lib.h" +#include "task.h" + +extern void timer_handler(); +extern void timer_init(); + +#endif diff --git a/10-SystemCall/include/types.h b/10-SystemCall/include/types.h new file mode 100644 index 0000000..15c7cbc --- /dev/null +++ b/10-SystemCall/include/types.h @@ -0,0 +1,13 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +#endif \ No newline at end of file diff --git a/10-SystemCall/include/user_api.h b/10-SystemCall/include/user_api.h new file mode 100644 index 0000000..116081d --- /dev/null +++ b/10-SystemCall/include/user_api.h @@ -0,0 +1,7 @@ +#ifndef __USER_API_H__ +#define __USER_API_H__ + +/* user mode syscall APIs */ +extern int gethid(unsigned int *hid); + +#endif /* __USER_API_H__ */ diff --git a/10-SystemCall/include/virtio.h b/10-SystemCall/include/virtio.h new file mode 100644 index 0000000..ff34616 --- /dev/null +++ b/10-SystemCall/include/virtio.h @@ -0,0 +1,129 @@ +#include "types.h" +/* + * References: + * 1. https://github.com/ianchen0119/xv6-riscv/blob/riscv/kernel/virtio.h + * 2. https://github.com/sgmarz/osblog/blob/master/risc_v/src/virtio.rs + */ +#define VIRTIO_MMIO_BASE 0x10001000 +/* OFFSET */ +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 // Magic value must be 0x74726976 +#define VIRTIO_MMIO_VERSION 0x004 // Version: 1 (Legacy) + +/* + * Device ID: + * 1 (Network Device) + * 2 (Block Device) + * 4 (Random number generator Device) + * 16 (GPU Device) + * 18 (Input Device) + */ + +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551 +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 // page size for PFN, write-only +#define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only +#define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only +#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c // used ring alignment, write-only +#define VIRTIO_MMIO_QUEUE_PFN 0x040 // physical page number for queue, read/write +#define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only +#define VIRTIO_MMIO_STATUS 0x070 // read/write +#define VIRTIO_MMIO_QueueDescLow 0x080 +#define VIRTIO_MMIO_QueueDescHigh 0x084 +#define VIRTIO_MMIO_QueueAvailLow 0x090 +#define VIRTIO_MMIO_QueueAvailHigh 0x094 +#define VIRTIO_MMIO_QueueUsedLow 0x0a0 +#define VIRTIO_MMIO_QueueUsedHigh 0x0a4 + +// status register bits, from qemu virtio_config.h +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +#define VIRTIO_CONFIG_S_DRIVER 2 +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +#define VIRTIO_CONFIG_S_FEATURES_OK 8 + +// device feature bits +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ +#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ +#define VIRTIO_F_ANY_LAYOUT 27 +#define VIRTIO_RING_F_INDIRECT_DESC 28 +#define VIRTIO_RING_F_EVENT_IDX 29 + +// this many virtio descriptors. +#define NUM 8 + +// 描述符包含這些訊息: 地址,地址長度,某些標誌和其他信息。使用此描述符,我們可以將設備指向 RAM 中任何緩衝區的內存地址。 +typedef struct virtq_desc +{ + /* + * addr: 我們可以在 64-bit 內存地址內的任何位置告訴設備存儲位置。 + * len: 讓 Device 知道有多少內存可用。 + * flags: 用於控制 descriptor 。 + * next: 告訴 Device 下一個描述符的 Index 。如果指定了 VIRTQ_DESC_F_NEXT, Device 僅讀取該字段。否則無效。 + */ + uint64 addr; + uint32 len; + uint16 flags; + uint16 next; +} virtq_desc_t; + +#define VRING_DESC_F_NEXT 1 // chained with another descriptor +#define VRING_DESC_F_WRITE 2 // device writes (vs read) +#define VRING_DESC_F_INDIRECT 4 // buffer contains a list of buffer descriptors + +/* + * 用來存放 descriptor 的索引,當 Device 收到通知時,它會檢查 AvailableRing 確認需要讀取哪些 Descriptor 。 + * 注意: Descriptor 和 AvailableRing 都存儲在 RAM 中。 + */ + +typedef struct virtq_avail +{ + uint16 flags; // always zero + uint16 idx; // driver will write ring[idx] next + uint16 ring[NUM]; // descriptor numbers of chain heads +} virtq_avail_t; + +/* + * 當內部的 Index 與 UsedRing 的 Index 相等,代表所有資料都已經被讀取,這個 Device 是唯一需要被寫進 Index 的。 + */ + +typedef struct virtq_used_elem +{ + uint32 id; // index of start of completed descriptor chain + uint32 len; +} virtq_used_elem_t; + +/* + * Device 可以使用 UsedRing 向 OS 發送訊息。 + * Device 通常使用它來告知 OS 它已完成先前通知的請求。 + * AvailableRing 與 UsedRing 非常相似,差異在於: OS 需要查看 UsedRing 得知哪個 Descriptor 已經被服務。 + */ + +typedef struct virtq_used +{ + uint16 flags; // always zero + uint16 idx; // device increments when it adds a ring[] entry + struct virtq_used_elem ring[NUM]; +} virtq_used_t; + +// these are specific to virtio block devices, e.g. disks, +// described in Section 5.2 of the spec. + +#define VIRTIO_BLK_T_IN 0 // read the disk +#define VIRTIO_BLK_T_OUT 1 // write the disk + +// the format of the first descriptor in a disk request. +// to be followed by two more descriptors containing +// the block, and a one-byte status. +typedef struct virtio_blk_req +{ + uint32 type; // VIRTIO_BLK_T_IN or ..._OUT + uint32 reserved; // 將 Header 擴充到 16-byte ,並將 64-bit sector 移到正確的位置。 + uint64 sector; +} virtio_blk_req_t; \ No newline at end of file diff --git a/10-SystemCall/os.ld b/10-SystemCall/os.ld new file mode 100644 index 0000000..32be052 --- /dev/null +++ b/10-SystemCall/os.ld @@ -0,0 +1,49 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M +} + +PHDRS +{ + text PT_LOAD; + data PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + .text : { + PROVIDE(_text_start = .); + *(.text.init) *(.text .text.*) + PROVIDE(_text_end = .); + } >ram AT>ram :text + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } >ram AT>ram :text + + .data : { + . = ALIGN(4096); + PROVIDE(_data_start = .); + *(.sdata .sdata.*) *(.data .data.*) + PROVIDE(_data_end = .); + } >ram AT>ram :data + + .bss :{ + PROVIDE(_bss_start = .); + *(.sbss .sbss.*) *(.bss .bss.*) + PROVIDE(_bss_end = .); + } >ram AT>ram :bss + + PROVIDE(_memory_start = ORIGIN(ram)); + PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); + + PROVIDE(_heap_start = _bss_end); + PROVIDE(_heap_size = _memory_end - _heap_start); +} diff --git a/10-SystemCall/src/.DS_Store b/10-SystemCall/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..06fb1290984bf637dfedb0531b15278d8bc03bf7 GIT binary patch literal 6148 zcmeHK!A=4(5S_vTt6n&IG42nVnGDkr@D$b^}VmOW1 z)?o^m0)I^bes?=`NmKl0wDJDVh8W?|W0c;97+ORvFefzpdehg;mw?&UIj>``I|#Tf zx}gzDj~0IaSk3Al17?Ca7d11!2fQ}TCDNKTHO2wAgBkCjLWw$xpz#*4Q(>1-;g-g6 zB+Zk6-va+ZVh+~Ud<~d8$}BV^>)mvvMsgT6p}6VbVhqn12BZ{(S0 zE)mDPhRd1W<}MNW2>d8w$#l%E)kAW<$xZ1V5pz~1$a*|C=X{foN3$O*fHRvd-ScR* zDPRhi0&4|$f5=c6bH&P|Y#pfN3II&dErvFKG_X&mm@8HuF#>bm73f`^UNM|^M}MZg zT(R=#-O1_Y!|6Xey`ebycg&v&om}qGYE!@z*jAwFAG>`1pMHP;-*&PmQ@|AXR|>df za6af`NqV+Mi{rC4L%Bdflags = 0; +} + +static inline int _is_free(struct Page *page) +{ + if (page->flags & PAGE_TAKEN) + { + return 0; + } + else + { + return 1; + } +} + +static inline void _set_flag(struct Page *page, uint8_t flags) +{ + page->flags |= flags; +} + +static inline int _is_last(struct Page *page) +{ + if (page->flags & PAGE_LAST) + { + return 1; + } + else + { + return 0; + } +} + +/* + * align the address to the border of page (256) + */ + +static inline uint32_t _align_page(uint32_t address) +{ + uint32_t order = (1 << PAGE_ORDER) - 1; + return (address + order) & (~order); +} + +void page_init() +{ + _num_pages = (HEAP_SIZE / PAGE_SIZE) - 2048; + lib_printf("HEAP_START = %x, HEAP_SIZE = %x, num of pages = %d\n", HEAP_START, HEAP_SIZE, _num_pages); + + struct Page *page = (struct Page *)HEAP_START; + for (int i = 0; i < _num_pages; i++) + { + _clear(page); + page++; + } + + _alloc_start = _align_page(HEAP_START + 2048 * PAGE_SIZE); + _alloc_end = _alloc_start + (PAGE_SIZE * _num_pages); + + lib_printf("TEXT: 0x%x -> 0x%x\n", TEXT_START, TEXT_END); + lib_printf("RODATA: 0x%x -> 0x%x\n", RODATA_START, RODATA_END); + lib_printf("DATA: 0x%x -> 0x%x\n", DATA_START, DATA_END); + lib_printf("BSS: 0x%x -> 0x%x\n", BSS_START, BSS_END); + lib_printf("HEAP: 0x%x -> 0x%x\n", _alloc_start, _alloc_end); +} + +/* + * Allocate a memory block which is composed of contiguous physical pages + * - npages: the number of PAGE_SIZE pages to allocate + */ + +void *malloc(size_t size) +{ + int npages = pageNum(size); + int found = 0; + struct Page *page_i = (struct Page *)HEAP_START; + for (int i = 0; i < (_num_pages - npages); i++) + { + if (_is_free(page_i)) + { + found = 1; + + /* + * meet a free page, continue to check if following + * (npages - 1) pages are also unallocated. + */ + + struct Page *page_j = page_i; + for (int j = i; j < (i + npages); j++) + { + if (!_is_free(page_j)) + { + found = 0; + break; + } + page_j++; + } + /* + * get a memory block which is good enough for us, + * take housekeeping, then return the actual start + * address of the first page of this memory block + */ + if (found) + { + struct Page *page_k = page_i; + for (int k = i; k < (i + npages); k++) + { + _set_flag(page_k, PAGE_TAKEN); + page_k++; + } + page_k--; + _set_flag(page_k, PAGE_LAST); + return (void *)(_alloc_start + i * PAGE_SIZE); + } + } + page_i++; + } + return NULL; +} + +/* + * Free the memory block + * - p: start address of the memory block + */ +void free(void *p) +{ + /* + * Assert (TBD) if p is invalid + */ + if (!p || (uint32_t)p >= _alloc_end) + { + return; + } + /* get the first page descriptor of this memory block */ + struct Page *page = (struct Page *)HEAP_START; + page += ((uint32_t)p - _alloc_start) / PAGE_SIZE; + /* loop and clear all the page descriptors of the memory block */ + while (!_is_free(page)) + { + if (_is_last(page)) + { + _clear(page); + break; + } + else + { + _clear(page); + page++; + ; + } + } +} + +void page_test() +{ + void *p = malloc(1024); + lib_printf("p = 0x%x\n", p); + + void *p2 = malloc(512); + lib_printf("p2 = 0x%x\n", p2); + + void *p3 = malloc(sizeof(int)); + lib_printf("p3 = 0x%x\n", p3); + + free(p); + free(p2); + free(p3); +} \ No newline at end of file diff --git a/10-SystemCall/src/lib.c b/10-SystemCall/src/lib.c new file mode 100644 index 0000000..77b47da --- /dev/null +++ b/10-SystemCall/src/lib.c @@ -0,0 +1,247 @@ +#include "lib.h" + +#define LSR_RX_READY (1 << 0) +#define EOF 0 + +void uart_init() +{ + /* disable interrupts */ + UART_REGW(UART_IER, 0x00); + + /* Baud rate setting */ + uint8_t lcr = UART_REGR(UART_LCR); + UART_REGW(UART_LCR, lcr | (1 << 7)); + UART_REGW(UART_DLL, 0x03); + UART_REGW(UART_DLM, 0x00); + + lcr = 0; + UART_REGW(UART_LCR, lcr | (3 << 0)); + + uint8_t ier = UART_REGR(UART_IER); + UART_REGW(UART_IER, ier | (1 << 0)); +} + +char *lib_gets(char *s) +{ + int ch; + char *p = s; + + while ((ch = lib_getc()) != '\n' && ch != EOF) + { + if (ch == -1) + { + continue; + } + *s = (char)ch; + s++; + } + + *s = '\0'; + return p; +} + +int lib_getc(void) +{ + if (*UART_LSR & LSR_RX_READY) + { + return *UART_RHR == '\r' ? '\n' : *UART_RHR; + } + else + { + return -1; + } +} + +void lib_isr(void) +{ + for (;;) + { + int c = lib_getc(); + if (c == -1) + { + break; + } + else + { + lib_putc((char)c); + lib_putc('\n'); + } + } +} + +void lib_delay(volatile int count) +{ + count *= 50000; + while (count--) + ; +} + +int lib_putc(char ch) +{ + while ((*UART_LSR & UART_LSR_EMPTY_MASK) == 0) + ; + return *UART_THR = ch; +} + +void lib_puts(char *s) +{ + while (*s) + lib_putc(*s++); +} + +int lib_vsnprintf(char *out, size_t n, const char *s, va_list vl) +{ + int format = 0; + int longarg = 0; + size_t pos = 0; + for (; *s; s++) + { + if (format) + { + switch (*s) + { + case 'l': + { + longarg = 1; + break; + } + case 'p': + { + longarg = 1; + if (out && pos < n) + { + out[pos] = '0'; + } + pos++; + if (out && pos < n) + { + out[pos] = 'x'; + } + pos++; + } + case 'x': + { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + int hexdigits = 2 * (longarg ? sizeof(long) : sizeof(int)) - 1; + for (int i = hexdigits; i >= 0; i--) + { + int d = (num >> (4 * i)) & 0xF; + if (out && pos < n) + { + out[pos] = (d < 10 ? '0' + d : 'a' + d - 10); + } + pos++; + } + longarg = 0; + format = 0; + break; + } + case 'd': + { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + if (num < 0) + { + num = -num; + if (out && pos < n) + { + out[pos] = '-'; + } + pos++; + } + long digits = 1; + for (long nn = num; nn /= 10; digits++) + ; + for (int i = digits - 1; i >= 0; i--) + { + if (out && pos + i < n) + { + out[pos + i] = '0' + (num % 10); + } + num /= 10; + } + pos += digits; + longarg = 0; + format = 0; + break; + } + case 's': + { + const char *s2 = va_arg(vl, const char *); + while (*s2) + { + if (out && pos < n) + { + out[pos] = *s2; + } + pos++; + s2++; + } + longarg = 0; + format = 0; + break; + } + case 'c': + { + if (out && pos < n) + { + out[pos] = (char)va_arg(vl, int); + } + pos++; + longarg = 0; + format = 0; + break; + } + default: + break; + } + } + else if (*s == '%') + { + format = 1; + } + else + { + if (out && pos < n) + { + out[pos] = *s; + } + pos++; + } + } + if (out && pos < n) + { + out[pos] = 0; + } + else if (out && n) + { + out[n - 1] = 0; + } + return pos; +} + +static char out_buf[1000]; // buffer for lib_vprintf() + +int lib_vprintf(const char *s, va_list vl) +{ + int res = lib_vsnprintf(NULL, -1, s, vl); + if (res + 1 >= sizeof(out_buf)) + { + lib_puts("error: lib_vprintf() output string size overflow\n"); + while (1) + { + } + } + lib_vsnprintf(out_buf, res + 1, s, vl); + lib_puts(out_buf); + return res; +} + +int lib_printf(const char *s, ...) +{ + int res = 0; + va_list vl; + va_start(vl, s); + res = lib_vprintf(s, vl); + va_end(vl); + return res; +} diff --git a/10-SystemCall/src/lock.c b/10-SystemCall/src/lock.c new file mode 100644 index 0000000..309782a --- /dev/null +++ b/10-SystemCall/src/lock.c @@ -0,0 +1,32 @@ +#include "os.h" + +void lock_init(lock_t *lock) +{ + lock->locked = 0; +} + +void lock_acquire(lock_t *lock) +{ + for (;;) + { + if (!atomic_swap(lock)) + { + break; + } + } +} + +void lock_free(lock_t *lock) +{ + lock->locked = 0; +} + +void basic_lock() +{ + w_mstatus(r_mstatus() & ~MSTATUS_MIE); +} + +void basic_unlock() +{ + w_mstatus(r_mstatus() | MSTATUS_MIE); +} diff --git a/10-SystemCall/src/mem.s b/10-SystemCall/src/mem.s new file mode 100644 index 0000000..dede6f3 --- /dev/null +++ b/10-SystemCall/src/mem.s @@ -0,0 +1,30 @@ +.section .rodata +.global HEAP_START +HEAP_START: .word _heap_start + +.global HEAP_SIZE +HEAP_SIZE: .word _heap_size + +.global TEXT_START +TEXT_START: .word _text_start + +.global TEXT_END +TEXT_END: .word _text_end + +.global DATA_START +DATA_START: .word _data_start + +.global DATA_END +DATA_END: .word _data_end + +.global RODATA_START +RODATA_START: .word _rodata_start + +.global RODATA_END +RODATA_END: .word _rodata_end + +.global BSS_START +BSS_START: .word _bss_start + +.global BSS_END +BSS_END: .word _bss_end \ No newline at end of file diff --git a/10-SystemCall/src/os.c b/10-SystemCall/src/os.c new file mode 100644 index 0000000..ac8207a --- /dev/null +++ b/10-SystemCall/src/os.c @@ -0,0 +1,52 @@ +#include "os.h" +#include "riscv.h" +extern void page_init(void); +extern void trap_init(void); + +void panic(char *s) +{ + lib_puts(s); + for (;;) + { + } +} + +void os_kernel() +{ + task_os(); +} + +void disk_read() +{ + virtio_tester(0); +} + +void os_start() +{ + uart_init(); + page_init(); + lib_puts("OS start\n"); + user_init(); + trap_init(); + plic_init(); + virtio_disk_init(); + timer_init(); // start timer interrupt ... +} + +int os_main(void) +{ + os_start(); + disk_read(); + page_test(); + int current_task = 0; + + while (1) + { + lib_puts("OS: Activate next task\n"); + task_go(current_task); + lib_puts("OS: Back to OS\n"); + current_task = (current_task + 1) % taskTop; // Round Robin Scheduling + lib_puts("\n"); + } + return 0; +} diff --git a/10-SystemCall/src/plic.c b/10-SystemCall/src/plic.c new file mode 100644 index 0000000..32e0c8a --- /dev/null +++ b/10-SystemCall/src/plic.c @@ -0,0 +1,48 @@ +#include "os.h" + +// ref: https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h +// Intro: https://github.com/ianchen0119/AwesomeCS/wiki/2-5-RISC-V::%E4%B8%AD%E6%96%B7%E8%88%87%E7%95%B0%E5%B8%B8%E8%99%95%E7%90%86----PLIC-%E4%BB%8B%E7%B4%B9 +#define PLIC_BASE 0x0c000000L +#define PLIC_PRIORITY(id) (PLIC_BASE + (id)*4) +#define PLIC_PENDING(id) (PLIC_BASE + 0x1000 + ((id) / 32)) +#define PLIC_MENABLE(hart) (PLIC_BASE + 0x2000 + (hart)*0x80) +#define PLIC_MTHRESHOLD(hart) (PLIC_BASE + 0x200000 + (hart)*0x1000) +#define PLIC_MCLAIM(hart) (PLIC_BASE + 0x200004 + (hart)*0x1000) +#define PLIC_MCOMPLETE(hart) (PLIC_BASE + 0x200004 + (hart)*0x1000) + +void plic_init() +{ + int hart = r_tp(); + // QEMU Virt machine support 7 priority (1 - 7), + // The "0" is reserved, and the lowest priority is "1". + + *(uint32_t *)PLIC_PRIORITY(UART0_IRQ) = 1; + *(uint32_t *)PLIC_PRIORITY(VIRTIO_IRQ) = 1; + + /* Enable UART0 and VIRTIO */ + + *(uint32_t *)PLIC_MENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO_IRQ); + + /* Set priority threshold for UART0. */ + + *(uint32_t *)PLIC_MTHRESHOLD(hart) = 0; + + /* enable machine-mode external interrupts. */ + w_mie(r_mie() | MIE_MEIE); + + // enable machine-mode interrupts. + w_mstatus(r_mstatus() | MSTATUS_MIE); +} + +int plic_claim() +{ + int hart = r_tp(); + int irq = *(uint32_t *)PLIC_MCLAIM(hart); + return irq; +} + +void plic_complete(int irq) +{ + int hart = r_tp(); + *(uint32_t *)PLIC_MCOMPLETE(hart) = irq; +} \ No newline at end of file diff --git a/10-SystemCall/src/start.s b/10-SystemCall/src/start.s new file mode 100644 index 0000000..e295645 --- /dev/null +++ b/10-SystemCall/src/start.s @@ -0,0 +1,34 @@ +.equ STACK_SIZE, 8192 + +.global _start + +_start: + # setup stacks per hart + csrr t0, mhartid # read current hart id + slli t0, t0, 10 # shift left the hart id by 1024 + la sp, stacks + STACK_SIZE # set the initial stack pointer + # to the end of the stack space + add sp, sp, t0 # move the current hart stack pointer + # to its place in the stack space + + # park harts with id != 0 + csrr a0, mhartid # read current hart id + bnez a0, park # if we're not on the hart 0 + +# li t0, 1 << 7 +# li t0, 0xffffffff +# csrw pmpaddr0, t0 +# li t0, 0xf +# csrw pmpcfg0, t0 +# csrr a1, mstatus +# or t0, t0, a1 +# csrw mstatus, t0 + # we park the hart + j os_main # hart 0 jump to c + +park: + wfi + j park + +stacks: + .skip STACK_SIZE * 4 # allocate space for the harts stacks diff --git a/10-SystemCall/src/string.c b/10-SystemCall/src/string.c new file mode 100644 index 0000000..ae863f4 --- /dev/null +++ b/10-SystemCall/src/string.c @@ -0,0 +1,40 @@ +#include "string.h" + +void *memset(void *dst, int c, unsigned int n) +{ + char *cdst = (char *)dst; + int i; + for (i = 0; i < n; i++) + { + cdst[i] = c; + } + return dst; +} + +void * +memcpy(void *dst, const void *src, unsigned int n) +{ + return memmove(dst, src, n); +} + +void * +memmove(void *dst, const void *src, unsigned int n) +{ + const char *s; + char *d; + + s = src; + d = dst; + if (s < d && s + n > d) + { + s += n; + d += n; + while (n-- > 0) + *--d = *--s; + } + else + while (n-- > 0) + *d++ = *s++; + + return dst; +} \ No newline at end of file diff --git a/10-SystemCall/src/sys.s b/10-SystemCall/src/sys.s new file mode 100644 index 0000000..d395ba1 --- /dev/null +++ b/10-SystemCall/src/sys.s @@ -0,0 +1,185 @@ +# This Code derived from xv6-riscv (64bit) +# -- https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/swtch.S + +# ============ MACRO ================== +.macro ctx_save base + sw ra, 0(\base) + sw sp, 4(\base) + sw s0, 8(\base) + sw s1, 12(\base) + sw s2, 16(\base) + sw s3, 20(\base) + sw s4, 24(\base) + sw s5, 28(\base) + sw s6, 32(\base) + sw s7, 36(\base) + sw s8, 40(\base) + sw s9, 44(\base) + sw s10, 48(\base) + sw s11, 52(\base) + sw a0, 56(\base) + sw a7, 60(\base) +.endm + +.macro ctx_load base + lw ra, 0(\base) + lw sp, 4(\base) + lw s0, 8(\base) + lw s1, 12(\base) + lw s2, 16(\base) + lw s3, 20(\base) + lw s4, 24(\base) + lw s5, 28(\base) + lw s6, 32(\base) + lw s7, 36(\base) + lw s8, 40(\base) + lw s9, 44(\base) + lw s10, 48(\base) + lw s11, 52(\base) + lw a0, 56(\base) + lw a7, 60(\base) +.endm + +.macro reg_save base + # save the registers. + sw ra, 0(\base) + sw sp, 4(\base) + sw gp, 8(\base) + sw tp, 12(\base) + sw t0, 16(\base) + sw t1, 20(\base) + sw t2, 24(\base) + sw s0, 28(\base) + sw s1, 32(\base) + sw a0, 36(\base) + sw a1, 40(\base) + sw a2, 44(\base) + sw a3, 48(\base) + sw a4, 52(\base) + sw a5, 56(\base) + sw a6, 60(\base) + sw a7, 64(\base) + sw s2, 68(\base) + sw s3, 72(\base) + sw s4, 76(\base) + sw s5, 80(\base) + sw s6, 84(\base) + sw s7, 88(\base) + sw s8, 92(\base) + sw s9, 96(\base) + sw s10, 100(\base) + sw s11, 104(\base) + sw t3, 108(\base) + sw t4, 112(\base) + sw t5, 116(\base) +.endm + +.macro reg_load base + # restore registers. + lw ra, 0(\base) + lw sp, 4(\base) + lw gp, 8(\base) + lw tp, 12(\base) + lw t0, 16(\base) + lw t1, 20(\base) + lw t2, 24(\base) + lw s0, 28(\base) + lw s1, 32(\base) + lw a0, 36(\base) + lw a1, 40(\base) + lw a2, 44(\base) + lw a3, 48(\base) + lw a4, 52(\base) + lw a5, 56(\base) + lw a6, 60(\base) + lw a7, 64(\base) + lw s2, 68(\base) + lw s3, 72(\base) + lw s4, 76(\base) + lw s5, 80(\base) + lw s6, 84(\base) + lw s7, 88(\base) + lw s8, 92(\base) + lw s9, 96(\base) + lw s10, 100(\base) + lw s11, 104(\base) + lw t3, 108(\base) + lw t4, 112(\base) + lw t5, 116(\base) + lw t6, 120(\base) +.endm +# ============ Macro END ================== + +# Context switch +# +# void sys_switch(struct context *old, struct context *new); +# +# Save current registers in old. Load from new. + +.text + +.globl sys_switch +.align 4 +sys_switch: + + ctx_save a0 # a0 => struct context *old + ctx_load a1 # a1 => struct context *new + + ret # pc=ra; swtch to new task (new->ra) + +.globl atomic_swap +.align 4 +atomic_swap: + li a5, 1 + amoswap.w.aq a5, a5, 0(a0) + mv a0, a5 + ret + +.globl trap_vector +# the trap vector base address must always be aligned on a 4-byte boundary +.align 4 +trap_vector: + # save context(registers). + csrrw t6, mscratch, t6 # swap t6 and mscratch + reg_save t6 + csrw mscratch, t6 + + # save mepc to context of current task + csrr a0, mepc + sw a0, 124(t6) + + # call the C trap handler in trap.c + csrr a0, mepc + csrr a1, mcause + csrr a2, mscratch + call trap_handler + + # trap_handler will return the return address via a0. + csrw mepc, a0 + + # load context(registers). + csrr t6, mscratch + reg_load t6 + mret + +# void switch_to(struct context *next); +# a0: pointer to the context of the next task +.globl switch_to +.align 4 +switch_to: + # switch mscratch to point to the context of the next task + csrw mscratch, a0 + # set mepc to the pc of the next task + lw a1, 124(a0) + csrw mepc, a1 + + # Restore all GP registers + # Use t6 to point to the context of the new task + mv t6, a0 + reg_load t6 + + # Do actual context switching. + # Notice this will enable global interrupt + mret + +.end diff --git a/10-SystemCall/src/syscall.c b/10-SystemCall/src/syscall.c new file mode 100644 index 0000000..5422805 --- /dev/null +++ b/10-SystemCall/src/syscall.c @@ -0,0 +1,30 @@ +#include "os.h" + +int sys_gethid(unsigned int *ptr_hid) +{ + lib_printf("--> sys_gethid, arg0 = 0x%x\n", ptr_hid); + if (ptr_hid == NULL) { + lib_printf("ptr_hid == NULL\n"); + return -1; + } else { + lib_printf("ptr_hid != NULL\n"); + *ptr_hid = r_mhartid(); + return 0; + } +} + +void do_syscall(struct context *ctx) +{ + uint32_t syscall_num = ctx->a7; + lib_printf("syscall_num: %d\n", syscall_num); + switch (syscall_num) { + case 1: + ctx->a0 = sys_gethid((unsigned int *)(ctx->a0)); + break; + default: + lib_printf("Unknown syscall no: %d\n", syscall_num); + ctx->a0 = -1; + } + + return; +} diff --git a/10-SystemCall/src/task.c b/10-SystemCall/src/task.c new file mode 100644 index 0000000..0387e74 --- /dev/null +++ b/10-SystemCall/src/task.c @@ -0,0 +1,35 @@ +#include "task.h" +#include "lib.h" + +uint8_t task_stack[MAX_TASK][STACK_SIZE]; +struct context ctx_os; +struct context ctx_tasks[MAX_TASK]; +struct context *ctx_now; +int taskTop = 0; // total number of task + +// create a new task +int task_create(void (*task)(void)) +{ + int i = taskTop++; + ctx_tasks[i].ra = (reg_t)task; + // ctx_tasks[i].pc = (reg_t)task; + ctx_tasks[i].sp = (reg_t)&task_stack[i][STACK_SIZE - 1]; + return i; +} + +// switch to task[i] +void task_go(int i) +{ + ctx_now = &ctx_tasks[i]; + // switch_to(ctx_now); + sys_switch(&ctx_os, &ctx_tasks[i]); +} + +// switch back to os +void task_os() +{ + struct context *ctx = ctx_now; + ctx_now = &ctx_os; + // switch_to(&ctx_os); + sys_switch(ctx, &ctx_os); +} diff --git a/10-SystemCall/src/timer.c b/10-SystemCall/src/timer.c new file mode 100644 index 0000000..fb8aade --- /dev/null +++ b/10-SystemCall/src/timer.c @@ -0,0 +1,39 @@ +#include "timer.h" + +// a scratch area per CPU for machine-mode timer interrupts. +reg_t timer_scratch[NCPU][5]; + +#define interval 20000000 // cycles; about 2 second in qemu. + +void timer_init() +{ + // each CPU has a separate source of timer interrupts. + int id = r_mhartid(); + + // ask the CLINT for a timer interrupt. + // int interval = 1000000; // cycles; about 1/10th second in qemu. + + *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + reg_t *scratch = &timer_scratch[id][0]; + scratch[3] = CLINT_MTIMECMP(id); + scratch[4] = interval; + w_mscratch((reg_t)scratch); + + // enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); +} + +static int timer_count = 0; + +void timer_handler() +{ + lib_printf("timer_handler: %d\n", ++timer_count); + int id = r_mhartid(); + //lib_printf("hid: %d\n", id); + *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; +} diff --git a/10-SystemCall/src/trap.c b/10-SystemCall/src/trap.c new file mode 100644 index 0000000..011217f --- /dev/null +++ b/10-SystemCall/src/trap.c @@ -0,0 +1,101 @@ +#include "os.h" +#include "riscv.h" +extern void trap_vector(); +extern void virtio_disk_isr(); +extern void do_syscall(struct context *ctx); + +void trap_init() +{ + // set the machine-mode trap handler. + w_mtvec((reg_t)trap_vector); +} + +void external_handler() +{ + int irq = plic_claim(); + if (irq == UART0_IRQ) + { + lib_isr(); + } + else if (irq == VIRTIO_IRQ) + { + lib_puts("Virtio IRQ\n"); + virtio_disk_isr(); + } + else if (irq) + { + lib_printf("unexpected interrupt irq = %d\n", irq); + } + + if (irq) + { + plic_complete(irq); + } +} + +reg_t trap_handler(reg_t epc, reg_t cause, struct context *ctx) +{ + reg_t return_pc = epc; + reg_t cause_code = cause & 0xfff; + + if (cause & 0x80000000) + { + /* Asynchronous trap - interrupt */ + switch (cause_code) + { + case 3: + /* software interruption */ + break; + case 7: + /* timer interruption */ + // disable machine-mode timer interrupts. + w_mie(r_mie() & ~(1 << 7)); + timer_handler(); + return_pc = (reg_t)&os_kernel; + // enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); + break; + case 11: + /* external interruption */ + external_handler(); + break; + default: + lib_puts("unknown async exception!\n"); + break; + } + } + else + { + switch (cause_code) + { + case 2: + lib_puts("Illegal instruction!\n"); + break; + case 5: + lib_puts("Fault load!\n"); + break; + case 7: + lib_puts("Fault store!\n"); + break; + case 8: + lib_puts("Environment call from U-mode!\n"); + do_syscall(ctx); + return_pc += 4; + break; + case 11: + lib_puts("Environment call from M-mode!\n"); + do_syscall(ctx); + return_pc += 4; + break; + default: + /* Synchronous trap - exception */ + lib_printf("Sync exceptions! cause code: %d\n", cause_code); + break; + } + // for (;;) + // { + /* code */ + // } + } + return return_pc; +} diff --git a/10-SystemCall/src/user.c b/10-SystemCall/src/user.c new file mode 100644 index 0000000..41c203f --- /dev/null +++ b/10-SystemCall/src/user.c @@ -0,0 +1,91 @@ +#include "os.h" +#include "user_api.h" + +int shared_var = 500; + +lock_t lock; + +void user_task0(void) +{ + lib_puts("Task0: Created!\n"); + while (1) + { + lib_puts("Task0: Running...\n"); + lib_delay(1000); + } +} + +void user_task1(void) +{ + lib_puts("Task1: Created!\n"); + while (1) + { + lib_puts("Task1: Running...\n"); + lib_delay(1000); + } +} + +void user_task2(void) +{ + lib_puts("Task2: Created!\n"); + while (1) + { + for (int i = 0; i < 50; i++) + { + lock_acquire(&lock); + shared_var++; + lock_free(&lock); + lib_delay(100); + } + lib_printf("The value of shared_var is: %d \n", shared_var); + } +} + +void user_task3(void) +{ + lib_puts("Task3: Created!\n"); + while (1) + { + lib_puts("Trying to get the lock... \n"); + lock_acquire(&lock); + lib_puts("Get the lock!\n"); + lock_free(&lock); + lib_delay(1000); + } +} + +void user_task4(void) +{ + lib_puts("Task4: Created!\n"); + unsigned int hid = -1; + + /* + * if syscall is supported, this will trigger exception, + * code = 2 (Illegal instruction) + */ + // hid = r_mhartid(); + // lib_printf("hart id is %d\n", hid); + + // perform system call from the user mode + int ret = -1; + ret = gethid(&hid); + // ret = gethid(NULL); + if (!ret) { + lib_printf("system call returned!, hart id is %d\n", hid); + } else { + lib_printf("gethid() failed, return: %d\n", ret); + } + + while (1) + { + lib_puts("Task4: Running...\n"); + lib_delay(1000); + } +} + +void user_init() +{ + task_create(&user_task0); + task_create(&user_task4); + // task_create(&user_task1); +} diff --git a/10-SystemCall/src/usys.s b/10-SystemCall/src/usys.s new file mode 100644 index 0000000..b5d1a3a --- /dev/null +++ b/10-SystemCall/src/usys.s @@ -0,0 +1,6 @@ +.global gethid +gethid: + li a7, 1 + ecall + ret + diff --git a/10-SystemCall/src/virtio.c b/10-SystemCall/src/virtio.c new file mode 100644 index 0000000..6454ad6 --- /dev/null +++ b/10-SystemCall/src/virtio.c @@ -0,0 +1,312 @@ +/* This Code derived from xv6-riscv (64bit) + * -- https://github.com/mit-pdos/xv6-riscv/ + */ + +#include "virtio.h" +#include "os.h" + +#define R(addr) ((volatile uint32 *)(VIRTIO_MMIO_BASE + (addr))) +#define BSIZE 512 // block size +#define PGSHIFT 12 + +struct blk +{ + uint32 dev; + uint32 blockno; + lock_t lock; + int disk; + unsigned char data[BSIZE]; +}; + +static struct disk +{ + char pages[2 * PGSIZE]; + /* descriptor */ + virtq_desc_t *desc; + /* AvailableRing */ + virtq_avail_t *avail; + /* UsedRing */ + virtq_used_t *used; + /* For decord each descriptor is free or not */ + char free[NUM]; + struct + { + struct blk *b; + char status; + } info[NUM]; + uint16 used_idx; + /* Disk command headers */ + virtio_blk_req_t ops[NUM]; + + struct lock vdisk_lock; +} __attribute__((aligned(PGSIZE))) disk; + +struct blk b[3]; + +void virtio_tester(int write) +{ + if (!b[0].dev) + { + lib_puts("buffer init...\n"); + for (size_t i = 0; i < 1; i++) + { + b[i].dev = 1; // always is 1 + b[i].blockno = 1; + for (size_t j = 0; j < BSIZE; j++) + { + b[i].data[j] = 0; + } + + lock_init(&(b[i].lock)); + } + } + + lib_puts("block read...\n"); + virtio_disk_rw(&b[0], write); + size_t i = 0; + for (; i < 10; i++) + { + lib_printf("%x \n", b[0].data[i]); + } + lib_puts("\n"); +} + +void virtio_disk_init() +{ + uint32 status = 0; + + lock_init(&disk.vdisk_lock); + + if (*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || + *R(VIRTIO_MMIO_VERSION) != 1 || + *R(VIRTIO_MMIO_DEVICE_ID) != 2 || + *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551) + { + panic("could not find virtio disk"); + } + /* Reset the device */ + *R(VIRTIO_MMIO_STATUS) = status; + /* Set the ACKNOWLEDGE status bit to the status register. */ + status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; + *R(VIRTIO_MMIO_STATUS) = status; + /* Set the DRIVER status bit to the status register. */ + status |= VIRTIO_CONFIG_S_DRIVER; + *R(VIRTIO_MMIO_STATUS) = status; + /* negotiate features */ + uint32 features = *R(VIRTIO_MMIO_DEVICE_FEATURES); + features &= ~(1 << VIRTIO_BLK_F_RO); + features &= ~(1 << VIRTIO_BLK_F_SCSI); + features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE); + features &= ~(1 << VIRTIO_BLK_F_MQ); + features &= ~(1 << VIRTIO_F_ANY_LAYOUT); + features &= ~(1 << VIRTIO_RING_F_EVENT_IDX); + features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); + *R(VIRTIO_MMIO_DRIVER_FEATURES) = features; + + /* tell device that feature negotiation is complete. */ + status |= VIRTIO_CONFIG_S_FEATURES_OK; + *R(VIRTIO_MMIO_STATUS) = status; + + /* tell device we're completely ready. */ + status |= VIRTIO_CONFIG_S_DRIVER_OK; + *R(VIRTIO_MMIO_STATUS) = status; + + *R(VIRTIO_MMIO_GUEST_PAGE_SIZE) = PGSIZE; + /* initialize queue 0. */ + *R(VIRTIO_MMIO_QUEUE_SEL) = 0; + uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX); + if (max == 0) + panic("virtio disk has no queue 0\n"); + if (max < NUM) + panic("virtio disk max queue too short\n"); + *R(VIRTIO_MMIO_QUEUE_NUM) = NUM; + memset(disk.pages, 0, sizeof(disk.pages)); + *R(VIRTIO_MMIO_QUEUE_PFN) = ((uint32)disk.pages) / PGSIZE; + *R(VIRTIO_MMIO_QUEUE_ALIGN) = PGSIZE; + *R(VIRTIO_MMIO_QUEUE_READY) = 1; + // desc = pages -- num * virtq_desc + // avail = pages + 0x40 -- 2 * uint16, then num * uint16 + // used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem + disk.desc = (struct virtq_desc *)disk.pages; + disk.avail = (struct virtq_avail *)(disk.pages + NUM * sizeof(virtq_desc_t)); + disk.used = (struct virtq_used *)(disk.pages + PGSIZE); + + *R(VIRTIO_MMIO_QueueDescLow) = disk.desc; + *R(VIRTIO_MMIO_QueueAvailLow) = disk.avail; + *R(VIRTIO_MMIO_QueueUsedLow) = disk.used; + + // all NUM descriptors start out unused. + for (int i = 0; i < NUM; i++) + disk.free[i] = 1; + lib_puts("Disk init work is success!\n"); +} + +// find a free descriptor, mark it non-free, return its index. +static int +alloc_desc() +{ + for (int i = 0; i < NUM; i++) + { + if (disk.free[i]) + { + disk.free[i] = 0; + return i; + } + } + return -1; +} + +// mark a descriptor as free. +static void +free_desc(int i) +{ + if (i >= NUM) + panic("free_desc 1"); + if (disk.free[i]) + panic("free_desc 2"); + disk.desc[i].addr = 0; + disk.desc[i].len = 0; + disk.desc[i].flags = 0; + disk.desc[i].next = 0; + disk.free[i] = 1; +} + +// free a chain of descriptors. +static void +free_chain(int i) +{ + while (1) + { + int flag = disk.desc[i].flags; + int nxt = disk.desc[i].next; + free_desc(i); + if (flag & VRING_DESC_F_NEXT) + i = nxt; + else + break; + } +} + +// allocate three descriptors (they need not be contiguous). +// disk transfers always use three descriptors. +static int +alloc3_desc(int *idx) +{ + for (int i = 0; i < 3; i++) + { + idx[i] = alloc_desc(); + if (idx[i] < 0) + { + for (int j = 0; j < i; j++) + free_desc(idx[j]); + return -1; + } + } + return 0; +} + +void virtio_disk_rw(struct blk *b, int write) +{ + uint64 sector = b->blockno * (BSIZE / 512); + + lock_acquire(&disk.vdisk_lock); + + // allocate the three descriptors. + int idx[3]; + while (1) + { + if (alloc3_desc(idx) == 0) + { + break; + } + } + + // format the three descriptors. + // qemu's virtio-blk.c reads them. + + virtio_blk_req_t *buf0 = &disk.ops[idx[0]]; + + if (write) + buf0->type = VIRTIO_BLK_T_OUT; // write the disk + else + buf0->type = VIRTIO_BLK_T_IN; // read the disk + buf0->reserved = 0; // The reserved portion is used to pad the header to 16 bytes and move the 32-bit sector field to the correct place. + buf0->sector = sector; // specify the sector that we wanna modified. + + disk.desc[idx[0]].addr = buf0; + disk.desc[idx[0]].len = sizeof(struct virtio_blk_req); + disk.desc[idx[0]].flags = VRING_DESC_F_NEXT; + disk.desc[idx[0]].next = idx[1]; + + disk.desc[idx[1]].addr = ((uint32)b->data) & 0xffffffff; + disk.desc[idx[1]].len = BSIZE; + if (write) + disk.desc[idx[1]].flags = 0; // device reads b->data + else + disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data + disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT; + disk.desc[idx[1]].next = idx[2]; + + disk.info[idx[0]].status = 0; + disk.desc[idx[2]].addr = (uint32)&disk.info[idx[0]].status; + disk.desc[idx[2]].len = 1; + disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status + disk.desc[idx[2]].next = 0; + + // record struct buf for virtio_disk_intr(). + b->disk = 1; + disk.info[idx[0]].b = b; + + __sync_synchronize(); + + // tell the device the first index in our chain of descriptors. + disk.avail->ring[disk.avail->idx % NUM] = idx[0]; + + __sync_synchronize(); + + // tell the device another avail ring entry is available. + disk.avail->idx += 1; // not % NUM ... + + *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // The device starts immediately when we write 0 to queue_notify. + while (b->disk == 1) + { + } + + disk.info[idx[0]].b = 0; + free_chain(idx[0]); + + lock_free(&disk.vdisk_lock); +} + +void virtio_disk_isr() +{ + //lock_acquire(&disk.vdisk_lock); + + // the device won't raise another interrupt until we tell it + // we've seen this interrupt, which the following line does. + // this may race with the device writing new entries to + // the "used" ring, in which case we may process the new + // completion entries in this interrupt, and have nothing to do + // in the next interrupt, which is harmless. + *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; + + __sync_synchronize(); + + // the device increments disk.used->idx when it + // adds an entry to the used ring. + + while (disk.used_idx != disk.used->idx) + { + __sync_synchronize(); + int id = disk.used->ring[disk.used_idx % NUM].id; + + if (disk.info[id].status != 0) + panic("virtio_disk_intr status"); + + struct blk *b = disk.info[id].b; + b->disk = 0; + disk.used_idx += 1; + } + + //lock_free(&disk.vdisk_lock); +} \ No newline at end of file diff --git a/README.md b/README.md index 8b4ff73..38ad38e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ And you should start your git-bash to build the project. (It works for me in vsc - Learning VirtIO Protocol & Device driver implementation - [09-MemoryAllocator](09-MemoryAllocator) - Understanding how to write the linker script & how the heap works - +- [10-SystemCall](10-SystemCall) + - Invoking a mini ecall from machine mode. ## Building and Verification - Changes the current working directory to the specified one and then