mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 04:24:33 +00:00
add ch10
This commit is contained in:
BIN
10-SystemCall/.DS_Store
vendored
Normal file
BIN
10-SystemCall/.DS_Store
vendored
Normal file
Binary file not shown.
54
10-SystemCall/Makefile
Normal file
54
10-SystemCall/Makefile
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
CC = riscv64-unknown-elf-gcc
|
||||||
|
CFLAGS = -I./include -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -w -D CONFIG_SYSCALL
|
||||||
|
GDB = riscv64-unknown-elf-gdb
|
||||||
|
SOURCE = src/
|
||||||
|
|
||||||
|
OBJ = \
|
||||||
|
$(SOURCE)start.s \
|
||||||
|
$(SOURCE)sys.s \
|
||||||
|
$(SOURCE)mem.s \
|
||||||
|
$(SOURCE)lib.c \
|
||||||
|
$(SOURCE)timer.c \
|
||||||
|
$(SOURCE)os.c \
|
||||||
|
$(SOURCE)task.c \
|
||||||
|
$(SOURCE)user.c \
|
||||||
|
$(SOURCE)trap.c \
|
||||||
|
$(SOURCE)lock.c \
|
||||||
|
$(SOURCE)plic.c \
|
||||||
|
$(SOURCE)virtio.c \
|
||||||
|
$(SOURCE)string.c \
|
||||||
|
$(SOURCE)alloc.c \
|
||||||
|
$(SOURCE)syscall.c \
|
||||||
|
$(SOURCE)usys.s \
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 4 -machine virt -bios none
|
||||||
|
QFLAGS += -drive if=none,format=raw,file=hdd.dsk,id=x0
|
||||||
|
QFLAGS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
|
||||||
|
|
||||||
|
OBJDUMP = riscv64-unknown-elf-objdump
|
||||||
|
|
||||||
|
all: clean os.elf hdd.dsk qemu
|
||||||
|
|
||||||
|
test: clean os.elf qemu
|
||||||
|
|
||||||
|
os.elf: $(OBJ)
|
||||||
|
$(CC) $(CFLAGS) -T os.ld -o os.elf $^
|
||||||
|
|
||||||
|
qemu: $(TARGET) hdd.dsk
|
||||||
|
@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 *.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
|
||||||
83
10-SystemCall/README.md
Normal file
83
10-SystemCall/README.md
Normal file
@@ -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).
|
||||||
4
10-SystemCall/gdbinit
Normal file
4
10-SystemCall/gdbinit
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
BIN
10-SystemCall/include/.DS_Store
vendored
Normal file
BIN
10-SystemCall/include/.DS_Store
vendored
Normal file
Binary file not shown.
28
10-SystemCall/include/lib.h
Normal file
28
10-SystemCall/include/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
|
||||||
42
10-SystemCall/include/os.h
Normal file
42
10-SystemCall/include/os.h
Normal file
@@ -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
|
||||||
183
10-SystemCall/include/riscv.h
Normal file
183
10-SystemCall/include/riscv.h
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
#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
|
||||||
|
{
|
||||||
|
/* 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__ */
|
||||||
10
10-SystemCall/include/string.h
Normal file
10
10-SystemCall/include/string.h
Normal file
@@ -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
|
||||||
9
10-SystemCall/include/sys.h
Normal file
9
10-SystemCall/include/sys.h
Normal file
@@ -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
|
||||||
16
10-SystemCall/include/task.h
Normal file
16
10-SystemCall/include/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
|
||||||
12
10-SystemCall/include/timer.h
Normal file
12
10-SystemCall/include/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
|
||||||
13
10-SystemCall/include/types.h
Normal file
13
10-SystemCall/include/types.h
Normal file
@@ -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
|
||||||
7
10-SystemCall/include/user_api.h
Normal file
7
10-SystemCall/include/user_api.h
Normal file
@@ -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__ */
|
||||||
129
10-SystemCall/include/virtio.h
Normal file
129
10-SystemCall/include/virtio.h
Normal file
@@ -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;
|
||||||
49
10-SystemCall/os.ld
Normal file
49
10-SystemCall/os.ld
Normal file
@@ -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);
|
||||||
|
}
|
||||||
BIN
10-SystemCall/src/.DS_Store
vendored
Normal file
BIN
10-SystemCall/src/.DS_Store
vendored
Normal file
Binary file not shown.
215
10-SystemCall/src/alloc.c
Normal file
215
10-SystemCall/src/alloc.c
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/* This Code derived from RVOS
|
||||||
|
* -- https://github.com/plctlab/riscv-operating-system-mooc
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
extern uint32_t TEXT_START;
|
||||||
|
extern uint32_t TEXT_END;
|
||||||
|
extern uint32_t DATA_START;
|
||||||
|
extern uint32_t DATA_END;
|
||||||
|
extern uint32_t RODATA_START;
|
||||||
|
extern uint32_t RODATA_END;
|
||||||
|
extern uint32_t BSS_START;
|
||||||
|
extern uint32_t BSS_END;
|
||||||
|
extern uint32_t HEAP_START;
|
||||||
|
extern uint32_t HEAP_SIZE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _alloc_start points to the actual start address of heap pool
|
||||||
|
* _alloc_end points to the actual end address of heap pool
|
||||||
|
* _num_pages holds the actual max number of pages we can allocate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint32_t _alloc_start = 0;
|
||||||
|
static uint32_t _alloc_end = 0;
|
||||||
|
static uint32_t _num_pages = 0;
|
||||||
|
|
||||||
|
#define PAGE_SIZE 256
|
||||||
|
#define PAGE_ORDER 8
|
||||||
|
|
||||||
|
#define PAGE_TAKEN (uint8_t)(1 << 0)
|
||||||
|
#define PAGE_LAST (uint8_t)(1 << 1)
|
||||||
|
#define pageNum(x) ((x) / ((PAGE_SIZE) + 1)) + 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Page Descriptor
|
||||||
|
* flags:
|
||||||
|
* - 00: This means this page hasn't been allocated
|
||||||
|
* - 01: This means this page was allocated
|
||||||
|
* - 11: This means this page was allocated and is the last page of the memory block allocated
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct Page
|
||||||
|
{
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void _clear(struct Page *page)
|
||||||
|
{
|
||||||
|
page->flags = 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);
|
||||||
|
}
|
||||||
247
10-SystemCall/src/lib.c
Normal file
247
10-SystemCall/src/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;
|
||||||
|
}
|
||||||
32
10-SystemCall/src/lock.c
Normal file
32
10-SystemCall/src/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);
|
||||||
|
}
|
||||||
30
10-SystemCall/src/mem.s
Normal file
30
10-SystemCall/src/mem.s
Normal file
@@ -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
|
||||||
52
10-SystemCall/src/os.c
Normal file
52
10-SystemCall/src/os.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
48
10-SystemCall/src/plic.c
Normal file
48
10-SystemCall/src/plic.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
34
10-SystemCall/src/start.s
Normal file
34
10-SystemCall/src/start.s
Normal file
@@ -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
|
||||||
40
10-SystemCall/src/string.c
Normal file
40
10-SystemCall/src/string.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
185
10-SystemCall/src/sys.s
Normal file
185
10-SystemCall/src/sys.s
Normal file
@@ -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
|
||||||
30
10-SystemCall/src/syscall.c
Normal file
30
10-SystemCall/src/syscall.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
35
10-SystemCall/src/task.c
Normal file
35
10-SystemCall/src/task.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
39
10-SystemCall/src/timer.c
Normal file
39
10-SystemCall/src/timer.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
101
10-SystemCall/src/trap.c
Normal file
101
10-SystemCall/src/trap.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
91
10-SystemCall/src/user.c
Normal file
91
10-SystemCall/src/user.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
6
10-SystemCall/src/usys.s
Normal file
6
10-SystemCall/src/usys.s
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.global gethid
|
||||||
|
gethid:
|
||||||
|
li a7, 1
|
||||||
|
ecall
|
||||||
|
ret
|
||||||
|
|
||||||
312
10-SystemCall/src/virtio.c
Normal file
312
10-SystemCall/src/virtio.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
- Learning VirtIO Protocol & Device driver implementation
|
||||||
- [09-MemoryAllocator](09-MemoryAllocator)
|
- [09-MemoryAllocator](09-MemoryAllocator)
|
||||||
- Understanding how to write the linker script & how the heap works
|
- 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
|
## Building and Verification
|
||||||
|
|
||||||
- Changes the current working directory to the specified one and then
|
- Changes the current working directory to the specified one and then
|
||||||
|
|||||||
Reference in New Issue
Block a user