diff --git a/os/Cargo.lock b/os/Cargo.lock index b85588b1..85e21762 100644 --- a/os/Cargo.lock +++ b/os/Cargo.lock @@ -1,5 +1,132 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + [[package]] name = "os" version = "0.1.0" +dependencies = [ + "lazy_static", + "riscv", +] + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "riscv" +version = "0.6.0" +source = "git+https://github.com/rcore-os/riscv#21e32ee1dc786cc0d5006ceee0040ce4f8398575" +dependencies = [ + "bare-metal", + "bit_field", + "bitflags", + "riscv-target", +] + +[[package]] +name = "riscv-target" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] diff --git a/os/Cargo.toml b/os/Cargo.toml index b6770dc1..43768d78 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -7,3 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } \ No newline at end of file diff --git a/os/build.rs b/os/build.rs index a358a266..3794b21a 100644 --- a/os/build.rs +++ b/os/build.rs @@ -37,7 +37,8 @@ _num_app: .quad app_{}_end "#, apps.len() - 1)?; - for (idx, app_with_extension) in apps.iter().enumerate() { + for (idx, app) in apps.iter().enumerate() { + println!("app_{}: {}", idx, app); writeln!(f, r#" .section .data .global app_{0}_start @@ -45,7 +46,7 @@ _num_app: app_{0}_start: .incbin "{2}{1}.bin" app_{0}_end: - "#, idx, app_with_extension, TARGET_PATH)?; + "#, idx, app, TARGET_PATH)?; } Ok(()) } \ No newline at end of file diff --git a/os/src/batch.rs b/os/src/batch.rs new file mode 100644 index 00000000..567b4b30 --- /dev/null +++ b/os/src/batch.rs @@ -0,0 +1,117 @@ +use core::cell::RefCell; +use lazy_static::*; +use crate::trap::TrapContext; + +const USER_STACK_SIZE: usize = 4096 * 2; +const KERNEL_STACK_SIZE: usize = 4096 * 2; +const MAX_APP_NUM: usize = 16; +const APP_BASE_ADDRESS: usize = 0x80040000; +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() - core::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 { + inner: RefCell, +} +struct AppManagerInner { + num_app: usize, + next_app: usize, + app_start: [usize; MAX_APP_NUM + 1], +} +unsafe impl Sync for AppManager {} + +impl AppManagerInner { + pub fn print_app_info(&self) { + println!("num_app = {}", self.num_app); + for i in 0..self.num_app { + println!("app_{} [{:#x}, {:#x})", i, self.app_start[i], self.app_start[i + 1]); + } + } + + unsafe fn load_app(&self, app_id: usize) { + // clear app area + (APP_BASE_ADDRESS..APP_BASE_ADDRESS + APP_SIZE_LIMIT).for_each(|addr| { + (addr as *mut u8).write_volatile(0); + }); + let app_src = core::slice::from_raw_parts( + self.app_start[app_id] as *const u8, + self.app_start[app_id + 1] - self.app_start[app_id] + ); + let app_dst = core::slice::from_raw_parts_mut( + APP_BASE_ADDRESS as *mut u8, + app_src.len() + ); + app_dst.copy_from_slice(app_src); + } + + pub fn run_next_app(&mut self) { + unsafe { self.load_app(self.next_app); } + self.next_app += 1; + extern "C" { fn __restore(cx: &mut TrapContext); } + unsafe { + __restore(KERNEL_STACK.push_context( + TrapContext::app_init_context(APP_BASE_ADDRESS, USER_STACK.get_sp()) + )); + } + } +} + +lazy_static! { + static ref APP_MANAGER: AppManager = AppManager { + inner: RefCell::new({ + extern "C" { fn _num_app(); } + let num_app_ptr = _num_app as usize as *const usize; + let num_app = unsafe { num_app_ptr.read_volatile() }; + let mut app_start: [usize; MAX_APP_NUM + 1] = [0; MAX_APP_NUM + 1]; + let app_start_raw: &[usize] = unsafe { + core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) + }; + &app_start[..=num_app].copy_from_slice(app_start_raw); + AppManagerInner { + num_app, + next_app: 0, + app_start, + } + }), + }; +} + +pub fn init() { + print_app_info(); +} + +pub fn print_app_info() { + APP_MANAGER.inner.borrow().print_app_info(); +} + +pub fn run_next_app() { + APP_MANAGER.inner.borrow_mut().run_next_app(); +} diff --git a/os/src/main.rs b/os/src/main.rs index d571e81f..67b32312 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -10,6 +10,7 @@ mod lang_items; mod sbi; mod syscall; mod trap; +mod batch; global_asm!(include_str!("entry.asm")); global_asm!(include_str!("link_app.S")); @@ -28,17 +29,7 @@ fn clear_bss() { pub fn rust_main() -> ! { clear_bss(); println!("Hello, world!"); - extern "C" { - fn _num_app(); - } - let num_app_ptr = _num_app as usize as *const usize; - let num_app = unsafe { num_app_ptr.read_volatile() }; - println!("num_app = {}", num_app); - let app_start: &[usize] = unsafe { - core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) - }; - for i in 0..num_app { - println!("app_{} [{:#x}, {:#x})", i, app_start[i], app_start[i + 1]); - } + batch::init(); + batch::run_next_app(); panic!("Shutdown machine!"); } \ No newline at end of file diff --git a/os/src/trap.rs b/os/src/trap.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs new file mode 100644 index 00000000..b4245227 --- /dev/null +++ b/os/src/trap/context.rs @@ -0,0 +1,23 @@ +use riscv::register::sstatus::{Sstatus, self, SPP}; + +#[repr(C)] +pub struct TrapContext { + x: [usize; 32], + sstatus: Sstatus, + 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 mut sstatus = sstatus::read(); + sstatus.set_spp(SPP::User); + let mut cx = Self { + x: [0; 32], + sstatus, + sepc: entry, + }; + cx.set_sp(sp); + cx + } +} diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs new file mode 100644 index 00000000..2d7db829 --- /dev/null +++ b/os/src/trap/mod.rs @@ -0,0 +1,38 @@ +mod context; + +use riscv::register::{ + mtvec::TrapMode, + stvec, + scause::{ + self, + Trap, + Exception, + }, + stval, +}; + +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(Exception::UserEnvCall) => { + panic!("Triggered UserEnvCall!"); + } + _ => { + panic!("Unsupported trap!"); + } + } + cx +} + +pub use context::TrapContext; \ 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 00000000..9d6967cb --- /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/lib.rs b/user/src/lib.rs index 0a6eb264..c905e412 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -11,8 +11,8 @@ mod lang_items; #[no_mangle] #[link_section = ".text.entry"] pub extern "C" fn _start() -> ! { - main(); - loop {} + syscall::sys_exit(main()); + panic!("unreachable after sys_exit!"); } #[linkage = "weak"] diff --git a/user/src/syscall.rs b/user/src/syscall.rs index fbd743ea..edb26d77 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,6 +1,7 @@ pub const STDOUT: usize = 1; const SYSCALL_WRITE: usize = 64; +const SYSCALL_EXIT: usize = 93; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -17,4 +18,8 @@ fn syscall(id: usize, args: [usize; 3]) -> isize { pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) -} \ No newline at end of file +} + +pub fn sys_exit(xstate: i32) -> isize { + syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]) +}