diff --git a/README.md b/README.md index 9251dac..8592b91 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,24 @@ ## Build +### in dir `user` + +``` bash +cargo build --release +rust-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/00_hello_world -O binary target/riscv64gc-unknown-none-elf/release/00_hello_world.bin +... +``` + +### in dir `os` + ``` bash LOG=TRACE cargo build --release ``` ## Run +rustsbi-qemu should be downloaded manually. + ``` bash qemu-system-riscv64 -machine virt -nographic -bios target/riscv64gc-unknown-none-elf/release/rustsbi-qemu.bin -device loader,file=target/riscv64gc-unknown-none-elf/release/os ``` \ No newline at end of file diff --git a/os/Cargo.lock b/os/Cargo.lock index 58d7cf0..ba98085 100644 --- a/os/Cargo.lock +++ b/os/Cargo.lock @@ -2,11 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "log" @@ -20,9 +35,64 @@ version = "0.1.0" dependencies = [ "lazy_static", "log", + "riscv", "sbi-rt", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "riscv" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + [[package]] name = "sbi-rt" version = "0.0.3" @@ -37,3 +107,26 @@ name = "sbi-spec" version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/os/Cargo.toml b/os/Cargo.toml index c9551ee..d7dfdfb 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -lazy_static = "1.5.0" +lazy_static = { version = "1.5.0", features = ["spin_no_std"] } log = "0.4.22" +riscv = "0.12.1" sbi-rt = "0.0.3" diff --git a/os/src/batch.rs b/os/src/batch.rs index 7c80ae9..cf243f3 100644 --- a/os/src/batch.rs +++ b/os/src/batch.rs @@ -1,14 +1,52 @@ -use core::{arch::asm, slice}; +use core::{arch::asm, mem, slice}; use lazy_static::*; use log::info; -use crate::{sbi::shutdown, sync::UPSafeCell}; +use crate::{sbi::shutdown, sync::UPSafeCell, trap::TrapContext}; +const KERNEL_STACK_SIZE: usize = 4096 * 2; +const USER_STACK_SIZE: usize = 4096 * 2; const MAX_APP_NUM: usize = 16; const APP_BASE_ADDRESS: usize = 0x80400000; const APP_SIZE_LIMIT: usize = 0x20000; +#[repr(align(4096))] +struct KernelStack { + data: [u8; KERNEL_STACK_SIZE], +} +#[repr(align(4096))] +struct UserStack { + data: [u8; USER_STACK_SIZE], +} + +static KERNEL_STACK: KernelStack = KernelStack { + data: [0; KERNEL_STACK_SIZE], +}; +static USER_STACK: UserStack = UserStack { + data: [0; USER_STACK_SIZE], +}; + +impl KernelStack { + fn get_sp(&self) -> usize { + self.data.as_ptr() as usize + KERNEL_STACK_SIZE + } + + pub fn push_context(&self, cx: TrapContext) -> &'static mut TrapContext { + let cx_ptr = (self.get_sp() - mem::size_of::()) as *mut TrapContext; + unsafe { + *cx_ptr = cx; + } + unsafe { cx_ptr.as_mut().unwrap() } + } +} + +impl UserStack { + fn get_sp(&self) -> usize { + self.data.as_ptr() as usize + USER_STACK_SIZE + } +} + struct AppManager { num_app: usize, current_app: usize, @@ -68,6 +106,7 @@ lazy_static! { } pub fn init() { + print_app_info(); } pub fn print_app_info() { @@ -83,5 +122,14 @@ pub fn run_next_app() -> ! { app_manager.move_to_next_app(); drop(app_manager); - // todo + extern "C" { + fn __restore(cx_addr: usize); + } + unsafe { + __restore(KERNEL_STACK.push_context(TrapContext::app_init_context( + APP_BASE_ADDRESS, + USER_STACK.get_sp(), + )) as *const _ as usize); + } + panic!("Unreachable in batch::run_current_app"); } \ No newline at end of file diff --git a/os/src/link_app.S b/os/src/link_app.S new file mode 100644 index 0000000..c55d8e3 --- /dev/null +++ b/os/src/link_app.S @@ -0,0 +1,47 @@ + + .align 3 + .section .data + .global _num_app +_num_app: + .quad 5 + .quad app_0_start + .quad app_1_start + .quad app_2_start + .quad app_3_start + .quad app_4_start + .quad app_4_end + + .section .data + .global app_0_start + .global app_0_end +app_0_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/00_hello_world.bin" +app_0_end: + + .section .data + .global app_1_start + .global app_1_end +app_1_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/01_store_fault.bin" +app_1_end: + + .section .data + .global app_2_start + .global app_2_end +app_2_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/02_power.bin" +app_2_end: + + .section .data + .global app_3_start + .global app_3_end +app_3_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/03_priv_inst.bin" +app_3_end: + + .section .data + .global app_4_start + .global app_4_end +app_4_start: + .incbin "../user/target/riscv64gc-unknown-none-elf/release/04_priv_csr.bin" +app_4_end: diff --git a/os/src/main.rs b/os/src/main.rs index abc0fe9..0268870 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -10,11 +10,14 @@ mod console; mod batch; mod sync; +mod trap; +mod syscall; use core::arch::global_asm; use log::{debug, error, info, trace, warn}; global_asm!(include_str!("entry.asm")); +global_asm!(include_str!("link_app.S")); #[no_mangle] pub fn rust_main() -> ! { @@ -24,7 +27,9 @@ pub fn rust_main() -> ! { print_system_info(); print_to_console(); - panic!("Shutdown machine!"); + trap::init(); + batch::init(); + batch::run_next_app(); } fn clear_bss() { diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs new file mode 100644 index 0000000..453febd --- /dev/null +++ b/os/src/syscall/fs.rs @@ -0,0 +1,17 @@ +use core::{slice, str}; + +const FD_STDOUT: usize = 1; + +pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { + match fd { + FD_STDOUT => { + let slice = unsafe { slice::from_raw_parts(buf, len) }; + let str = str::from_utf8(slice).unwrap(); + print!("{}", str); + len as isize + } + _ => { + panic!("Unsupported fd in sys_write!"); + } + } +} \ No newline at end of file diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs new file mode 100644 index 0000000..5be165c --- /dev/null +++ b/os/src/syscall/mod.rs @@ -0,0 +1,16 @@ +use fs::sys_write; +use process::sys_exit; + +const SYSCALL_WRITE: usize = 64; +const SYSCALL_EXIT: usize = 93; + +mod fs; +mod process; + +pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { + match syscall_id { + SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), + SYSCALL_EXIT => sys_exit(args[0] as i32), + _ => panic!("Unsupported syscall_id: {}", syscall_id) + } +} \ No newline at end of file diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs new file mode 100644 index 0000000..b93fa4d --- /dev/null +++ b/os/src/syscall/process.rs @@ -0,0 +1,8 @@ +use log::info; + +use crate::batch::run_next_app; + +pub fn sys_exit(exit_code: i32) -> ! { + info!(target: "kernel", "Application exited with code {}", exit_code); + run_next_app() +} \ No newline at end of file diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs new file mode 100644 index 0000000..6abbfd6 --- /dev/null +++ b/os/src/trap/context.rs @@ -0,0 +1,28 @@ +use riscv::register::sstatus::{self, Sstatus, SPP}; + +#[repr(C)] +pub struct TrapContext { + pub x: [usize; 32], + pub sstatus: Sstatus, + pub sepc: usize, +} + +impl TrapContext { + pub fn set_sp(&mut self, sp: usize) { + self.x[2] = sp; + } + + pub fn app_init_context(entry: usize, sp: usize) -> Self { + let cur_sstatus = sstatus::read(); + unsafe { + sstatus::set_spp(SPP::User); + } + let mut cx = Self { + x: [0; 32], + sstatus: cur_sstatus, + sepc: entry, + }; + cx.set_sp(sp); + cx + } +} \ No newline at end of file diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs new file mode 100644 index 0000000..06b25b4 --- /dev/null +++ b/os/src/trap/mod.rs @@ -0,0 +1,44 @@ +mod context; +pub use context::TrapContext; +use log::error; + +use core::arch::global_asm; + +use riscv::{interrupt::{Exception, Trap}, register::{scause, stval, stvec::{self, TrapMode}}}; + +use crate::{batch::run_next_app, syscall}; + +global_asm!(include_str!("trap.S")); + +pub fn init() { + extern "C" { + fn __alltraps(); + } + unsafe { + stvec::write(__alltraps as usize, TrapMode::Direct); + } +} + +#[no_mangle] +pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext { + let scause = scause::read(); + let stval = stval::read(); + match scause.cause() { + Trap::Exception(e) if e == Exception::UserEnvCall as usize => { + cx.sepc += 4; + cx.x[10] = syscall::syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize; + } + Trap::Exception(e) if e == Exception::StoreFault as usize || e == Exception::StorePageFault as usize => { + error!(target: "kernel", "PageFault in application, kernel killed it."); + run_next_app(); + } + Trap::Exception(e) if e == Exception::IllegalInstruction as usize => { + error!(target: "kernel", "IllegalInstruction in application, kernel killed it."); + run_next_app(); + } + _ => { + panic!("un supported strp {:?}, stval = {:#x}!", scause.cause(), stval); + } + } + cx +} \ No newline at end of file diff --git a/os/src/trap/trap.S b/os/src/trap/trap.S new file mode 100644 index 0000000..9d6967c --- /dev/null +++ b/os/src/trap/trap.S @@ -0,0 +1,64 @@ +.altmacro +.macro SAVE_GP n + sd x\n, \n*8(sp) +.endm +.macro LOAD_GP n + ld x\n, \n*8(sp) +.endm + .section .text + .globl __alltraps + .globl __restore + .align 2 +__alltraps: + csrrw sp, sscratch, sp + # now sp->kernel stack, sscratch->user stack + # allocate a TrapContext on kernel stack + addi sp, sp, -34*8 + # save general-purpose registers + sd x1, 1*8(sp) + # skip sp(x2), we will save it later + sd x3, 3*8(sp) + # skip tp(x4), application does not use it + # save x5~x31 + .set n, 5 + .rept 27 + SAVE_GP %n + .set n, n+1 + .endr + # we can use t0/t1/t2 freely, because they were saved on kernel stack + csrr t0, sstatus + csrr t1, sepc + sd t0, 32*8(sp) + sd t1, 33*8(sp) + # read user stack from sscratch and save it on the kernel stack + csrr t2, sscratch + sd t2, 2*8(sp) + # set input argument of trap_handler(cx: &mut TrapContext) + mv a0, sp + call trap_handler + +__restore: + # case1: start running app by __restore + # case2: back to U after handling trap + mv sp, a0 + # now sp->kernel stack(after allocated), sscratch->user stack + # restore sstatus/sepc + ld t0, 32*8(sp) + ld t1, 33*8(sp) + ld t2, 2*8(sp) + csrw sstatus, t0 + csrw sepc, t1 + csrw sscratch, t2 + # restore general-purpuse registers except sp/tp + ld x1, 1*8(sp) + ld x3, 3*8(sp) + .set n, 5 + .rept 27 + LOAD_GP %n + .set n, n+1 + .endr + # release TrapContext on kernel stack + addi sp, sp, 34*8 + # now sp->kernel stack, sscratch->user stack + csrrw sp, sscratch, sp + sret diff --git a/user/src/bin/02_powers.rs b/user/src/bin/02_power.rs similarity index 100% rename from user/src/bin/02_powers.rs rename to user/src/bin/02_power.rs