diff --git a/.gitignore b/.gitignore index 770aba77..9fc90c58 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ easy-fs-fuse/Cargo.lock easy-fs-fuse/target/* tools/ pushall.sh +*.bak diff --git a/os/src/boards/k210.rs b/os/src/boards/k210.rs index 4fea2d11..583f05aa 100644 --- a/os/src/boards/k210.rs +++ b/os/src/boards/k210.rs @@ -1 +1,3 @@ +//! Constants used in rCore for K210 devel board + pub const CLOCK_FREQ: usize = 403000000 / 62; diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index 98874fbf..dafcc7b4 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -1 +1,3 @@ +//! Constants used in rCore for K210 devel board + pub const CLOCK_FREQ: usize = 12500000; diff --git a/os/src/config.rs b/os/src/config.rs index 4294ff6f..ff7ffbe1 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -1,4 +1,6 @@ -pub const USER_STACK_SIZE: usize = 4096 * 2; +//! Constants used in rCore + +pub const USER_STACK_SIZE: usize = 4096; pub const KERNEL_STACK_SIZE: usize = 4096 * 2; pub const MAX_APP_NUM: usize = 4; pub const APP_BASE_ADDRESS: usize = 0x80400000; diff --git a/os/src/console.rs b/os/src/console.rs index dda4911a..cc6a91bf 100644 --- a/os/src/console.rs +++ b/os/src/console.rs @@ -1,3 +1,5 @@ +//! SBI console driver, for text output + use crate::sbi::console_putchar; use core::fmt::{self, Write}; @@ -16,6 +18,7 @@ pub fn print(args: fmt::Arguments) { Stdout.write_fmt(args).unwrap(); } +/// print string macro #[macro_export] macro_rules! print { ($fmt: literal $(, $($arg: tt)+)?) => { @@ -23,6 +26,7 @@ macro_rules! print { } } +/// println string macro #[macro_export] macro_rules! println { ($fmt: literal $(, $($arg: tt)+)?) => { diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs index af3e5152..db902c35 100644 --- a/os/src/lang_items.rs +++ b/os/src/lang_items.rs @@ -1,3 +1,5 @@ +//! The panic handler + use crate::sbi::shutdown; use core::panic::PanicInfo; diff --git a/os/src/loader.rs b/os/src/loader.rs index f2ed4f04..af9e508d 100644 --- a/os/src/loader.rs +++ b/os/src/loader.rs @@ -1,3 +1,10 @@ +//! Loading user applications into memory +//! +//! For chapter 3, user applications are simply part of the data included in the +//! kernel binary, so we only need to copy them to the space allocated for each +//! app to load them. We also allocate fixed spaces for each task's +//! [`KernelStack`] and [`UserStack`]. + use crate::config::*; use crate::trap::TrapContext; use core::arch::asm; diff --git a/os/src/main.rs b/os/src/main.rs index 78c41810..55ca76e6 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -1,3 +1,22 @@ +//! The main module and entrypoint +//! +//! Various facilities of the kernels are implemented as submodules. The most +//! important ones are: +//! +//! - [`trap`]: Handles all cases of switching from userspace to the kernel +//! - [`task`]: Task management +//! - [`syscall`]: System call handling and implementation +//! +//! The operating system also starts in this module. Kernel code starts +//! executing from `entry.asm`, after which [`rust_main()`] is called to +//! initialize various pieces of functionality. (See its source code for +//! details.) +//! +//! We then call [`task::run_first_task()`] and for the first time go to +//! userspace. + +#![deny(missing_docs)] +#![deny(warnings)] #![no_std] #![no_main] #![feature(panic_info_message)] @@ -25,6 +44,8 @@ mod trap; global_asm!(include_str!("entry.asm")); global_asm!(include_str!("link_app.S")); + +/// clear BSS segment fn clear_bss() { extern "C" { fn sbss(); @@ -36,6 +57,7 @@ fn clear_bss() { } } +/// the rust entry-point of os #[no_mangle] pub fn rust_main() -> ! { clear_bss(); diff --git a/os/src/sbi.rs b/os/src/sbi.rs index 37efee33..404004d1 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -1,17 +1,18 @@ -#![allow(unused)] +//! SBI call wrappers use core::arch::asm; const SBI_SET_TIMER: usize = 0; const SBI_CONSOLE_PUTCHAR: usize = 1; -const SBI_CONSOLE_GETCHAR: usize = 2; -const SBI_CLEAR_IPI: usize = 3; -const SBI_SEND_IPI: usize = 4; -const SBI_REMOTE_FENCE_I: usize = 5; -const SBI_REMOTE_SFENCE_VMA: usize = 6; -const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; const SBI_SHUTDOWN: usize = 8; +// const SBI_CONSOLE_GETCHAR: usize = 2; +// const SBI_CLEAR_IPI: usize = 3; +// const SBI_SEND_IPI: usize = 4; +// const SBI_REMOTE_FENCE_I: usize = 5; +// const SBI_REMOTE_SFENCE_VMA: usize = 6; +// const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; +/// handle SBI call with `which` SBI_id and other arguments #[inline(always)] fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { let mut ret; @@ -38,9 +39,9 @@ pub fn console_putchar(c: usize) { } /// use sbi call to getchar from console (qemu uart handler) -pub fn console_getchar() -> usize { - sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) -} +// pub fn console_getchar() -> usize { +// sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) +// } /// use sbi call to shutdown the kernel pub fn shutdown() -> ! { diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index d1ce5bcf..4743e31e 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -1,3 +1,5 @@ +//! Synchronization and interior mutability primitives + mod up; pub use up::UPSafeCell; diff --git a/os/src/sync/up.rs b/os/src/sync/up.rs index c7b2c9ee..e8ba20c8 100644 --- a/os/src/sync/up.rs +++ b/os/src/sync/up.rs @@ -1,3 +1,5 @@ +//! Uniprocessor interior mutability primitives + use core::cell::{RefCell, RefMut}; /// Wrap a static data structure inside it so that we are @@ -22,7 +24,7 @@ impl UPSafeCell { inner: RefCell::new(value), } } - /// Panic if the data has been borrowed. + /// Exclusive access inner data in UPSafeCell. Panic if the data has been borrowed. pub fn exclusive_access(&self) -> RefMut<'_, T> { self.inner.borrow_mut() } diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 3e38eae7..2174f26e 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,5 +1,8 @@ +//! File and filesystem-related syscalls + const FD_STDOUT: usize = 1; +/// write buf of length `len` to a file with `fd` pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { match fd { FD_STDOUT => { diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 17d17b73..ee52f5b4 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,3 +1,15 @@ +//! Implementation of syscalls +//! +//! The single entry point to all system calls, [`syscall()`], is called +//! whenever userspace wishes to perform a system call using the `ecall` +//! instruction. In this case, the processor raises an 'Environment call from +//! U-mode' exception, which is handled as one of the cases in +//! [`crate::trap::trap_handler`]. +//! +//! For clarity, each single syscall is implemented as its own function, named +//! `sys_` then the name of the syscall. You can find functions like this in +//! submodules, and you should also implement syscalls this way. + const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; const SYSCALL_YIELD: usize = 124; diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index 85314ea2..bdf5c8bd 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,3 +1,4 @@ +//! Process management syscalls use crate::task::{exit_current_and_run_next, suspend_current_and_run_next}; use crate::timer::get_time_ms; diff --git a/os/src/task/context.rs b/os/src/task/context.rs index 375042d8..5784a6fc 100644 --- a/os/src/task/context.rs +++ b/os/src/task/context.rs @@ -1,3 +1,5 @@ +//! Implementation of [`TaskContext`] + #[derive(Copy, Clone)] #[repr(C)] pub struct TaskContext { diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 453d6a7d..3ea4f989 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -1,3 +1,14 @@ +//! Task management implementation +//! +//! Everything about task management, like starting and switching tasks is +//! implemented here. +//! +//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls +//! all the tasks in the operating system. +//! +//! Be careful when you see [`__switch`]. Control flow around this function +//! might not be what you expect. + mod context; mod switch; #[allow(clippy::module_inception)] @@ -12,6 +23,15 @@ use task::{TaskControlBlock, TaskStatus}; pub use context::TaskContext; +/// The task manager, where all the tasks are managed. +/// +/// Functions implemented on `TaskManager` deals with all task state transitions +/// and task context switching. For convenience, you can find wrappers around it +/// in the module level. +/// +/// Most of `TaskManager` are hidden behind the field `inner`, to defer +/// borrowing checks to runtime. You can see examples on how to use `inner` in +/// existing functions on `TaskManager`. pub struct TaskManager { /// total number of tasks num_app: usize, @@ -82,7 +102,7 @@ impl TaskManager { inner.tasks[current].task_status = TaskStatus::Exited; } - /// Find next task to run and return app id. + /// Find next task to run and return task id. /// /// In this case, we only return the first `Ready` task in task list. fn find_next_task(&self) -> Option { diff --git a/os/src/task/switch.rs b/os/src/task/switch.rs index 59f8b1a0..3535a5ca 100644 --- a/os/src/task/switch.rs +++ b/os/src/task/switch.rs @@ -1,8 +1,17 @@ +//! Rust wrapper around `__switch`. +//! +//! Switching to a different task's context happens here. The actual +//! implementation must not be in Rust and (essentially) has to be in assembly +//! language (Do you know why?), so this module really is just a wrapper around +//! `switch.S`. + use super::TaskContext; use core::arch::global_asm; global_asm!(include_str!("switch.S")); extern "C" { + /// Switch to the context of `next_task_cx_ptr`, saving the current context + /// in `current_task_cx_ptr`. pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext); } diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 43c4f9c2..d1c6dd83 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,9 +1,12 @@ +//! Types related to task management + use super::TaskContext; #[derive(Copy, Clone)] pub struct TaskControlBlock { pub task_status: TaskStatus, pub task_cx: TaskContext, + // LAB1: Add whatever you need about the Task. } #[derive(Copy, Clone, PartialEq)] diff --git a/os/src/timer.rs b/os/src/timer.rs index 1d101d43..b5ce0be8 100644 --- a/os/src/timer.rs +++ b/os/src/timer.rs @@ -1,3 +1,5 @@ +//! RISC-V timer-related functionality + use crate::config::CLOCK_FREQ; use crate::sbi::set_timer; use riscv::register::time; diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index dcb4c2bb..2ac6e034 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -1,3 +1,17 @@ +//! Trap handling functionality +//! +//! For rCore, we have a single trap entry point, namely `__alltraps`. At +//! initialization in [`init()`], we set the `stvec` CSR to point to it. +//! +//! All traps go through `__alltraps`, which is defined in `trap.S`. The +//! assembly language code does just enough work restore the kernel space +//! context, ensuring that Rust code safely runs, and transfers control to +//! [`trap_handler()`]. +//! +//! It then calls different functionality based on what exactly the exception +//! was. For example, timer interrupts trigger task preemption, and syscalls go +//! to [`syscall()`]. + mod context; use crate::syscall::syscall;