├── kernel ├── .gitignore ├── src │ ├── file │ │ ├── mod.rs │ │ └── load.rs │ ├── sync │ │ ├── mod.rs │ │ ├── mutex.rs │ │ └── event_bus.rs │ ├── asm │ │ ├── boot.asm │ │ └── linkage.asm │ ├── syscall │ │ ├── timer.rs │ │ ├── fs.rs │ │ ├── mod.rs │ │ └── process.rs │ ├── mem │ │ ├── address │ │ │ ├── mod.rs │ │ │ ├── physical_address.rs │ │ │ ├── virtual_address.rs │ │ │ ├── page_number.rs │ │ │ └── frame_number.rs │ │ ├── mod.rs │ │ ├── heap_allocator.rs │ │ ├── user_ptr.rs │ │ ├── frame_allocator.rs │ │ ├── page_table.rs │ │ └── segment.rs │ ├── task │ │ ├── mod.rs │ │ ├── tid.rs │ │ ├── pid.rs │ │ ├── thread.rs │ │ └── process.rs │ ├── timer.rs │ ├── executor │ │ ├── scheduler.rs │ │ ├── context.rs │ │ ├── mod.rs │ │ └── future.rs │ ├── lang_items.rs │ ├── constant.rs │ ├── console.rs │ ├── linker.ld │ ├── logging.rs │ ├── sbi.rs │ └── main.rs └── Cargo.toml ├── kernel-lib ├── src │ ├── constant.rs │ ├── bin │ │ ├── hello_world.rs │ │ ├── privileged_instruction.rs │ │ ├── page_fault.rs │ │ ├── sleep.rs │ │ ├── init.rs │ │ ├── fork.rs │ │ └── shell.rs │ ├── linker.ld │ ├── lang_items.rs │ ├── heap_allocator.rs │ ├── console.rs │ ├── logging.rs │ ├── lib.rs │ └── syscall.rs └── Cargo.toml ├── Cargo.toml ├── bootloader └── opensbi-jump.bin ├── doc └── index.html ├── rust-toolchain.toml ├── .gitignore ├── rustfmt.toml ├── .cargo └── config.toml ├── Makefile ├── LICENSE ├── .github └── workflows │ └── cargo.yml └── README.md /kernel/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /kernel-lib/src/constant.rs: -------------------------------------------------------------------------------- 1 | pub const USER_HEAP_SIZE: usize = 4096 * 8; 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "kernel", 4 | "kernel-lib" 5 | ] 6 | -------------------------------------------------------------------------------- /kernel/src/file/mod.rs: -------------------------------------------------------------------------------- 1 | mod load; 2 | 3 | pub use load::{get_bin, get_bin_count, get_bin_data, print_bin_name}; 4 | -------------------------------------------------------------------------------- /bootloader/opensbi-jump.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyang-sde/rust-kernel-riscv/HEAD/bootloader/opensbi-jump.bin -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | channel = "nightly" 4 | components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] 5 | -------------------------------------------------------------------------------- /kernel-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-lib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | linked_list_allocator = "0.10.5" 8 | log = "0.4.17" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust Toolchain 2 | debug/ 3 | target/ 4 | Cargo.lock 5 | **/*.rs.bk 6 | *.pdb 7 | .gdb_history 8 | 9 | # Configuration 10 | .vscode 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 100 2 | format_code_in_doc_comments = true 3 | group_imports = "StdExternalCrate" 4 | imports_granularity = "Crate" 5 | imports_layout = "HorizontalVertical" 6 | wrap_comments = true 7 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/hello_world.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use log::info; 5 | 6 | extern crate kernel_lib; 7 | 8 | #[no_mangle] 9 | fn main() -> i32 { 10 | info!("hello, world!"); 11 | 0 12 | } 13 | -------------------------------------------------------------------------------- /kernel/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `sync` module provides synchronization primitives for concurrent programming. 2 | 3 | mod event_bus; 4 | mod mutex; 5 | 6 | pub use event_bus::{wait_for_event, Event, EventBus}; 7 | pub use mutex::{Mutex, MutexGuard}; 8 | -------------------------------------------------------------------------------- /kernel/src/asm/boot.asm: -------------------------------------------------------------------------------- 1 | .section .text.boot 2 | .globl _start 3 | _start: 4 | la sp, boot_stack_top 5 | call rust_main 6 | 7 | .section .bss.stack 8 | boot_stack_bottom: 9 | .space 4096 * 16 10 | .globl boot_stack_top 11 | boot_stack_top: 12 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | profile-rustflags = true 3 | 4 | [build] 5 | target = "riscv64gc-unknown-none-elf" 6 | 7 | [profile.dev.package.kernel] 8 | rustflags = ["-C", "link-arg=-Tkernel/src/linker.ld"] 9 | 10 | [profile.dev.package.kernel-lib] 11 | rustflags = ["-C", "link-arg=-Tkernel-lib/src/linker.ld"] 12 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/privileged_instruction.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate kernel_lib; 5 | 6 | use core::arch::asm; 7 | 8 | use log::warn; 9 | 10 | #[no_mangle] 11 | fn main() -> i32 { 12 | warn!("attempt to execute a privileged instruction"); 13 | unsafe { 14 | asm!("sret"); 15 | } 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/page_fault.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate kernel_lib; 5 | 6 | use core::ptr::null_mut; 7 | 8 | use log::warn; 9 | 10 | #[no_mangle] 11 | fn main() -> i32 { 12 | warn!("attempt to write to an invalid location"); 13 | unsafe { 14 | null_mut::().write_volatile(0); 15 | } 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = { version = "4.3.0", default-features = false } 8 | bitflags = "2.0.2" 9 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 10 | linked_list_allocator = "0.10.5" 11 | log = "0.4.17" 12 | riscv = "0.10.1" 13 | xmas-elf = "0.9.0" 14 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate kernel_lib; 5 | 6 | use kernel_lib::{get_time, sched_yield}; 7 | use log::info; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | let start_time = get_time(); 12 | while get_time() < start_time + 1000 { 13 | sched_yield(); 14 | } 15 | info!("slept for 1 second"); 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /kernel/src/syscall/timer.rs: -------------------------------------------------------------------------------- 1 | //! The `timer` module provides time-related system calls. 2 | 3 | use crate::{executor::ControlFlow, syscall::SystemCall, timer}; 4 | 5 | impl SystemCall<'_> { 6 | /// Returns the current system time in milliseconds. 7 | pub fn sys_get_time(&self) -> (isize, ControlFlow) { 8 | (timer::get_time() as isize, ControlFlow::Continue) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /kernel/src/mem/address/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `address` module defines various structs for the Sv39 page table specification. 2 | 3 | mod frame_number; 4 | mod page_number; 5 | mod physical_address; 6 | mod virtual_address; 7 | 8 | pub use frame_number::FrameNumber; 9 | pub use page_number::{PageNumber, PageRange}; 10 | pub use physical_address::PhysicalAddress; 11 | pub use virtual_address::VirtualAddress; 12 | -------------------------------------------------------------------------------- /kernel/src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod frame_allocator; 3 | mod heap_allocator; 4 | mod page_table; 5 | mod segment; 6 | mod user_ptr; 7 | 8 | pub use address::{FrameNumber, PageNumber, PhysicalAddress, VirtualAddress}; 9 | pub use frame_allocator::deallocate_frame; 10 | pub use segment::{MapPermission, PageSet, KERNEL_SPACE}; 11 | pub use user_ptr::UserPtr; 12 | 13 | pub fn init() { 14 | heap_allocator::init(); 15 | frame_allocator::init(); 16 | KERNEL_SPACE.lock().init(); 17 | } 18 | -------------------------------------------------------------------------------- /kernel/src/task/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `task` module provides types for representing processes and threads. 2 | 3 | mod pid; 4 | mod process; 5 | mod thread; 6 | mod tid; 7 | 8 | use alloc::sync::Arc; 9 | 10 | use lazy_static::{initialize, lazy_static}; 11 | pub use process::{Process, Status}; 12 | pub use thread::Thread; 13 | 14 | lazy_static! { 15 | static ref INIT_PROCESS: Arc = Process::new("init"); 16 | } 17 | 18 | /// Spawns the init process. 19 | pub fn init() { 20 | initialize(&INIT_PROCESS); 21 | } 22 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/init.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use kernel_lib::{exec, fork, wait}; 5 | use log::info; 6 | 7 | extern crate kernel_lib; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | if fork() == 0 { 12 | exec("shell\0"); 13 | } else { 14 | loop { 15 | let mut exit_code = 0; 16 | let pid = wait(&mut exit_code); 17 | info!( 18 | "released a zombie process (pid: {}, exit_code: {})", 19 | pid, exit_code 20 | ); 21 | } 22 | } 23 | 0 24 | } 25 | -------------------------------------------------------------------------------- /kernel-lib/src/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x10000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | .text : { 9 | *(.text.init) 10 | *(.text .text.*) 11 | } 12 | . = ALIGN(4K); 13 | 14 | .rodata : { 15 | *(.rodata .rodata.*) 16 | *(.srodata .srodata.*) 17 | } 18 | . = ALIGN(4K); 19 | 20 | .data : { 21 | *(.data .data.*) 22 | *(.sdata .sdata.*) 23 | } 24 | 25 | .bss : { 26 | *(.bss .bss.*) 27 | *(.sbss .sbss.*) 28 | } 29 | 30 | /DISCARD/ : { 31 | *(.eh_frame) 32 | *(.debug*) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/fork.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use kernel_lib::{exit, fork, wait}; 5 | use log::info; 6 | 7 | extern crate kernel_lib; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | const MAX_CHILD: i32 = 8; 12 | for i in 0..MAX_CHILD { 13 | let pid = fork(); 14 | if pid == 0 { 15 | info!("the child process {} has been spawned", i); 16 | exit(0); 17 | } else { 18 | info!("forked a child process with PID = {}", pid); 19 | } 20 | assert!(pid > 0); 21 | } 22 | 23 | let mut exit_code: usize = 0; 24 | for _ in 0..MAX_CHILD { 25 | wait(&mut exit_code); 26 | } 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/task/tid.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | pub type Tid = usize; 4 | 5 | pub struct TidAllocator { 6 | state: Tid, 7 | deallocated_tid: Vec, 8 | } 9 | 10 | impl TidAllocator { 11 | pub fn new() -> Self { 12 | TidAllocator { 13 | state: 0, 14 | deallocated_tid: Vec::new(), 15 | } 16 | } 17 | 18 | pub fn allocate(&mut self) -> Tid { 19 | if let Some(tid) = self.deallocated_tid.pop() { 20 | tid 21 | } else { 22 | let tid = self.state; 23 | self.state += 1; 24 | tid 25 | } 26 | } 27 | 28 | pub fn deallocate(&mut self, tid: Tid) { 29 | self.deallocated_tid.push(tid); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kernel/src/timer.rs: -------------------------------------------------------------------------------- 1 | //! The `timer` module provides functions to configure the timer interrupt. 2 | 3 | use riscv::register::{sie, time}; 4 | 5 | use crate::{constant::CLOCK_FREQ, sbi}; 6 | 7 | /// The number of timer ticks per second. 8 | const TICK_PER_SEC: usize = 100; 9 | 10 | /// Returns the current system time in milliseconds. 11 | pub fn get_time() -> usize { 12 | time::read() / (CLOCK_FREQ / 1000) 13 | } 14 | 15 | /// Sets the timer interrupt trigger for the next timer tick. 16 | pub fn set_trigger() { 17 | sbi::set_timer(time::read() + CLOCK_FREQ / TICK_PER_SEC); 18 | } 19 | 20 | /// Enables the system timer interrupt. 21 | pub fn enable_timer_interrupt() { 22 | unsafe { 23 | sie::set_stimer(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /kernel/src/executor/scheduler.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::VecDeque; 2 | 3 | use async_task::Runnable; 4 | 5 | pub trait Scheduler { 6 | fn schedule(&mut self, runnable: Runnable); 7 | 8 | fn task(&mut self) -> Option; 9 | } 10 | 11 | /// The `TaskQueue` struct represents a queue of [Runnable] tasks, which are either kernel threads 12 | /// or user threads. 13 | pub struct TaskQueue { 14 | queue: VecDeque, 15 | } 16 | 17 | impl TaskQueue { 18 | pub fn new() -> Self { 19 | Self { 20 | queue: VecDeque::new(), 21 | } 22 | } 23 | } 24 | 25 | impl Scheduler for TaskQueue { 26 | fn schedule(&mut self, runnable: Runnable) { 27 | self.queue.push_back(runnable) 28 | } 29 | 30 | fn task(&mut self) -> Option { 31 | self.queue.pop_front() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /kernel-lib/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | //! The `lang_items` module contains Rust lang items. 2 | //! Rust lang items are functionalities that isn't hard-coded into the language, 3 | //! but is implemented in libraries, with a special marker to tell the compiler it exists. 4 | //! Since the kernel doesn't depend on the `std` crate, it has to implement some 5 | //! lang items, such as the `panic_handler`. 6 | 7 | use core::panic::PanicInfo; 8 | 9 | use log::error; 10 | 11 | #[panic_handler] 12 | fn panic(info: &PanicInfo) -> ! { 13 | if let Some(location) = info.location() { 14 | error!( 15 | "panic at {}:{}: {}", 16 | location.file(), 17 | location.line(), 18 | info.message().unwrap() 19 | ); 20 | } else { 21 | error!("panic: {}", info.message().unwrap()); 22 | } 23 | loop {} 24 | } 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build fmt doc qemu qemu-gdb gdb clean 2 | 3 | build: 4 | cargo build 5 | 6 | fmt: 7 | cargo fmt 8 | 9 | doc: 10 | cargo doc --no-deps --bin kernel --lib 11 | 12 | qemu: build 13 | qemu-system-riscv64 \ 14 | -machine virt \ 15 | -nographic \ 16 | -bios bootloader/opensbi-jump.bin \ 17 | -device loader,file=target/riscv64gc-unknown-none-elf/debug/kernel,addr=0x80200000 18 | 19 | qemu-gdb: build 20 | qemu-system-riscv64 \ 21 | -machine virt \ 22 | -nographic \ 23 | -bios bootloader/opensbi-jump.bin \ 24 | -device loader,file=target/riscv64gc-unknown-none-elf/debug/kernel,addr=0x80200000 \ 25 | -s -S 26 | 27 | gdb: 28 | riscv64-unknown-elf-gdb \ 29 | -ex 'file target/riscv64gc-unknown-none-elf/debug/kernel' \ 30 | -ex 'set arch riscv:rv64' \ 31 | -ex 'target remote localhost:1234' 32 | 33 | clean: 34 | cargo clean 35 | -------------------------------------------------------------------------------- /kernel/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | //! The `lang_items` module contains Rust lang items. 2 | //! Rust lang items are functionalities that isn't hard-coded into the language, 3 | //! but is implemented in libraries, with a special marker to tell the compiler it exists. 4 | //! Since the kernel doesn't depend on the `std` crate, it has to implement some 5 | //! lang items, such as the `panic_handler`. 6 | 7 | use core::panic::PanicInfo; 8 | 9 | use log::error; 10 | 11 | use crate::sbi; 12 | 13 | #[panic_handler] 14 | fn panic(info: &PanicInfo) -> ! { 15 | if let Some(location) = info.location() { 16 | error!( 17 | "panic at {}:{}: {}", 18 | location.file(), 19 | location.line(), 20 | info.message().unwrap() 21 | ); 22 | } else { 23 | error!("panic: {}", info.message().unwrap()); 24 | } 25 | sbi::shutdown(); 26 | } 27 | -------------------------------------------------------------------------------- /kernel/src/constant.rs: -------------------------------------------------------------------------------- 1 | //! The `constant` module defines several parameters for the kernel. 2 | 3 | /// The size of the kernel heap, in bytes. 4 | pub const KERNEL_HEAP_SIZE: usize = 4096 * 768; 5 | 6 | /// The size of the user stack, in bytes. 7 | pub const USER_STACK_SIZE: usize = 4096 * 2; 8 | 9 | /// The size of a page in memory, in bytes. 10 | pub const PAGE_SIZE: usize = 4096; 11 | 12 | /// The number of bits needed to represent a page size. 13 | pub const PAGE_SIZE_BIT: usize = 12; 14 | 15 | /// The memory limit for the kernel, in bytes. 16 | pub const MEM_LIMIT: usize = 0x81000000; 17 | 18 | /// The address of the trampoline page, which is used for switching between user and kernel space. 19 | pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; 20 | 21 | /// The base address of the trap context. 22 | pub const TRAP_CONTEXT_BASE: usize = usize::MAX - 256 * PAGE_SIZE + 1; 23 | 24 | /// The clock frequency of the system, in Hz. 25 | pub const CLOCK_FREQ: usize = 12500000; 26 | -------------------------------------------------------------------------------- /kernel-lib/src/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The `heap_allocator` module provides a heap allocator for the user. 2 | //! The heap is initialized with a fixed size as the [USER_HEAP_SIZE] constant. 3 | 4 | use core::alloc::Layout; 5 | 6 | use linked_list_allocator::LockedHeap; 7 | 8 | use crate::constant::USER_HEAP_SIZE; 9 | 10 | #[global_allocator] 11 | static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 12 | 13 | static mut USER_HEAP: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; 14 | 15 | /// Initializes the user heap with a fixed size as the [KERNEL_HEAP_SIZE] 16 | /// constant. This function must be called before the heap can be used. 17 | pub fn init() { 18 | unsafe { 19 | HEAP_ALLOCATOR 20 | .lock() 21 | .init(USER_HEAP.as_mut_ptr(), USER_HEAP_SIZE); 22 | } 23 | } 24 | 25 | /// Panics when heap allocation fails. 26 | #[alloc_error_handler] 27 | pub fn handle_alloc_error(layout: Layout) -> ! { 28 | panic!("failed to allocate the desired layout {:?}", layout); 29 | } 30 | -------------------------------------------------------------------------------- /kernel/src/mem/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The `heap_allocator` module provides a heap allocator for the kernel. 2 | //! The heap is initialized with a fixed size as the [KERNEL_HEAP_SIZE] constant. 3 | 4 | use core::alloc::Layout; 5 | 6 | use linked_list_allocator::LockedHeap; 7 | 8 | use crate::constant::KERNEL_HEAP_SIZE; 9 | 10 | #[global_allocator] 11 | static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 12 | 13 | static mut KERNEL_HEAP: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 14 | 15 | /// Initializes the kernel heap with a fixed size as the [KERNEL_HEAP_SIZE] 16 | /// constant. This function must be called before the heap can be used. 17 | pub fn init() { 18 | unsafe { 19 | HEAP_ALLOCATOR 20 | .lock() 21 | .init(KERNEL_HEAP.as_mut_ptr(), KERNEL_HEAP_SIZE); 22 | } 23 | } 24 | 25 | /// Panics when heap allocation fails. 26 | #[alloc_error_handler] 27 | pub fn handle_alloc_error(layout: Layout) -> ! { 28 | panic!("failed to allocate the desired layout {:?}", layout); 29 | } 30 | -------------------------------------------------------------------------------- /kernel/src/console.rs: -------------------------------------------------------------------------------- 1 | //! The `console` module contains functions that interacts with the debug console. 2 | //! It exports useful macros such as `print!` and `println!`. 3 | 4 | use core::fmt::{self, Write}; 5 | 6 | use crate::sbi; 7 | 8 | /// The `Console` struct implements the [Write] trait, which invokes the [sbi::console_putchar] 9 | /// function. 10 | struct Console; 11 | 12 | impl Write for Console { 13 | fn write_str(&mut self, string: &str) -> fmt::Result { 14 | for char in string.bytes() { 15 | sbi::console_putchar(char.into()); 16 | } 17 | Ok(()) 18 | } 19 | } 20 | 21 | pub fn print(args: fmt::Arguments) { 22 | Console.write_fmt(args).unwrap(); 23 | } 24 | 25 | /// Print to the debug console. 26 | #[macro_export] 27 | macro_rules! print { 28 | ($($arg:tt)*) => ($crate::console::print(format_args!($($arg)*))); 29 | } 30 | 31 | /// Print to the debug console, with a newline. 32 | #[macro_export] 33 | macro_rules! println { 34 | () => ($crate::print!("\n")); 35 | ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); 36 | } 37 | -------------------------------------------------------------------------------- /kernel-lib/src/console.rs: -------------------------------------------------------------------------------- 1 | //! The `console` module contains functions that interacts with the debug console. 2 | //! It exports useful macros such as `print!` and `println!`. 3 | 4 | use core::fmt::{self, Write}; 5 | 6 | use crate::{read, write}; 7 | 8 | const STDIN: usize = 0; 9 | const STDOUT: usize = 1; 10 | 11 | struct Console; 12 | 13 | impl Write for Console { 14 | fn write_str(&mut self, string: &str) -> fmt::Result { 15 | write(STDOUT, string.as_bytes()); 16 | Ok(()) 17 | } 18 | } 19 | 20 | pub fn getchar() -> u8 { 21 | let mut char = [0; 1]; 22 | read(STDIN, &mut char); 23 | char[0] 24 | } 25 | 26 | pub fn print(args: fmt::Arguments) { 27 | Console.write_fmt(args).unwrap(); 28 | } 29 | 30 | /// Print to the debug console. 31 | #[macro_export] 32 | macro_rules! print { 33 | ($($arg:tt)*) => ($crate::console::print(format_args!($($arg)*))); 34 | } 35 | 36 | /// Print to the debug console, with a newline. 37 | #[macro_export] 38 | macro_rules! println { 39 | () => ($crate::print!("\n")); 40 | ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Xiaoyang Liu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/cargo.yml: -------------------------------------------------------------------------------- 1 | name: cargo 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: dtolnay/rust-toolchain@master 10 | with: 11 | toolchain: nightly 12 | targets: riscv64gc-unknown-none-elf 13 | - run: make build 14 | 15 | fmt: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: dtolnay/rust-toolchain@master 20 | with: 21 | toolchain: nightly 22 | targets: riscv64gc-unknown-none-elf 23 | - run: make fmt 24 | 25 | doc: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | - uses: dtolnay/rust-toolchain@master 30 | with: 31 | toolchain: nightly 32 | targets: riscv64gc-unknown-none-elf 33 | - run: make doc 34 | - run: mv target/riscv64gc-unknown-none-elf/doc/* doc 35 | - uses: peaceiris/actions-gh-pages@v3 36 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: doc 40 | publish_branch: gh-pages 41 | cname: rust-kernel-riscv.com 42 | -------------------------------------------------------------------------------- /kernel/src/task/pid.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use lazy_static::lazy_static; 4 | 5 | use crate::sync::Mutex; 6 | 7 | pub type Pid = usize; 8 | 9 | pub struct PidHandle { 10 | pid: Pid, 11 | } 12 | 13 | impl PidHandle { 14 | pub fn new(pid: Pid) -> Self { 15 | Self { pid } 16 | } 17 | 18 | pub fn pid(&self) -> Pid { 19 | self.pid 20 | } 21 | } 22 | 23 | impl Drop for PidHandle { 24 | fn drop(&mut self) { 25 | PID_ALLOCATOR.lock().deallocate(self.pid); 26 | } 27 | } 28 | 29 | pub struct PidAllocator { 30 | state: Pid, 31 | deallocated_pid: Vec, 32 | } 33 | 34 | impl PidAllocator { 35 | pub fn new() -> Self { 36 | PidAllocator { 37 | state: 0, 38 | deallocated_pid: Vec::new(), 39 | } 40 | } 41 | 42 | pub fn allocate(&mut self) -> PidHandle { 43 | if let Some(pid) = self.deallocated_pid.pop() { 44 | PidHandle::new(pid) 45 | } else { 46 | let pid_handle = PidHandle::new(self.state); 47 | self.state += 1; 48 | pid_handle 49 | } 50 | } 51 | 52 | pub fn deallocate(&mut self, pid: Pid) { 53 | self.deallocated_pid.push(pid); 54 | } 55 | } 56 | 57 | lazy_static! { 58 | static ref PID_ALLOCATOR: Mutex = Mutex::new(PidAllocator::new()); 59 | } 60 | 61 | pub fn allocate_pid() -> PidHandle { 62 | PID_ALLOCATOR.lock().allocate() 63 | } 64 | -------------------------------------------------------------------------------- /kernel/src/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | The linker script determines the layout of the output file, 3 | which moves the `.text.boot` section defined in `src/boot.asm` 4 | to the base address of the kernel. 5 | It defines various symbols that contains the start and the end 6 | address of each section. 7 | */ 8 | 9 | OUTPUT_ARCH(riscv) 10 | ENTRY(_start) 11 | BASE_ADDRESS = 0x80200000; 12 | 13 | SECTIONS 14 | { 15 | . = BASE_ADDRESS; 16 | kernel_start = .; 17 | 18 | text_start = .; 19 | .text : { 20 | *(.text.boot) 21 | . = ALIGN(4k); 22 | 23 | trampoline_start = .; 24 | *(.text.trampoline) 25 | . = ALIGN(4k); 26 | trampoline_end = .; 27 | 28 | *(.text .text.*) 29 | } 30 | . = ALIGN(4K); 31 | text_end = .; 32 | 33 | rodata_start = .; 34 | .rodata : { 35 | *(.rodata .rodata.*) 36 | *(.srodata .srodata.*) 37 | } 38 | . = ALIGN(4K); 39 | rodata_end = .; 40 | 41 | data_start = .; 42 | .data : { 43 | *(.data .data.*) 44 | *(.sdata .sdata.*) 45 | } 46 | . = ALIGN(4K); 47 | data_end = .; 48 | 49 | bss_stack_start = .; 50 | .bss : { 51 | *(.bss.stack) 52 | bss_stack_end = .; 53 | 54 | bss_start = .; 55 | *(.bss .bss.*) 56 | *(.sbss .sbss.*) 57 | } 58 | . = ALIGN(4K); 59 | bss_end = .; 60 | 61 | kernel_end = .; 62 | 63 | /DISCARD/ : { 64 | *(.eh_frame) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /kernel/src/executor/context.rs: -------------------------------------------------------------------------------- 1 | //! The `context` module provides a `TrapContext` struct that save and restore 2 | //! the context of a thread when an exception or interrupt occurs. 3 | 4 | use riscv::register::sstatus::Sstatus; 5 | 6 | /// The `TrapContext` struct is used to save and restore the context of a thread when an exception 7 | /// or interrupt occurs. It contains the values of all the general-purpose registers of the thread, 8 | /// the `sstatus` register, the `sepc` register, the address of the kernel stack, and the `satp` 9 | /// register value that refers to the kernel page table. 10 | #[repr(C)] 11 | pub struct TrapContext { 12 | user_register: [usize; 32], 13 | user_sstatus: Sstatus, 14 | user_sepc: usize, 15 | 16 | kernel_stack: usize, 17 | kernel_satp: usize, 18 | } 19 | 20 | impl TrapContext { 21 | pub fn user_status(&self) -> Sstatus { 22 | self.user_sstatus 23 | } 24 | 25 | pub fn set_user_status(&mut self, user_sstatus: Sstatus) { 26 | self.user_sstatus = user_sstatus; 27 | } 28 | 29 | pub fn user_sepc(&self) -> usize { 30 | self.user_sepc 31 | } 32 | 33 | pub fn set_user_sepc(&mut self, user_sepc: usize) { 34 | self.user_sepc = user_sepc; 35 | } 36 | 37 | pub fn user_register(&self, index: usize) -> usize { 38 | self.user_register[index] 39 | } 40 | 41 | pub fn set_user_register(&mut self, index: usize, value: usize) { 42 | self.user_register[index] = value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kernel/src/logging.rs: -------------------------------------------------------------------------------- 1 | //! The `logging` module implements the [log::Log] trait. 2 | 3 | use log::{self, Level, LevelFilter, Metadata, Record}; 4 | 5 | use crate::println; 6 | 7 | struct KernelLogger; 8 | 9 | impl log::Log for KernelLogger { 10 | fn enabled(&self, _: &Metadata) -> bool { 11 | true 12 | } 13 | 14 | fn log(&self, record: &Record) { 15 | if !self.enabled(record.metadata()) { 16 | return; 17 | } 18 | 19 | let color: u8 = match record.level() { 20 | Level::Error => 31, 21 | Level::Warn => 93, 22 | Level::Info => 34, 23 | Level::Debug => 32, 24 | Level::Trace => 90, 25 | }; 26 | println!( 27 | "\u{1B}[{}m[S][{}] {}\u{1B}[0m", 28 | color, 29 | record.level(), 30 | record.args() 31 | ); 32 | } 33 | 34 | fn flush(&self) {} 35 | } 36 | 37 | /// Initialize the kernel logger with the specified log level, 38 | /// or defaults to LevelFilter::Info 39 | pub fn init() { 40 | static LOGGER: KernelLogger = KernelLogger; 41 | log::set_logger(&LOGGER).unwrap(); 42 | log::set_max_level(match option_env!("LOG_LEVEL") { 43 | Some("error") => LevelFilter::Error, 44 | Some("warn") => LevelFilter::Warn, 45 | Some("info") => LevelFilter::Info, 46 | Some("debug") => LevelFilter::Debug, 47 | Some("trace") => LevelFilter::Trace, 48 | _ => LevelFilter::Info, 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /kernel-lib/src/logging.rs: -------------------------------------------------------------------------------- 1 | //! The `logging` module implements the [log::Log] trait. 2 | 3 | use log::{self, Level, LevelFilter, Metadata, Record}; 4 | 5 | use crate::println; 6 | 7 | struct KernelLogger; 8 | 9 | impl log::Log for KernelLogger { 10 | fn enabled(&self, _: &Metadata) -> bool { 11 | true 12 | } 13 | 14 | fn log(&self, record: &Record) { 15 | if !self.enabled(record.metadata()) { 16 | return; 17 | } 18 | 19 | let color: u8 = match record.level() { 20 | Level::Error => 31, 21 | Level::Warn => 93, 22 | Level::Info => 34, 23 | Level::Debug => 32, 24 | Level::Trace => 90, 25 | }; 26 | println!( 27 | "\u{1B}[{}m[U][{}] {}\u{1B}[0m", 28 | color, 29 | record.level(), 30 | record.args() 31 | ); 32 | } 33 | 34 | fn flush(&self) {} 35 | } 36 | 37 | /// Initialize the kernel logger with the specified log level, 38 | /// or defaults to LevelFilter::Info 39 | pub fn init() { 40 | static LOGGER: KernelLogger = KernelLogger; 41 | log::set_logger(&LOGGER).unwrap(); 42 | log::set_max_level(match option_env!("LOG_LEVEL") { 43 | Some("error") => LevelFilter::Error, 44 | Some("warn") => LevelFilter::Warn, 45 | Some("info") => LevelFilter::Info, 46 | Some("debug") => LevelFilter::Debug, 47 | Some("trace") => LevelFilter::Trace, 48 | _ => LevelFilter::Info, 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /kernel-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(linkage)] 4 | #![feature(panic_info_message)] 5 | #![feature(alloc_error_handler)] 6 | 7 | pub mod console; 8 | mod constant; 9 | mod heap_allocator; 10 | mod lang_items; 11 | mod logging; 12 | mod syscall; 13 | 14 | use syscall::{ 15 | sys_exec, 16 | sys_exit, 17 | sys_fork, 18 | sys_get_time, 19 | sys_read, 20 | sys_sched_yield, 21 | sys_waitpid, 22 | sys_write, 23 | }; 24 | 25 | #[no_mangle] 26 | #[link_section = ".text.init"] 27 | pub extern "C" fn _start() -> ! { 28 | logging::init(); 29 | heap_allocator::init(); 30 | 31 | exit(main()); 32 | panic!("failed to invoke `exit`") 33 | } 34 | 35 | #[linkage = "weak"] 36 | #[no_mangle] 37 | fn main() -> i32 { 38 | panic!("failed to find the `main` function"); 39 | } 40 | 41 | pub fn read(fd: usize, buffer: &mut [u8]) -> isize { 42 | sys_read(fd, buffer) 43 | } 44 | 45 | pub fn write(fd: usize, buffer: &[u8]) -> isize { 46 | sys_write(fd, buffer) 47 | } 48 | 49 | pub fn exit(exit_code: i32) -> isize { 50 | sys_exit(exit_code) 51 | } 52 | 53 | pub fn sched_yield() -> isize { 54 | sys_sched_yield() 55 | } 56 | 57 | pub fn get_time() -> isize { 58 | sys_get_time() 59 | } 60 | 61 | pub fn fork() -> isize { 62 | sys_fork() 63 | } 64 | 65 | pub fn exec(path: &str) -> isize { 66 | sys_exec(path) 67 | } 68 | 69 | pub fn wait(exit_code: &mut usize) -> isize { 70 | sys_waitpid(-1, exit_code as *mut usize) 71 | } 72 | 73 | pub fn waitpid(pid: usize, exit_code: &mut usize) -> isize { 74 | sys_waitpid(pid as isize, exit_code as *mut usize) 75 | } 76 | -------------------------------------------------------------------------------- /kernel/src/executor/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `executor` module provides an executor that schedules and runs both the kernel threads and 2 | //! the user threads. 3 | 4 | use alloc::boxed::Box; 5 | use core::future::Future; 6 | 7 | use async_task::{Runnable, Task}; 8 | use lazy_static::lazy_static; 9 | use riscv::register::{stvec, utvec::TrapMode}; 10 | 11 | use crate::{ 12 | constant::TRAMPOLINE, 13 | executor::scheduler::{Scheduler, TaskQueue}, 14 | sync::Mutex, 15 | }; 16 | 17 | mod context; 18 | mod future; 19 | mod scheduler; 20 | 21 | pub use context::TrapContext; 22 | pub use future::{spawn_thread, yield_now, ControlFlow}; 23 | 24 | /// Initializes the `stvec` to the address of the `_enter_kernel_space` function, which is located 25 | /// at the beginning of the [TRAMPOLINE] page. 26 | pub fn init() { 27 | unsafe { 28 | stvec::write(TRAMPOLINE, TrapMode::Direct); 29 | } 30 | } 31 | 32 | lazy_static! { 33 | static ref SCHEDULER: Mutex> = Mutex::new(Box::new(TaskQueue::new())); 34 | } 35 | 36 | fn spawn(future: F) -> (Runnable, Task) 37 | where 38 | F: Future + Send + 'static, 39 | F::Output: Send + 'static, 40 | { 41 | async_task::spawn(future, |runnable| { 42 | SCHEDULER.lock().schedule(runnable); 43 | }) 44 | } 45 | 46 | /// Runs an event loop that executes all the tasks in the `TASK_QUEUE` until there are no more task 47 | /// left. 48 | pub fn run_until_complete() { 49 | loop { 50 | let task = SCHEDULER.lock().task(); 51 | if let Some(task) = task { 52 | task.run(); 53 | } else { 54 | break; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /kernel-lib/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | const SYSCALL_READ: usize = 63; 4 | const SYSCALL_WRITE: usize = 64; 5 | const SYSCALL_EXIT: usize = 93; 6 | const SYSCALL_SCHED_YIELD: usize = 128; 7 | const SYSCALL_GET_TIME: usize = 169; 8 | const SYSCALL_FORK: usize = 220; 9 | const SYSCALL_EXEC: usize = 221; 10 | const SYSCALL_WAITPID: usize = 260; 11 | 12 | fn syscall(id: usize, args: [usize; 3]) -> isize { 13 | let mut result: isize; 14 | unsafe { 15 | asm!( 16 | "ecall", 17 | inlateout("a0") args[0] => result, 18 | in("a1") args[1], 19 | in("a2") args[2], 20 | in("a7") id, 21 | ); 22 | } 23 | result 24 | } 25 | 26 | pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { 27 | syscall( 28 | SYSCALL_READ, 29 | [fd, buffer.as_mut_ptr() as usize, buffer.len()], 30 | ) 31 | } 32 | 33 | pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { 34 | syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) 35 | } 36 | 37 | pub fn sys_exit(exit_code: i32) -> isize { 38 | syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]) 39 | } 40 | 41 | pub fn sys_sched_yield() -> isize { 42 | syscall(SYSCALL_SCHED_YIELD, [0, 0, 0]) 43 | } 44 | 45 | pub fn sys_get_time() -> isize { 46 | syscall(SYSCALL_GET_TIME, [0, 0, 0]) 47 | } 48 | 49 | pub fn sys_fork() -> isize { 50 | syscall(SYSCALL_FORK, [0, 0, 0]) 51 | } 52 | 53 | pub fn sys_waitpid(pid: isize, exit_code: *mut usize) -> isize { 54 | syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0]) 55 | } 56 | 57 | pub fn sys_exec(path: &str) -> isize { 58 | syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) 59 | } 60 | -------------------------------------------------------------------------------- /kernel-lib/src/bin/shell.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use alloc::string::String; 5 | 6 | use kernel_lib::{console::getchar, exec, exit, fork, waitpid}; 7 | 8 | extern crate alloc; 9 | #[macro_use] 10 | extern crate kernel_lib; 11 | 12 | const LF: u8 = 0x0au8; 13 | const CR: u8 = 0x0du8; 14 | const DL: u8 = 0x7fu8; 15 | const BS: u8 = 0x08u8; 16 | 17 | #[no_mangle] 18 | fn main() -> i32 { 19 | print!("$ "); 20 | 21 | let mut line = String::new(); 22 | loop { 23 | let char = getchar(); 24 | match char { 25 | LF | CR => { 26 | println!(""); 27 | if line.is_empty() { 28 | continue; 29 | } else if line == "exit" { 30 | exit(0); 31 | } else { 32 | let pid = fork() as usize; 33 | if pid == 0 { 34 | line.push('\0'); 35 | if exec(line.as_str()) == -1 { 36 | return -4; 37 | } 38 | } else { 39 | let mut exit_code = 0; 40 | waitpid(pid, &mut exit_code); 41 | } 42 | line.clear(); 43 | print!("$ "); 44 | } 45 | } 46 | BS | DL => { 47 | if line.is_empty() { 48 | continue; 49 | } 50 | print!("{0} {0}", BS as char); 51 | line.pop(); 52 | } 53 | _ => { 54 | print!("{}", char as char); 55 | line.push(char as char); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kernel/src/sbi.rs: -------------------------------------------------------------------------------- 1 | //! The `sbi` module contains functions that invokes the RISC-V interface. 2 | //! SBI is an interface between the Supervisor Execution Environment and the supervisor. 3 | //! It allows the supervisor to execute some privileged operations with the `ecall` instruction. 4 | //! For more details, please refer to the 5 | //! [RISC-V SBI Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc). 6 | 7 | use core::arch::asm; 8 | 9 | use log::info; 10 | 11 | const CONSOLE_PUTCHAR_EXTENSION: usize = 0x01; 12 | const CONSOLE_GETCHAR_EXTENSION: usize = 0x02; 13 | const SYSTEM_RESET_EXTENSION: usize = 0x53525354; 14 | const TIMER_EXTENSION: usize = 0x54494D45; 15 | 16 | #[inline] 17 | fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> (isize, isize) { 18 | let (error, value); 19 | unsafe { 20 | asm!( 21 | "ecall", 22 | inlateout("a0") arg0 => error, 23 | inlateout("a1") arg1 => value, 24 | in("a6") function, 25 | in("a7") extension, 26 | ) 27 | } 28 | (error, value) 29 | } 30 | 31 | #[inline] 32 | pub fn set_timer(stime_value: usize) { 33 | sbi_call(TIMER_EXTENSION, 0, stime_value, 0); 34 | } 35 | 36 | /// Write data present in `char` to debug console. 37 | #[inline] 38 | pub fn console_putchar(char: usize) { 39 | sbi_call(CONSOLE_PUTCHAR_EXTENSION, 0, char, 0); 40 | } 41 | 42 | #[inline] 43 | pub fn console_getchar() -> usize { 44 | let (value, _) = sbi_call(CONSOLE_GETCHAR_EXTENSION, 0, 0, 0); 45 | value.max(0) as usize 46 | } 47 | 48 | /// Put all the harts to shutdown state. 49 | #[inline] 50 | pub fn shutdown() -> ! { 51 | info!("shutdown"); 52 | sbi_call(SYSTEM_RESET_EXTENSION, 0, 0, 0); 53 | panic!("failed to shutdown"); 54 | } 55 | -------------------------------------------------------------------------------- /kernel/src/syscall/fs.rs: -------------------------------------------------------------------------------- 1 | //! The `fs` module provides system calls to interact with the file system. 2 | 3 | use core::str; 4 | 5 | use log::error; 6 | 7 | use crate::{ 8 | executor::{yield_now, ControlFlow}, 9 | mem::UserPtr, 10 | print, 11 | sbi, 12 | syscall::SystemCall, 13 | }; 14 | 15 | const STDIN: usize = 0; 16 | const STDOUT: usize = 1; 17 | 18 | impl SystemCall<'_> { 19 | /// Reads the content from a file descriptor and writes them to a buffer. 20 | pub async fn sys_read( 21 | &self, 22 | fd: usize, 23 | mut buffer: UserPtr, 24 | _length: usize, 25 | ) -> (isize, ControlFlow) { 26 | match fd { 27 | STDIN => { 28 | let mut char; 29 | loop { 30 | char = sbi::console_getchar(); 31 | if char == 0 { 32 | yield_now().await; 33 | } else { 34 | break; 35 | } 36 | } 37 | *buffer = char as u8; 38 | (1, ControlFlow::Continue) 39 | } 40 | _ => { 41 | error!("the file descriptor {} is not supported in 'sys_write'", fd); 42 | (-1, ControlFlow::Continue) 43 | } 44 | } 45 | } 46 | 47 | /// Writes the contents of a buffer to a file descriptor. 48 | pub fn sys_write(&self, fd: usize, buffer: UserPtr, length: usize) -> (isize, ControlFlow) { 49 | match fd { 50 | STDOUT => { 51 | for buffer in buffer.as_buffer(length) { 52 | print!("{}", str::from_utf8(buffer).unwrap()); 53 | } 54 | (length as isize, ControlFlow::Continue) 55 | } 56 | _ => { 57 | error!("the file descriptor {} is not supported in 'sys_write'", fd); 58 | (-1, ControlFlow::Continue) 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kernel/src/asm/linkage.asm: -------------------------------------------------------------------------------- 1 | .align 3 2 | .section .data 3 | .global _bin_count 4 | .global _bin_address 5 | .global _bin_name 6 | 7 | _bin_count: 8 | .quad 7 9 | 10 | _bin_address: 11 | .quad bin_0_start 12 | .quad bin_0_end 13 | .quad bin_1_start 14 | .quad bin_1_end 15 | .quad bin_2_start 16 | .quad bin_2_end 17 | .quad bin_3_start 18 | .quad bin_3_end 19 | .quad bin_4_start 20 | .quad bin_4_end 21 | .quad bin_5_start 22 | .quad bin_5_end 23 | .quad bin_6_start 24 | .quad bin_6_end 25 | 26 | _bin_name: 27 | .string "fork" 28 | .string "hello_world" 29 | .string "init" 30 | .string "page_fault" 31 | .string "privileged_instruction" 32 | .string "shell" 33 | .string "sleep" 34 | 35 | .section .data 36 | .global bin_0_start 37 | .global bin_0_end 38 | .align 3 39 | bin_0_start: 40 | .incbin "target/riscv64gc-unknown-none-elf/debug/fork" 41 | bin_0_end: 42 | 43 | .section .data 44 | .global bin_1_start 45 | .global bin_1_end 46 | .align 3 47 | bin_1_start: 48 | .incbin "target/riscv64gc-unknown-none-elf/debug/hello_world" 49 | bin_1_end: 50 | 51 | .section .data 52 | .global bin_2_start 53 | .global bin_2_end 54 | .align 3 55 | bin_2_start: 56 | .incbin "target/riscv64gc-unknown-none-elf/debug/init" 57 | bin_2_end: 58 | 59 | .section .data 60 | .global bin_3_start 61 | .global bin_3_end 62 | .align 3 63 | bin_3_start: 64 | .incbin "target/riscv64gc-unknown-none-elf/debug/page_fault" 65 | bin_3_end: 66 | 67 | .section .data 68 | .global bin_4_start 69 | .global bin_4_end 70 | .align 3 71 | bin_4_start: 72 | .incbin "target/riscv64gc-unknown-none-elf/debug/privileged_instruction" 73 | bin_4_end: 74 | 75 | .section .data 76 | .global bin_5_start 77 | .global bin_5_end 78 | .align 3 79 | bin_5_start: 80 | .incbin "target/riscv64gc-unknown-none-elf/debug/shell" 81 | bin_5_end: 82 | 83 | .section .data 84 | .global bin_6_start 85 | .global bin_6_end 86 | .align 3 87 | bin_6_start: 88 | .incbin "target/riscv64gc-unknown-none-elf/debug/sleep" 89 | bin_6_end: 90 | -------------------------------------------------------------------------------- /kernel/src/file/load.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::{slice, str}; 3 | 4 | use lazy_static::lazy_static; 5 | use log::info; 6 | 7 | extern "C" { 8 | fn _bin_count(); 9 | fn _bin_address(); 10 | fn _bin_name(); 11 | } 12 | 13 | lazy_static! { 14 | static ref BIN_NAME_LIST: Vec<&'static str> = { 15 | let mut bin_name_list = Vec::new(); 16 | let mut bin_name_pointer = _bin_name as usize as *const u8; 17 | let bin_count = get_bin_count(); 18 | unsafe { 19 | for _ in 0..bin_count { 20 | let mut bin_name_length = 0; 21 | while bin_name_pointer.add(bin_name_length).read_volatile() != b'\0' { 22 | bin_name_length += 1; 23 | } 24 | let bin_name_slice = slice::from_raw_parts(bin_name_pointer, bin_name_length); 25 | bin_name_list.push(str::from_utf8(bin_name_slice).unwrap()); 26 | bin_name_pointer = bin_name_pointer.add(bin_name_length).add(1); 27 | } 28 | } 29 | bin_name_list 30 | }; 31 | } 32 | 33 | pub fn get_bin_count() -> usize { 34 | unsafe { (_bin_count as *const usize).read_volatile() } 35 | } 36 | 37 | pub fn get_bin(name: &str) -> Option<&'static [u8]> { 38 | let bin_count = get_bin_count(); 39 | (0..bin_count) 40 | .find(|&bin_index| BIN_NAME_LIST[bin_index] == name) 41 | .map(get_bin_data) 42 | } 43 | 44 | pub fn get_bin_data(bin_index: usize) -> &'static [u8] { 45 | let bin_address_pointer = _bin_address as *const usize; 46 | 47 | unsafe { 48 | let bin_address_start = bin_address_pointer.add(bin_index * 2).read_volatile(); 49 | let bin_address_end = bin_address_pointer.add(bin_index * 2 + 1).read_volatile(); 50 | slice::from_raw_parts( 51 | bin_address_start as *const u8, 52 | bin_address_end - bin_address_start, 53 | ) 54 | } 55 | } 56 | 57 | pub fn print_bin_name() { 58 | let bin_count = get_bin_count(); 59 | let mut bin_name_list = Vec::new(); 60 | for bin_index in 0..bin_count { 61 | let bin_name = BIN_NAME_LIST[bin_index]; 62 | if bin_name == "init" { 63 | continue; 64 | } 65 | bin_name_list.push(bin_name); 66 | } 67 | info!("built-in binaries: {}", bin_name_list.join(", ")); 68 | } 69 | -------------------------------------------------------------------------------- /kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | //! `rust-kernel-riscv` is an open-source project that implements an operating system kernel on RISC-V architecture with Rust programming language. The project draws inspiration from several open-source implementations, such as [xv6-riscv](https://github.com/mit-pdos/xv6-riscv) and [zCore](https://github.com/rcore-os/zCore). 2 | //! 3 | //! - The kernel leverages Rust's asynchronous programming model to schedule threads in both the 4 | //! kernel and user space, which makes context switching more efficient and eliminates the need of 5 | //! allocating a separate kernel stack for each user process. 6 | //! 7 | //! - The kernel implements the kernel page-table isolation, which prevents the kernel space and the 8 | //! user space to share a same page table and mitigates potential Meltdown attacks. 9 | 10 | #![no_std] 11 | #![no_main] 12 | #![feature(alloc_error_handler)] 13 | #![feature(panic_info_message)] 14 | #![feature(naked_functions)] 15 | 16 | extern crate alloc; 17 | 18 | #[macro_use] 19 | mod console; 20 | mod constant; 21 | mod executor; 22 | mod file; 23 | mod lang_items; 24 | mod logging; 25 | mod mem; 26 | mod sbi; 27 | mod sync; 28 | mod syscall; 29 | mod task; 30 | mod timer; 31 | 32 | use core::arch::global_asm; 33 | 34 | use log::info; 35 | 36 | global_asm!(include_str!("asm/boot.asm")); 37 | global_asm!(include_str!("asm/linkage.asm")); 38 | 39 | /// Initializes the thread executor and spawns the `INIT_PROCESS`. 40 | #[no_mangle] 41 | pub fn rust_main() { 42 | clear_bss(); 43 | logging::init(); 44 | 45 | mem::init(); 46 | 47 | timer::enable_timer_interrupt(); 48 | timer::set_trigger(); 49 | 50 | info!("rust-kernel has booted"); 51 | file::print_bin_name(); 52 | 53 | task::init(); 54 | executor::init(); 55 | executor::run_until_complete(); 56 | 57 | sbi::shutdown(); 58 | } 59 | 60 | /// Initializes the `.bss` section with zeros. 61 | fn clear_bss() { 62 | extern "C" { 63 | /// The `bss_start` is a symbol declared in the `src/linker.ld`, 64 | /// which represent the start address of the `.bss` section. 65 | fn bss_start(); 66 | /// The `bss_end` is a symbol declared in the `src/linker.ld`, 67 | /// which represent the end address of the `.bss` section. 68 | fn bss_end(); 69 | } 70 | 71 | (bss_start as usize..bss_end as usize) 72 | .for_each(|address| unsafe { (address as *mut u8).write_volatile(0) }) 73 | } 74 | -------------------------------------------------------------------------------- /kernel/src/sync/mutex.rs: -------------------------------------------------------------------------------- 1 | //! The `mutex` module provides a mutual exclusion primitive useful for protecting shared data. 2 | 3 | use core::{ 4 | cell::UnsafeCell, 5 | hint, 6 | marker::PhantomData, 7 | ops::{Deref, DerefMut}, 8 | sync::atomic::{AtomicBool, Ordering}, 9 | }; 10 | 11 | /// The `Mutex` struct is a mutual exclusion primitive useful for protecting shared data, which 12 | /// implements the [Send] and [Sync] traits. 13 | pub struct Mutex { 14 | lock: AtomicBool, 15 | cell: UnsafeCell, 16 | phantom: PhantomData, 17 | } 18 | 19 | impl Mutex { 20 | /// Creates a new `Mutex` with the given initial value. 21 | pub fn new(value: T) -> Self { 22 | Self { 23 | lock: AtomicBool::new(false), 24 | cell: UnsafeCell::new(value), 25 | phantom: PhantomData, 26 | } 27 | } 28 | 29 | /// Acquires a lock on the `Mutex` and returns a [MutexGuard] that provides exclusive access to 30 | /// the shared resource. 31 | pub fn lock(&self) -> MutexGuard { 32 | while self 33 | .lock 34 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) 35 | .is_err() 36 | { 37 | while self.lock.load(Ordering::Relaxed) { 38 | hint::spin_loop(); 39 | } 40 | } 41 | 42 | MutexGuard::new(self) 43 | } 44 | 45 | /// Releases the lock on the `Mutex`. 46 | pub fn unlock(&self) { 47 | self.lock.store(false, Ordering::Release); 48 | } 49 | } 50 | 51 | unsafe impl Sync for Mutex {} 52 | 53 | unsafe impl Send for Mutex {} 54 | 55 | /// The `MutexGuard` struct is an RAII guard to allow scoped unlock of the lock. When the guard goes 56 | /// out of scope, the [Mutex] it guards will be unlocked. 57 | pub struct MutexGuard<'a, T> { 58 | mutex: &'a Mutex, 59 | } 60 | 61 | impl<'a, T> MutexGuard<'a, T> { 62 | /// Creates a new `MutexGuard` for the given [Mutex]. 63 | pub fn new(mutex: &'a Mutex) -> Self { 64 | Self { mutex } 65 | } 66 | } 67 | 68 | impl<'a, T> Deref for MutexGuard<'a, T> { 69 | type Target = T; 70 | 71 | fn deref(&self) -> &T { 72 | unsafe { &*self.mutex.cell.get() } 73 | } 74 | } 75 | 76 | impl<'a, T> DerefMut for MutexGuard<'a, T> { 77 | fn deref_mut(&mut self) -> &mut T { 78 | unsafe { &mut *self.mutex.cell.get() } 79 | } 80 | } 81 | 82 | impl<'a, T> Drop for MutexGuard<'a, T> { 83 | fn drop(&mut self) { 84 | self.mutex.unlock(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /kernel/src/mem/address/physical_address.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, Sub}; 2 | 3 | use crate::{ 4 | constant::{PAGE_SIZE, PAGE_SIZE_BIT}, 5 | mem::FrameNumber, 6 | }; 7 | 8 | const PHYSICAL_ADDRESS_SIZE: usize = 56; 9 | 10 | /// The `PhysicalAddress` struct represents a 56-bit physical address defined in the Sv39 11 | /// page table format. 12 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 13 | pub struct PhysicalAddress { 14 | bits: usize, 15 | } 16 | 17 | impl PhysicalAddress { 18 | /// Returns the [FrameNumber] that represents the frame that contains the physical address. 19 | pub fn floor(&self) -> FrameNumber { 20 | FrameNumber { 21 | bits: self.bits / PAGE_SIZE, 22 | } 23 | } 24 | 25 | /// Returns the [FrameNumber] that represents the frame that contains the physical address, 26 | /// rounding up to the next frame if the physical address is not aligned to a frame. 27 | pub fn ceil(&self) -> FrameNumber { 28 | FrameNumber { 29 | bits: (self.bits + PAGE_SIZE - 1) / PAGE_SIZE, 30 | } 31 | } 32 | 33 | /// Returns the byte offset of the physical address within its containing frame. 34 | pub fn page_offset(&self) -> usize { 35 | self.bits & (PAGE_SIZE - 1) 36 | } 37 | 38 | /// Returns `true` if the physical address is aligned to a frame. 39 | pub fn is_aligned(&self) -> bool { 40 | self.page_offset() == 0 41 | } 42 | 43 | /// Returns a raw pointer to the physical address. 44 | pub fn as_ptr(&self) -> *const u8 { 45 | self.bits as *const u8 46 | } 47 | 48 | /// Returns a mutable raw pointer to the physical address. 49 | pub fn as_ptr_mut(&self) -> *mut u8 { 50 | self.bits as *mut u8 51 | } 52 | 53 | pub fn as_ref(&self) -> &'static T { 54 | unsafe { (self.bits as *const T).as_ref().unwrap() } 55 | } 56 | 57 | pub fn as_mut(&self) -> &'static mut T { 58 | unsafe { (self.bits as *mut T).as_mut().unwrap() } 59 | } 60 | } 61 | 62 | impl Add for PhysicalAddress { 63 | type Output = Self; 64 | 65 | fn add(self, rhs: usize) -> Self { 66 | Self::from(self.bits + rhs) 67 | } 68 | } 69 | 70 | impl Sub for PhysicalAddress { 71 | type Output = Self; 72 | 73 | fn sub(self, rhs: usize) -> Self { 74 | Self::from(self.bits - rhs) 75 | } 76 | } 77 | 78 | impl From for PhysicalAddress { 79 | fn from(value: usize) -> Self { 80 | Self { 81 | bits: value & ((1 << PHYSICAL_ADDRESS_SIZE) - 1), 82 | } 83 | } 84 | } 85 | 86 | impl From for usize { 87 | fn from(value: PhysicalAddress) -> Self { 88 | value.bits 89 | } 90 | } 91 | 92 | impl From for PhysicalAddress { 93 | fn from(value: FrameNumber) -> Self { 94 | Self { 95 | bits: usize::from(value) << PAGE_SIZE_BIT, 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /kernel/src/syscall/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `syscall` module provides system calls for interacting with the operating system. 2 | 3 | use crate::{executor::ControlFlow, mem::UserPtr, task::Thread}; 4 | 5 | mod fs; 6 | mod process; 7 | mod timer; 8 | 9 | const SYSCALL_READ: usize = 63; 10 | const SYSCALL_WRITE: usize = 64; 11 | const SYSCALL_EXIT: usize = 93; 12 | const SYSCALL_SCHED_YIELD: usize = 128; 13 | const SYSCALL_GET_TIME: usize = 169; 14 | const SYSCALL_FORK: usize = 220; 15 | const SYSCALL_EXEC: usize = 221; 16 | const SYSCALL_WAITPID: usize = 260; 17 | 18 | /// The `SystemCall` struct provides an interface for invoking system calls on a given thread. 19 | pub struct SystemCall<'a> { 20 | thread: &'a Thread, 21 | } 22 | 23 | impl<'a> SystemCall<'a> { 24 | /// Constructs a new `SystemCall` instance with the given thread. 25 | pub fn new(thread: &'a Thread) -> Self { 26 | Self { thread } 27 | } 28 | 29 | /// Invokes a system call with the given arguments. 30 | pub async fn execute(&mut self) -> ControlFlow { 31 | let trap_context = self.thread.state().lock().kernel_trap_context_mut(); 32 | 33 | // Increments the program counter to skip the `ecall` instruction 34 | trap_context.set_user_sepc(trap_context.user_sepc() + 4); 35 | 36 | let system_call_id = trap_context.user_register(17); 37 | let argument_0 = trap_context.user_register(10); 38 | let argument_1 = trap_context.user_register(11); 39 | let argument_2 = trap_context.user_register(12); 40 | 41 | let (exit_code, control_flow) = match system_call_id { 42 | SYSCALL_READ => { 43 | self.sys_read( 44 | argument_0, 45 | UserPtr::new(self.thread.satp(), argument_1), 46 | argument_2, 47 | ) 48 | .await 49 | } 50 | SYSCALL_WRITE => self.sys_write( 51 | argument_0, 52 | UserPtr::new(self.thread.satp(), argument_1), 53 | argument_2, 54 | ), 55 | SYSCALL_EXIT => self.sys_exit(argument_0), 56 | SYSCALL_SCHED_YIELD => self.sys_sched_yield(), 57 | SYSCALL_GET_TIME => self.sys_get_time(), 58 | SYSCALL_FORK => self.sys_fork(), 59 | SYSCALL_EXEC => self.sys_exec(UserPtr::new(self.thread.satp(), argument_0)), 60 | SYSCALL_WAITPID => { 61 | self.sys_waitpid( 62 | argument_0 as isize, 63 | UserPtr::new(self.thread.satp(), argument_1), 64 | ) 65 | .await 66 | } 67 | _ => panic!("unsupported syscall {}", system_call_id), 68 | }; 69 | 70 | if control_flow == ControlFlow::Continue || control_flow == ControlFlow::Yield { 71 | trap_context.set_user_register(10, exit_code as usize); 72 | } 73 | control_flow 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /kernel/src/mem/address/virtual_address.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Sub}; 2 | 3 | use crate::{ 4 | constant::{PAGE_SIZE, PAGE_SIZE_BIT}, 5 | mem::PageNumber, 6 | }; 7 | 8 | const VIRTUAL_ADDRESS_SIZE: usize = 39; 9 | 10 | /// The `VirtualAddress` struct represents a 39-bit virtual address defined in the Sv39 11 | /// page table format. 12 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 13 | pub struct VirtualAddress { 14 | bits: usize, 15 | } 16 | 17 | impl VirtualAddress { 18 | /// Returns the [PageNumber] that represents the page that contains the virtual address. 19 | pub fn floor(&self) -> PageNumber { 20 | PageNumber::from(self.bits / PAGE_SIZE) 21 | } 22 | 23 | /// Returns the [PageNumber] that represents the page that contains the virtual address, 24 | /// rounding up to the next frame if the physical address is not aligned to a frame. 25 | pub fn ceil(&self) -> PageNumber { 26 | PageNumber::from((self.bits - 1 + PAGE_SIZE) / PAGE_SIZE) 27 | } 28 | 29 | /// Returns the byte offset of the virtual address within its containing page. 30 | pub fn page_offset(&self) -> usize { 31 | self.bits & (PAGE_SIZE - 1) 32 | } 33 | 34 | /// Returns `true` if the virtual address is aligned to a page. 35 | pub fn is_aligned(&self) -> bool { 36 | self.page_offset() == 0 37 | } 38 | 39 | /// Returns a raw pointer to the virtual address. 40 | pub fn as_ptr(&self) -> *const u8 { 41 | self.bits as *const u8 42 | } 43 | 44 | /// Returns a mutable raw pointer to the virtual address. 45 | pub fn as_ptr_mut(&self) -> *mut u8 { 46 | self.bits as *mut u8 47 | } 48 | } 49 | 50 | impl Add for VirtualAddress { 51 | type Output = Self; 52 | 53 | fn add(self, rhs: usize) -> Self { 54 | Self::from(self.bits + rhs) 55 | } 56 | } 57 | 58 | impl AddAssign for VirtualAddress { 59 | fn add_assign(&mut self, rhs: usize) { 60 | self.bits += rhs; 61 | } 62 | } 63 | 64 | impl Sub for VirtualAddress { 65 | type Output = Self; 66 | 67 | fn sub(self, rhs: usize) -> Self { 68 | Self::from(self.bits - rhs) 69 | } 70 | } 71 | 72 | impl From for VirtualAddress { 73 | fn from(value: usize) -> Self { 74 | assert!( 75 | (value >> VIRTUAL_ADDRESS_SIZE) == 0 76 | || (value >> VIRTUAL_ADDRESS_SIZE) == (1 << 25) - 1 77 | ); 78 | Self { bits: value } 79 | } 80 | } 81 | 82 | impl From for usize { 83 | fn from(value: VirtualAddress) -> Self { 84 | value.bits 85 | } 86 | } 87 | 88 | impl From for VirtualAddress { 89 | fn from(value: PageNumber) -> Self { 90 | let mut bits = usize::from(value) << PAGE_SIZE_BIT; 91 | 92 | if (bits >> (VIRTUAL_ADDRESS_SIZE - 1)) == 1 { 93 | bits |= !((1 << VIRTUAL_ADDRESS_SIZE) - 1); 94 | } 95 | Self { bits } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /kernel/src/sync/event_bus.rs: -------------------------------------------------------------------------------- 1 | //! The `event_bus` module provides an [EventBus] that supports publish-subscribe-style 2 | //! communication between different tasks. 3 | 4 | use alloc::{boxed::Box, sync::Arc, vec::Vec}; 5 | use core::{ 6 | future::Future, 7 | pin::Pin, 8 | task::{Context, Poll}, 9 | }; 10 | 11 | use bitflags::bitflags; 12 | 13 | use crate::sync::Mutex; 14 | 15 | bitflags! { 16 | #[derive(Default, Copy, Clone)] 17 | /// The `Event` struct represents events that can be subscribed to on [EventBus]. 18 | pub struct Event: u32 { 19 | /// Indicates that a child process has quit. 20 | const CHILD_PROCESS_QUIT = 1 << 0; 21 | } 22 | } 23 | 24 | type EventCallback = Box bool + Send>; 25 | 26 | /// The `EventBus` structsupports publish-subscribe-style communication between different tasks. 27 | #[derive(Default)] 28 | pub struct EventBus { 29 | event: Event, 30 | callback_list: Vec, 31 | } 32 | 33 | impl EventBus { 34 | /// Creates a new event bus. 35 | pub fn new() -> Arc> { 36 | Arc::new(Mutex::new(Self::default())) 37 | } 38 | 39 | /// Publishes an event on the event bus. 40 | pub fn push(&mut self, event: Event) { 41 | self.event.set(event, true); 42 | for callback in &self.callback_list { 43 | callback(event); 44 | } 45 | } 46 | 47 | /// Clears an event from the event bus. 48 | pub fn clear(&mut self, event: Event) { 49 | self.event.remove(event); 50 | } 51 | 52 | /// Subscribes to events on the event bus and executes the given callback function when an event 53 | /// is published. 54 | pub fn subscribe(&mut self, callback: EventCallback) { 55 | self.callback_list.push(callback); 56 | } 57 | } 58 | 59 | /// The `EventBusFuture` struct is a future that completes when a specified event is published on 60 | /// an [EventBus]. 61 | struct EventBusFuture { 62 | event_bus: Arc>, 63 | subscribed_event: Event, 64 | } 65 | 66 | impl Future for EventBusFuture { 67 | type Output = Event; 68 | 69 | fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll { 70 | let mut event_bus = self.event_bus.lock(); 71 | if event_bus.event.contains(self.subscribed_event) { 72 | return Poll::Ready(event_bus.event); 73 | } 74 | 75 | let subscribed_event = self.subscribed_event; 76 | let waker = context.waker().clone(); 77 | event_bus.subscribe(Box::new(move |event| { 78 | if event.contains(subscribed_event) { 79 | waker.wake_by_ref(); 80 | true 81 | } else { 82 | false 83 | } 84 | })); 85 | Poll::Pending 86 | } 87 | } 88 | 89 | /// Returns a future that completes when a specified event is published on an [EventBus]. 90 | pub fn wait_for_event( 91 | event_bus: Arc>, 92 | subscribed_event: Event, 93 | ) -> impl Future { 94 | EventBusFuture { 95 | event_bus, 96 | subscribed_event, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /kernel/src/syscall/process.rs: -------------------------------------------------------------------------------- 1 | //! The `process` module provides system calls to interact with processes. 2 | 3 | use alloc::vec::Vec; 4 | 5 | use crate::{ 6 | executor::ControlFlow, 7 | mem::UserPtr, 8 | sync::{wait_for_event, Event}, 9 | syscall::SystemCall, 10 | task::Status, 11 | }; 12 | 13 | impl SystemCall<'_> { 14 | /// Terminates the current thread with the given exit code. 15 | pub fn sys_exit(&self, exit_code: usize) -> (isize, ControlFlow) { 16 | (0, ControlFlow::Exit(exit_code)) 17 | } 18 | 19 | /// Yields the CPU to another thread. 20 | pub fn sys_sched_yield(&self) -> (isize, ControlFlow) { 21 | (0, ControlFlow::Yield) 22 | } 23 | 24 | /// Forks the current process and create a new child process. 25 | pub fn sys_fork(&self) -> (isize, ControlFlow) { 26 | let process = self.thread.process().fork(); 27 | (process.pid() as isize, ControlFlow::Continue) 28 | } 29 | 30 | /// Waits for a child process with the given process to terminate, and return the PID and exit. 31 | pub async fn sys_waitpid( 32 | &self, 33 | pid: isize, 34 | mut wait_status: UserPtr, 35 | ) -> (isize, ControlFlow) { 36 | loop { 37 | let process = self.thread.process(); 38 | let mut process_state = process.state().lock(); 39 | let child_list = process_state.child_list_mut(); 40 | 41 | if let Some((pid, exit_code)) = match pid { 42 | -1 | 0 => child_list.iter().find_map(|child_process| { 43 | let child_process_state = child_process.state().lock(); 44 | if child_process_state.status() == Status::Zombie { 45 | Some((child_process.pid(), child_process_state.exit_code())) 46 | } else { 47 | None 48 | } 49 | }), 50 | pid => child_list.iter().find_map(|child_process| { 51 | let child_process_state = child_process.state().lock(); 52 | if child_process.pid() == pid as usize 53 | && child_process_state.status() == Status::Zombie 54 | { 55 | Some((child_process.pid(), child_process_state.exit_code())) 56 | } else { 57 | None 58 | } 59 | }), 60 | } { 61 | child_list.retain(|child_process| child_process.pid() != pid); 62 | *wait_status = exit_code; 63 | return (pid as isize, ControlFlow::Continue); 64 | } else { 65 | let event_bus = process.event_bus(); 66 | drop(process_state); 67 | wait_for_event(event_bus.clone(), Event::CHILD_PROCESS_QUIT).await; 68 | event_bus.lock().clear(Event::CHILD_PROCESS_QUIT); 69 | } 70 | } 71 | } 72 | 73 | /// Replaces the current process with a new process loaded from the executable file with a given 74 | /// name. 75 | pub fn sys_exec(&self, path: UserPtr) -> (isize, ControlFlow) { 76 | self.thread.process().exec(&path.as_string(), Vec::new()); 77 | (0, ControlFlow::Continue) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /kernel/src/mem/address/page_number.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Sub; 2 | 3 | use crate::{executor::TrapContext, mem::VirtualAddress}; 4 | 5 | const PAGE_NUMBER_SIZE: usize = 27; 6 | 7 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 8 | pub struct PageNumber { 9 | bits: usize, 10 | } 11 | 12 | impl PageNumber { 13 | pub fn index(&self) -> [usize; 3] { 14 | let mask = (1 << 9) - 1; 15 | [ 16 | (self.bits >> 18) & mask, 17 | (self.bits >> 9) & mask, 18 | self.bits & mask, 19 | ] 20 | } 21 | 22 | pub fn offset(&mut self, rhs: usize) -> Self { 23 | PageNumber { 24 | bits: (self.bits + rhs) & ((1 << PAGE_NUMBER_SIZE) - 1), 25 | } 26 | } 27 | 28 | /// Interprets the page as a [TrapContext] and return a mutable reference to it. 29 | pub fn as_trap_context_mut(&self) -> &'static mut TrapContext { 30 | let virtual_address = VirtualAddress::from(*self); 31 | unsafe { 32 | (virtual_address.as_ptr_mut() as *mut TrapContext) 33 | .as_mut() 34 | .unwrap() 35 | } 36 | } 37 | } 38 | 39 | impl From for PageNumber { 40 | fn from(value: usize) -> Self { 41 | Self { 42 | bits: value & ((1 << PAGE_NUMBER_SIZE) - 1), 43 | } 44 | } 45 | } 46 | 47 | impl From for usize { 48 | fn from(value: PageNumber) -> Self { 49 | value.bits 50 | } 51 | } 52 | 53 | impl Sub for PageNumber { 54 | type Output = usize; 55 | 56 | fn sub(self, rhs: PageNumber) -> usize { 57 | self.bits - rhs.bits 58 | } 59 | } 60 | 61 | impl From for PageNumber { 62 | fn from(value: VirtualAddress) -> Self { 63 | value.floor() 64 | } 65 | } 66 | 67 | /// The `PageRange` struct represents a range of page numbers, 68 | /// with `start` and `end` field holding [PageNumber] values. 69 | #[derive(Clone)] 70 | pub struct PageRange { 71 | start: PageNumber, 72 | end: PageNumber, 73 | } 74 | 75 | impl PageRange { 76 | pub fn new(start: PageNumber, end: PageNumber) -> Self { 77 | Self { start, end } 78 | } 79 | 80 | pub fn iter(&self) -> PageRangeIterator { 81 | PageRangeIterator::new(self.start, self.end) 82 | } 83 | 84 | pub fn len(&self) -> usize { 85 | self.end - self.start 86 | } 87 | 88 | pub fn start(&self) -> PageNumber { 89 | self.start 90 | } 91 | 92 | pub fn end(&self) -> PageNumber { 93 | self.end 94 | } 95 | } 96 | 97 | impl IntoIterator for PageRange { 98 | type Item = PageNumber; 99 | type IntoIter = PageRangeIterator; 100 | 101 | fn into_iter(self) -> Self::IntoIter { 102 | PageRangeIterator::new(self.start, self.end) 103 | } 104 | } 105 | 106 | pub struct PageRangeIterator { 107 | state: PageNumber, 108 | end: PageNumber, 109 | } 110 | 111 | impl PageRangeIterator { 112 | pub fn new(start: PageNumber, end: PageNumber) -> Self { 113 | Self { state: start, end } 114 | } 115 | } 116 | 117 | impl Iterator for PageRangeIterator { 118 | type Item = PageNumber; 119 | 120 | fn next(&mut self) -> Option { 121 | if self.state == self.end { 122 | None 123 | } else { 124 | let result = self.state; 125 | self.state = self.state.offset(1); 126 | Some(result) 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /kernel/src/mem/user_ptr.rs: -------------------------------------------------------------------------------- 1 | use alloc::{string::String, vec::Vec}; 2 | use core::{ 3 | marker::PhantomData, 4 | ops::{Deref, DerefMut}, 5 | }; 6 | 7 | use crate::{ 8 | constant::PAGE_SIZE, 9 | mem::{address::PageRange, page_table::PageTable, PageNumber, VirtualAddress}, 10 | }; 11 | 12 | #[repr(C)] 13 | #[derive(Clone, Copy)] 14 | pub struct UserPtr { 15 | satp: usize, 16 | ptr: *mut T, 17 | phantom: PhantomData, 18 | } 19 | 20 | unsafe impl Send for UserPtr {} 21 | unsafe impl Sync for UserPtr {} 22 | 23 | impl UserPtr { 24 | pub fn new(satp: usize, ptr: usize) -> Self { 25 | Self { 26 | satp, 27 | ptr: ptr as *mut T, 28 | phantom: PhantomData, 29 | } 30 | } 31 | 32 | pub fn as_string(&self) -> String { 33 | let page_table = PageTable::from_satp(self.satp); 34 | let mut virtual_address = VirtualAddress::from(self.ptr as usize); 35 | let mut string = String::new(); 36 | loop { 37 | let char_pointer = page_table.translate(virtual_address).unwrap().as_ptr() as *const u8; 38 | 39 | let char = unsafe { *char_pointer as char }; 40 | 41 | if char == '\0' { 42 | break; 43 | } 44 | string.push(char); 45 | virtual_address += 1; 46 | } 47 | string 48 | } 49 | 50 | pub fn as_buffer(&self, length: usize) -> Vec<&'static [u8]> { 51 | let page_table = PageTable::from_satp(self.satp); 52 | let mut translated_buffer = Vec::new(); 53 | 54 | let buffer_address_start = VirtualAddress::from(self.ptr as usize); 55 | let buffer_address_end = buffer_address_start + length; 56 | 57 | let page_range = PageRange::new( 58 | PageNumber::from(buffer_address_start), 59 | PageNumber::from(buffer_address_end).offset(1), 60 | ); 61 | 62 | for (index, page_number) in page_range.iter().enumerate() { 63 | let frame_number = page_table 64 | .translate_page(page_number) 65 | .unwrap() 66 | .frame_number(); 67 | let lower_bound = { 68 | if index == 0 { 69 | buffer_address_start.page_offset() 70 | } else { 71 | 0 72 | } 73 | }; 74 | 75 | let upper_bound = { 76 | if index == page_range.len() - 1 { 77 | buffer_address_end.page_offset() 78 | } else { 79 | PAGE_SIZE 80 | } 81 | }; 82 | translated_buffer.push(&frame_number.as_bytes()[lower_bound..upper_bound]); 83 | } 84 | 85 | translated_buffer 86 | } 87 | } 88 | 89 | impl Deref for UserPtr { 90 | type Target = T; 91 | 92 | fn deref(&self) -> &T { 93 | let page_table = PageTable::from_satp(self.satp); 94 | let virtual_address = VirtualAddress::from(self.ptr as usize); 95 | page_table.translate(virtual_address).unwrap().as_ref() 96 | } 97 | } 98 | 99 | impl DerefMut for UserPtr { 100 | fn deref_mut(&mut self) -> &mut T { 101 | let page_table = PageTable::from_satp(self.satp); 102 | let virtual_address = VirtualAddress::from(self.ptr as usize); 103 | page_table.translate(virtual_address).unwrap().as_mut() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /kernel/src/mem/address/frame_number.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem, 3 | ops::{Add, AddAssign}, 4 | slice, 5 | }; 6 | 7 | use crate::{ 8 | constant::PAGE_SIZE, 9 | executor::TrapContext, 10 | mem::{page_table::PageTableEntry, PhysicalAddress}, 11 | }; 12 | 13 | const FRAME_NUMBER_SIZE: usize = 44; 14 | 15 | /// The `FrameNumber` struct represents the number of a 44-bit page frame defined in the Sv39 16 | /// page table format. 17 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 18 | pub struct FrameNumber { 19 | pub bits: usize, 20 | } 21 | 22 | impl FrameNumber { 23 | /// Interprets the frame as a slice of `u8` and return a reference to the slice. 24 | pub fn as_bytes(&self) -> &'static [u8] { 25 | let physical_address = PhysicalAddress::from(*self); 26 | unsafe { 27 | slice::from_raw_parts(physical_address.as_ptr(), PAGE_SIZE / mem::size_of::()) 28 | } 29 | } 30 | 31 | /// Interprets the frame as a slice of `u8` and return a mutable reference to the slice. 32 | pub fn as_bytes_mut(&self) -> &'static mut [u8] { 33 | let physical_address = PhysicalAddress::from(*self); 34 | unsafe { 35 | slice::from_raw_parts_mut( 36 | physical_address.as_ptr_mut(), 37 | PAGE_SIZE / mem::size_of::(), 38 | ) 39 | } 40 | } 41 | 42 | /// Interprets the frame as a slice of [PageTableEntry] and return a reference to the slice. 43 | pub fn as_pte(&self) -> &'static [PageTableEntry] { 44 | let physical_address = PhysicalAddress::from(*self); 45 | unsafe { 46 | slice::from_raw_parts( 47 | physical_address.as_ptr() as *const PageTableEntry, 48 | PAGE_SIZE / mem::size_of::(), 49 | ) 50 | } 51 | } 52 | 53 | /// Interprets the frame as a slice of [PageTableEntry] and return a mutable reference to the 54 | /// slice. 55 | pub fn as_pte_mut(&self) -> &'static mut [PageTableEntry] { 56 | let physical_address = PhysicalAddress::from(*self); 57 | unsafe { 58 | slice::from_raw_parts_mut( 59 | physical_address.as_ptr_mut() as *mut PageTableEntry, 60 | PAGE_SIZE / mem::size_of::(), 61 | ) 62 | } 63 | } 64 | 65 | /// Interprets the frame as a [TrapContext] and return a mutable reference to it. 66 | pub fn as_trap_context_mut(&self) -> &'static mut TrapContext { 67 | let physical_address = PhysicalAddress::from(*self); 68 | unsafe { 69 | (physical_address.as_ptr_mut() as *mut TrapContext) 70 | .as_mut() 71 | .unwrap() 72 | } 73 | } 74 | } 75 | 76 | impl Add for FrameNumber { 77 | type Output = Self; 78 | 79 | fn add(self, rhs: usize) -> Self { 80 | Self::from(self.bits + rhs) 81 | } 82 | } 83 | 84 | impl AddAssign for FrameNumber { 85 | fn add_assign(&mut self, rhs: usize) { 86 | *self = *self + rhs; 87 | } 88 | } 89 | 90 | impl From for FrameNumber { 91 | fn from(value: usize) -> Self { 92 | Self { 93 | bits: value & ((1 << FRAME_NUMBER_SIZE) - 1), 94 | } 95 | } 96 | } 97 | 98 | impl From for usize { 99 | fn from(value: FrameNumber) -> Self { 100 | value.bits 101 | } 102 | } 103 | 104 | impl From for FrameNumber { 105 | fn from(value: PhysicalAddress) -> Self { 106 | assert_eq!(value.page_offset(), 0); 107 | value.floor() 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /kernel/src/mem/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The `frame_allocator` module provides a frame allocator for the kernel. 2 | 3 | use alloc::vec::Vec; 4 | 5 | use lazy_static::lazy_static; 6 | 7 | use crate::{ 8 | constant::MEM_LIMIT, 9 | mem::{FrameNumber, PhysicalAddress}, 10 | sync::Mutex, 11 | }; 12 | 13 | /// The `FrameTracker` struct represents a frame in the physical memory. 14 | /// It contains the frame number and is responsible for zeroing out the frame when it is created. 15 | /// It deallocates the frame when it is dropped, which follows the RAII idiom. 16 | pub struct FrameTracker { 17 | frame_number: FrameNumber, 18 | } 19 | 20 | impl FrameTracker { 21 | /// Initializes a frame with a specific [FrameNumber] and zeros out the frame. 22 | pub fn new(frame_number: FrameNumber) -> Self { 23 | for byte in frame_number.as_bytes_mut() { 24 | *byte = 0; 25 | } 26 | 27 | Self { frame_number } 28 | } 29 | 30 | pub fn frame_number(&self) -> FrameNumber { 31 | self.frame_number 32 | } 33 | } 34 | 35 | impl Drop for FrameTracker { 36 | fn drop(&mut self) { 37 | deallocate_frame(self.frame_number) 38 | } 39 | } 40 | 41 | trait FrameAllocator { 42 | fn new() -> Self; 43 | fn allocate(&mut self) -> Option; 44 | fn deallocate(&mut self, frame_number: FrameNumber); 45 | } 46 | 47 | pub struct StackFrameAllocator { 48 | frame_start: FrameNumber, 49 | frame_end: FrameNumber, 50 | deallocated_page: Vec, 51 | } 52 | 53 | impl StackFrameAllocator { 54 | fn init(&mut self, frame_start: FrameNumber, frame_end: FrameNumber) { 55 | self.frame_start = frame_start; 56 | self.frame_end = frame_end; 57 | } 58 | } 59 | 60 | impl FrameAllocator for StackFrameAllocator { 61 | fn new() -> Self { 62 | StackFrameAllocator { 63 | frame_start: FrameNumber::from(0), 64 | frame_end: FrameNumber::from(0), 65 | deallocated_page: Vec::new(), 66 | } 67 | } 68 | 69 | fn allocate(&mut self) -> Option { 70 | if let Some(frame_number) = self.deallocated_page.pop() { 71 | Some(frame_number) 72 | } else if self.frame_start == self.frame_end { 73 | None 74 | } else { 75 | let result = Some(self.frame_start); 76 | self.frame_start += 1; 77 | result 78 | } 79 | } 80 | 81 | fn deallocate(&mut self, frame_number: FrameNumber) { 82 | if self.frame_start <= frame_number || self.deallocated_page.contains(&frame_number) { 83 | panic!( 84 | "the frame {:#x} has not been allocated", 85 | usize::from(frame_number) 86 | ) 87 | } 88 | self.deallocated_page.push(frame_number); 89 | } 90 | } 91 | 92 | lazy_static! { 93 | pub static ref FRAME_ALLOCATOR: Mutex = 94 | Mutex::new(StackFrameAllocator::new()); 95 | } 96 | 97 | /// Initializes a frame allocator that manages the physical address from `kernel_end` to 98 | /// [MEM_LIMIT]. 99 | pub fn init() { 100 | extern "C" { 101 | fn kernel_end(); 102 | } 103 | 104 | FRAME_ALLOCATOR.lock().init( 105 | PhysicalAddress::from(kernel_end as usize).ceil(), 106 | PhysicalAddress::from(MEM_LIMIT).floor(), 107 | ); 108 | } 109 | 110 | /// Allocates a frame and returns a [FrameTracker] to track the allocated frame when succeeded. 111 | pub fn allocate_frame() -> Option { 112 | FRAME_ALLOCATOR.lock().allocate().map(FrameTracker::new) 113 | } 114 | 115 | /// Deallocates the frame with a specific [FrameNumber]. 116 | pub fn deallocate_frame(frame_number: FrameNumber) { 117 | FRAME_ALLOCATOR.lock().deallocate(frame_number); 118 | } 119 | -------------------------------------------------------------------------------- /kernel/src/mem/page_table.rs: -------------------------------------------------------------------------------- 1 | //! The `page_table` module defines a 3-level page table 2 | //! that follows the RISC-V Sv39 page table specification. 3 | //! The page table supports 512 GB of virtual-address space. 4 | 5 | #![macro_use] 6 | use alloc::{vec, vec::Vec}; 7 | 8 | use bitflags::bitflags; 9 | 10 | use crate::mem::{ 11 | frame_allocator::{allocate_frame, FrameTracker}, 12 | FrameNumber, 13 | PageNumber, 14 | PhysicalAddress, 15 | VirtualAddress, 16 | }; 17 | 18 | bitflags! { 19 | #[derive(Copy, Clone)] 20 | pub struct PTEFlags: usize { 21 | const V = 1 << 0; 22 | const R = 1 << 1; 23 | const W = 1 << 2; 24 | const X = 1 << 3; 25 | const U = 1 << 4; 26 | const G = 1 << 5; 27 | const A = 1 << 6; 28 | const D = 1 << 7; 29 | const COW = 1 << 8; 30 | } 31 | } 32 | 33 | #[derive(Copy, Clone, Default)] 34 | #[repr(C)] 35 | pub struct PageTableEntry { 36 | bits: usize, 37 | } 38 | 39 | impl PageTableEntry { 40 | pub fn new(frame_number: FrameNumber, flag: PTEFlags) -> Self { 41 | PageTableEntry { 42 | bits: usize::from(frame_number) << 10 | flag.bits(), 43 | } 44 | } 45 | 46 | pub fn frame_number(&self) -> FrameNumber { 47 | FrameNumber::from(self.bits >> 10 & ((1 << 44) - 1)) 48 | } 49 | 50 | pub fn flags(&self) -> PTEFlags { 51 | PTEFlags::from_bits(self.bits & ((1 << 10) - 1)).unwrap() 52 | } 53 | 54 | pub fn is_valid(&self) -> bool { 55 | self.flags().contains(PTEFlags::V) 56 | } 57 | 58 | pub fn is_readable(&self) -> bool { 59 | self.flags().contains(PTEFlags::R) 60 | } 61 | 62 | pub fn is_writable(&self) -> bool { 63 | self.flags().contains(PTEFlags::W) 64 | } 65 | 66 | pub fn is_executable(&self) -> bool { 67 | self.flags().contains(PTEFlags::X) 68 | } 69 | 70 | pub fn is_cow(&self) -> bool { 71 | self.flags().contains(PTEFlags::COW) 72 | } 73 | } 74 | 75 | #[repr(C)] 76 | pub struct PageTable { 77 | root_frame_number: FrameNumber, 78 | frame_list: Vec, 79 | } 80 | 81 | impl PageTable { 82 | pub fn new() -> Self { 83 | let frame = allocate_frame().unwrap(); 84 | PageTable { 85 | root_frame_number: frame.frame_number(), 86 | frame_list: vec![frame], 87 | } 88 | } 89 | 90 | /// Returns the value of the `satp` register that points to the page table. 91 | pub fn satp(&self) -> usize { 92 | 8 << 60 | usize::from(self.root_frame_number) 93 | } 94 | 95 | /// Creates a [PageTable] where the `root_frame_number` points to the frame in the `satp` 96 | /// register. 97 | pub fn from_satp(satp: usize) -> Self { 98 | Self { 99 | root_frame_number: FrameNumber::from(satp & ((1 << 44) - 1)), 100 | frame_list: Vec::new(), 101 | } 102 | } 103 | 104 | /// Maps a [PageNumber] to a [FrameNumber] and sets the [PageTableEntry] with [PTEFlags]. 105 | pub fn map(&mut self, page_number: PageNumber, frame_number: FrameNumber, flags: PTEFlags) { 106 | let pte = self.create_pte(page_number).unwrap(); 107 | *pte = PageTableEntry::new(frame_number, flags | PTEFlags::V); 108 | } 109 | 110 | /// Clears the [PageTableEntry] corresponding to the [PageNumber]. 111 | pub fn unmap(&mut self, page_number: PageNumber) { 112 | let pte = self.find_pte(page_number).unwrap(); 113 | *pte = PageTableEntry::default(); 114 | } 115 | 116 | /// Finds the page table with a [VirtualAddress] and returns a [PhysicalAddress]. 117 | pub fn translate(&self, virtual_address: VirtualAddress) -> Option { 118 | let page_number = PageNumber::from(virtual_address); 119 | self.find_pte(page_number).map(|pte| { 120 | let frame_number = pte.frame_number(); 121 | PhysicalAddress::from(frame_number) + virtual_address.page_offset() 122 | }) 123 | } 124 | 125 | /// Finds the page table with a [PageNumber] and returns a [PageTableEntry]. 126 | pub fn translate_page(&self, page_number: PageNumber) -> Option { 127 | self.find_pte(page_number).map(|pte| *pte) 128 | } 129 | 130 | /// Finds the page table with a [PageNumber] and returns a mutable reference to a 131 | /// [PageTableEntry]. 132 | fn find_pte(&self, page_number: PageNumber) -> Option<&mut PageTableEntry> { 133 | let index = page_number.index(); 134 | let mut frame_number = self.root_frame_number; 135 | for (i, pte_index) in index.iter().enumerate() { 136 | let pte = &mut frame_number.as_pte_mut()[*pte_index]; 137 | if i == 2 { 138 | return Some(pte); 139 | } 140 | 141 | if pte.is_valid() { 142 | frame_number = pte.frame_number(); 143 | } else { 144 | return None; 145 | } 146 | } 147 | None 148 | } 149 | 150 | /// Finds the page table with a [PageNumber] and returns a mutable reference to a 151 | /// [PageTableEntry]. Creates a new [PageTableEntry] if not existed. 152 | fn create_pte(&mut self, page_number: PageNumber) -> Option<&mut PageTableEntry> { 153 | let index = page_number.index(); 154 | let mut frame_number = self.root_frame_number; 155 | for (i, pte_index) in index.iter().enumerate() { 156 | let pte = &mut frame_number.as_pte_mut()[*pte_index]; 157 | if i == 2 { 158 | return Some(pte); 159 | } 160 | 161 | if !pte.is_valid() { 162 | let frame = allocate_frame().unwrap(); 163 | *pte = PageTableEntry::new(frame.frame_number(), PTEFlags::V); 164 | self.frame_list.push(frame); 165 | } 166 | frame_number = pte.frame_number(); 167 | } 168 | None 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `rust-kernel-riscv` 2 | 3 | [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/xiaoyang-sde/rust-kernel-riscv/cargo.yml?branch=master&style=for-the-badge&logo=github)](https://github.com/xiaoyang-sde/rust-kernel-riscv/actions) 4 | 5 | `rust-kernel-riscv` is an open-source project that implements an operating system kernel on RISC-V architecture with Rust programming language. The project draws inspiration from several open-source implementations, such as [xv6-riscv](https://github.com/mit-pdos/xv6-riscv) and [zCore](https://github.com/rcore-os/zCore). 6 | 7 | - The kernel leverages Rust's asynchronous programming model to schedule threads in both the kernel and user space, which makes context switching more efficient and eliminates the need of allocating a separate kernel stack for each user process. 8 | 9 | - The kernel implements the kernel page-table isolation, which prevents the kernel space and the user space to share the same page table and mitigates potential Meltdown attacks. 10 | 11 | ## Build 12 | 13 | - Install the `riscv64gc-unknown-none-elf` target and related components: 14 | 15 | ```console 16 | rustup install nightly 17 | 18 | rustup target add riscv64gc-unknown-none-elf 19 | rustup component add llvm-tools-preview 20 | rustup component add rust-src 21 | 22 | cargo install cargo-binutils 23 | ``` 24 | 25 | - Install [QEMU](https://www.qemu.org) with a package manager such as Homebrew: 26 | 27 | ```console 28 | brew install qemu 29 | ``` 30 | 31 | - Build and run the kernel with QEMU: 32 | 33 | ```console 34 | make qemu 35 | ``` 36 | 37 | ## Design Document 38 | 39 | ### Executor 40 | 41 | The kernel executor handles the management and execution of tasks, which can be either user threads or kernel threads. As of the current implementation, the `TaskQueue` is a wrapper around the `VecDeque` type, which store and execute tasks in a FIFO order. The `run_until_complete` function blocks the calling thread and runs all the tasks in the `TaskQueue`. 42 | 43 | ```rs 44 | lazy_static! { 45 | static ref TASK_QUEUE: Mutex = Mutex::new(TaskQueue::new()); 46 | } 47 | 48 | pub fn run_until_complete() { 49 | loop { 50 | let task = TASK_QUEUE.lock().pop_front(); 51 | if let Some(task) = task { 52 | task.run(); 53 | } else { 54 | break; 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | ### Trampoline 61 | 62 | The trampoline is a dedicated page that acts as a bridge for transferring control between supervisor and user modes, which is located at the same address (`0xFFFFFFFFFFFFF000`) in both kernel and user thread page tables. Identical mapping is required because the program counter must point to a valid location after switching the page table. 63 | 64 | The trampoline contains a pair of naked functions, `_enter_kernel_space` and `_enter_user_space`: 65 | 66 | - `_enter_user_space` stores callee-saved registers on the kernel stack, switches to the page table of the user thread, and restores the context (registers, `sstatuc`, `sepc`) of the user thread from a `TrapContext`. Following these steps, it uses a `sret` instruction to return to user mode. 67 | 68 | - `_enter_kernel_space` stores the context (registers, `sstatuc`, `sepc`) of the user thread to a `TrapContext`, switch to the page table of the kernel, and restores the callee-saved registers from the kernel stack. Following these steps, it uses a `ret` instruction to jump to the `thread_loop`, which will handle the exception or interrupt. 69 | 70 | ### User Thread 71 | 72 | Each user thread is represented with the `executor::future::thread_loop` future. The executor runs a future with its `poll` method, and the `thread_loop` invokes `_enter_user_space` function to enter the user mode. The `_enter_user_space` returns when an exception or interrupt occurs, and the `thread_loop` handles them and decide whether to continue, yield, or terminate the thread. The `spawn_thread` function is used to add a new user thread to the executor. 73 | 74 | ```rs 75 | async fn thread_loop(thread: Arc) { 76 | loop { 77 | let trap_context = thread.state().lock().user_trap_context_mut(); 78 | _enter_user_space(trap_context, thread.satp()); 79 | 80 | // Invokes related methods to handle the exception or interrupt, 81 | // which returns a variant of the `ControlFlow` enum 82 | // (Please refer to the source code) 83 | 84 | // Decides whether to continue, yield, or terminate the thread 85 | match control_flow { 86 | ControlFlow::Continue => continue, 87 | ControlFlow::Yield => yield_now().await, 88 | ControlFlow::Exit(exit_code) => { 89 | thread.exit(exit_code); 90 | break; 91 | } 92 | } 93 | } 94 | } 95 | 96 | pub fn spawn_thread(thread: Arc) { 97 | let (runnable, task) = executor::spawn(thread_loop(thread)); 98 | runnable.schedule(); 99 | task.detach(); 100 | } 101 | ``` 102 | 103 | ### Lifetime of a User Thread 104 | 105 | - The user thread is initiated using the spawn_thread function, which encapsulates it within the `thread_loop` future and incorporates it into the executor. 106 | - The executor chooses a task from the TaskQueue and polls it, executing the `thread_loop`. 107 | - To enter user mode, the `thread_loop` calls `_enter_user_space`. 108 | - The user thread operates in user mode. 109 | - If a trap (exception or interrupt) arises, the `_enter_kernel_space` specified in the stvec register is triggered, returning control to the `thread_loop`. 110 | - The `thread_loop` manages the trap and determines whether to continue, yield, or terminate the thread. If it chooses to continue, the `thread_loop` moves on to the next iteration. 111 | 112 | ## Development Roadmap 113 | 114 | - [ ] File system with asynchronous interface 115 | - [ ] Virtio driver 116 | - [ ] TCP/IP stack 117 | - [ ] Linux-compatible system call interface 118 | - [ ] [musl libc-test](https://wiki.musl-libc.org/libc-test.html) 119 | -------------------------------------------------------------------------------- /kernel/src/task/thread.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::{Arc, Weak}; 2 | 3 | use crate::{ 4 | constant::{PAGE_SIZE, TRAP_CONTEXT_BASE, USER_STACK_SIZE}, 5 | executor::TrapContext, 6 | mem::{FrameNumber, MapPermission, PageNumber, VirtualAddress}, 7 | sync::Mutex, 8 | task::{tid::Tid, Process}, 9 | }; 10 | 11 | pub struct Thread { 12 | tid: Tid, 13 | process: Weak, 14 | user_stack_base: VirtualAddress, 15 | 16 | state: Mutex, 17 | } 18 | 19 | impl Thread { 20 | pub fn new( 21 | process: Arc, 22 | user_stack_base: VirtualAddress, 23 | allocate_resource: bool, 24 | ) -> Self { 25 | let mut process_state = process.state().lock(); 26 | let tid = process_state.allocate_tid(); 27 | 28 | let user_stack_bottom = user_stack_base + tid * (PAGE_SIZE + USER_STACK_SIZE); 29 | let user_stack_top = user_stack_bottom + USER_STACK_SIZE; 30 | if allocate_resource { 31 | process_state.page_set_mut().insert_frame( 32 | user_stack_bottom, 33 | user_stack_top, 34 | MapPermission::R | MapPermission::W | MapPermission::U, 35 | ); 36 | } 37 | 38 | let trap_context_bottom = VirtualAddress::from(TRAP_CONTEXT_BASE) + tid * PAGE_SIZE; 39 | let trap_context_top = trap_context_bottom + PAGE_SIZE; 40 | if allocate_resource { 41 | process_state.page_set_mut().insert_frame( 42 | trap_context_bottom, 43 | trap_context_top, 44 | MapPermission::R | MapPermission::W, 45 | ); 46 | } 47 | let trap_context_page = PageNumber::from(trap_context_bottom); 48 | let trap_context_frame = process_state 49 | .page_set() 50 | .translate(trap_context_page) 51 | .unwrap() 52 | .frame_number(); 53 | 54 | Self { 55 | tid, 56 | process: Arc::downgrade(&process), 57 | user_stack_base, 58 | state: Mutex::new(ThreadState::new( 59 | trap_context_page, 60 | trap_context_frame, 61 | user_stack_bottom, 62 | )), 63 | } 64 | } 65 | 66 | pub fn reallocate_resource(&self, user_stack_base: VirtualAddress) { 67 | let process = self.process(); 68 | let mut process_state = process.state().lock(); 69 | 70 | let user_stack_bottom = user_stack_base + self.tid() * (PAGE_SIZE + USER_STACK_SIZE); 71 | let user_stack_top = user_stack_bottom + USER_STACK_SIZE; 72 | process_state.page_set_mut().insert_frame( 73 | user_stack_bottom, 74 | user_stack_top, 75 | MapPermission::R | MapPermission::W | MapPermission::U, 76 | ); 77 | 78 | let mut thread_state = self.state().lock(); 79 | thread_state.set_user_stack_bottom(user_stack_bottom); 80 | 81 | let trap_context_bottom = VirtualAddress::from(TRAP_CONTEXT_BASE) + self.tid() * PAGE_SIZE; 82 | let trap_context_top = trap_context_bottom + PAGE_SIZE; 83 | process_state.page_set_mut().insert_frame( 84 | trap_context_bottom, 85 | trap_context_top, 86 | MapPermission::R | MapPermission::W, 87 | ); 88 | let trap_context_page = PageNumber::from(trap_context_bottom); 89 | let trap_context_frame = process_state 90 | .page_set() 91 | .translate(trap_context_page) 92 | .unwrap() 93 | .frame_number(); 94 | thread_state.set_trap_context_frame(trap_context_frame); 95 | } 96 | 97 | pub fn tid(&self) -> Tid { 98 | self.tid 99 | } 100 | 101 | pub fn user_stack_base(&self) -> VirtualAddress { 102 | self.user_stack_base 103 | } 104 | 105 | pub fn process(&self) -> Arc { 106 | self.process.upgrade().unwrap() 107 | } 108 | 109 | pub fn state(&self) -> &Mutex { 110 | &self.state 111 | } 112 | 113 | pub fn satp(&self) -> usize { 114 | self.process().state().lock().page_set().satp() 115 | } 116 | 117 | pub fn clone_frame(&self, virtual_address: VirtualAddress) -> bool { 118 | self.process() 119 | .state() 120 | .lock() 121 | .page_set_mut() 122 | .clone_frame(virtual_address) 123 | } 124 | 125 | pub fn exit(&self, exit_code: usize) { 126 | self.process().exit(exit_code); 127 | } 128 | 129 | fn deallocate_user_stack(&self) { 130 | let user_stack_bottom = self.user_stack_base + self.tid() * (PAGE_SIZE + USER_STACK_SIZE); 131 | self.process() 132 | .state() 133 | .lock() 134 | .page_set_mut() 135 | .remove_segment(user_stack_bottom); 136 | } 137 | 138 | fn deallocate_trap_context(&self) { 139 | let trap_context_bottom = VirtualAddress::from(TRAP_CONTEXT_BASE) + self.tid() * PAGE_SIZE; 140 | self.process() 141 | .state() 142 | .lock() 143 | .page_set_mut() 144 | .remove_segment(trap_context_bottom); 145 | } 146 | } 147 | 148 | impl Drop for Thread { 149 | fn drop(&mut self) { 150 | self.deallocate_user_stack(); 151 | self.deallocate_trap_context(); 152 | self.process().state().lock().deallocated_tid(self.tid()); 153 | } 154 | } 155 | 156 | pub struct ThreadState { 157 | trap_context_page: PageNumber, 158 | trap_context_frame: FrameNumber, 159 | user_stack_bottom: VirtualAddress, 160 | } 161 | 162 | impl ThreadState { 163 | pub fn new( 164 | trap_context_page: PageNumber, 165 | trap_context_frame: FrameNumber, 166 | user_stack_bottom: VirtualAddress, 167 | ) -> Self { 168 | Self { 169 | trap_context_page, 170 | trap_context_frame, 171 | user_stack_bottom, 172 | } 173 | } 174 | 175 | pub fn set_user_stack_bottom(&mut self, user_stack_bottom: VirtualAddress) { 176 | self.user_stack_bottom = user_stack_bottom; 177 | } 178 | 179 | pub fn user_stack_top(&self) -> VirtualAddress { 180 | self.user_stack_bottom + USER_STACK_SIZE 181 | } 182 | 183 | pub fn set_trap_context_frame(&mut self, trap_context_frame: FrameNumber) { 184 | self.trap_context_frame = trap_context_frame; 185 | } 186 | 187 | pub fn kernel_trap_context_mut(&self) -> &'static mut TrapContext { 188 | self.trap_context_frame.as_trap_context_mut() 189 | } 190 | 191 | pub fn user_trap_context_mut(&self) -> &'static mut TrapContext { 192 | self.trap_context_page.as_trap_context_mut() 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /kernel/src/task/process.rs: -------------------------------------------------------------------------------- 1 | use alloc::{ 2 | collections::BTreeMap, 3 | string::String, 4 | sync::{Arc, Weak}, 5 | vec::Vec, 6 | }; 7 | 8 | use lazy_static::lazy_static; 9 | use log::info; 10 | 11 | use crate::{ 12 | executor, 13 | file, 14 | mem::PageSet, 15 | sync::{Event, EventBus, Mutex}, 16 | task::{ 17 | pid::{self, Pid, PidHandle}, 18 | thread::Thread, 19 | tid::{Tid, TidAllocator}, 20 | }, 21 | }; 22 | 23 | lazy_static! { 24 | static ref PROCESS_MAP: Mutex>> = Mutex::new(BTreeMap::new()); 25 | } 26 | 27 | fn get_process(pid: Pid) -> Option> { 28 | PROCESS_MAP.lock().get(&pid).cloned() 29 | } 30 | 31 | fn insert_process(pid: Pid, process: Arc) { 32 | PROCESS_MAP.lock().insert(pid, process); 33 | } 34 | 35 | fn remove_process(pid: Pid) { 36 | PROCESS_MAP.lock().remove(&pid); 37 | } 38 | 39 | #[derive(PartialEq, Eq, Clone, Copy)] 40 | pub enum Status { 41 | Runnable, 42 | Zombie, 43 | } 44 | 45 | pub struct Process { 46 | pid_handle: PidHandle, 47 | 48 | state: Mutex, 49 | event_bus: Arc>, 50 | } 51 | 52 | pub struct ProcessState { 53 | status: Status, 54 | exit_code: usize, 55 | page_set: PageSet, 56 | tid_allocator: TidAllocator, 57 | parent: Option>, 58 | child_list: Vec>, 59 | thread_list: Vec>, 60 | } 61 | 62 | impl Process { 63 | /// Creates a process with a main thread that runs a specific executable file. 64 | pub fn new(bin_name: &str) -> Arc { 65 | let elf_data = file::get_bin(bin_name).unwrap(); 66 | let (page_set, user_stack_base, entry_point) = PageSet::from_elf(elf_data); 67 | 68 | let pid_handle = pid::allocate_pid(); 69 | let process = Arc::new(Self { 70 | pid_handle, 71 | state: Mutex::new(ProcessState::new(page_set, None)), 72 | event_bus: EventBus::new(), 73 | }); 74 | 75 | let thread = Arc::new(Thread::new(process.clone(), user_stack_base, true)); 76 | let thread_state = thread.state().lock(); 77 | let trap_context = thread_state.kernel_trap_context_mut(); 78 | trap_context.set_user_register(2, usize::from(thread_state.user_stack_top())); 79 | trap_context.set_user_sepc(usize::from(entry_point)); 80 | drop(thread_state); 81 | 82 | process 83 | .state() 84 | .lock() 85 | .thread_list_mut() 86 | .push(thread.clone()); 87 | insert_process(process.pid(), process.clone()); 88 | executor::spawn_thread(thread); 89 | process 90 | } 91 | 92 | /// Forks the current process and create a new child process. 93 | pub fn fork(self: &Arc) -> Arc { 94 | let pid_handle = pid::allocate_pid(); 95 | 96 | let mut process_state = self.state().lock(); 97 | let page_set = PageSet::clone_from(process_state.page_set_mut()); 98 | 99 | let child_process = Arc::new(Self { 100 | pid_handle, 101 | state: Mutex::new(ProcessState::new(page_set, Some(Arc::downgrade(self)))), 102 | event_bus: EventBus::new(), 103 | }); 104 | process_state.child_list_mut().push(child_process.clone()); 105 | 106 | let user_stack_base = process_state.main_thread().user_stack_base(); 107 | drop(process_state); 108 | 109 | let thread = Arc::new(Thread::new(child_process.clone(), user_stack_base, false)); 110 | let trap_context = thread.state().lock().kernel_trap_context_mut(); 111 | trap_context.set_user_register(10, 0); 112 | child_process 113 | .state() 114 | .lock() 115 | .thread_list_mut() 116 | .push(thread.clone()); 117 | 118 | insert_process(child_process.pid(), child_process.clone()); 119 | executor::spawn_thread(thread); 120 | child_process 121 | } 122 | 123 | /// Replaces the current process with a new process loaded from the executable file with a given 124 | /// name. 125 | pub fn exec(self: &Arc, bin_name: &str, _argument_list: Vec) { 126 | let elf_data = file::get_bin(bin_name).unwrap(); 127 | let (page_set, user_stack_base, entry_point) = PageSet::from_elf(elf_data); 128 | 129 | let mut process_state = self.state().lock(); 130 | process_state.set_page_set(page_set); 131 | let thread = process_state.main_thread_mut().clone(); 132 | drop(process_state); 133 | 134 | thread.reallocate_resource(user_stack_base); 135 | let thread_state = thread.state().lock(); 136 | let trap_context = thread_state.kernel_trap_context_mut(); 137 | trap_context.set_user_register(2, usize::from(thread_state.user_stack_top())); 138 | trap_context.set_user_sepc(usize::from(entry_point)); 139 | } 140 | 141 | /// Terminates the current thread with the given exit code. 142 | pub fn exit(&self, exit_code: usize) { 143 | let mut process_state = self.state().lock(); 144 | process_state.set_status(Status::Zombie); 145 | process_state.set_exit_code(exit_code); 146 | if self.pid() != 0 { 147 | let init_process = get_process(0).unwrap(); 148 | let mut init_process_state = init_process.state().lock(); 149 | for child_process in process_state.child_list_mut() { 150 | init_process_state 151 | .child_list_mut() 152 | .push(child_process.clone()); 153 | } 154 | } 155 | process_state.thread_list_mut().clear(); 156 | process_state.child_list_mut().clear(); 157 | 158 | if let Some(parent) = process_state.parent() { 159 | if let Some(parent) = parent.upgrade() { 160 | parent.event_bus().lock().push(Event::CHILD_PROCESS_QUIT); 161 | } 162 | } 163 | 164 | remove_process(self.pid()); 165 | info!("process {} exited with {}", self.pid(), exit_code); 166 | } 167 | 168 | pub fn pid(&self) -> Pid { 169 | self.pid_handle.pid() 170 | } 171 | 172 | pub fn state(&self) -> &Mutex { 173 | &self.state 174 | } 175 | 176 | pub fn event_bus(&self) -> Arc> { 177 | self.event_bus.clone() 178 | } 179 | } 180 | 181 | impl ProcessState { 182 | pub fn new(page_set: PageSet, parent: Option>) -> Self { 183 | Self { 184 | page_set, 185 | parent, 186 | tid_allocator: TidAllocator::new(), 187 | child_list: Vec::new(), 188 | thread_list: Vec::new(), 189 | exit_code: 0, 190 | status: Status::Runnable, 191 | } 192 | } 193 | 194 | pub fn parent(&self) -> Option> { 195 | self.parent.clone() 196 | } 197 | 198 | pub fn status(&self) -> Status { 199 | self.status 200 | } 201 | 202 | pub fn set_status(&mut self, status: Status) { 203 | self.status = status; 204 | } 205 | 206 | pub fn exit_code(&self) -> usize { 207 | self.exit_code 208 | } 209 | 210 | pub fn set_exit_code(&mut self, exit_code: usize) { 211 | self.exit_code = exit_code; 212 | } 213 | 214 | pub fn child_list_mut(&mut self) -> &mut Vec> { 215 | &mut self.child_list 216 | } 217 | 218 | pub fn thread_list_mut(&mut self) -> &mut Vec> { 219 | &mut self.thread_list 220 | } 221 | 222 | pub fn main_thread(&self) -> &Arc { 223 | &self.thread_list[0] 224 | } 225 | 226 | pub fn main_thread_mut(&mut self) -> &mut Arc { 227 | &mut self.thread_list[0] 228 | } 229 | 230 | pub fn page_set(&self) -> &PageSet { 231 | &self.page_set 232 | } 233 | 234 | pub fn page_set_mut(&mut self) -> &mut PageSet { 235 | &mut self.page_set 236 | } 237 | 238 | pub fn set_page_set(&mut self, page_set: PageSet) { 239 | self.page_set = page_set; 240 | } 241 | 242 | pub fn allocate_tid(&mut self) -> Tid { 243 | self.tid_allocator.allocate() 244 | } 245 | 246 | pub fn deallocated_tid(&mut self, tid: Tid) { 247 | self.tid_allocator.deallocate(tid); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /kernel/src/executor/future.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::{ 3 | arch::asm, 4 | future::Future, 5 | mem::transmute, 6 | pin::Pin, 7 | task::{Context, Poll}, 8 | }; 9 | 10 | use log::error; 11 | use riscv::register::{ 12 | scause, 13 | scause::{Exception, Interrupt}, 14 | stval, 15 | }; 16 | 17 | use crate::{ 18 | constant::TRAMPOLINE, 19 | executor, 20 | executor::TrapContext, 21 | mem::VirtualAddress, 22 | syscall::SystemCall, 23 | task::Thread, 24 | timer, 25 | }; 26 | 27 | /// The `ControlFlow` enum specifies the operation that the executor should execute on a thread 28 | /// prior to returning to user space. 29 | #[derive(PartialEq, Eq)] 30 | pub enum ControlFlow { 31 | Continue, 32 | Yield, 33 | Exit(usize), 34 | } 35 | 36 | /// The `thread_loop` future represents the lifetime of a user thread. 37 | async fn thread_loop(thread: Arc) { 38 | // There are two mappings of the _enter_user_space function in the kernel page table. 39 | // The first mapping is included in the identical mapping for all physical addresses, 40 | // while the second mapping is included in the `TRAMPOLINE` page. 41 | // When invoking the `_enter_user_space` function, the second mapping should be used 42 | // because it is also mapped in the user space, ensuring that the CPU can continue executing 43 | // at the same address after switching to the user space page table. 44 | 45 | // The function pointer `_enter_user_space` is cast to the `fn(&mut TrapContext, usize)` type 46 | // to ensure proper calling convention. 47 | let _enter_user_space: fn(&mut TrapContext, usize) = { 48 | unsafe { transmute(_enter_user_space as usize - _enter_kernel_space as usize + TRAMPOLINE) } 49 | }; 50 | 51 | loop { 52 | let trap_context = thread.state().lock().user_trap_context_mut(); 53 | _enter_user_space(trap_context, thread.satp()); 54 | 55 | let scause = scause::read(); 56 | let stval = stval::read(); 57 | let control_flow = match scause.cause() { 58 | scause::Trap::Exception(Exception::UserEnvCall) => { 59 | SystemCall::new(&thread).execute().await 60 | } 61 | scause::Trap::Exception(Exception::LoadPageFault) => { 62 | error!("page fault at {:#x}", stval); 63 | ControlFlow::Exit(1) 64 | } 65 | scause::Trap::Exception(Exception::StorePageFault) => { 66 | if thread.clone_frame(VirtualAddress::from(stval)) { 67 | ControlFlow::Continue 68 | } else { 69 | error!("page fault at {:#x}", stval); 70 | ControlFlow::Exit(1) 71 | } 72 | } 73 | scause::Trap::Exception(Exception::IllegalInstruction) => { 74 | error!("illegal instruction"); 75 | ControlFlow::Exit(1) 76 | } 77 | scause::Trap::Exception(Exception::InstructionMisaligned) => { 78 | error!("misaligned instruction"); 79 | ControlFlow::Exit(1) 80 | } 81 | scause::Trap::Interrupt(Interrupt::SupervisorTimer) => { 82 | timer::set_trigger(); 83 | ControlFlow::Yield 84 | } 85 | _ => { 86 | panic!("unsupported trap {:?}", scause.cause()) 87 | } 88 | }; 89 | 90 | match control_flow { 91 | ControlFlow::Continue => continue, 92 | ControlFlow::Yield => yield_now().await, 93 | ControlFlow::Exit(exit_code) => { 94 | thread.exit(exit_code); 95 | break; 96 | } 97 | } 98 | } 99 | } 100 | 101 | pub fn spawn_thread(thread: Arc) { 102 | let (runnable, task) = executor::spawn(thread_loop(thread)); 103 | runnable.schedule(); 104 | task.detach(); 105 | } 106 | 107 | #[naked] 108 | #[link_section = ".text.trampoline"] 109 | unsafe extern "C" fn _enter_kernel_space() { 110 | asm!( 111 | ".p2align 2", 112 | // Reads the address of `trap_context` from sscratch 113 | // and store the user stack pointer to sscratch 114 | "csrrw sp, sscratch, sp", 115 | // Stores the registers to `trap_context.user_register` 116 | "sd zero, 0 * 8(sp)", 117 | "sd ra, 1 * 8(sp)", 118 | "sd gp, 3 * 8(sp)", 119 | "sd tp, 4 * 8(sp)", 120 | "sd t0, 5 * 8(sp)", 121 | "sd t1, 6 * 8(sp)", 122 | "sd t2, 7 * 8(sp)", 123 | "sd s0, 8 * 8(sp)", 124 | "sd s1, 9 * 8(sp)", 125 | "sd a0, 10 * 8(sp)", 126 | "sd a1, 11 * 8(sp)", 127 | "sd a2, 12 * 8(sp)", 128 | "sd a3, 13 * 8(sp)", 129 | "sd a4, 14 * 8(sp)", 130 | "sd a5, 15 * 8(sp)", 131 | "sd a6, 16 * 8(sp)", 132 | "sd a7, 17 * 8(sp)", 133 | "sd s2, 18 * 8(sp)", 134 | "sd s3, 19 * 8(sp)", 135 | "sd s4, 20 * 8(sp)", 136 | "sd s5, 21 * 8(sp)", 137 | "sd s6, 22 * 8(sp)", 138 | "sd s7, 23 * 8(sp)", 139 | "sd s8, 24 * 8(sp)", 140 | "sd s9, 25 * 8(sp)", 141 | "sd s10, 26 * 8(sp)", 142 | "sd s11, 27 * 8(sp)", 143 | "sd t3, 28 * 8(sp)", 144 | "sd t4, 29 * 8(sp)", 145 | "sd t5, 30 * 8(sp)", 146 | "sd t6, 31 * 8(sp)", 147 | // Saves sstatus to `trap_context.user_sstatus` 148 | "csrr t0, sstatus", 149 | "sd t0, 32 * 8(sp)", 150 | // Saves sepc to `trap_context.user_sepc` 151 | "csrr t1, sepc", 152 | "sd t1, 33 * 8(sp)", 153 | // Stores the address of `trap_context` to sscratch 154 | // and read the user stack pointer to t2 155 | "csrrw t2, sscratch, sp", 156 | // Stores the user stack pointer to `trap_context.user_register` 157 | "sd t2, 2 * 8(sp)", 158 | // Reads `trap_context.kernel_satp` to t3 159 | "ld t3, 35 * 8(sp)", 160 | // Reads the stack pointer from `trap_context.kernel_stack` 161 | "ld sp, 34 * 8(sp)", 162 | // Writes the address of the page table of the kernel to satp 163 | "csrw satp, t3", 164 | "sfence.vma", 165 | // Reads the return address, global pointer, thread pointer from the kernel stack 166 | "ld ra, 0 * 8(sp)", 167 | "ld gp, 1 * 8(sp)", 168 | "ld tp, 2 * 8(sp)", 169 | // Stores the callee-saved registers on the kernel stack 170 | "ld s0, 3 * 8(sp)", 171 | "ld s1, 4 * 8(sp)", 172 | "ld s2, 5 * 8(sp)", 173 | "ld s3, 6 * 8(sp)", 174 | "ld s4, 7 * 8(sp)", 175 | "ld s5, 8 * 8(sp)", 176 | "ld s6, 9 * 8(sp)", 177 | "ld s7, 10 * 8(sp)", 178 | "ld s8, 11 * 8(sp)", 179 | "ld s9, 12 * 8(sp)", 180 | "ld s10, 13 * 8(sp)", 181 | "ld s11, 14 * 8(sp)", 182 | // deallocate 15 words on the kernel stack 183 | "addi sp, sp, 15 * 8", 184 | "ret", 185 | options(noreturn) 186 | ) 187 | } 188 | 189 | #[naked] 190 | #[link_section = ".text.trampoline"] 191 | unsafe extern "C" fn _enter_user_space(trap_context: &mut TrapContext, user_satp: usize) { 192 | asm!( 193 | ".p2align 2", 194 | // Allocates 15 words on the kernel stack 195 | "addi sp, sp, -15 * 8", 196 | // Stores the return address, global pointer, thread pointer on the kernel stack 197 | "sd ra, 0 * 8(sp)", 198 | "sd gp, 1 * 8(sp)", 199 | "sd tp, 2 * 8(sp)", 200 | // Stores the callee-saved registers on the kernel stack 201 | "sd s0, 3 * 8(sp)", 202 | "sd s1, 4 * 8(sp)", 203 | "sd s2, 5 * 8(sp)", 204 | "sd s3, 6 * 8(sp)", 205 | "sd s4, 7 * 8(sp)", 206 | "sd s5, 8 * 8(sp)", 207 | "sd s6, 9 * 8(sp)", 208 | "sd s7, 10 * 8(sp)", 209 | "sd s8, 11 * 8(sp)", 210 | "sd s9, 12 * 8(sp)", 211 | "sd s10, 13 * 8(sp)", 212 | "sd s11, 14 * 8(sp)", 213 | // Writes the address of the page table of the process to satp 214 | // and read the address of the page table of the kernel to a1 215 | "csrrw a1, satp, a1", 216 | "sfence.vma", 217 | // Stores the stack pointer to `trap_context.kernel_stack` 218 | // and move the stack pointer to `trap_context` 219 | "sd sp, 34 * 8(a0)", 220 | "mv sp, a0", 221 | // Stores the address of the page table of the kernel to `trap_context.kernel_satp` 222 | "sd a1, 35 * 8(sp)", 223 | // Reads `trap_context.user_sstatus` to t0 224 | "ld t0, 32 * 8(sp)", 225 | "csrw sstatus, t0", 226 | // Reads `trap_context.user_sepc` to t1 227 | "ld t1, 33 * 8(sp)", 228 | "csrw sepc, t1", 229 | // Reads the registers from `trap_context.user_register` 230 | "ld zero, 0 * 8(sp)", 231 | "ld ra, 1 * 8(sp)", 232 | "ld gp, 3 * 8(sp)", 233 | "ld tp, 4 * 8(sp)", 234 | "ld t0, 5 * 8(sp)", 235 | "ld t1, 6 * 8(sp)", 236 | "ld t2, 7 * 8(sp)", 237 | "ld s0, 8 * 8(sp)", 238 | "ld s1, 9 * 8(sp)", 239 | "ld a0, 10 * 8(sp)", 240 | "ld a1, 11 * 8(sp)", 241 | "ld a2, 12 * 8(sp)", 242 | "ld a3, 13 * 8(sp)", 243 | "ld a4, 14 * 8(sp)", 244 | "ld a5, 15 * 8(sp)", 245 | "ld a6, 16 * 8(sp)", 246 | "ld a7, 17 * 8(sp)", 247 | "ld s2, 18 * 8(sp)", 248 | "ld s3, 19 * 8(sp)", 249 | "ld s4, 20 * 8(sp)", 250 | "ld s5, 21 * 8(sp)", 251 | "ld s6, 22 * 8(sp)", 252 | "ld s7, 23 * 8(sp)", 253 | "ld s8, 24 * 8(sp)", 254 | "ld s9, 25 * 8(sp)", 255 | "ld s10, 26 * 8(sp)", 256 | "ld s11, 27 * 8(sp)", 257 | "ld t3, 28 * 8(sp)", 258 | "ld t4, 29 * 8(sp)", 259 | "ld t5, 30 * 8(sp)", 260 | "ld t6, 31 * 8(sp)", 261 | // Saves the address of `trap_context` to sscratch 262 | "csrw sscratch, sp", 263 | // Reads the user stack pointer from `trap_context.user_register` 264 | "ld sp, 2 * 8(sp)", 265 | "sret", 266 | options(noreturn) 267 | ) 268 | } 269 | 270 | pub async fn yield_now() { 271 | YieldFuture::new().await 272 | } 273 | 274 | struct YieldFuture { 275 | state: bool, 276 | } 277 | 278 | impl YieldFuture { 279 | fn new() -> Self { 280 | YieldFuture { state: false } 281 | } 282 | } 283 | 284 | impl Future for YieldFuture { 285 | type Output = (); 286 | 287 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { 288 | if self.state { 289 | return Poll::Ready(()); 290 | } 291 | self.state = true; 292 | cx.waker().wake_by_ref(); 293 | Poll::Pending 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /kernel/src/mem/segment.rs: -------------------------------------------------------------------------------- 1 | use alloc::{collections::BTreeMap, sync::Arc, vec::Vec}; 2 | use core::arch::asm; 3 | 4 | use bitflags::bitflags; 5 | use lazy_static::lazy_static; 6 | use riscv::register::satp; 7 | use xmas_elf::{program::Type, ElfFile}; 8 | 9 | use crate::{ 10 | constant::{MEM_LIMIT, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT_BASE}, 11 | mem::{ 12 | address::PageRange, 13 | frame_allocator::{allocate_frame, FrameTracker}, 14 | page_table::{PTEFlags, PageTable, PageTableEntry}, 15 | FrameNumber, 16 | PageNumber, 17 | PhysicalAddress, 18 | VirtualAddress, 19 | }, 20 | sync::Mutex, 21 | }; 22 | 23 | #[derive(Copy, Clone, PartialEq, Debug)] 24 | pub enum MapType { 25 | Identical, 26 | Framed, 27 | } 28 | 29 | bitflags! { 30 | #[derive(Clone, Copy)] 31 | pub struct MapPermission: usize { 32 | const R = 1 << 1; 33 | const W = 1 << 2; 34 | const X = 1 << 3; 35 | const U = 1 << 4; 36 | } 37 | } 38 | 39 | extern "C" { 40 | fn text_start(); 41 | fn text_end(); 42 | fn rodata_start(); 43 | fn rodata_end(); 44 | fn data_start(); 45 | fn data_end(); 46 | fn bss_stack_start(); 47 | fn bss_end(); 48 | fn kernel_end(); 49 | fn trampoline_start(); 50 | } 51 | 52 | /// The `PageSegment` struct represents a consecutive range of pages, 53 | /// which are mapped to frames in the same method (`Identical` or `Framed`) 54 | /// and have the same permissions. 55 | #[derive(Clone)] 56 | pub struct PageSegment { 57 | page_range: PageRange, 58 | frame_map: BTreeMap>, 59 | map_type: MapType, 60 | map_permission: MapPermission, 61 | } 62 | 63 | impl PageSegment { 64 | pub fn new( 65 | start_address: VirtualAddress, 66 | end_address: VirtualAddress, 67 | map_type: MapType, 68 | map_permission: MapPermission, 69 | ) -> Self { 70 | Self { 71 | page_range: PageRange::new(start_address.floor(), end_address.ceil()), 72 | frame_map: BTreeMap::new(), 73 | map_type, 74 | map_permission, 75 | } 76 | } 77 | 78 | pub fn start(&self) -> PageNumber { 79 | self.page_range.start() 80 | } 81 | 82 | pub fn end(&self) -> PageNumber { 83 | self.page_range.end() 84 | } 85 | 86 | pub fn page_range(&self) -> &PageRange { 87 | &self.page_range 88 | } 89 | 90 | pub fn frame_map(&self) -> &BTreeMap> { 91 | &self.frame_map 92 | } 93 | 94 | pub fn frame_map_mut(&mut self) -> &mut BTreeMap> { 95 | &mut self.frame_map 96 | } 97 | 98 | /// Maps the range of pages represented with `page_range` to frames in the `page_table`. 99 | pub fn map_range(&mut self, page_table: &mut PageTable) { 100 | for page_number in self.page_range.iter() { 101 | self.map_page(page_table, page_number); 102 | } 103 | } 104 | 105 | /// Unmaps the range of pages represented with `page_range` from frames in the `page_table`. 106 | pub fn unmap_range(&mut self, page_table: &mut PageTable) { 107 | for page_number in self.page_range.iter() { 108 | self.unmap_page(page_table, page_number); 109 | } 110 | } 111 | 112 | /// Maps a page with `page_number` to a frame in the `page_table`. 113 | pub fn map_page(&mut self, page_table: &mut PageTable, page_number: PageNumber) { 114 | let frame_number = match self.map_type { 115 | MapType::Identical => FrameNumber::from(usize::from(page_number)), 116 | MapType::Framed => { 117 | let frame = allocate_frame().unwrap(); 118 | let frame_number = frame.frame_number(); 119 | self.frame_map.insert(page_number, Arc::new(frame)); 120 | frame_number 121 | } 122 | }; 123 | 124 | let pte_flags = PTEFlags::from_bits(self.map_permission.bits()).unwrap(); 125 | page_table.map(page_number, frame_number, pte_flags); 126 | } 127 | 128 | /// Unmaps a page with `page_number` from a frame in the `page_table`. 129 | pub fn unmap_page(&mut self, page_table: &mut PageTable, page_number: PageNumber) { 130 | if self.map_type == MapType::Framed { 131 | self.frame_map.remove(&page_number); 132 | } 133 | page_table.unmap(page_number); 134 | } 135 | 136 | /// Writes `bytes` to the pages represented with `page_range`. 137 | pub fn clone_bytes(&mut self, page_table: &mut PageTable, bytes: &[u8]) { 138 | let mut offset = 0; 139 | for state in self.page_range.iter() { 140 | let source = &bytes[offset..bytes.len().min(offset + PAGE_SIZE)]; 141 | let destination = &mut page_table 142 | .translate_page(state) 143 | .unwrap() 144 | .frame_number() 145 | .as_bytes_mut()[..source.len()]; 146 | destination.clone_from_slice(source); 147 | 148 | offset += PAGE_SIZE; 149 | if offset >= bytes.len() { 150 | break; 151 | } 152 | } 153 | } 154 | } 155 | 156 | /// The `PageSet` struct represents a collection of related [PageSegment]. 157 | pub struct PageSet { 158 | page_table: PageTable, 159 | segment_list: Vec, 160 | } 161 | 162 | impl PageSet { 163 | pub fn new() -> Self { 164 | Self { 165 | page_table: PageTable::new(), 166 | segment_list: Vec::new(), 167 | } 168 | } 169 | 170 | pub fn clone_from(page_set: &mut Self) -> Self { 171 | let mut page_set_clone = Self::new(); 172 | page_set_clone.page_table.map( 173 | PageNumber::from(VirtualAddress::from(TRAMPOLINE)), 174 | FrameNumber::from(PhysicalAddress::from(trampoline_start as usize)), 175 | PTEFlags::R | PTEFlags::W | PTEFlags::X, 176 | ); 177 | 178 | let mut page_mappings = Vec::new(); 179 | for page_segment in page_set.segment_list().iter() { 180 | let page_segment_clone = page_segment.clone(); 181 | 182 | if page_segment_clone.start() >= PageNumber::from(TRAP_CONTEXT_BASE) { 183 | page_set_clone.push(page_segment_clone, None); 184 | for page_number in page_segment.page_range().iter() { 185 | let source = page_set.translate(page_number).unwrap().frame_number(); 186 | let destination = page_set_clone 187 | .translate(page_number) 188 | .unwrap() 189 | .frame_number(); 190 | destination 191 | .as_bytes_mut() 192 | .clone_from_slice(source.as_bytes()); 193 | } 194 | } else { 195 | page_set_clone.push_mapped(page_segment_clone, None); 196 | for page_number in page_segment.page_range().iter() { 197 | let pte = page_set.page_table.translate_page(page_number).unwrap(); 198 | let frame_number = pte.frame_number(); 199 | 200 | let mut pte_flags = pte.flags(); 201 | if pte.is_writable() { 202 | pte_flags.remove(PTEFlags::W); 203 | pte_flags.insert(PTEFlags::COW); 204 | } 205 | page_mappings.push((page_number, frame_number, pte_flags)); 206 | } 207 | } 208 | } 209 | 210 | for (page_number, frame_number, pte_flags) in page_mappings { 211 | page_set 212 | .page_table 213 | .map(page_number, frame_number, pte_flags); 214 | page_set_clone 215 | .page_table 216 | .map(page_number, frame_number, pte_flags); 217 | } 218 | page_set_clone 219 | } 220 | 221 | pub fn init(&self) { 222 | unsafe { 223 | satp::write(self.satp()); 224 | asm!("sfence.vma"); 225 | } 226 | } 227 | 228 | pub fn satp(&self) -> usize { 229 | self.page_table.satp() 230 | } 231 | 232 | pub fn translate(&self, page_number: PageNumber) -> Option { 233 | self.page_table.translate_page(page_number) 234 | } 235 | 236 | pub fn clone_frame(&mut self, virtual_address: VirtualAddress) -> bool { 237 | let page_number = PageNumber::from(virtual_address); 238 | let pte = self.page_table.translate_page(page_number).unwrap(); 239 | if pte.is_cow() { 240 | if let Some(page_segment) = self.find_segment_mut(virtual_address) { 241 | let source_frame_tracker = page_segment.frame_map().get(&page_number).unwrap(); 242 | let source_frame = source_frame_tracker.frame_number(); 243 | let mut pte_flags = pte.flags(); 244 | pte_flags.insert(PTEFlags::W); 245 | pte_flags.remove(PTEFlags::COW); 246 | 247 | if Arc::strong_count(source_frame_tracker) == 1 { 248 | self.page_table.map(page_number, source_frame, pte_flags); 249 | } else { 250 | let destination_frame_tracker = allocate_frame().unwrap(); 251 | let destination_frame = destination_frame_tracker.frame_number(); 252 | destination_frame 253 | .as_bytes_mut() 254 | .clone_from_slice(source_frame.as_bytes()); 255 | page_segment 256 | .frame_map_mut() 257 | .insert(page_number, Arc::new(destination_frame_tracker)); 258 | self.page_table 259 | .map(page_number, destination_frame, pte_flags); 260 | } 261 | } 262 | true 263 | } else { 264 | false 265 | } 266 | } 267 | 268 | pub fn push(&mut self, mut segment: PageSegment, bytes: Option<&[u8]>) { 269 | segment.map_range(&mut self.page_table); 270 | if let Some(bytes) = bytes { 271 | segment.clone_bytes(&mut self.page_table, bytes); 272 | } 273 | self.segment_list.push(segment); 274 | } 275 | 276 | pub fn push_mapped(&mut self, mut segment: PageSegment, bytes: Option<&[u8]>) { 277 | if let Some(bytes) = bytes { 278 | segment.clone_bytes(&mut self.page_table, bytes); 279 | } 280 | self.segment_list.push(segment); 281 | } 282 | 283 | pub fn segment_list(&self) -> &Vec { 284 | &self.segment_list 285 | } 286 | 287 | pub fn insert_frame( 288 | &mut self, 289 | start_address: VirtualAddress, 290 | end_address: VirtualAddress, 291 | map_permission: MapPermission, 292 | ) { 293 | self.push( 294 | PageSegment::new(start_address, end_address, MapType::Framed, map_permission), 295 | None, 296 | ); 297 | } 298 | 299 | pub fn find_segment_mut(&mut self, address: VirtualAddress) -> Option<&mut PageSegment> { 300 | self.segment_list.iter_mut().find(|segment| { 301 | VirtualAddress::from(segment.start()) <= address 302 | && address < VirtualAddress::from(segment.end()) 303 | }) 304 | } 305 | 306 | /// Removes a [PageSegment] that contains a specific [VirtualAddress]. 307 | pub fn remove_segment(&mut self, address: VirtualAddress) { 308 | if let Some((index, segment)) = 309 | self.segment_list 310 | .iter_mut() 311 | .enumerate() 312 | .find(|(_, segment)| { 313 | VirtualAddress::from(segment.start()) <= address 314 | && address < VirtualAddress::from(segment.end()) 315 | }) 316 | { 317 | segment.unmap_range(&mut self.page_table); 318 | self.segment_list.remove(index); 319 | } 320 | } 321 | 322 | pub fn from_kernel() -> Self { 323 | let mut page_set = Self::new(); 324 | page_set.page_table.map( 325 | PageNumber::from(VirtualAddress::from(TRAMPOLINE)), 326 | FrameNumber::from(PhysicalAddress::from(trampoline_start as usize)), 327 | PTEFlags::R | PTEFlags::X, 328 | ); 329 | 330 | page_set.push( 331 | PageSegment::new( 332 | VirtualAddress::from(text_start as usize), 333 | VirtualAddress::from(text_end as usize), 334 | MapType::Identical, 335 | MapPermission::R | MapPermission::X, 336 | ), 337 | None, 338 | ); 339 | 340 | page_set.push( 341 | PageSegment::new( 342 | VirtualAddress::from(rodata_start as usize), 343 | VirtualAddress::from(rodata_end as usize), 344 | MapType::Identical, 345 | MapPermission::R, 346 | ), 347 | None, 348 | ); 349 | 350 | page_set.push( 351 | PageSegment::new( 352 | VirtualAddress::from(data_start as usize), 353 | VirtualAddress::from(data_end as usize), 354 | MapType::Identical, 355 | MapPermission::R | MapPermission::W, 356 | ), 357 | None, 358 | ); 359 | 360 | page_set.push( 361 | PageSegment::new( 362 | VirtualAddress::from(bss_stack_start as usize), 363 | VirtualAddress::from(bss_end as usize), 364 | MapType::Identical, 365 | MapPermission::R | MapPermission::W, 366 | ), 367 | None, 368 | ); 369 | 370 | page_set.push( 371 | PageSegment::new( 372 | VirtualAddress::from(kernel_end as usize), 373 | VirtualAddress::from(MEM_LIMIT), 374 | MapType::Identical, 375 | MapPermission::R | MapPermission::W, 376 | ), 377 | None, 378 | ); 379 | 380 | page_set 381 | } 382 | 383 | pub fn from_elf(elf_data: &[u8]) -> (Self, VirtualAddress, VirtualAddress) { 384 | let mut page_set = Self::new(); 385 | page_set.page_table.map( 386 | PageNumber::from(VirtualAddress::from(TRAMPOLINE)), 387 | FrameNumber::from(PhysicalAddress::from(trampoline_start as usize)), 388 | PTEFlags::R | PTEFlags::X, 389 | ); 390 | 391 | let elf = ElfFile::new(elf_data).unwrap(); 392 | assert_eq!( 393 | elf.header.pt1.magic, 394 | [0x7f, b'E', b'L', b'F'], 395 | "the ELF is invalid" 396 | ); 397 | 398 | let mut virtual_address_limit = VirtualAddress::from(0); 399 | for program_header_index in 0..elf.header.pt2.ph_count() { 400 | let program_header = elf.program_header(program_header_index).unwrap(); 401 | if program_header.get_type().unwrap() == Type::Load { 402 | let start_address = VirtualAddress::from(program_header.virtual_addr() as usize); 403 | let end_address = VirtualAddress::from( 404 | (program_header.virtual_addr() + program_header.mem_size()) as usize, 405 | ); 406 | 407 | let mut map_permission = MapPermission::U; 408 | if program_header.flags().is_read() { 409 | map_permission |= MapPermission::R; 410 | } 411 | 412 | if program_header.flags().is_write() { 413 | map_permission |= MapPermission::W; 414 | } 415 | 416 | if program_header.flags().is_execute() { 417 | map_permission |= MapPermission::X; 418 | } 419 | 420 | let page_segment = 421 | PageSegment::new(start_address, end_address, MapType::Framed, map_permission); 422 | virtual_address_limit = VirtualAddress::from(page_segment.end()); 423 | 424 | page_set.push( 425 | page_segment, 426 | Some( 427 | &elf.input[program_header.offset() as usize 428 | ..(program_header.offset() + program_header.file_size()) as usize], 429 | ), 430 | ); 431 | } 432 | } 433 | 434 | let user_stack_base = virtual_address_limit + PAGE_SIZE; 435 | ( 436 | page_set, 437 | user_stack_base, 438 | VirtualAddress::from(elf.header.pt2.entry_point() as usize), 439 | ) 440 | } 441 | } 442 | 443 | lazy_static! { 444 | pub static ref KERNEL_SPACE: Arc> = Arc::new(Mutex::new(PageSet::from_kernel())); 445 | } 446 | --------------------------------------------------------------------------------