diff --git a/os/Cargo.lock b/os/Cargo.lock index d6406ea..3775338 100644 --- a/os/Cargo.lock +++ b/os/Cargo.lock @@ -2,6 +2,28 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -11,14 +33,41 @@ dependencies = [ "spin", ] +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "os" version = "0.1.0" dependencies = [ "lazy_static", + "riscv", "sbi-rt", ] +[[package]] +name = "riscv" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa3145d2fae3778b1e31ec2e827b228bdc6abd9b74bb5705ba46dcb82069bc4f" +dependencies = [ + "bit_field", + "critical-section", + "embedded-hal", +] + [[package]] name = "sbi-rt" version = "0.0.2" @@ -48,3 +97,9 @@ name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/os/Cargo.toml b/os/Cargo.toml index 43e795b..22acc70 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] sbi-rt = { version = "0.0.2", features = ["legacy"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +riscv = { version = "0.10.0" } diff --git a/os/src/batch.rs b/os/src/batch.rs index 349cdb2..2698466 100644 --- a/os/src/batch.rs +++ b/os/src/batch.rs @@ -1,7 +1,10 @@ use core::arch::asm; -use core::cell::UnsafeCell; + use lazy_static::lazy_static; -use crate::println; + +use crate::log_information; +use crate::sync::single_cell::SingleCell; +use crate::trap::context::TrapContext; const MAX_APP_NUMBER: usize = 32; const APP_BASE_ADDRESS: usize = 0x80400000; @@ -14,13 +17,13 @@ struct AppManager { } lazy_static! { - static ref APP_MANAGER: UnsafeCell = unsafe { - UnsafeCell::new({ + static ref APP_MANAGER: SingleCell = unsafe { + SingleCell::new({ extern "C" { fn _num_app(); } - let num_app_ptr = _num_app() as usize as *const usize; + let num_app_ptr = _num_app as usize as *const usize; let num_app = num_app_ptr.read_volatile(); let mut start_addresses: [usize; MAX_APP_NUMBER + 1] = [0; MAX_APP_NUMBER + 1]; let start_addresses_raw: &[usize] = core::slice::from_raw_parts( @@ -38,10 +41,10 @@ lazy_static! { impl AppManager { pub fn print_app_information(&self) { - println!("[kernel] Application count is {}.", self.app_count); + log_information!("[kernel] Application count is {}.", self.app_count); for i in 0..self.app_count { - println!("[kernel] Application_{} ({:#x} -> {:#x})", + log_information!("[kernel] Application_{} ({:#x} -> {:#x})", i, self.start_addresses[i], self.start_addresses[i + 1]); @@ -61,7 +64,7 @@ impl AppManager { panic!("Application id is invalid!"); } - println!("[kernel] Loading application_{}...", app_id); + log_information!("[kernel] Loading application_{}...", app_id); core::slice::from_raw_parts_mut( APP_BASE_ADDRESS as *mut u8, @@ -74,7 +77,7 @@ impl AppManager { ); let app_destination = core::slice::from_raw_parts_mut( APP_BASE_ADDRESS as *mut u8, - APP_SIZE_LIMIT + app_source.len() ); app_destination.copy_from_slice(app_source); @@ -84,3 +87,72 @@ impl AppManager { } } +const USER_STACK_SIZE: usize = 4096 * 2; +const KERNEL_STACK_SIZE: usize = 4096 * 2; + +#[repr(align(4096))] +struct UserStack { + data: [u8; USER_STACK_SIZE] +} + +impl UserStack { + fn get_sp(&self) -> usize { + self.data.as_ptr() as usize + USER_STACK_SIZE + } +} + +#[repr(align(4096))] +struct KernelStack { + data: [u8; KERNEL_STACK_SIZE] +} + +impl KernelStack { + fn get_sp(&self) -> usize { + self.data.as_ptr() as usize + KERNEL_STACK_SIZE + } + + pub fn push_context(&self, context: TrapContext) -> &'static mut TrapContext { + let context_pointer = (self.get_sp() - size_of::()) as *mut TrapContext; + unsafe { + *context_pointer = context; + context_pointer.as_mut().unwrap() + } + } +} + +static KERNEL_STACK: KernelStack = KernelStack { data: [0; KERNEL_STACK_SIZE]}; +static USER_STACK: UserStack = UserStack { data: [0; USER_STACK_SIZE]}; + +pub fn print_app_information() { + let app_manager = APP_MANAGER.exclusive_borrow(); + app_manager.print_app_information(); +} + +pub fn run_next_application() -> ! { + let mut app_manager = APP_MANAGER.exclusive_borrow(); + let current_app = app_manager.get_current_app(); + unsafe { + app_manager.load_app(current_app); + } + app_manager.move_to_next_app(); + + // We must drop resource manually + // As this function will never return! + drop(app_manager); + + extern "C" { + fn __restore(context_address: usize); + } + + unsafe { + let context_address = KERNEL_STACK.push_context(TrapContext::init_application_context( + APP_BASE_ADDRESS, + USER_STACK.get_sp() + )) as *const _ as usize; + __restore(context_address); + + unreachable!() + } +} + + diff --git a/os/src/main.rs b/os/src/main.rs index c837444..af83c41 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -9,6 +9,8 @@ mod sbi; mod console; mod batch; mod sync; +mod trap; +mod syscall; global_asm!(include_str!("entry.asm")); global_asm!(include_str!("link_app.asm")); @@ -17,11 +19,9 @@ global_asm!(include_str!("link_app.asm")); #[no_mangle] fn rust_main() -> ! { clear_bss(); - log_error!("Hello, rCore!"); - log_warning!("Hello, rCore!"); - log_information!("Hello, rCore!"); - log_debug!("Hello, rCore!"); - log_trace!("Hello, rCore!"); - sbi::shutdown(false); + log_information!("[kernel] Hello, rCore!"); + trap::init(); + batch::print_app_information(); + batch::run_next_application(); } diff --git a/os/src/sync.rs b/os/src/sync.rs index 47af97e..5c76622 100644 --- a/os/src/sync.rs +++ b/os/src/sync.rs @@ -1 +1 @@ -mod unsafe_cell; \ No newline at end of file +pub mod single_cell; \ No newline at end of file diff --git a/os/src/sync/single_cell.rs b/os/src/sync/single_cell.rs new file mode 100644 index 0000000..c647797 --- /dev/null +++ b/os/src/sync/single_cell.rs @@ -0,0 +1,24 @@ +use core::cell::{RefCell, RefMut}; + +/// 只能在单核场景下保持安全的屑RefCell +pub struct SingleCell { + inner: RefCell +} + +unsafe impl Sync for SingleCell { + +} + +impl SingleCell { + pub unsafe fn new(value: T) -> SingleCell { + SingleCell { + inner: RefCell::new(value) + } + } + + /// 独占访问 + /// 当已被借用时访问会Panic + pub fn exclusive_borrow(&self) -> RefMut<'_, T> { + self.inner.borrow_mut() + } +} diff --git a/os/src/sync/unsafe_cell.rs b/os/src/sync/unsafe_cell.rs deleted file mode 100644 index 502d8ce..0000000 --- a/os/src/sync/unsafe_cell.rs +++ /dev/null @@ -1,24 +0,0 @@ -use core::cell::{RefCell, RefMut}; - -/// 只能在单核场景下保持安全的屑RefCell -struct UnsafeCell { - inner_cell: RefCell -} - -unsafe impl Sync for UnsafeCell { - -} - -impl UnsafeCell { - pub unsafe fn new(value: T) -> UnsafeCell { - Self { - inner_cell: RefCell::new(value) - } - } - - /// 独占访问 - /// 当已被借用时访问会Panic - pub fn exclusive_access(&self) -> RefMut<'_, T> { - self.inner_cell.borrow_mut() - } -} \ No newline at end of file diff --git a/os/src/syscall.rs b/os/src/syscall.rs new file mode 100644 index 0000000..91cf8d9 --- /dev/null +++ b/os/src/syscall.rs @@ -0,0 +1,18 @@ +use crate::syscall::fs::sys_write; +use crate::syscall::process::sys_exit; + +mod fs; +mod process; + +const SYSCALL_WRITE: usize = 64; +const SYSCALL_EXIT: usize = 93; + +pub fn syscall(syscall_id: usize, args: [usize; 3]) -> usize { + 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: {}", syscall_id) + } + } +} diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs new file mode 100644 index 0000000..859e998 --- /dev/null +++ b/os/src/syscall/fs.rs @@ -0,0 +1,17 @@ +use crate::print; + +const STDOUT_FD: usize = 1; + +pub fn sys_write(fd: usize, buffer: *const u8, len: usize) -> usize { + match fd { + STDOUT_FD => { + let slice = unsafe { core::slice::from_raw_parts(buffer, len) }; + let str = core::str::from_utf8(slice).unwrap(); + print!("{}", str); + len as usize + } + _ => { + panic!("Unsupported file descriptor in sys_write"); + } + } +} diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs new file mode 100644 index 0000000..b6ec127 --- /dev/null +++ b/os/src/syscall/process.rs @@ -0,0 +1,7 @@ +use crate::batch::run_next_application; +use crate::println; + +pub fn sys_exit(exit_state: i32) -> ! { + println!("[kernel] Application exited with code {}.", exit_state); + run_next_application() +} \ No newline at end of file diff --git a/os/src/trap.asm b/os/src/trap.asm new file mode 100644 index 0000000..3b3811d --- /dev/null +++ b/os/src/trap.asm @@ -0,0 +1,67 @@ +.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 + .global __alltraps + .global __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 \ No newline at end of file diff --git a/os/src/trap.rs b/os/src/trap.rs new file mode 100644 index 0000000..d989dd4 --- /dev/null +++ b/os/src/trap.rs @@ -0,0 +1,47 @@ +use core::arch::global_asm; +use riscv::register::{ + mtvec::TrapMode, + scause::{self, Exception, Trap}, + stval, stvec +}; +use crate::batch::run_next_application; +use crate::println; +use crate::syscall::syscall; +use crate::trap::context::TrapContext; + +pub mod context; + +global_asm!(include_str!("trap.asm")); + +pub fn init() { + extern "C" { fn __alltraps(); } + unsafe { + stvec::write(__alltraps as usize, TrapMode::Direct); + } +} + +#[no_mangle] +pub fn trap_handler(context: &mut TrapContext) -> &mut TrapContext { + let scause = scause::read(); + let stval = stval::read(); + + match scause.cause() { + Trap::Exception(Exception::UserEnvCall) => { + context.sepc += 4; + context.x[10] = syscall(context.x[17], [context.x[10], context.x[11], context.x[12]]); + }, + Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => { + println!("[kernel] PageFault in application, kernel will kill it."); + run_next_application(); + }, + Trap::Exception(Exception::IllegalInstruction) => { + println!("[kernel] Illegal instruction in application, kernel will kill it."); + run_next_application(); + }, + _ => { + panic!("[Kernel] Unsupported trap: {:?}, stval = {:#x}!", scause.cause(), stval); + } + } + + context +} \ 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..8ef2412 --- /dev/null +++ b/os/src/trap/context.rs @@ -0,0 +1,29 @@ +use riscv::register::sstatus::{self, Sstatus, SPP}; + +#[repr(C)] +pub struct TrapContext { + pub x: [usize; 32], + pub s_status: Sstatus, + pub sepc: usize +} + +impl TrapContext { + pub unsafe fn init_application_context(entry: usize, sp: usize) -> Self { + let sstatus = sstatus::read(); + sstatus::set_spp(SPP::User); + let mut context = Self { + x: [0; 32], + s_status: sstatus, + sepc: entry + }; + + context.set_sp(sp); + context + } + + pub fn set_sp(&mut self, sp: usize) { + // x2 is stack pointer register + self.x[2] = sp; + } +} +