From 07ab6ae0b391cd2bc875d89ea088258b6ca39d68 Mon Sep 17 00:00:00 2001 From: ianchen0119 Date: Mon, 21 Jun 2021 17:58:29 +0800 Subject: [PATCH] Add the block device driver, but hasn't been tested yet. --- 08-BlockDeviceDriver/Makefile | 41 ++++++ 08-BlockDeviceDriver/README.md | 74 ++++++++++ 08-BlockDeviceDriver/gdbinit | 4 + 08-BlockDeviceDriver/lib.c | 247 ++++++++++++++++++++++++++++++++ 08-BlockDeviceDriver/lib.h | 28 ++++ 08-BlockDeviceDriver/lock.c | 32 +++++ 08-BlockDeviceDriver/os.c | 42 ++++++ 08-BlockDeviceDriver/os.h | 36 +++++ 08-BlockDeviceDriver/os.ld | 46 ++++++ 08-BlockDeviceDriver/plic.c | 45 ++++++ 08-BlockDeviceDriver/riscv.h | 162 +++++++++++++++++++++ 08-BlockDeviceDriver/start.s | 26 ++++ 08-BlockDeviceDriver/sys.h | 8 ++ 08-BlockDeviceDriver/sys.s | 152 ++++++++++++++++++++ 08-BlockDeviceDriver/task.c | 32 +++++ 08-BlockDeviceDriver/task.h | 16 +++ 08-BlockDeviceDriver/timer.c | 38 +++++ 08-BlockDeviceDriver/timer.h | 12 ++ 08-BlockDeviceDriver/trap.c | 69 +++++++++ 08-BlockDeviceDriver/user.c | 63 ++++++++ 08-BlockDeviceDriver/virtio.c | 254 +++++++++++++++++++++++++++++++++ 08-BlockDeviceDriver/virtio.h | 117 +++++++++++++++ 22 files changed, 1544 insertions(+) create mode 100644 08-BlockDeviceDriver/Makefile create mode 100644 08-BlockDeviceDriver/README.md create mode 100644 08-BlockDeviceDriver/gdbinit create mode 100644 08-BlockDeviceDriver/lib.c create mode 100644 08-BlockDeviceDriver/lib.h create mode 100644 08-BlockDeviceDriver/lock.c create mode 100644 08-BlockDeviceDriver/os.c create mode 100644 08-BlockDeviceDriver/os.h create mode 100644 08-BlockDeviceDriver/os.ld create mode 100644 08-BlockDeviceDriver/plic.c create mode 100644 08-BlockDeviceDriver/riscv.h create mode 100644 08-BlockDeviceDriver/start.s create mode 100644 08-BlockDeviceDriver/sys.h create mode 100644 08-BlockDeviceDriver/sys.s create mode 100644 08-BlockDeviceDriver/task.c create mode 100644 08-BlockDeviceDriver/task.h create mode 100644 08-BlockDeviceDriver/timer.c create mode 100644 08-BlockDeviceDriver/timer.h create mode 100644 08-BlockDeviceDriver/trap.c create mode 100644 08-BlockDeviceDriver/user.c create mode 100644 08-BlockDeviceDriver/virtio.c create mode 100644 08-BlockDeviceDriver/virtio.h diff --git a/08-BlockDeviceDriver/Makefile b/08-BlockDeviceDriver/Makefile new file mode 100644 index 0000000..1002385 --- /dev/null +++ b/08-BlockDeviceDriver/Makefile @@ -0,0 +1,41 @@ +CC = riscv64-unknown-elf-gcc +CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall +GDB = riscv64-unknown-elf-gdb + +OBJ = \ +start.s \ +sys.s \ +lib.c \ +timer.c \ +task.c \ +os.c \ +user.c \ +trap.c \ +lock.c \ +plic.c \ +virtio.c + +QEMU = qemu-system-riscv32 +QFLAGS = -nographic -smp 1 -machine virt -bios none + +OBJDUMP = riscv64-unknown-elf-objdump + +all: os.elf + +os.elf: $(OBJ) + $(CC) $(CFLAGS) -T os.ld -o os.elf $^ + +qemu: $(TARGET) + @qemu-system-riscv32 -M ? | grep virt >/dev/null || exit + @echo "Press Ctrl-A and then X to exit QEMU" + $(QEMU) $(QFLAGS) -kernel os.elf + +clean: + rm -f *.elf + +.PHONY : debug +debug: all + @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 \ No newline at end of file diff --git a/08-BlockDeviceDriver/README.md b/08-BlockDeviceDriver/README.md new file mode 100644 index 0000000..4d26342 --- /dev/null +++ b/08-BlockDeviceDriver/README.md @@ -0,0 +1,74 @@ +# 07-ExternInterrupt + +## Build & Run + +```sh +IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/07-ExterInterrupt (feat/getchar) +$ make +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 plic.c + +IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/07-ExterInterrupt (feat/getchar) +$ make qemu +Press Ctrl-A and then X to exit QEMU +qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf +OS start +OS: Activate next task +Task0: Created! +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +external interruption! +j +Task0: Running... +Task0: Running... +external interruption! +k +Task0: Running... +Task0: Running... +Task0: Running... +external interruption! +j +Task0: Running... +external interruption! +k +external interruption! +j +Task0: Running... +timer interruption! +timer_handler: 1 +OS: Back to OS +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/08-BlockDeviceDriver/gdbinit b/08-BlockDeviceDriver/gdbinit new file mode 100644 index 0000000..7ca6884 --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/lib.c b/08-BlockDeviceDriver/lib.c new file mode 100644 index 0000000..77b47da --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/lib.h b/08-BlockDeviceDriver/lib.h new file mode 100644 index 0000000..be9b9fd --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/lock.c b/08-BlockDeviceDriver/lock.c new file mode 100644 index 0000000..309782a --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/os.c b/08-BlockDeviceDriver/os.c new file mode 100644 index 0000000..03f122d --- /dev/null +++ b/08-BlockDeviceDriver/os.c @@ -0,0 +1,42 @@ +#include "os.h" + +extern void trap_init(void); + +void panic(char *s) +{ + lib_puts(s); + for (;;) + { + } +} + +void os_kernel() +{ + task_os(); +} + +void os_start() +{ + uart_init(); + lib_puts("OS start\n"); + user_init(); + trap_init(); + plic_init(); + timer_init(); // start timer interrupt ... +} + +int os_main(void) +{ + os_start(); + + 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/08-BlockDeviceDriver/os.h b/08-BlockDeviceDriver/os.h new file mode 100644 index 0000000..22ab98b --- /dev/null +++ b/08-BlockDeviceDriver/os.h @@ -0,0 +1,36 @@ +#ifndef __OS_H__ +#define __OS_H__ + +#include "riscv.h" +#include "lib.h" +#include "task.h" +#include "timer.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); + +#endif diff --git a/08-BlockDeviceDriver/os.ld b/08-BlockDeviceDriver/os.ld new file mode 100644 index 0000000..9c1a7df --- /dev/null +++ b/08-BlockDeviceDriver/os.ld @@ -0,0 +1,46 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M +} + +PHDRS +{ + text PT_LOAD; + data PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + .text : { + PROVIDE(_text_start = .); + *(.text.init) *(.text .text.*) + PROVIDE(_text_end = .); + } >ram AT>ram :text + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } >ram AT>ram :text + + .data : { + . = ALIGN(4096); + PROVIDE(_data_start = .); + *(.sdata .sdata.*) *(.data .data.*) + PROVIDE(_data_end = .); + } >ram AT>ram :data + + .bss :{ + PROVIDE(_bss_start = .); + *(.sbss .sbss.*) *(.bss .bss.*) + PROVIDE(_bss_end = .); + } >ram AT>ram :bss + + PROVIDE(_memory_start = ORIGIN(ram)); + PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); +} diff --git a/08-BlockDeviceDriver/plic.c b/08-BlockDeviceDriver/plic.c new file mode 100644 index 0000000..473a73f --- /dev/null +++ b/08-BlockDeviceDriver/plic.c @@ -0,0 +1,45 @@ +#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; + + /* Enable UART0 */ + *(uint32_t *)PLIC_MENABLE(hart) = (1 << UART0_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/08-BlockDeviceDriver/riscv.h b/08-BlockDeviceDriver/riscv.h new file mode 100644 index 0000000..6402c23 --- /dev/null +++ b/08-BlockDeviceDriver/riscv.h @@ -0,0 +1,162 @@ +#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 +{ + reg_t ra; + reg_t sp; + + // callee-saved + reg_t s0; + reg_t s1; + 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; +}; + +// 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_MASK (3 << 11) // previous mode. +#define MSTATUS_MPP_M (3 << 11) +#define MSTATUS_MPP_S (1 << 11) +#define MSTATUS_MPP_U (0 << 11) +#define MSTATUS_MIE (1 << 3) // machine-mode interrupt enable. + +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)); +} + +#endif diff --git a/08-BlockDeviceDriver/start.s b/08-BlockDeviceDriver/start.s new file mode 100644 index 0000000..aec0240 --- /dev/null +++ b/08-BlockDeviceDriver/start.s @@ -0,0 +1,26 @@ +.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 + # 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/08-BlockDeviceDriver/sys.h b/08-BlockDeviceDriver/sys.h new file mode 100644 index 0000000..4355ab3 --- /dev/null +++ b/08-BlockDeviceDriver/sys.h @@ -0,0 +1,8 @@ +#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); + +#endif diff --git a/08-BlockDeviceDriver/sys.s b/08-BlockDeviceDriver/sys.s new file mode 100644 index 0000000..15a6f55 --- /dev/null +++ b/08-BlockDeviceDriver/sys.s @@ -0,0 +1,152 @@ +# 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) +.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) +.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) + sw t6, 120(\base) +.endm + +.macro reg_load base + # restore registers. + lw ra, 0(\base) + lw sp, 4(\base) + lw gp, 8(\base) + # not this, in case we moved CPUs: 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. + +.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 + # call the C trap handler in trap.c + csrr a0, mepc + csrr a1, mcause + 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 + diff --git a/08-BlockDeviceDriver/task.c b/08-BlockDeviceDriver/task.c new file mode 100644 index 0000000..17a519e --- /dev/null +++ b/08-BlockDeviceDriver/task.c @@ -0,0 +1,32 @@ +#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].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]; + sys_switch(&ctx_os, &ctx_tasks[i]); +} + +// switch back to os +void task_os() +{ + struct context *ctx = ctx_now; + ctx_now = &ctx_os; + sys_switch(ctx, &ctx_os); +} diff --git a/08-BlockDeviceDriver/task.h b/08-BlockDeviceDriver/task.h new file mode 100644 index 0000000..1ac7e39 --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/timer.c b/08-BlockDeviceDriver/timer.c new file mode 100644 index 0000000..b2dc42c --- /dev/null +++ b/08-BlockDeviceDriver/timer.c @@ -0,0 +1,38 @@ +#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(); + *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; +} diff --git a/08-BlockDeviceDriver/timer.h b/08-BlockDeviceDriver/timer.h new file mode 100644 index 0000000..b971c37 --- /dev/null +++ b/08-BlockDeviceDriver/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/08-BlockDeviceDriver/trap.c b/08-BlockDeviceDriver/trap.c new file mode 100644 index 0000000..b3052ca --- /dev/null +++ b/08-BlockDeviceDriver/trap.c @@ -0,0 +1,69 @@ +#include "os.h" +extern void trap_vector(); + +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) + { + lib_printf("unexpected interrupt irq = %d\n", irq); + } + + if (irq) + { + plic_complete(irq); + } +} + +reg_t trap_handler(reg_t epc, reg_t cause) +{ + reg_t return_pc = epc; + reg_t cause_code = cause & 0xfff; + + if (cause & 0x80000000) + { + /* Asynchronous trap - interrupt */ + switch (cause_code) + { + case 3: + lib_puts("software interruption!\n"); + break; + case 7: + lib_puts("timer interruption!\n"); + // 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: + lib_puts("external interruption!\n"); + external_handler(); + break; + default: + lib_puts("unknown async exception!\n"); + break; + } + } + else + { + /* Synchronous trap - exception */ + lib_puts("Sync exceptions!\n"); + while (1) + { + /* code */ + } + } + return return_pc; +} diff --git a/08-BlockDeviceDriver/user.c b/08-BlockDeviceDriver/user.c new file mode 100644 index 0000000..9937e0f --- /dev/null +++ b/08-BlockDeviceDriver/user.c @@ -0,0 +1,63 @@ +#include "os.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_init() +{ + lock_init(&lock); + task_create(&user_task0); + task_create(&user_task1); + task_create(&user_task2); + task_create(&user_task3); +} diff --git a/08-BlockDeviceDriver/virtio.c b/08-BlockDeviceDriver/virtio.c new file mode 100644 index 0000000..d9cc58b --- /dev/null +++ b/08-BlockDeviceDriver/virtio.c @@ -0,0 +1,254 @@ +#include "virtio.h" +#include "os.h" +#define R(addr) ((volatile uint32_t *)(VIRTIO_MMIO_BASE + (addr))) + +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]; + /* Disk command headers */ + virtio_blk_req_t ops[NUM]; + + struct lock vdisk_lock; +} __attribute__((aligned(PGSIZE))) disk; + +void virtio_disk_init(){ + uint32_t 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"); + } + /* 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 */ + uint64 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) + lib_puts("virtio disk has no queue 0\n"); + if (max < NUM) + lib_puts("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) = ((uint64)disk.pages) >> PGSHIFT; + + // 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(struct virtq_desc)); + disk.used = (struct virtq_used *)(disk.pages + PGSIZE); + + // 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; + wakeup(&disk.free[0]); +} + +// 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 buf *b, int write) +{ + uint64 sector = b->blockno * (BSIZE / 512); + + lock_acquire(&disk.vdisk_lock); + + // the spec's Section 5.2 says that legacy block operations use + // three descriptors: one for type/reserved/sector, one for the + // data, one for a 1-byte status result. + + // 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. + + struct virtio_blk_req *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; + buf0->sector = sector; + + disk.desc[idx[0]].addr = (uint64) 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 = (uint64) b->data; + 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 = 0xff; // device writes 0 on success + disk.desc[idx[2]].addr = (uint64) &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; + + // 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 ... + + __sync_synchronize(); + + *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number + + // Wait for virtio_disk_intr() to say request has finished. + while(b->disk == 1) { + } + + disk.info[idx[0]].b = 0; + free_chain(idx[0]); + + lock_free(&disk.vdisk_lock); +} + +void +virtio_disk_intr() +{ + 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 buf *b = disk.info[id].b; + b->disk = 0; // disk is done with buf + wakeup(b); + + disk.used_idx += 1; + } + + lock_free(&disk.vdisk_lock); +} \ No newline at end of file diff --git a/08-BlockDeviceDriver/virtio.h b/08-BlockDeviceDriver/virtio.h new file mode 100644 index 0000000..08b5497 --- /dev/null +++ b/08-BlockDeviceDriver/virtio.h @@ -0,0 +1,117 @@ +/* + * 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_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 + +// 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_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; +} virtq_desc_t; +#define VRING_DESC_F_NEXT 1 // chained with another descriptor +#define VRING_DESC_F_WRITE 2 // device writes (vs read) + +/* + * 用來存放 descriptor 的索引,當 Device 收到通知時,它會檢查 AvailableRing 確認需要讀取哪些 Descriptor 。 + * 注意: Descriptor 和 AvailableRing 都存儲在 RAM 中。 + */ +typedef struct virtq_avail +{ + uint16_t flags; // always zero + uint16_t idx; // driver will write ring[idx] next + uint16_t ring[NUM]; // descriptor numbers of chain heads + uint16_t unused; +} virtq_avail_t; + +/* + * 當內部的 Index 與 UsedRing 的 Index 相等,代表所有資料都已經被讀取,這個 Device 是唯一需要被寫進 Index 的。 + */ + +typedef struct virtq_used_elem +{ + uint32_t id; // index of start of completed descriptor chain + uint32_t len; +} virtq_used_elem_t; + +/* + * Device 可以使用 UsedRing 向 OS 發送訊息。 + * Device 通常使用它來告知 OS 它已完成先前通知的請求。 + * AvailableRing 與 UsedRing 非常相似,差異在於: OS 需要查看 UsedRing 得知哪個 Descriptor 已經被服務。 + */ + +typedef struct virtq_used +{ + uint16_t flags; // always zero + uint16_t 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_t type; // VIRTIO_BLK_T_IN or ..._OUT + uint32_t reserved; // 將 Header 擴充到 16-byte ,並將 64-bit sector 移到正確的位置。 + uint64_t sector; +} virtio_blk_req_t; \ No newline at end of file