mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 12:34:33 +00:00
Add the block device driver, but hasn't been tested yet.
This commit is contained in:
41
08-BlockDeviceDriver/Makefile
Normal file
41
08-BlockDeviceDriver/Makefile
Normal file
@@ -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
|
||||||
74
08-BlockDeviceDriver/README.md
Normal file
74
08-BlockDeviceDriver/README.md
Normal file
@@ -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).
|
||||||
4
08-BlockDeviceDriver/gdbinit
Normal file
4
08-BlockDeviceDriver/gdbinit
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
247
08-BlockDeviceDriver/lib.c
Normal file
247
08-BlockDeviceDriver/lib.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
28
08-BlockDeviceDriver/lib.h
Normal file
28
08-BlockDeviceDriver/lib.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __LIB_H__
|
||||||
|
#define __LIB_H__
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#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
|
||||||
32
08-BlockDeviceDriver/lock.c
Normal file
32
08-BlockDeviceDriver/lock.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
42
08-BlockDeviceDriver/os.c
Normal file
42
08-BlockDeviceDriver/os.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
36
08-BlockDeviceDriver/os.h
Normal file
36
08-BlockDeviceDriver/os.h
Normal file
@@ -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
|
||||||
46
08-BlockDeviceDriver/os.ld
Normal file
46
08-BlockDeviceDriver/os.ld
Normal file
@@ -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));
|
||||||
|
}
|
||||||
45
08-BlockDeviceDriver/plic.c
Normal file
45
08-BlockDeviceDriver/plic.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
162
08-BlockDeviceDriver/riscv.h
Normal file
162
08-BlockDeviceDriver/riscv.h
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#ifndef __RISCV_H__
|
||||||
|
#define __RISCV_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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
|
||||||
26
08-BlockDeviceDriver/start.s
Normal file
26
08-BlockDeviceDriver/start.s
Normal file
@@ -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
|
||||||
8
08-BlockDeviceDriver/sys.h
Normal file
8
08-BlockDeviceDriver/sys.h
Normal file
@@ -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
|
||||||
152
08-BlockDeviceDriver/sys.s
Normal file
152
08-BlockDeviceDriver/sys.s
Normal file
@@ -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
|
||||||
|
|
||||||
32
08-BlockDeviceDriver/task.c
Normal file
32
08-BlockDeviceDriver/task.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
16
08-BlockDeviceDriver/task.h
Normal file
16
08-BlockDeviceDriver/task.h
Normal file
@@ -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
|
||||||
38
08-BlockDeviceDriver/timer.c
Normal file
38
08-BlockDeviceDriver/timer.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
12
08-BlockDeviceDriver/timer.h
Normal file
12
08-BlockDeviceDriver/timer.h
Normal file
@@ -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
|
||||||
69
08-BlockDeviceDriver/trap.c
Normal file
69
08-BlockDeviceDriver/trap.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
63
08-BlockDeviceDriver/user.c
Normal file
63
08-BlockDeviceDriver/user.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
254
08-BlockDeviceDriver/virtio.c
Normal file
254
08-BlockDeviceDriver/virtio.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
117
08-BlockDeviceDriver/virtio.h
Normal file
117
08-BlockDeviceDriver/virtio.h
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user