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
|
||||
- [09-MemoryAllocator](09-MemoryAllocator)
|
||||
- Understanding how to write the linker script & how the heap works
|
||||
|
||||
- [10-SystemCall](10-SystemCall)
|
||||
- Invoking a mini ecall from machine mode.
|
||||
## Building and Verification
|
||||
|
||||
- Changes the current working directory to the specified one and then
|
||||
|
||||
Reference in New Issue
Block a user