├── .dockerignore ├── rust-toolchain ├── os ├── src │ ├── sync │ │ ├── mod.rs │ │ └── condvar.rs │ ├── main.rs │ ├── lang_items.rs │ ├── lib.rs │ ├── timer.rs │ ├── init.rs │ ├── consts.rs │ ├── memory │ │ ├── memory_set │ │ │ ├── attr.rs │ │ │ ├── area.rs │ │ │ ├── handler.rs │ │ │ └── mod.rs │ │ ├── frame_allocator.rs │ │ ├── mod.rs │ │ └── paging.rs │ ├── boot │ │ ├── entry64.asm │ │ └── linker64.ld │ ├── process │ │ ├── switch.asm │ │ ├── mod.rs │ │ ├── scheduler.rs │ │ ├── thread_pool.rs │ │ ├── processor.rs │ │ └── structs.rs │ ├── fs │ │ ├── stdio.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── file.rs │ ├── io.rs │ ├── sbi.rs │ ├── trap │ │ └── trap.asm │ ├── context.rs │ ├── interrupt.rs │ └── syscall.rs ├── .cargo │ └── config ├── Cargo.toml ├── Makefile └── build.rs ├── usr ├── rust │ ├── .cargo │ │ └── config │ ├── src │ │ ├── bin │ │ │ ├── model.rs │ │ │ ├── hello_world.rs │ │ │ ├── notebook.rs │ │ │ ├── user_shell.rs │ │ │ └── write.rs │ │ ├── lib.rs │ │ ├── lang_items.rs │ │ ├── io.rs │ │ └── syscall.rs │ └── Cargo.toml └── Makefile ├── .gitignore ├── test ├── usr │ ├── fork_test.rs │ ├── test_test.rs │ ├── pipe_test.rs │ ├── wait_test.rs │ └── stride_test.rs ├── test_test.rs ├── pmm_test.rs ├── mutex_test.rs └── vm_test.rs ├── Makefile ├── .github └── workflows │ └── main.yml ├── Dockerfile ├── README.md └── test.py /.dockerignore: -------------------------------------------------------------------------------- 1 | */* 2 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2020-06-04 2 | -------------------------------------------------------------------------------- /os/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod condvar; 2 | -------------------------------------------------------------------------------- /usr/rust/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64imac-unknown-none-elf" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | os/target 2 | usr/rust/target 3 | usr/build 4 | os/src/link_user.S 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /os/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[allow(unused_imports)] 5 | #[allow(clippy::single_component_path_imports)] 6 | use os; 7 | -------------------------------------------------------------------------------- /os/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.riscv64imac-unknown-none-elf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tsrc/boot/linker64.ld", 4 | ] 5 | 6 | [build] 7 | target = "riscv64imac-unknown-none-elf" 8 | -------------------------------------------------------------------------------- /usr/rust/src/bin/model.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | extern crate user; 7 | 8 | #[no_mangle] 9 | pub fn main() -> usize { 10 | 0 11 | } 12 | -------------------------------------------------------------------------------- /os/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | use core::panic::PanicInfo; 2 | 3 | #[panic_handler] 4 | fn panic(info: &PanicInfo) -> ! { 5 | println!("{}", info); 6 | loop {} 7 | } 8 | 9 | #[no_mangle] 10 | extern "C" fn abort() -> ! { 11 | panic!("abort!"); 12 | } 13 | -------------------------------------------------------------------------------- /usr/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user" 3 | version = "0.1.0" 4 | authors = ["wyfcyx "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | buddy_system_allocator = "0.3" 11 | -------------------------------------------------------------------------------- /usr/rust/src/bin/hello_world.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user; 8 | 9 | #[no_mangle] 10 | pub fn main() -> usize { 11 | for _ in 0..10 { 12 | println!("Hello world! from user mode program!"); 13 | } 14 | 0 15 | } 16 | -------------------------------------------------------------------------------- /usr/rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(llvm_asm)] 3 | #![feature(lang_items)] 4 | #![feature(panic_info_message)] 5 | #![feature(linkage)] 6 | 7 | extern crate alloc; 8 | 9 | #[macro_use] 10 | pub mod io; 11 | 12 | pub mod lang_items; 13 | pub mod syscall; 14 | 15 | use buddy_system_allocator::LockedHeap; 16 | 17 | #[global_allocator] 18 | static DYNAMIC_ALLOCATOR: LockedHeap = LockedHeap::empty(); 19 | -------------------------------------------------------------------------------- /test/usr/fork_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user; 6 | 7 | use user::syscall::sys_fork; 8 | 9 | #[no_mangle] 10 | pub fn main() -> usize { 11 | let tid = sys_fork(); 12 | let tid = sys_fork(); 13 | if tid == 0 { 14 | println!("I am child"); 15 | } else { 16 | println!("I am father"); 17 | } 18 | println!("ret tid is: {}", tid); 19 | 0 20 | } -------------------------------------------------------------------------------- /os/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(llvm_asm)] 3 | #![feature(global_asm)] 4 | #![feature(alloc_error_handler)] 5 | #![feature(naked_functions)] 6 | #![feature(const_in_array_repeat_expressions)] 7 | 8 | extern crate alloc; 9 | 10 | #[macro_use] 11 | mod io; 12 | 13 | mod consts; 14 | mod context; 15 | mod fs; 16 | mod init; 17 | mod interrupt; 18 | mod lang_items; 19 | mod memory; 20 | mod process; 21 | mod sbi; 22 | mod sync; 23 | mod syscall; 24 | mod timer; 25 | -------------------------------------------------------------------------------- /os/src/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi::set_timer; 2 | use riscv::register::{sie, time}; 3 | 4 | pub static mut TICKS: usize = 0; 5 | 6 | static TIMEBASE: u64 = 100000; 7 | pub fn init() { 8 | unsafe { 9 | TICKS = 0; 10 | sie::set_stimer(); 11 | } 12 | clock_set_next_event(); 13 | println!("++++ setup timer! ++++"); 14 | } 15 | 16 | pub fn clock_set_next_event() { 17 | set_timer(get_cycle() + TIMEBASE); 18 | } 19 | 20 | fn get_cycle() -> u64 { 21 | time::read() as u64 22 | } 23 | -------------------------------------------------------------------------------- /usr/rust/src/bin/notebook.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user; 6 | 7 | use user::io::getc; 8 | 9 | const LF: u8 = 0x0au8; 10 | const CR: u8 = 0x0du8; 11 | 12 | #[no_mangle] 13 | pub fn main() { 14 | println!("Welcome to notebook!"); 15 | loop { 16 | let c = getc(); 17 | match c { 18 | LF | CR => { 19 | print!("{}", LF as char); 20 | print!("{}", CR as char) 21 | } 22 | _ => print!("{}", c as char), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /os/src/init.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("boot/entry64.asm")); 2 | global_asm!(include_str!("link_user.S")); 3 | 4 | use crate::consts::*; 5 | 6 | #[no_mangle] 7 | pub extern "C" fn rust_main() -> ! { 8 | extern "C" { 9 | fn end(); 10 | } 11 | crate::memory::init( 12 | ((end as usize - KERNEL_BEGIN_VADDR + KERNEL_BEGIN_PADDR) >> 12) + 1, 13 | PHYSICAL_MEMORY_END >> 12, 14 | ); 15 | crate::interrupt::init(); 16 | crate::fs::init(); 17 | crate::process::init(); 18 | crate::timer::init(); 19 | crate::process::run(); 20 | loop {} 21 | } 22 | -------------------------------------------------------------------------------- /os/src/consts.rs: -------------------------------------------------------------------------------- 1 | pub const PHYSICAL_MEMORY_END: usize = 0x88000000; 2 | 3 | pub const KERNEL_BEGIN_PADDR: usize = 0x80200000; 4 | pub const KERNEL_BEGIN_VADDR: usize = 0xffffffffc0200000; 5 | 6 | pub const MAX_PHYSICAL_MEMORY: usize = 0x8000000; 7 | pub const MAX_PHYSICAL_PAGES: usize = MAX_PHYSICAL_MEMORY >> 12; 8 | 9 | pub const KERNEL_HEAP_SIZE: usize = 0x800000; 10 | 11 | pub const PHYSICAL_MEMORY_OFFSET: usize = 0xffffffff40000000; 12 | 13 | pub const PAGE_SIZE: usize = 4096; 14 | 15 | pub const KERNEL_STACK_SIZE: usize = 0x80000; 16 | 17 | pub const USER_STACK_SIZE: usize = 0x80000; 18 | pub const USER_STACK_OFFSET: usize = 0xffffffff00000000; 19 | 20 | pub const NOFILE: usize = 16; 21 | -------------------------------------------------------------------------------- /os/src/sync/condvar.rs: -------------------------------------------------------------------------------- 1 | use crate::process::{current_tid, wake_up, yield_now, Tid}; 2 | use alloc::collections::VecDeque; 3 | use spin::Mutex; 4 | 5 | #[derive(Default)] 6 | pub struct Condvar { 7 | wait_queue: Mutex>, 8 | } 9 | 10 | impl Condvar { 11 | pub fn new() -> Self { 12 | Condvar::default() 13 | } 14 | 15 | pub fn wait(&self) { 16 | self.wait_queue.lock().push_back(current_tid()); 17 | yield_now(); 18 | } 19 | 20 | pub fn notify(&self) { 21 | let tid = self.wait_queue.lock().pop_front(); 22 | if let Some(tid) = tid { 23 | wake_up(tid); 24 | } 25 | /* yield_now(); */ 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /os/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "os" 3 | version = "0.1.0" 4 | authors = ["shinbokuow"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } 11 | spin = "0.5.2" 12 | buddy_system_allocator = "0.3" 13 | xmas-elf = "0.6" 14 | rcore-fs = { git = "https://github.com/rcore-os/rcore-fs", rev = "7f5eeac" } 15 | rcore-fs-sfs = { git = "https://github.com/rcore-os/rcore-fs", rev = "7f5eeac" } 16 | 17 | [build-dependencies] 18 | chrono = "0.4" 19 | 20 | [dependencies.lazy_static] 21 | version = "1.0" 22 | features = ["spin_no_std"] 23 | -------------------------------------------------------------------------------- /test/test_test.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("boot/entry64.asm")); 2 | global_asm!(include_str!("link_user.S")); 3 | 4 | use crate::consts::*; 5 | use crate::memory::{alloc_frame, dealloc_frame}; 6 | 7 | #[no_mangle] 8 | pub extern "C" fn rust_main() -> ! { 9 | extern "C" { 10 | fn end(); 11 | } 12 | crate::memory::init( 13 | ((end as usize - KERNEL_BEGIN_VADDR + KERNEL_BEGIN_PADDR) >> 12) + 1, 14 | PHYSICAL_MEMORY_END >> 12, 15 | ); 16 | crate::interrupt::init(); 17 | /* 18 | crate::fs::init(); 19 | crate::process::init(); 20 | crate::timer::init(); 21 | crate::process::run(); 22 | */ 23 | println!("Hello world!"); 24 | // crate::sbi::shutdown(); 25 | loop {} 26 | } 27 | -------------------------------------------------------------------------------- /os/src/memory/memory_set/attr.rs: -------------------------------------------------------------------------------- 1 | use crate::memory::paging::PageEntry; 2 | 3 | #[derive(Clone, Default, Debug)] 4 | pub struct MemoryAttr { 5 | user: bool, 6 | readonly: bool, 7 | execute: bool, 8 | } 9 | 10 | impl MemoryAttr { 11 | pub fn set_user(mut self) -> Self { 12 | self.user = true; 13 | self 14 | } 15 | pub fn set_readonly(mut self) -> Self { 16 | self.readonly = true; 17 | self 18 | } 19 | pub fn set_execute(mut self) -> Self { 20 | self.execute = true; 21 | self 22 | } 23 | 24 | pub fn apply(&self, entry: &mut PageEntry) { 25 | entry.set_present(true); 26 | entry.set_user(self.user); 27 | entry.set_writable(!self.readonly); 28 | entry.set_execute(self.execute); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /os/src/boot/entry64.asm: -------------------------------------------------------------------------------- 1 | .section .text.entry 2 | .globl _start 3 | _start: 4 | lui t0, %hi(boot_page_table_sv39) 5 | li t1, 0xffffffffc0000000 - 0x80000000 6 | sub t0, t0, t1 7 | srli t0, t0, 12 8 | li t1, 8 << 60 9 | or t0, t0, t1 10 | csrw satp, t0 11 | sfence.vma 12 | 13 | lui sp, %hi(bootstacktop) 14 | 15 | lui t0, %hi(rust_main) 16 | addi t0, t0, %lo(rust_main) 17 | jr t0 18 | 19 | .section .bss.stack 20 | .align 12 21 | .global bootstack 22 | bootstack: 23 | .space 4096 * 4 24 | .global bootstacktop 25 | bootstacktop: 26 | 27 | .section .data 28 | .align 12 # page align 29 | boot_page_table_sv39: 30 | # 0xffffffff_c0000000 -> 0x80000000 (1G) 31 | .zero 8 * 511 32 | .quad (0x80000 << 10) | 0xcf # VRWXAD 33 | -------------------------------------------------------------------------------- /os/Makefile: -------------------------------------------------------------------------------- 1 | target := riscv64imac-unknown-none-elf 2 | mode := debug 3 | kernel := target/$(target)/$(mode)/os 4 | bin := target/$(target)/$(mode)/kernel.bin 5 | 6 | objdump := rust-objdump --arch-name=riscv64 7 | objcopy := rust-objcopy --binary-architecture=riscv64 8 | 9 | .PHONY: kernel build clean qemu run env 10 | 11 | env: 12 | cargo install cargo-binutils 13 | rustup component add llvm-tools-preview 14 | rustup target add $(target) 15 | 16 | export USER_IMG = ../usr/build/riscv64.img 17 | 18 | kernel: 19 | cargo build 20 | 21 | $(bin): kernel 22 | $(objcopy) $(kernel) --strip-all -O binary $@ 23 | 24 | asm: 25 | $(objdump) -d $(kernel) | less 26 | 27 | build: $(bin) 28 | 29 | clean: 30 | cargo clean 31 | 32 | qemu: build 33 | qemu-system-riscv64 \ 34 | -machine virt \ 35 | -nographic \ 36 | -bios default \ 37 | -device loader,file=$(bin),addr=0x80200000 38 | 39 | run: build qemu 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DOCKER_NAME ?= panqinglin/tutorial 2 | .PHONY: docker build_docker 3 | all: 4 | make -C usr user_img 5 | make -C os build 6 | run: 7 | make -C usr user_img 8 | make -C os run 9 | clean: 10 | make -C usr clean 11 | make -C os clean 12 | env: 13 | make -C os env 14 | docker: 15 | docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME} 16 | 17 | build_docker: qemu-4.1.1.tar.xz riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 18 | docker build -t ${DOCKER_NAME} . 19 | rm qemu-4.1.1.tar.xz 20 | rm riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 21 | 22 | qemu-4.1.1.tar.xz: 23 | wget https://download.qemu.org/qemu-4.1.1.tar.xz 24 | 25 | riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz: 26 | wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz 27 | 28 | -------------------------------------------------------------------------------- /os/src/process/switch.asm: -------------------------------------------------------------------------------- 1 | .equ XLENB, 8 2 | .macro Load a1, a2 3 | ld \a1, \a2*XLENB(sp) 4 | .endm 5 | .macro Store a1, a2 6 | sd \a1, \a2*XLENB(sp) 7 | .endm 8 | addi sp, sp, -14*XLENB 9 | sd sp, 0(a0) 10 | Store ra, 0 11 | Store s0, 2 12 | Store s1, 3 13 | Store s2, 4 14 | Store s3, 5 15 | Store s4, 6 16 | Store s5, 7 17 | Store s6, 8 18 | Store s7, 9 19 | Store s8, 10 20 | Store s9, 11 21 | Store s10, 12 22 | Store s11, 13 23 | csrr s11, satp 24 | Store s11, 1 25 | 26 | ld sp, 0(a1) 27 | Load s11, 1 28 | csrw satp, s11 29 | sfence.vma 30 | Load ra, 0 31 | Load s0, 2 32 | Load s1, 3 33 | Load s2, 4 34 | Load s3, 5 35 | Load s4, 6 36 | Load s5, 7 37 | Load s6, 8 38 | Load s7, 9 39 | Load s8, 10 40 | Load s9, 11 41 | Load s10, 12 42 | Load s11, 13 43 | addi sp, sp, 14*XLENB 44 | 45 | sd zero, 0(a1) 46 | ret 47 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions-rs/toolchain@v1 11 | with: 12 | components: rustfmt, clippy 13 | - name: Setup Environment 14 | run: cd os && make env 15 | - name: Check user 16 | run: cd usr/rust/ && cargo fmt -- --check && cargo clippy -- -D warnings 17 | - name: Check os 18 | run: export USER_IMG="../usr/build/riscv64.img" && cd os && cargo fmt -- --check && cargo clippy -- -D warnings 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Setup Environment 24 | run: cd os && make env 25 | - name: Build user 26 | run: cd usr && make user_img 27 | - name: Build kernel 28 | run: cd os && make build 29 | -------------------------------------------------------------------------------- /usr/Makefile: -------------------------------------------------------------------------------- 1 | target := riscv64imac-unknown-none-elf 2 | mode := debug 3 | rust_src_dir := rust/src/bin 4 | rust_target_dir := rust/target/$(target)/$(mode) 5 | rust_srcs := $(wildcard $(rust_src_dir)/*.rs) 6 | rust_targets := $(patsubst $(rust_src_dir)/%.rs, $(rust_target_dir)/%, $(rust_srcs)) 7 | out_dir := build/riscv64 8 | sfsimg := build/riscv64.img 9 | .PHONY: rcore-fs-fuse rust user_img clean 10 | 11 | 12 | rcore-fs-fuse: 13 | ifeq ($(shell which rcore-fs-fuse),) 14 | @echo Installing rcore-fs-fuse 15 | @cargo install rcore-fs-fuse --git https://github.com/rcore-os/rcore-fs --rev 7f5eeac 16 | endif 17 | 18 | rust: 19 | @cd rust && cargo build 20 | @echo targets includes $(rust_targets) 21 | @rm -rf $(out_dir)/rust && mkdir -p $(out_dir)/rust 22 | @rm -f $(sfsimg) 23 | @cp $(rust_targets) $(out_dir)/rust 24 | 25 | $(sfsimg): rcore-fs-fuse rust 26 | @dd if=/dev/zero of=$(out_dir)/temp bs=1k count=2 27 | @rcore-fs-fuse --fs sfs $@ $(out_dir) zip 28 | 29 | user_img: $(sfsimg) 30 | 31 | clean: 32 | @rm -rf build/ 33 | -------------------------------------------------------------------------------- /usr/rust/src/bin/user_shell.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user; 8 | 9 | const LF: u8 = 0x0au8; 10 | const CR: u8 = 0x0du8; 11 | 12 | use alloc::string::String; 13 | use user::io::getc; 14 | use user::syscall::sys_exec; 15 | 16 | #[no_mangle] 17 | pub fn main() { 18 | println!("Rust user shell"); 19 | let mut line: String = String::new(); 20 | print!(">> "); 21 | loop { 22 | let c = getc(); 23 | match c { 24 | LF | CR => { 25 | println!(); 26 | if !line.is_empty() { 27 | line.push('\0'); 28 | println!("searching for program {}", line); 29 | sys_exec(line.as_ptr()); 30 | line.clear(); 31 | } 32 | print!(">> "); 33 | } 34 | _ => { 35 | print!("{}", c as char); 36 | line.push(c as char); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /os/src/fs/stdio.rs: -------------------------------------------------------------------------------- 1 | use crate::sync::condvar::*; 2 | use alloc::{collections::VecDeque, sync::Arc}; 3 | use lazy_static::*; 4 | use spin::Mutex; 5 | 6 | pub struct Stdin { 7 | buf: Mutex>, 8 | pushed: Condvar, 9 | } 10 | 11 | impl Stdin { 12 | pub fn new() -> Self { 13 | Stdin { 14 | buf: Mutex::new(VecDeque::new()), 15 | pushed: Condvar::new(), 16 | } 17 | } 18 | 19 | pub fn push(&self, ch: char) { 20 | self.buf.lock().push_back(ch); 21 | self.pushed.notify(); 22 | } 23 | 24 | pub fn pop(&self) -> char { 25 | loop { 26 | let ret = self.buf.lock().pop_front(); 27 | match ret { 28 | Some(ch) => { 29 | return ch; 30 | } 31 | None => { 32 | self.pushed.wait(); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | lazy_static! { 40 | pub static ref STDIN: Arc = Arc::new(Stdin::new()); 41 | } 42 | -------------------------------------------------------------------------------- /os/src/fs/device.rs: -------------------------------------------------------------------------------- 1 | use rcore_fs::dev::*; 2 | use spin::RwLock; 3 | 4 | pub struct MemBuf(RwLock<&'static mut [u8]>); 5 | 6 | impl MemBuf { 7 | pub unsafe fn new(begin: usize, end: usize) -> Self { 8 | use core::slice; 9 | MemBuf(RwLock::new(slice::from_raw_parts_mut( 10 | begin as *mut u8, 11 | end - begin, 12 | ))) 13 | } 14 | } 15 | 16 | impl Device for MemBuf { 17 | fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { 18 | let slice = self.0.read(); 19 | let len = buf.len().min(slice.len() - offset); 20 | buf[..len].copy_from_slice(&slice[offset..offset + len]); 21 | Ok(len) 22 | } 23 | fn write_at(&self, offset: usize, buf: &[u8]) -> Result { 24 | let mut slice = self.0.write(); 25 | let len = buf.len().min(slice.len() - offset); 26 | slice[offset..offset + len].copy_from_slice(&buf[..len]); 27 | Ok(len) 28 | } 29 | fn sync(&self) -> Result<()> { 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /os/src/boot/linker64.ld: -------------------------------------------------------------------------------- 1 | /* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore */ 2 | 3 | /* Simple linker script for the ucore kernel. 4 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 5 | 6 | OUTPUT_ARCH(riscv) 7 | ENTRY(_start) 8 | 9 | BASE_ADDRESS = 0xffffffffc0200000; 10 | 11 | SECTIONS 12 | { 13 | /* Load the kernel at this address: "." means the current address */ 14 | . = BASE_ADDRESS; 15 | start = .; 16 | 17 | .text : { 18 | stext = .; 19 | *(.text.entry) 20 | *(.text .text.*) 21 | . = ALIGN(4K); 22 | etext = .; 23 | } 24 | 25 | .rodata : { 26 | srodata = .; 27 | *(.rodata .rodata.*) 28 | . = ALIGN(4K); 29 | erodata = .; 30 | } 31 | 32 | .data : { 33 | sdata = .; 34 | *(.data .data.*) 35 | edata = .; 36 | } 37 | 38 | .stack : { 39 | *(.bss.stack) 40 | } 41 | 42 | .bss : { 43 | sbss = .; 44 | *(.bss .bss.*) 45 | ebss = .; 46 | } 47 | 48 | PROVIDE(end = .); 49 | } 50 | -------------------------------------------------------------------------------- /os/src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi; 2 | use core::fmt::{self, Write}; 3 | 4 | pub fn putchar(ch: char) { 5 | sbi::console_putchar(ch as u8 as usize); 6 | } 7 | 8 | pub fn puts(s: &str) { 9 | for ch in s.chars() { 10 | putchar(ch); 11 | } 12 | } 13 | 14 | struct Stdout; 15 | 16 | impl fmt::Write for Stdout { 17 | fn write_str(&mut self, s: &str) -> fmt::Result { 18 | puts(s); 19 | Ok(()) 20 | } 21 | } 22 | 23 | pub fn _print(args: fmt::Arguments) { 24 | Stdout.write_fmt(args).unwrap(); 25 | } 26 | 27 | #[macro_export] 28 | macro_rules! print { 29 | ($($arg:tt)*) => ({ 30 | $crate::io::_print(format_args!($($arg)*)); 31 | }); 32 | } 33 | 34 | #[macro_export] 35 | macro_rules! println { 36 | () => ($crate::print!("\n")); 37 | ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); 38 | } 39 | 40 | // 调用 OpenSBI 接口 41 | pub fn getchar_option() -> Option { 42 | let c = sbi::console_getchar() as isize; 43 | match c { 44 | -1 => None, 45 | c => Some(c as u8 as char), 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /os/build.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | use chrono::prelude::*; 3 | use std::fs::File; 4 | use std::io::{Result, Write}; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-env-changed=USER_IMG"); 8 | if let Ok(user_img) = std::env::var("USER_IMG") { 9 | println!("cargo:rerun-if-changed={}", user_img); 10 | } 11 | gen_link_user_asm().unwrap(); 12 | } 13 | 14 | /// Generate assembly file for linking user image 15 | fn gen_link_user_asm() -> Result<()> { 16 | let mut f = File::create("src/link_user.S").unwrap(); 17 | let user_img = std::env::var("USER_IMG").unwrap(); 18 | let local: DateTime = Local::now(); 19 | 20 | writeln!(f, "# generated by build.rs - do not edit")?; 21 | // 如果不加上时间戳,由于link_user.S没有改变,按照Rust增量更新机制,用户程序镜像不会更新 22 | writeln!(f, "# last modified at: {}", local.to_string())?; 23 | writeln!( 24 | f, 25 | r#" 26 | .section .data 27 | .global _user_img_start 28 | .global _user_img_end 29 | _user_img_start: 30 | .incbin "{}" 31 | _user_img_end: 32 | "#, 33 | user_img 34 | )?; 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /usr/rust/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | use crate::syscall::sys_exit; 2 | use core::alloc::Layout; 3 | use core::panic::PanicInfo; 4 | 5 | #[linkage = "weak"] 6 | #[no_mangle] 7 | fn main() -> usize { 8 | panic!("No main() linked"); 9 | } 10 | 11 | use crate::DYNAMIC_ALLOCATOR; 12 | 13 | fn init_heap() { 14 | const HEAP_SIZE: usize = 0x1000; 15 | static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 16 | unsafe { 17 | DYNAMIC_ALLOCATOR 18 | .lock() 19 | .init(HEAP.as_ptr() as usize, HEAP_SIZE); 20 | } 21 | } 22 | 23 | #[panic_handler] 24 | fn panic(_info: &PanicInfo) -> ! { 25 | let location = _info.location().unwrap(); 26 | let message = _info.message().unwrap(); 27 | println!( 28 | "\nPANIC in {} at line {} \n\t{}", 29 | location.file(), 30 | location.line(), 31 | message 32 | ); 33 | loop {} 34 | } 35 | 36 | #[no_mangle] 37 | pub extern "C" fn _start(_args: isize, _argv: *const u8) -> ! { 38 | init_heap(); 39 | sys_exit(main()) 40 | } 41 | 42 | #[no_mangle] 43 | pub extern "C" fn abort() { 44 | panic!("abort"); 45 | } 46 | 47 | #[lang = "oom"] 48 | fn oom(_: Layout) -> ! { 49 | panic!("out of memory!"); 50 | } 51 | -------------------------------------------------------------------------------- /usr/rust/src/bin/write.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user; 8 | 9 | use user::io::*; 10 | use user::syscall::{sys_close, sys_open, sys_read, sys_write}; 11 | 12 | const BUFFER_SIZE: usize = 20; 13 | const FILE: &str = "temp\0"; 14 | const TEXT: &str = "Hello world!\0"; 15 | 16 | #[no_mangle] 17 | pub fn main() -> usize { 18 | // 将字符串写到文件 temp 中 19 | let write_fd = sys_open(FILE.as_ptr(), O_WRONLY); 20 | sys_write(write_fd as usize, TEXT.as_ptr(), TEXT.len()); 21 | println!("write to file 'temp' successfully..."); 22 | sys_close(write_fd as i32); 23 | 24 | // 将字符串从文件 temp 读入内存 25 | let read_fd = sys_open(FILE.as_ptr(), O_RDONLY); 26 | let read = [0u8; BUFFER_SIZE]; 27 | sys_read(read_fd as usize, read[0] as *mut u8, BUFFER_SIZE); 28 | println!("read from file 'temp' successfully..."); 29 | 30 | // 检查功能是否正确 31 | let len = (0..BUFFER_SIZE).find(|&i| read[i] as u8 == 0).unwrap(); 32 | print!("content = "); 33 | for (i, item) in read.iter().enumerate().take(len) { 34 | assert!(*item == TEXT.as_bytes()[i]); 35 | putchar(*item as char); 36 | } 37 | putchar('\n'); 38 | sys_close(read_fd as i32); 39 | 0 40 | } 41 | -------------------------------------------------------------------------------- /test/usr/test_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user; 8 | 9 | use user::io::*; 10 | use user::syscall::{ 11 | sys_open, 12 | sys_close, 13 | sys_read, 14 | sys_write, 15 | }; 16 | 17 | const BUFFER_SIZE: usize = 20; 18 | const FILE: &'static str = "temp\0"; 19 | const TEXT: &'static str = "Hello world!\0"; 20 | 21 | #[no_mangle] 22 | pub fn main() -> usize { 23 | // 将字符串写到文件 temp 中 24 | let write_fd = sys_open(FILE.as_ptr(), O_WRONLY); 25 | sys_write(write_fd as usize, TEXT.as_ptr(), TEXT.len()); 26 | println!("write to file 'temp' successfully..."); 27 | sys_close(write_fd as i32); 28 | 29 | // 将字符串从文件 temp 读入内存 30 | let read_fd = sys_open(FILE.as_ptr(), O_RDONLY); 31 | let mut read = [0u8; BUFFER_SIZE]; 32 | sys_read(read_fd as usize, &read[0] as *const u8, BUFFER_SIZE); 33 | println!("read from file 'temp' successfully..."); 34 | 35 | // 检查功能是否正确 36 | let len = (0..BUFFER_SIZE).find(|&i| read[i] as u8 == 0).unwrap(); 37 | print!("content = "); 38 | for i in 0usize..len { 39 | assert!(read[i] == TEXT.as_bytes()[i]); 40 | putchar(read[i] as char); 41 | } 42 | putchar('\n'); 43 | sys_close(read_fd as i32); 44 | 0 45 | } 46 | -------------------------------------------------------------------------------- /usr/rust/src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::syscall::sys_read; 2 | use crate::syscall::sys_write; 3 | use core::fmt::{self, Write}; 4 | 5 | pub fn putchar(ch: char) { 6 | sys_write(STDOUT, &ch as *const char as *const u8, 1); 7 | } 8 | 9 | pub fn puts(s: &str) { 10 | for ch in s.chars() { 11 | putchar(ch); 12 | } 13 | } 14 | 15 | #[macro_export] 16 | macro_rules! print { 17 | ($($arg:tt)*) => ({ 18 | $crate::io::_print(format_args!($($arg)*)); 19 | }); 20 | } 21 | 22 | #[macro_export] 23 | macro_rules! println { 24 | () => ($crate::print!("\n")); 25 | ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); 26 | } 27 | 28 | struct Stdout; 29 | 30 | impl fmt::Write for Stdout { 31 | fn write_str(&mut self, s: &str) -> fmt::Result { 32 | puts(s); 33 | Ok(()) 34 | } 35 | } 36 | 37 | pub fn _print(args: fmt::Arguments) { 38 | Stdout.write_fmt(args).unwrap(); 39 | } 40 | 41 | pub const STDIN: usize = 0; 42 | pub const STDOUT: usize = 1; 43 | 44 | pub fn getc() -> u8 { 45 | let mut c = 0u8; 46 | assert_eq!(sys_read(STDIN, &mut c, 1), 1); 47 | c 48 | } 49 | 50 | pub const O_RDONLY: i32 = 0; // 只读 51 | pub const O_WRONLY: i32 = 1; // 只写 52 | pub const O_RDWR: i32 = 2; // 可读可写 53 | pub const O_CREAT: i32 = 64; // 打开文件时若文件不存在,创建它 54 | pub const O_APPEND: i32 = 1024; // 从文件结尾开始写入 55 | -------------------------------------------------------------------------------- /usr/rust/src/syscall.rs: -------------------------------------------------------------------------------- 1 | enum SyscallId { 2 | Open = 56, 3 | Close = 57, 4 | Read = 63, 5 | Write = 64, 6 | Exit = 93, 7 | Exec = 221, 8 | } 9 | 10 | #[inline(always)] 11 | fn sys_call(syscall_id: SyscallId, arg0: usize, arg1: usize, arg2: usize, arg3: usize) -> i64 { 12 | let id = syscall_id as usize; 13 | let mut ret: i64; 14 | unsafe { 15 | llvm_asm!( 16 | "ecall" 17 | : "={x10}"(ret) 18 | : "{x17}"(id), "{x10}"(arg0), "{x11}"(arg1), "{x12}"(arg2), "{x13}"(arg3) 19 | : "memory" 20 | : "volatile" 21 | ); 22 | } 23 | ret 24 | } 25 | 26 | pub fn sys_open(path: *const u8, flags: i32) -> i64 { 27 | sys_call(SyscallId::Open, path as usize, flags as usize, 0, 0) 28 | } 29 | 30 | pub fn sys_close(fd: i32) -> i64 { 31 | sys_call(SyscallId::Close, fd as usize, 0, 0, 0) 32 | } 33 | 34 | pub fn sys_write(fd: usize, base: *const u8, len: usize) -> i64 { 35 | sys_call(SyscallId::Write, fd, base as usize, len, 0) 36 | } 37 | 38 | pub fn sys_exit(code: usize) -> ! { 39 | sys_call(SyscallId::Exit, code, 0, 0, 0); 40 | loop {} 41 | } 42 | 43 | pub fn sys_read(fd: usize, base: *mut u8, len: usize) -> i64 { 44 | sys_call(SyscallId::Read, fd, base as usize, len, 0) 45 | } 46 | 47 | pub fn sys_exec(path: *const u8) { 48 | sys_call(SyscallId::Exec, path as usize, 0, 0, 0); 49 | } 50 | -------------------------------------------------------------------------------- /os/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | mod device; 2 | pub mod file; 3 | pub mod stdio; 4 | 5 | use alloc::{sync::Arc, vec::Vec}; 6 | use lazy_static::*; 7 | use rcore_fs::vfs::*; 8 | use rcore_fs_sfs::SimpleFileSystem; 9 | 10 | lazy_static! { 11 | pub static ref ROOT_INODE: Arc = { 12 | let device = { 13 | extern "C" { 14 | fn _user_img_start(); 15 | fn _user_img_end(); 16 | }; 17 | let start = _user_img_start as usize; 18 | let end = _user_img_end as usize; 19 | Arc::new(unsafe { device::MemBuf::new(start, end) }) 20 | }; 21 | let sfs = SimpleFileSystem::open(device).expect("failed to open SFS"); 22 | sfs.root_inode() 23 | }; 24 | } 25 | 26 | pub trait INodeExt { 27 | fn read_as_vec(&self) -> Result>; 28 | } 29 | 30 | impl INodeExt for dyn INode { 31 | fn read_as_vec(&self) -> Result> { 32 | let size = self.metadata()?.size; 33 | let mut buf = Vec::with_capacity(size); 34 | unsafe { 35 | buf.set_len(size); 36 | } 37 | self.read_at(0, buf.as_mut_slice())?; 38 | Ok(buf) 39 | } 40 | } 41 | 42 | pub fn init() { 43 | println!("available programs in rust/ are:"); 44 | let mut id = 0; 45 | let rust_dir = ROOT_INODE.lookup("rust").unwrap(); 46 | while let Ok(name) = rust_dir.get_entry(id) { 47 | id += 1; 48 | println!(" {}", name); 49 | } 50 | println!("++++ setup fs! ++++") 51 | } 52 | -------------------------------------------------------------------------------- /test/usr/pipe_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user; 8 | 9 | use user::io::*; 10 | use user::syscall::{ 11 | sys_pipe, 12 | sys_close, 13 | sys_read, 14 | sys_write, 15 | sys_fork, 16 | }; 17 | use alloc::string::String; 18 | 19 | #[no_mangle] 20 | pub fn main() -> usize { 21 | let mut pipefd: [i32; 2] = [0; 2]; 22 | sys_pipe(&mut pipefd); 23 | println!("fd_read = {}, fd_write = {}", pipefd[0], pipefd[1]); 24 | let pid = sys_fork(); 25 | if pid == 0 { 26 | // child process, read from pipe 27 | // close write end of pipe 28 | sys_close(pipefd[1]); 29 | let mut string = String::from(""); 30 | let ch: u8 = 0; 31 | loop { 32 | sys_read(pipefd[0] as usize, &ch as *const u8, 1); 33 | if ch == 0 { 34 | break; 35 | } 36 | string.push(ch as char); 37 | } 38 | println!("message received in child process = {}", string); 39 | } else { 40 | // parent process, write to pipe 41 | // close read end of pipe 42 | sys_close(pipefd[0]); 43 | let string = String::from("Hello world!"); 44 | for ch in string.bytes() { 45 | sys_write(pipefd[1] as usize, &ch as *const u8, 1); 46 | } 47 | let ch: u8 = 0; 48 | sys_write(pipefd[1] as usize, &ch as *const u8, 1); 49 | println!("message sent to child process pid {}!", pid); 50 | } 51 | 0 52 | } 53 | -------------------------------------------------------------------------------- /test/usr/wait_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user; 6 | 7 | use user::syscall::{sys_exit as exit, sys_yield as yield_now, sys_fork as fork, sys_wait as waitpid}; 8 | 9 | #[no_mangle] 10 | pub fn main() -> isize { 11 | let magic: usize = 0x10384; 12 | let mut pid: usize = 0; 13 | let mut code: i32 = 0; 14 | println!("I am the parent. Forking the child..."); 15 | pid = fork() as usize; 16 | if (pid == 0) { 17 | println!("I am the child."); 18 | yield_now(); 19 | yield_now(); 20 | yield_now(); 21 | yield_now(); 22 | yield_now(); 23 | yield_now(); 24 | yield_now(); 25 | exit(magic); 26 | } 27 | else { 28 | println!("I am parent, fork a child pid {}", pid); 29 | } 30 | if pid <= 0 { 31 | panic!("pid <= 0"); 32 | } 33 | println!("I am the parent, waiting now.."); 34 | let wait_pid = waitpid(pid, &mut code); 35 | println!("{}, {:x}", wait_pid, code); 36 | if wait_pid != 0 || code != magic as i32 { 37 | panic!("wait_test1 fail"); 38 | } 39 | if !(waitpid(pid, &mut code) != 0) { 40 | panic!("wait_test2 fail"); 41 | } 42 | println!("waitpid {} ok.", pid); 43 | println!("wait_test pass."); 44 | return 0; 45 | } 46 | 47 | /* 48 | out put: 49 | 50 | I am the parent. Forking the child... 51 | I am the child. 52 | I am parent, fork a child pid 2 53 | I am the parent, waiting now.. 54 | thread 2 exited, exit code = 66436 55 | waitpid 2 ok. 56 | wait_test pass. 57 | thread 1 exited, exit code = 0 58 | */ 59 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM buildpack-deps:xenial 2 | 3 | ENV RUSTUP_HOME=/usr/local/rustup \ 4 | CARGO_HOME=/usr/local/cargo \ 5 | PATH=/usr/local/cargo/bin:$PATH 6 | 7 | RUN set -eux; \ 8 | \ 9 | url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \ 10 | wget "$url"; \ 11 | chmod +x rustup-init; \ 12 | ./rustup-init -y --no-modify-path --default-toolchain nightly-2019-12-08; \ 13 | rm rustup-init; \ 14 | chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ 15 | rustup --version; \ 16 | cargo --version; \ 17 | rustc --version; 18 | 19 | # 以上部分来自https://hub.docker.com/r/rustlang/rust/dockerfile 20 | 21 | # install QEMU 22 | ADD qemu-4.1.1.tar.xz . 23 | RUN cd qemu-4.1.1 \ 24 | && ./configure --target-list=riscv64-softmmu \ 25 | && make -j \ 26 | && make install \ 27 | && cd .. \ 28 | && rm qemu-4.1.1 -r 29 | 30 | # riscv gcc needed by rustc 31 | ADD riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz . 32 | ENV PATH=$PWD/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin:$PATH 33 | 34 | # install others 35 | RUN apt update \ 36 | && apt install less device-tree-compiler -y \ 37 | && apt-get clean \ 38 | && rm -rf /var/lib/apt/lists/* 39 | 40 | # install Rust tools 41 | RUN echo '[source.crates-io]' >> $CARGO_HOME/config \ 42 | && echo 'replace-with = \047ustc\047' >> $CARGO_HOME/config \ 43 | && echo '[source.ustc]' >> $CARGO_HOME/config \ 44 | && echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_HOME/config \ 45 | && cargo install cargo-binutils cargo-xbuild \ 46 | && rustup component add rust-src \ 47 | && rustup component add llvm-tools-preview \ 48 | && rustup target add riscv64imac-unknown-none-elf 49 | -------------------------------------------------------------------------------- /test/usr/stride_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user; 6 | 7 | use user::syscall::{ 8 | set_priority, sys_exit as exit, sys_fork as fork, sys_gettime as gettime_msec, 9 | }; 10 | 11 | fn spin_delay() { 12 | let mut j = true; 13 | for i in 0..10 { 14 | j = !j; 15 | } 16 | } 17 | 18 | #[no_mangle] 19 | pub fn main() -> usize { 20 | const TOTAL: usize = 5; 21 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 22 | let MAX_TIME = 1000; 23 | set_priority(TOTAL + 1); 24 | let start_time = gettime_msec(); 25 | for i in 0..TOTAL { 26 | let pids = fork() as usize; 27 | if pids == 0 { 28 | let mut acc = 0; 29 | set_priority(i + 1); 30 | loop { 31 | spin_delay(); 32 | acc += 1; 33 | if acc % 400 == 0 { 34 | let time = gettime_msec() - start_time; 35 | if time > MAX_TIME { 36 | exit(acc); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | println!("main: fork ok."); 43 | return 0; 44 | } 45 | 46 | /* 47 | out put: 48 | 49 | main: fork ok. 50 | thread 1 exited, exit code = 0 51 | >> thread 6 exited, exit code = 517600 52 | thread 5 exited, exit code = 408400 53 | thread 3 exited, exit code = 210400 54 | thread 4 exited, exit code = 316800 55 | thread 2 exited, exit code = 111600 56 | 57 | // 多出来的 `>>` 是由于目前 rcore 的 wait/fork 不完善导致的 58 | // 等一位哥哥来修复 59 | // 到时候测试估计就检察 exit code 60 | // 检察方式(大概): 61 | // sort(code, code + 5); 62 | // for i in 0..5 { 63 | // assert!((code[i] * 2 / code[0] + 1) / 2 == i + 1); 64 | // } 65 | */ 66 | -------------------------------------------------------------------------------- /os/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod processor; 2 | pub mod scheduler; 3 | pub mod structs; 4 | pub mod thread_pool; 5 | 6 | use crate::fs::{INodeExt, ROOT_INODE}; 7 | use alloc::boxed::Box; 8 | use processor::Processor; 9 | use scheduler::RRScheduler; 10 | use structs::Thread; 11 | use thread_pool::ThreadPool; 12 | 13 | pub type Tid = usize; 14 | pub type ExitCode = usize; 15 | 16 | static CPU: Processor = Processor::new(); 17 | 18 | pub fn init() { 19 | let scheduler = RRScheduler::new(1); 20 | let thread_pool = ThreadPool::new(100, Box::new(scheduler)); 21 | let idle = Thread::new_kernel(Processor::idle_main as usize); 22 | idle.append_initial_arguments([&CPU as *const Processor as usize, 0, 0]); 23 | CPU.init(idle, Box::new(thread_pool)); 24 | 25 | execute("rust/user_shell", None); 26 | 27 | println!("++++ setup process! ++++"); 28 | } 29 | 30 | pub fn execute(path: &str, host_tid: Option) -> bool { 31 | let find_result = ROOT_INODE.lookup(path); 32 | match find_result { 33 | Ok(inode) => { 34 | let data = inode.read_as_vec().unwrap(); 35 | let user_thread = unsafe { Thread::new_user(data.as_slice(), host_tid) }; 36 | CPU.add_thread(user_thread); 37 | true 38 | } 39 | Err(_) => { 40 | println!("command not found!"); 41 | false 42 | } 43 | } 44 | } 45 | 46 | pub fn tick() { 47 | CPU.tick(); 48 | } 49 | 50 | pub fn run() { 51 | CPU.run(); 52 | } 53 | 54 | pub fn exit(code: usize) { 55 | CPU.exit(code); 56 | } 57 | 58 | pub fn yield_now() { 59 | CPU.yield_now(); 60 | } 61 | 62 | pub fn wake_up(tid: Tid) { 63 | CPU.wake_up(tid); 64 | } 65 | pub fn current_tid() -> usize { 66 | CPU.current_tid() 67 | } 68 | 69 | pub fn current_thread_mut() -> &'static mut Thread { 70 | CPU.current_thread_mut() 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rCore_tutorial 2 | 3 | [![Actions Status](https://github.com/rcore-os/rCore_tutorial/workflows/CI/badge.svg)](https://github.com/rcore-os/rCore_tutorial/actions) 4 | 5 | ## WARNING 6 | 7 | This project is *no longer maintained*, please try [Tutorial v3.5](https://github.com/rcore-os/rCore-Tutorial-v3). 8 | 9 | ## Documentations 10 | 11 | Please read 12 | - https://rcore-os.github.io/rCore_tutorial_doc/ 13 | - [Docs Repository](https://github.com/rcore-os/rCore_tutorial_doc) 14 | - https://github.com/rcore-os/rCore/wiki/os-tutorial-os2atc 15 | 16 | ## Prerequisite 17 | 18 | You need: `rustup` installed, ensure `~/.cargo/bin` is added to PATH and run: 19 | 20 | ```shell 21 | make env 22 | ``` 23 | 24 | ## Quick Try 25 | 26 | ```shell 27 | $ make run 28 | ## If everything is OK, then you will see below info: 29 | ...... 30 | OpenSBI v0.4 (Jul 2 2019 11:53:53) 31 | ____ _____ ____ _____ 32 | / __ \ / ____| _ \_ _| 33 | | | | |_ __ ___ _ __ | (___ | |_) || | 34 | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | 35 | | |__| | |_) | __/ | | |____) | |_) || |_ 36 | \____/| .__/ \___|_| |_|_____/|____/_____| 37 | | | 38 | |_| 39 | 40 | Platform Name : QEMU Virt Machine 41 | Platform HART Features : RV64ACDFIMSU 42 | Platform Max HARTs : 8 43 | Current Hart : 0 44 | Firmware Base : 0x80000000 45 | Firmware Size : 112 KB 46 | Runtime SBI Version : 0.1 47 | 48 | PMP0: 0x0000000080000000-0x000000008001ffff (A) 49 | PMP1: 0x0000000000000000-0xffffffffffffffff (A,R,W,X) 50 | switch satp from 0x8000000000080256 to 0x800000000008119f 51 | ++++ setup memory! ++++ 52 | ++++ setup interrupt! ++++ 53 | available programs in rust/ are: 54 | . 55 | .. 56 | user_shell 57 | notebook 58 | greenthread 59 | hello_world 60 | model 61 | ++++ setup fs! ++++ 62 | ++++ setup process! ++++ 63 | ++++ setup timer! ++++ 64 | Rust user shell 65 | >> 66 | ``` 67 | -------------------------------------------------------------------------------- /os/src/memory/memory_set/area.rs: -------------------------------------------------------------------------------- 1 | use super::{attr::MemoryAttr, handler::MemoryHandler}; 2 | use crate::consts::PAGE_SIZE; 3 | use crate::memory::paging::{PageRange, PageTableImpl}; 4 | use alloc::boxed::Box; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct MemoryArea { 8 | start: usize, 9 | end: usize, 10 | handler: Box, 11 | attr: MemoryAttr, 12 | } 13 | 14 | impl MemoryArea { 15 | pub fn map(&self, pt: &mut PageTableImpl) { 16 | for page in PageRange::new(self.start, self.end) { 17 | self.handler.map(pt, page, &self.attr); 18 | } 19 | } 20 | 21 | #[allow(dead_code)] 22 | fn unmap(&self, pt: &mut PageTableImpl) { 23 | for page in PageRange::new(self.start, self.end) { 24 | self.handler.unmap(pt, page); 25 | } 26 | } 27 | 28 | pub fn is_overlap_with(&self, start_addr: usize, end_addr: usize) -> bool { 29 | let p1 = self.start / PAGE_SIZE; 30 | let p2 = (self.end - 1) / PAGE_SIZE + 1; 31 | let p3 = start_addr / PAGE_SIZE; 32 | let p4 = (end_addr - 1) / PAGE_SIZE + 1; 33 | !((p1 >= p4) || (p2 <= p3)) 34 | } 35 | 36 | pub fn new( 37 | start_addr: usize, 38 | end_addr: usize, 39 | handler: Box, 40 | attr: MemoryAttr, 41 | ) -> Self { 42 | MemoryArea { 43 | start: start_addr, 44 | end: end_addr, 45 | handler, 46 | attr, 47 | } 48 | } 49 | 50 | pub fn page_copy(&self, pt: &mut PageTableImpl, src: usize, length: usize) { 51 | let mut l = length; 52 | let mut s = src; 53 | for page in PageRange::new(self.start, self.end) { 54 | self.handler 55 | .page_copy(pt, page, s, if l < PAGE_SIZE { l } else { PAGE_SIZE }); 56 | s += PAGE_SIZE; 57 | if l >= PAGE_SIZE { 58 | l -= PAGE_SIZE; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /os/src/fs/file.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::ROOT_INODE; 2 | use alloc::sync::Arc; 3 | use rcore_fs::vfs::INode; 4 | 5 | #[derive(Copy, Clone, Debug)] 6 | pub enum FileDescriptorType { 7 | FdNone, 8 | FdInode, 9 | #[allow(dead_code)] 10 | FdDevice, 11 | } 12 | 13 | #[derive(Clone)] 14 | pub struct File { 15 | fdtype: FileDescriptorType, 16 | readable: bool, 17 | writable: bool, 18 | pub inode: Option>, 19 | offset: usize, 20 | } 21 | 22 | impl File { 23 | pub fn default() -> Self { 24 | File { 25 | fdtype: FileDescriptorType::FdNone, 26 | readable: false, 27 | writable: false, 28 | inode: None, 29 | offset: 0, 30 | } 31 | } 32 | pub fn set_readable(&mut self, v: bool) { 33 | self.readable = v; 34 | } 35 | pub fn set_writable(&mut self, v: bool) { 36 | self.writable = v; 37 | } 38 | pub fn get_readable(&self) -> bool { 39 | self.readable 40 | } 41 | pub fn get_writable(&self) -> bool { 42 | self.writable 43 | } 44 | pub fn set_fdtype(&mut self, t: FileDescriptorType) { 45 | self.fdtype = t; 46 | } 47 | pub fn get_fdtype(&self) -> FileDescriptorType { 48 | self.fdtype 49 | } 50 | pub fn set_offset(&mut self, o: usize) { 51 | self.offset = o; 52 | } 53 | pub fn get_offset(&self) -> usize { 54 | self.offset 55 | } 56 | 57 | #[allow(unused_unsafe)] 58 | pub fn open_file(&mut self, path: &'static str, flags: i32) { 59 | self.set_fdtype(FileDescriptorType::FdInode); 60 | self.set_readable(true); 61 | if (flags & 1) > 0 { 62 | self.set_readable(false); 63 | } 64 | if (flags & 3) > 0 { 65 | self.set_writable(true); 66 | } 67 | unsafe { 68 | self.inode = Some(ROOT_INODE.lookup(path).unwrap().clone()); 69 | } 70 | self.set_offset(0); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /os/src/memory/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | use crate::consts::MAX_PHYSICAL_PAGES; 2 | use spin::Mutex; 3 | 4 | pub struct SegmentTreeAllocator { 5 | a: [u8; MAX_PHYSICAL_PAGES << 1], 6 | m: usize, 7 | n: usize, 8 | offset: usize, 9 | } 10 | 11 | impl SegmentTreeAllocator { 12 | pub fn init(&mut self, l: usize, r: usize) { 13 | self.offset = l - 1; 14 | self.n = r - l; 15 | self.m = 1; 16 | while self.m < self.n + 2 { 17 | self.m <<= 1; 18 | } 19 | for i in 1..(self.m << 1) { 20 | self.a[i] = 1; 21 | } 22 | for i in 1..self.n { 23 | self.a[self.m + i] = 0; 24 | } 25 | for i in (1..self.m).rev() { 26 | self.a[i] = self.a[i << 1] & self.a[(i << 1) | 1]; 27 | } 28 | } 29 | 30 | pub fn alloc(&mut self) -> usize { 31 | // assume that we never run out of physical memory 32 | if self.a[1] == 1 { 33 | panic!("physical memory depleted!"); 34 | } 35 | let mut p = 1; 36 | while p < self.m { 37 | if self.a[p << 1] == 0 { 38 | p <<= 1; 39 | } else { 40 | p = (p << 1) | 1; 41 | } 42 | } 43 | let result = p + self.offset - self.m; 44 | self.a[p] = 1; 45 | p >>= 1; 46 | while p > 0 { 47 | self.a[p] = self.a[p << 1] & self.a[(p << 1) | 1]; 48 | p >>= 1; 49 | } 50 | result 51 | } 52 | 53 | pub fn dealloc(&mut self, n: usize) { 54 | let mut p = n + self.m - self.offset; 55 | assert!(self.a[p] == 1); 56 | self.a[p] = 0; 57 | p >>= 1; 58 | while p > 0 { 59 | self.a[p] = self.a[p << 1] & self.a[(p << 1) | 1]; 60 | p >>= 1; 61 | } 62 | } 63 | } 64 | 65 | pub static SEGMENT_TREE_ALLOCATOR: Mutex = Mutex::new(SegmentTreeAllocator { 66 | a: [0; MAX_PHYSICAL_PAGES << 1], 67 | m: 0, 68 | n: 0, 69 | offset: 0, 70 | }); 71 | -------------------------------------------------------------------------------- /os/src/sbi.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[inline(always)] 4 | fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 5 | let ret; 6 | unsafe { 7 | llvm_asm!("ecall" 8 | : "={x10}" (ret) 9 | : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which) 10 | : "memory" 11 | : "volatile"); 12 | } 13 | ret 14 | } 15 | 16 | pub fn console_putchar(ch: usize) { 17 | sbi_call(SBI_CONSOLE_PUTCHAR, ch, 0, 0); 18 | } 19 | 20 | pub fn console_getchar() -> usize { 21 | sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) 22 | } 23 | 24 | pub fn shutdown() -> ! { 25 | sbi_call(SBI_SHUTDOWN, 0, 0, 0); 26 | unreachable!() 27 | } 28 | 29 | pub fn set_timer(stime_value: u64) { 30 | #[cfg(target_pointer_width = "32")] 31 | sbi_call( 32 | SBI_SET_TIMER, 33 | stime_value as usize, 34 | (stime_value >> 32) as usize, 35 | 0, 36 | ); 37 | #[cfg(target_pointer_width = "64")] 38 | sbi_call(SBI_SET_TIMER, stime_value as usize, 0, 0); 39 | } 40 | 41 | pub fn clear_ipi() { 42 | sbi_call(SBI_CLEAR_IPI, 0, 0, 0); 43 | } 44 | 45 | pub fn send_ipi(hart_mask: usize) { 46 | sbi_call(SBI_SEND_IPI, &hart_mask as *const _ as usize, 0, 0); 47 | } 48 | 49 | pub fn remote_fence_i(hart_mask: usize) { 50 | sbi_call(SBI_REMOTE_FENCE_I, &hart_mask as *const _ as usize, 0, 0); 51 | } 52 | 53 | pub fn remote_sfence_vma(hart_mask: usize, _start: usize, _size: usize) { 54 | sbi_call(SBI_REMOTE_SFENCE_VMA, &hart_mask as *const _ as usize, 0, 0); 55 | } 56 | 57 | pub fn remote_sfence_vma_asid(hart_mask: usize, _start: usize, _size: usize, _asid: usize) { 58 | sbi_call( 59 | SBI_REMOTE_SFENCE_VMA_ASID, 60 | &hart_mask as *const _ as usize, 61 | 0, 62 | 0, 63 | ); 64 | } 65 | 66 | const SBI_SET_TIMER: usize = 0; 67 | const SBI_CONSOLE_PUTCHAR: usize = 1; 68 | const SBI_CONSOLE_GETCHAR: usize = 2; 69 | const SBI_CLEAR_IPI: usize = 3; 70 | const SBI_SEND_IPI: usize = 4; 71 | const SBI_REMOTE_FENCE_I: usize = 5; 72 | const SBI_REMOTE_SFENCE_VMA: usize = 6; 73 | const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; 74 | const SBI_SHUTDOWN: usize = 8; 75 | -------------------------------------------------------------------------------- /os/src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | mod frame_allocator; 2 | pub mod memory_set; 3 | pub mod paging; 4 | 5 | use crate::consts::*; 6 | use buddy_system_allocator::LockedHeap; 7 | use frame_allocator::SEGMENT_TREE_ALLOCATOR as FRAME_ALLOCATOR; 8 | use memory_set::{attr::MemoryAttr, handler::Linear, MemorySet}; 9 | use riscv::addr::Frame; 10 | use riscv::register::sstatus; 11 | 12 | pub fn init(l: usize, r: usize) { 13 | unsafe { 14 | sstatus::set_sum(); 15 | } 16 | FRAME_ALLOCATOR.lock().init(l, r); 17 | init_heap(); 18 | kernel_remap(); 19 | println!("++++ setup memory! ++++"); 20 | } 21 | 22 | pub fn alloc_frame() -> Option { 23 | Some(Frame::of_ppn(FRAME_ALLOCATOR.lock().alloc())) 24 | } 25 | 26 | pub fn dealloc_frame(f: Frame) { 27 | FRAME_ALLOCATOR.lock().dealloc(f.number()) 28 | } 29 | 30 | fn init_heap() { 31 | static mut HEAP: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 32 | unsafe { 33 | DYNAMIC_ALLOCATOR 34 | .lock() 35 | .init(HEAP.as_ptr() as usize, KERNEL_HEAP_SIZE); 36 | } 37 | } 38 | 39 | pub fn access_pa_via_va(pa: usize) -> usize { 40 | pa + PHYSICAL_MEMORY_OFFSET 41 | } 42 | 43 | pub fn kernel_remap() { 44 | let mut memory_set = MemorySet::new(); 45 | 46 | extern "C" { 47 | fn bootstack(); 48 | fn bootstacktop(); 49 | } 50 | memory_set.push( 51 | bootstack as usize, 52 | bootstacktop as usize, 53 | MemoryAttr::default(), 54 | Linear::new(PHYSICAL_MEMORY_OFFSET), 55 | None, 56 | ); 57 | memory_set.push( 58 | access_pa_via_va(0x0c00_2000), 59 | access_pa_via_va(0x0c00_3000), 60 | MemoryAttr::default(), 61 | Linear::new(PHYSICAL_MEMORY_OFFSET), 62 | None, 63 | ); 64 | memory_set.push( 65 | access_pa_via_va(0x1000_0000), 66 | access_pa_via_va(0x1000_1000), 67 | MemoryAttr::default(), 68 | Linear::new(PHYSICAL_MEMORY_OFFSET), 69 | None, 70 | ); 71 | 72 | unsafe { 73 | memory_set.activate(); 74 | } 75 | } 76 | 77 | #[global_allocator] 78 | static DYNAMIC_ALLOCATOR: LockedHeap = LockedHeap::empty(); 79 | 80 | #[alloc_error_handler] 81 | fn alloc_error_handler(_: core::alloc::Layout) -> ! { 82 | panic!("alloc_error_handler do nothing but panic!"); 83 | } 84 | -------------------------------------------------------------------------------- /os/src/trap/trap.asm: -------------------------------------------------------------------------------- 1 | .equ XLENB, 8 2 | .macro LOAD a1, a2 3 | ld \a1, \a2*XLENB(sp) 4 | .endm 5 | 6 | .macro STORE a1, a2 7 | sd \a1, \a2*XLENB(sp) 8 | .endm 9 | 10 | .macro SAVE_ALL 11 | csrrw sp, sscratch, sp 12 | bnez sp, trap_from_user 13 | trap_from_kernel: 14 | csrr sp, sscratch 15 | trap_from_user: 16 | addi sp, sp, -36*XLENB 17 | STORE x1, 1 18 | STORE x3, 3 19 | STORE x4, 4 20 | STORE x5, 5 21 | STORE x6, 6 22 | STORE x7, 7 23 | STORE x8, 8 24 | STORE x9, 9 25 | STORE x10, 10 26 | STORE x11, 11 27 | STORE x12, 12 28 | STORE x13, 13 29 | STORE x14, 14 30 | STORE x15, 15 31 | STORE x16, 16 32 | STORE x17, 17 33 | STORE x18, 18 34 | STORE x19, 19 35 | STORE x20, 20 36 | STORE x21, 21 37 | STORE x22, 22 38 | STORE x23, 23 39 | STORE x24, 24 40 | STORE x25, 25 41 | STORE x26, 26 42 | STORE x27, 27 43 | STORE x28, 28 44 | STORE x29, 29 45 | STORE x30, 30 46 | STORE x31, 31 47 | 48 | csrrw s0, sscratch, x0 49 | csrr s1, sstatus 50 | csrr s2, sepc 51 | csrr s3, stval 52 | csrr s4, scause 53 | 54 | STORE s0, 2 55 | STORE s1, 32 56 | STORE s2, 33 57 | STORE s3, 34 58 | STORE s4, 35 59 | .endm 60 | 61 | .macro RESTORE_ALL 62 | LOAD s1, 32 63 | LOAD s2, 33 64 | andi s0, s1, 1 << 8 65 | bnez s0, _to_kernel 66 | _to_user: 67 | addi s0, sp, 36*XLENB 68 | csrw sscratch, s0 69 | _to_kernel: 70 | csrw sstatus, s1 71 | csrw sepc, s2 72 | LOAD x1, 1 73 | LOAD x3, 3 74 | LOAD x4, 4 75 | LOAD x5, 5 76 | LOAD x6, 6 77 | LOAD x7, 7 78 | LOAD x8, 8 79 | LOAD x9, 9 80 | LOAD x10, 10 81 | LOAD x11, 11 82 | LOAD x12, 12 83 | LOAD x13, 13 84 | LOAD x14, 14 85 | LOAD x15, 15 86 | LOAD x16, 16 87 | LOAD x17, 17 88 | LOAD x18, 18 89 | LOAD x19, 19 90 | LOAD x20, 20 91 | LOAD x21, 21 92 | LOAD x22, 22 93 | LOAD x23, 23 94 | LOAD x24, 24 95 | LOAD x25, 25 96 | LOAD x26, 26 97 | LOAD x27, 27 98 | LOAD x28, 28 99 | LOAD x29, 29 100 | LOAD x30, 30 101 | LOAD x31, 31 102 | 103 | LOAD x2, 2 104 | .endm 105 | 106 | .section .text 107 | .globl __alltraps 108 | __alltraps: 109 | SAVE_ALL 110 | mv a0, sp 111 | jal rust_trap 112 | 113 | .globl __trapret 114 | __trapret: 115 | RESTORE_ALL 116 | sret 117 | -------------------------------------------------------------------------------- /test/pmm_test.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("boot/entry64.asm")); 2 | 3 | use crate::consts::*; 4 | use crate::memory::{alloc_frame, dealloc_frame}; 5 | 6 | #[no_mangle] 7 | pub extern "C" fn rust_main() -> ! { 8 | let FF_grade = FirstFitAllocator_test(); 9 | extern "C" { 10 | fn end(); 11 | } 12 | crate::memory::init( 13 | ((end as usize - KERNEL_BEGIN_VADDR + KERNEL_BEGIN_PADDR) >> 12) + 1, 14 | PHYSICAL_MEMORY_END >> 12, 15 | ); 16 | println!("First Fit Allocator: {} / 8", FF_grade); 17 | crate::sbi::shutdown(); 18 | } 19 | 20 | use riscv::addr::Frame; 21 | 22 | fn alloc(cnt: usize) -> Option { 23 | if let Some(frames) = crate::memory::alloc_frames(cnt) { 24 | return Some(frames.number()); 25 | } 26 | return None; 27 | } 28 | 29 | fn dealloc(ppn: usize, cnt: usize) { 30 | crate::memory::dealloc_frames(Frame::of_ppn(ppn), cnt) 31 | } 32 | 33 | fn FirstFitAllocator_test() -> usize { 34 | let mut grade: usize = 0; 35 | crate::memory::init_allocator(1, 6); 36 | let mut p0 = alloc(5); 37 | if p0.is_none() { 38 | return grade; 39 | } 40 | let mut p0 = p0.unwrap(); 41 | if !alloc(1).is_none() { 42 | return grade; 43 | } 44 | dealloc(p0 + 2, 3); 45 | if !alloc(4).is_none() { 46 | return grade; 47 | } else { 48 | grade += 1; 49 | } 50 | let mut p1 = alloc(3); 51 | if p1.is_none() { 52 | return grade; 53 | } else { 54 | grade += 1; 55 | } 56 | let mut p1 = p1.unwrap(); 57 | if !alloc(1).is_none() { 58 | return grade; 59 | } else { 60 | grade += 1; 61 | } 62 | if p0 + 2 != p1 { 63 | return grade; 64 | } else { 65 | grade += 1; 66 | } 67 | let mut p2 = p0 + 1; 68 | dealloc(p0, 1); 69 | dealloc(p1, 3); 70 | p0 = alloc(1).unwrap(); 71 | if p0 != p2 - 1 { 72 | return grade; 73 | } else { 74 | grade += 1; 75 | } 76 | dealloc(p0, 1); 77 | p0 = alloc(2).unwrap(); 78 | if p0 != p2 + 1 { 79 | return grade; 80 | } else { 81 | grade += 1; 82 | } 83 | dealloc(p0, 2); 84 | dealloc(p2, 1); 85 | let mut p0 = alloc(5); 86 | if p0.is_none() { 87 | return grade; 88 | } else { 89 | grade += 1; 90 | } 91 | if !alloc(1).is_none() { 92 | return grade; 93 | } else { 94 | grade += 1; 95 | } 96 | dealloc(p0.unwrap(), 5); 97 | return grade; 98 | } 99 | -------------------------------------------------------------------------------- /os/src/process/scheduler.rs: -------------------------------------------------------------------------------- 1 | use super::Tid; 2 | use alloc::vec::Vec; 3 | 4 | pub trait Scheduler { 5 | fn push(&mut self, tid: Tid); 6 | fn pop(&mut self) -> Option; 7 | fn tick(&mut self) -> bool; 8 | fn exit(&mut self, tid: Tid); 9 | } 10 | 11 | #[derive(Default)] 12 | struct RRInfo { 13 | valid: bool, 14 | time: usize, 15 | prev: usize, 16 | next: usize, 17 | } 18 | 19 | pub struct RRScheduler { 20 | threads: Vec, 21 | max_time: usize, 22 | current: usize, 23 | } 24 | 25 | impl RRScheduler { 26 | pub fn new(max_time_slice: usize) -> Self { 27 | let mut rr = RRScheduler { 28 | threads: Vec::default(), 29 | max_time: max_time_slice, 30 | current: 0, 31 | }; 32 | rr.threads.push(RRInfo { 33 | valid: false, 34 | time: 0, 35 | prev: 0, 36 | next: 0, 37 | }); 38 | rr 39 | } 40 | } 41 | impl Scheduler for RRScheduler { 42 | fn push(&mut self, tid: Tid) { 43 | let tid = tid + 1; 44 | if tid + 1 > self.threads.len() { 45 | self.threads.resize_with(tid + 1, Default::default); 46 | } 47 | 48 | if self.threads[tid].time == 0 { 49 | self.threads[tid].time = self.max_time; 50 | } 51 | 52 | let prev = self.threads[0].prev; 53 | self.threads[tid].valid = true; 54 | self.threads[prev].next = tid; 55 | self.threads[tid].prev = prev; 56 | self.threads[0].prev = tid; 57 | self.threads[tid].next = 0; 58 | } 59 | 60 | fn pop(&mut self) -> Option { 61 | let ret = self.threads[0].next; 62 | if ret != 0 { 63 | let next = self.threads[ret].next; 64 | let prev = self.threads[ret].prev; 65 | self.threads[next].prev = prev; 66 | self.threads[prev].next = next; 67 | self.threads[ret].prev = 0; 68 | self.threads[ret].next = 0; 69 | self.threads[ret].valid = false; 70 | self.current = ret; 71 | Some(ret - 1) 72 | } else { 73 | None 74 | } 75 | } 76 | 77 | // 当前线程的可用时间片 -= 1 78 | fn tick(&mut self) -> bool { 79 | let tid = self.current; 80 | if tid != 0 { 81 | self.threads[tid].time -= 1; 82 | return self.threads[tid].time == 0; 83 | } 84 | true 85 | } 86 | 87 | fn exit(&mut self, tid: Tid) { 88 | let tid = tid + 1; 89 | if self.current == tid { 90 | self.current = 0; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | tests = { 4 | 'labkernel': (False, 'test_test.rs'), 5 | 'lab2': (False, 'pmm_test.rs'), 6 | 'lab3': (False, 'vm_test.rs'), 7 | 'labuser': (True, 'test_test.rs'), 8 | 'lab5': (True, 'fork_test.rs'), 9 | 'lab6': (True, 'stride_test.rs'), 10 | 'lab7': (False, 'mutex_test.rs'), 11 | 'lab8': (True, 'pipe_test.rs'), 12 | } 13 | if sys.argv[1] == 'clean': 14 | os.system('rm lab*') 15 | exit() 16 | print('testing ' + sys.argv[1] + '...') 17 | try: 18 | user_test, test_file = tests[sys.argv[1]] 19 | if user_test: 20 | # user_test 21 | # save process/mod.rs 22 | os.system('\\cp os/src/process/mod.rs os/src/process/mod_backup.rs') 23 | # replace with user test 24 | os.system('\\cp test/usr/' + test_file + 25 | ' usr/rust/src/bin/' + test_file) 26 | s = open('os/src/process/mod.rs').read() 27 | s = s.replace('rust/user_shell', 'rust/' + 28 | test_file[:test_file.find('.')]) 29 | with open('os/src/process/mod.rs', 'w') as f: 30 | f.write(s) 31 | # try test 32 | c = os.system('make clean') 33 | c = os.system('make run > ' + sys.argv[1] + '.result') 34 | if c == 0: 35 | print('test successfully') 36 | else: 37 | print('test failed') 38 | print('see ' + sys.argv[1] + '.result') 39 | # remove user test 40 | os.system('rm usr/rust/src/bin/' + test_file) 41 | # backup process/mod.rs 42 | os.system('\\cp os/src/process/mod_backup.rs os/src/process/mod.rs') 43 | os.system('rm os/src/process/mod_backup.rs') 44 | # open result file 45 | if c == 0: 46 | os.system('cat ' + sys.argv[1] + '.result | less') 47 | else: 48 | # kernel test 49 | # save init.rs 50 | os.system('\\cp os/src/init.rs os/src/init_backup.rs') 51 | # replace with kernel test 52 | os.system('\\cp test/' + test_file + ' os/src/init.rs') 53 | # try test 54 | c = os.system('make run > ' + sys.argv[1] + '.result') 55 | if c == 0: 56 | print('test successfully') 57 | else: 58 | print('test failed') 59 | print('see ' + sys.argv[1] + '.result') 60 | # backup init.rs 61 | os.system('\\cp os/src/init_backup.rs os/src/init.rs') 62 | os.system('rm os/src/init_backup.rs') 63 | # open result file 64 | if c == 0: 65 | os.system('cat ' + sys.argv[1] + '.result | less') 66 | except: 67 | print('Usage: python3 test.py labX/clean (X={2,3,5,6,7,8,kernel,user})') 68 | -------------------------------------------------------------------------------- /os/src/process/thread_pool.rs: -------------------------------------------------------------------------------- 1 | use crate::alloc::{boxed::Box, vec::Vec}; 2 | use crate::process::scheduler::Scheduler; 3 | use crate::process::structs::*; 4 | use crate::process::Tid; 5 | 6 | pub struct ThreadInfo { 7 | pub status: Status, 8 | pub thread: Option>, 9 | } 10 | 11 | pub struct ThreadPool { 12 | pub threads: Vec>, 13 | scheduler: Box, 14 | } 15 | 16 | impl ThreadPool { 17 | pub fn new(size: usize, scheduler: Box) -> ThreadPool { 18 | ThreadPool { 19 | threads: { 20 | let mut v = Vec::new(); 21 | v.resize_with(size, Default::default); 22 | v 23 | }, 24 | scheduler, 25 | } 26 | } 27 | fn alloc_tid(&self) -> Tid { 28 | for (i, info) in self.threads.iter().enumerate() { 29 | if info.is_none() { 30 | return i; 31 | } 32 | } 33 | panic!("alloc tid failed!"); 34 | } 35 | 36 | pub fn add(&mut self, _thread: Box) { 37 | let tid = self.alloc_tid(); 38 | self.threads[tid] = Some(ThreadInfo { 39 | status: Status::Ready, 40 | thread: Some(_thread), 41 | }); 42 | self.scheduler.push(tid); 43 | } 44 | 45 | pub fn acquire(&mut self) -> Option<(Tid, Box)> { 46 | if let Some(tid) = self.scheduler.pop() { 47 | let mut thread_info = self.threads[tid].as_mut().expect("thread not exist!"); 48 | thread_info.status = Status::Running(tid); 49 | Some((tid, thread_info.thread.take().expect("thread not exist!"))) 50 | } else { 51 | None 52 | } 53 | } 54 | 55 | pub fn retrieve(&mut self, tid: Tid, thread: Box) { 56 | if self.threads[tid].is_none() { 57 | return; 58 | } 59 | let mut thread_info = self.threads[tid].as_mut().expect("thread not exist!"); 60 | thread_info.thread = Some(thread); 61 | if let Status::Running(_) = thread_info.status { 62 | thread_info.status = Status::Ready; 63 | self.scheduler.push(tid); 64 | } 65 | } 66 | 67 | pub fn tick(&mut self) -> bool { 68 | self.scheduler.tick() 69 | } 70 | 71 | pub fn exit(&mut self, tid: Tid) { 72 | self.threads[tid] = None; 73 | self.scheduler.exit(tid); 74 | } 75 | 76 | pub fn wakeup(&mut self, tid: Tid) { 77 | let proc = self.threads[tid] 78 | .as_mut() 79 | .expect("thread not exist when waking up"); 80 | proc.status = Status::Ready; 81 | self.scheduler.push(tid); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/mutex_test.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("boot/entry64.asm")); 2 | global_asm!(include_str!("link_user.S")); 3 | 4 | use crate::consts::*; 5 | use crate::memory::{alloc_frame, dealloc_frame}; 6 | 7 | #[no_mangle] 8 | pub extern "C" fn rust_main() -> ! { 9 | extern "C" { 10 | fn end(); 11 | } 12 | crate::memory::init( 13 | ((end as usize - KERNEL_BEGIN_VADDR + KERNEL_BEGIN_PADDR) >> 12) + 1, 14 | PHYSICAL_MEMORY_END >> 12, 15 | ); 16 | crate::interrupt::init(); 17 | crate::fs::init(); 18 | crate::process::init(); 19 | crate::process::spawn(philosopher_using_mutex); 20 | crate::timer::init(); 21 | crate::process::run(); 22 | loop {} 23 | } 24 | 25 | use crate::process::{sleep, spawn}; 26 | use crate::sync::SleepLock as Mutex; 27 | use alloc::vec; 28 | use alloc::{sync::Arc, vec::Vec}; 29 | 30 | struct Philosopher { 31 | name: &'static str, 32 | left: usize, 33 | right: usize, 34 | } 35 | 36 | impl Philosopher { 37 | fn new(name: &'static str, left: usize, right: usize) -> Philosopher { 38 | Philosopher { name, left, right } 39 | } 40 | 41 | fn eat(&self, table: &Arc) { 42 | table.eat(self.name, self.left, self.right); 43 | } 44 | 45 | fn think(&self) { 46 | println!("{} is thinking.", self.name); 47 | sleep(1); 48 | } 49 | } 50 | 51 | trait Table: Send + Sync { 52 | fn eat(&self, name: &str, left: usize, right: usize); 53 | } 54 | 55 | struct MutexTable { 56 | forks: Vec>, 57 | } 58 | 59 | impl Table for MutexTable { 60 | fn eat(&self, name: &str, left: usize, right: usize) { 61 | let left = self.forks[left].lock(); 62 | let right = self.forks[right].lock(); 63 | println!("{} is eating, using forks: {}, {}", name, *left, *right); 64 | sleep(1); 65 | } 66 | } 67 | 68 | fn philosopher(table: Arc) { 69 | let philosophers = vec![ 70 | Philosopher::new("1", 0, 1), 71 | Philosopher::new("2", 1, 2), 72 | Philosopher::new("3", 2, 3), 73 | Philosopher::new("4", 3, 4), 74 | Philosopher::new("5", 0, 4), 75 | ]; 76 | 77 | for p in philosophers { 78 | let table = table.clone(); 79 | spawn(move || { 80 | for i in 0..5 { 81 | p.think(); 82 | p.eat(&table); 83 | println!("{} iter {} end.", p.name, i); 84 | } 85 | }) 86 | } 87 | } 88 | 89 | fn philosopher_using_mutex() { 90 | println!("philosophers using mutex"); 91 | 92 | let table = Arc::new(MutexTable { 93 | forks: vec![ 94 | Mutex::new(0), 95 | Mutex::new(1), 96 | Mutex::new(2), 97 | Mutex::new(3), 98 | Mutex::new(4), 99 | ], 100 | }); 101 | philosopher(table); 102 | } 103 | -------------------------------------------------------------------------------- /os/src/memory/memory_set/handler.rs: -------------------------------------------------------------------------------- 1 | use super::attr::MemoryAttr; 2 | use crate::consts::PAGE_SIZE; 3 | use crate::memory::access_pa_via_va; 4 | use crate::memory::alloc_frame; 5 | use crate::memory::paging::PageTableImpl; 6 | use alloc::boxed::Box; 7 | use core::fmt::Debug; 8 | 9 | pub trait MemoryHandler: Debug + 'static { 10 | fn box_clone(&self) -> Box; 11 | fn map(&self, pt: &mut PageTableImpl, va: usize, attr: &MemoryAttr); 12 | fn unmap(&self, pt: &mut PageTableImpl, va: usize); 13 | fn page_copy(&self, pt: &mut PageTableImpl, va: usize, src: usize, length: usize); 14 | } 15 | 16 | impl Clone for Box { 17 | fn clone(&self) -> Box { 18 | self.box_clone() 19 | } 20 | } 21 | 22 | #[derive(Debug, Clone)] 23 | pub struct Linear { 24 | offset: usize, 25 | } 26 | 27 | impl Linear { 28 | pub fn new(off: usize) -> Self { 29 | Linear { offset: off } 30 | } 31 | } 32 | impl MemoryHandler for Linear { 33 | fn box_clone(&self) -> Box { 34 | Box::new(self.clone()) 35 | } 36 | fn map(&self, pt: &mut PageTableImpl, va: usize, attr: &MemoryAttr) { 37 | attr.apply(pt.map(va, va - self.offset)); 38 | } 39 | fn unmap(&self, pt: &mut PageTableImpl, va: usize) { 40 | pt.unmap(va); 41 | } 42 | fn page_copy(&self, pt: &mut PageTableImpl, va: usize, src: usize, length: usize) { 43 | let pa = pt.get_entry(va).expect("get pa error!").0.addr().as_usize(); 44 | assert!(va == access_pa_via_va(pa)); 45 | assert!(va == pa + self.offset); 46 | unsafe { 47 | let dst = core::slice::from_raw_parts_mut(va as *mut u8, PAGE_SIZE); 48 | if length > 0 { 49 | let src = core::slice::from_raw_parts(src as *const u8, PAGE_SIZE); 50 | dst[..length].clone_from_slice(&src[..length]); 51 | } 52 | #[allow(clippy::needless_range_loop)] 53 | for i in length..PAGE_SIZE { 54 | dst[i] = 0; 55 | } 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug, Clone)] 61 | pub struct ByFrame; 62 | impl ByFrame { 63 | pub fn new() -> Self { 64 | ByFrame {} 65 | } 66 | } 67 | impl MemoryHandler for ByFrame { 68 | fn box_clone(&self) -> Box { 69 | Box::new(self.clone()) 70 | } 71 | 72 | fn map(&self, pt: &mut PageTableImpl, va: usize, attr: &MemoryAttr) { 73 | let frame = alloc_frame().expect("alloc_frame failed!"); 74 | let pa = frame.start_address().as_usize(); 75 | attr.apply(pt.map(va, pa)); 76 | } 77 | 78 | fn unmap(&self, pt: &mut PageTableImpl, va: usize) { 79 | pt.unmap(va); 80 | } 81 | fn page_copy(&self, pt: &mut PageTableImpl, va: usize, src: usize, length: usize) { 82 | let pa = pt.get_entry(va).expect("get pa error!").0.addr().as_usize(); 83 | unsafe { 84 | let dst = core::slice::from_raw_parts_mut(access_pa_via_va(pa) as *mut u8, PAGE_SIZE); 85 | if length > 0 { 86 | let src = core::slice::from_raw_parts(src as *const u8, PAGE_SIZE); 87 | dst[..length].clone_from_slice(&src[..length]); 88 | } 89 | #[allow(clippy::needless_range_loop)] 90 | for i in length..PAGE_SIZE { 91 | dst[i] = 0; 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /test/vm_test.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("boot/entry64.asm")); 2 | 3 | use crate::consts::*; 4 | use crate::memory::{alloc_frame, dealloc_frame}; 5 | use crate::memory::{ 6 | access_pa_via_va, 7 | memory_set::{ 8 | attr::MemoryAttr, 9 | handler::{ByFrameSwappingOut, ByFrameWithRpa, Linear}, 10 | MemorySet, 11 | }, 12 | }; 13 | use crate::memory::paging::PageTableImpl; 14 | use alloc::sync::Arc; 15 | use spin::Mutex; 16 | 17 | #[no_mangle] 18 | pub extern "C" fn rust_main() -> ! { 19 | extern "C" { 20 | fn end(); 21 | } 22 | crate::memory::init( 23 | ((end as usize - KERNEL_BEGIN_VADDR + KERNEL_BEGIN_PADDR) >> 12) + 1, 24 | PHYSICAL_MEMORY_END >> 12, 25 | ); 26 | crate::interrupt::init(); 27 | page_test(); 28 | crate::sbi::shutdown(); 29 | } 30 | 31 | fn page_test() { 32 | let mut memory_set = MemorySet::new(); 33 | 34 | extern "C" { 35 | fn bootstack(); 36 | fn bootstacktop(); 37 | } 38 | memory_set.push( 39 | bootstack as usize, 40 | bootstacktop as usize, 41 | MemoryAttr::new(), 42 | Linear::new(PHYSICAL_MEMORY_OFFSET), 43 | None, 44 | ); 45 | memory_set.push( 46 | access_pa_via_va(0x0c00_2000), 47 | access_pa_via_va(0x0c00_3000), 48 | MemoryAttr::new(), 49 | Linear::new(PHYSICAL_MEMORY_OFFSET), 50 | None, 51 | ); 52 | memory_set.push( 53 | access_pa_via_va(0x1000_0000), 54 | access_pa_via_va(0x1000_1000), 55 | MemoryAttr::new(), 56 | Linear::new(PHYSICAL_MEMORY_OFFSET), 57 | None, 58 | ); 59 | memory_set.push( 60 | 0x4000_0000, 61 | 0x4000_8000, 62 | MemoryAttr::new(), 63 | ByFrameWithRpa::new(), 64 | None, 65 | ); 66 | memory_set.push( 67 | 0x4000_8000, 68 | 0x4001_0000, 69 | MemoryAttr::new(), 70 | ByFrameSwappingOut::new(), 71 | None, 72 | ); 73 | 74 | unsafe { 75 | memory_set.activate(); 76 | } 77 | 78 | let table = memory_set.get_table(); 79 | 80 | let ptr1 = unsafe { &mut *(0x4000_a000 as *mut u64) }; 81 | *ptr1 = 0xdeaddead; 82 | let ptr2 = unsafe { &mut *(0x4000_c000 as *mut u64) }; 83 | *ptr2 = 0xdeaddead; 84 | 85 | let mut count = 0; 86 | println!("test begin"); 87 | count += check_a_to_b(&table, 0x4000_8000, 0x4000_0000); 88 | count += check_a_to_b(&table, 0x4000_9000, 0x4000_1000); 89 | count += check_a_to_b(&table, 0x4000_b000, 0x4000_2000); 90 | count += check_a_to_b(&table, 0x4000_d000, 0x4000_3000); 91 | count += check_a_to_b(&table, 0x4000_e000, 0x4000_4000); 92 | count += check_a_to_b(&table, 0x4000_f000, 0x4000_5000); 93 | count += check_a_to_b(&table, 0x4000_a000, 0x4000_6000); 94 | count += check_a_to_b(&table, 0x4000_c000, 0x4000_7000); 95 | println!("test end"); 96 | println!("COUNT: {} / 8", count); 97 | } 98 | 99 | fn check_a_to_b(table: &Arc>, a: usize, b: usize) -> usize { 100 | let predicted = table.lock().get_entry(a).unwrap().target(); 101 | let ptr = unsafe { &mut *(b as *mut u64) }; 102 | *ptr = 0xdeaddead; 103 | let result = table.lock().get_entry(b).unwrap().target(); 104 | if predicted == result { 105 | 1 106 | } else { 107 | 0 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /os/src/memory/memory_set/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod area; 2 | pub mod attr; 3 | pub mod handler; 4 | 5 | use crate::consts::*; 6 | use crate::memory::access_pa_via_va; 7 | use crate::memory::paging::PageTableImpl; 8 | use alloc::{boxed::Box, vec::Vec}; 9 | use area::MemoryArea; 10 | use attr::MemoryAttr; 11 | use handler::{Linear, MemoryHandler}; 12 | 13 | pub struct MemorySet { 14 | areas: Vec, 15 | page_table: PageTableImpl, 16 | } 17 | 18 | impl MemorySet { 19 | pub fn push( 20 | &mut self, 21 | start: usize, 22 | end: usize, 23 | attr: MemoryAttr, 24 | handler: impl MemoryHandler, 25 | data: Option<(usize, usize)>, 26 | ) { 27 | assert!(start <= end, "invalid memory area!"); 28 | assert!(self.test_free_area(start, end), "memory area overlap!"); 29 | let area = MemoryArea::new(start, end, Box::new(handler), attr); 30 | area.map(&mut self.page_table); 31 | if let Some((src, length)) = data { 32 | area.page_copy(&mut self.page_table, src, length); 33 | } 34 | self.areas.push(area); 35 | } 36 | fn test_free_area(&self, start: usize, end: usize) -> bool { 37 | self.areas 38 | .iter() 39 | .find(|area| area.is_overlap_with(start, end)) 40 | .is_none() 41 | } 42 | pub unsafe fn activate(&self) { 43 | self.page_table.activate(); 44 | } 45 | pub fn new() -> Self { 46 | let mut memory_set = MemorySet { 47 | areas: Vec::new(), 48 | page_table: PageTableImpl::new_bare(), 49 | }; 50 | memory_set.map_kernel_and_physical_memory(); 51 | memory_set 52 | } 53 | pub fn map_kernel_and_physical_memory(&mut self) { 54 | extern "C" { 55 | fn stext(); 56 | fn etext(); 57 | fn srodata(); 58 | fn erodata(); 59 | fn sdata(); 60 | fn edata(); 61 | fn sbss(); 62 | fn ebss(); 63 | fn end(); 64 | } 65 | let offset = PHYSICAL_MEMORY_OFFSET; 66 | // 各段全部采用偏移量固定的线性映射 67 | // .text R|X 68 | self.push( 69 | stext as usize, 70 | etext as usize, 71 | MemoryAttr::default().set_readonly().set_execute(), 72 | Linear::new(offset), 73 | None, 74 | ); 75 | // .rodata R 76 | self.push( 77 | srodata as usize, 78 | erodata as usize, 79 | MemoryAttr::default().set_readonly(), 80 | Linear::new(offset), 81 | None, 82 | ); 83 | // .data R|W 84 | self.push( 85 | sdata as usize, 86 | edata as usize, 87 | MemoryAttr::default(), 88 | Linear::new(offset), 89 | None, 90 | ); 91 | // .bss R|W 92 | self.push( 93 | sbss as usize, 94 | ebss as usize, 95 | MemoryAttr::default(), 96 | Linear::new(offset), 97 | None, 98 | ); 99 | // 物理内存 R|W 100 | self.push( 101 | (end as usize / PAGE_SIZE + 1) * PAGE_SIZE, 102 | access_pa_via_va(PHYSICAL_MEMORY_END), 103 | MemoryAttr::default(), 104 | Linear::new(offset), 105 | None, 106 | ); 107 | } 108 | pub fn token(&self) -> usize { 109 | self.page_table.token() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /os/src/context.rs: -------------------------------------------------------------------------------- 1 | use core::mem::zeroed; 2 | use riscv::register::sstatus; 3 | use riscv::register::{scause::Scause, sstatus::Sstatus}; 4 | 5 | #[repr(C)] 6 | pub struct TrapFrame { 7 | pub x: [usize; 32], // General registers 8 | pub sstatus: Sstatus, // Supervisor Status Register 9 | pub sepc: usize, // Supervisor exception program counter 10 | pub stval: usize, // Supervisor trap value 11 | pub scause: Scause, // Scause register: record the cause of exception/interrupt/trap 12 | } 13 | 14 | #[repr(C)] 15 | pub struct Context { 16 | pub content_addr: usize, 17 | } 18 | 19 | impl Context { 20 | #[naked] 21 | #[inline(never)] 22 | pub unsafe extern "C" fn switch(&mut self, _target: &mut Context) { 23 | llvm_asm!(include_str!("process/switch.asm") :::: "volatile"); 24 | } 25 | 26 | pub fn null() -> Context { 27 | Context { content_addr: 0 } 28 | } 29 | 30 | pub unsafe fn new_kernel_thread(entry: usize, kstack_top: usize, satp: usize) -> Context { 31 | ContextContent::new_kernel_thread(entry, kstack_top, satp).push_at(kstack_top) 32 | } 33 | 34 | pub unsafe fn append_initial_arguments(&self, args: [usize; 3]) { 35 | let context_content = &mut *(self.content_addr as *mut ContextContent); 36 | context_content.tf.x[10] = args[0]; 37 | context_content.tf.x[11] = args[1]; 38 | context_content.tf.x[12] = args[2]; 39 | } 40 | 41 | pub unsafe fn new_user_thread( 42 | entry: usize, 43 | ustack_top: usize, 44 | kstack_top: usize, 45 | satp: usize, 46 | ) -> Self { 47 | ContextContent::new_user_thread(entry, ustack_top, satp).push_at(kstack_top) 48 | } 49 | } 50 | 51 | #[repr(C)] 52 | pub struct ContextContent { 53 | pub ra: usize, 54 | satp: usize, 55 | s: [usize; 12], 56 | tf: TrapFrame, 57 | } 58 | 59 | extern "C" { 60 | fn __trapret(); 61 | } 62 | 63 | impl ContextContent { 64 | fn new_kernel_thread(entry: usize, kstack_top: usize, satp: usize) -> ContextContent { 65 | ContextContent { 66 | ra: __trapret as usize, 67 | satp, 68 | s: [0; 12], 69 | tf: { 70 | let mut tf: TrapFrame = unsafe { zeroed() }; 71 | tf.x[2] = kstack_top; 72 | tf.sepc = entry; 73 | tf.sstatus = sstatus::read(); 74 | tf.sstatus.set_spp(sstatus::SPP::Supervisor); 75 | tf.sstatus.set_spie(true); 76 | tf.sstatus.set_sie(false); 77 | tf 78 | }, 79 | } 80 | } 81 | 82 | fn new_user_thread(entry: usize, ustack_top: usize, satp: usize) -> Self { 83 | ContextContent { 84 | ra: __trapret as usize, 85 | satp, 86 | s: [0; 12], 87 | tf: { 88 | let mut tf: TrapFrame = unsafe { zeroed() }; 89 | tf.x[2] = ustack_top; 90 | tf.sepc = entry; 91 | tf.sstatus = sstatus::read(); 92 | tf.sstatus.set_spie(true); 93 | tf.sstatus.set_sie(false); 94 | tf.sstatus.set_spp(sstatus::SPP::User); 95 | tf 96 | }, 97 | } 98 | } 99 | 100 | unsafe fn push_at(self, stack_top: usize) -> Context { 101 | let ptr = (stack_top as *mut ContextContent).sub(1); 102 | *ptr = self; 103 | Context { 104 | content_addr: ptr as usize, 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /os/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use crate::context::TrapFrame; 2 | use crate::memory::access_pa_via_va; 3 | use crate::process::tick; 4 | use crate::timer::clock_set_next_event; 5 | use riscv::register::sie; 6 | use riscv::register::{ 7 | scause::{Exception, Interrupt, Trap}, 8 | sscratch, sstatus, stvec, 9 | }; 10 | 11 | global_asm!(include_str!("trap/trap.asm")); 12 | 13 | pub fn init() { 14 | unsafe { 15 | extern "C" { 16 | fn __alltraps(); 17 | } 18 | sscratch::write(0); 19 | stvec::write(__alltraps as usize, stvec::TrapMode::Direct); 20 | 21 | sstatus::set_sie(); 22 | 23 | // enable external interrupt 24 | sie::set_sext(); 25 | 26 | // closed by OpenSBI, so we open them manually 27 | // see https://github.com/rcore-os/rCore/blob/54fddfbe1d402ac1fafd9d58a0bd4f6a8dd99ece/kernel/src/arch/riscv32/board/virt/mod.rs#L4 28 | init_external_interrupt(); 29 | enable_serial_interrupt(); 30 | } 31 | println!("++++ setup interrupt! ++++"); 32 | } 33 | 34 | pub unsafe fn init_external_interrupt() { 35 | let hart0_s_mode_interrupt_enables: *mut u32 = access_pa_via_va(0x0c00_2080) as *mut u32; 36 | const SERIAL: u32 = 0xa; 37 | hart0_s_mode_interrupt_enables.write_volatile(1 << SERIAL); 38 | } 39 | 40 | pub unsafe fn enable_serial_interrupt() { 41 | let uart16550: *mut u8 = access_pa_via_va(0x10000000) as *mut u8; 42 | uart16550.add(4).write_volatile(0x0B); 43 | uart16550.add(1).write_volatile(0x01); 44 | } 45 | 46 | #[no_mangle] 47 | pub fn rust_trap(tf: &mut TrapFrame) { 48 | match tf.scause.cause() { 49 | Trap::Exception(Exception::Breakpoint) => breakpoint(&mut tf.sepc), 50 | Trap::Interrupt(Interrupt::SupervisorTimer) => super_timer(), 51 | Trap::Exception(Exception::InstructionPageFault) => page_fault(tf), 52 | Trap::Exception(Exception::LoadPageFault) => page_fault(tf), 53 | Trap::Exception(Exception::StorePageFault) => page_fault(tf), 54 | Trap::Exception(Exception::UserEnvCall) => syscall(tf), 55 | Trap::Interrupt(Interrupt::SupervisorExternal) => external(), 56 | _ => panic!("undefined trap!"), 57 | } 58 | } 59 | 60 | fn breakpoint(sepc: &mut usize) { 61 | println!("a breakpoint set @0x{:x}", sepc); 62 | *sepc += 2; 63 | } 64 | 65 | fn super_timer() { 66 | clock_set_next_event(); 67 | tick(); 68 | } 69 | fn page_fault(tf: &mut TrapFrame) { 70 | println!( 71 | "{:?} va = {:#x} instruction = {:#x}", 72 | tf.scause.cause(), 73 | tf.stval, 74 | tf.sepc 75 | ); 76 | panic!("page fault!"); 77 | } 78 | 79 | fn syscall(tf: &mut TrapFrame) { 80 | tf.sepc += 4; 81 | let ret = crate::syscall::syscall(tf.x[17], [tf.x[10], tf.x[11], tf.x[12]], tf); 82 | tf.x[10] = ret as usize; 83 | } 84 | 85 | fn external() { 86 | let _ = try_serial(); 87 | } 88 | 89 | fn try_serial() -> bool { 90 | match super::io::getchar_option() { 91 | Some(ch) => { 92 | if ch == '\r' { 93 | crate::fs::stdio::STDIN.push('\n'); 94 | } else { 95 | crate::fs::stdio::STDIN.push(ch); 96 | } 97 | true 98 | } 99 | None => false, 100 | } 101 | } 102 | 103 | #[inline(always)] 104 | pub fn disable_and_store() -> usize { 105 | let sstatus: usize; 106 | unsafe { 107 | llvm_asm!("csrci sstatus, 1 << 1" : "=r"(sstatus) ::: "volatile"); 108 | } 109 | sstatus 110 | } 111 | 112 | #[inline(always)] 113 | pub fn restore(flags: usize) { 114 | unsafe { 115 | llvm_asm!("csrs sstatus, $0" :: "r"(flags) :: "volatile"); 116 | } 117 | } 118 | 119 | #[inline(always)] 120 | pub fn enable_and_wfi() { 121 | unsafe { 122 | llvm_asm!("csrsi sstatus, 1 << 1; wfi" :::: "volatile"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /os/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use crate::context::TrapFrame; 2 | use crate::fs::file::FileDescriptorType; 3 | use crate::process; 4 | 5 | pub const SYS_OPEN: usize = 56; 6 | pub const SYS_CLOSE: usize = 57; 7 | pub const SYS_WRITE: usize = 64; 8 | pub const SYS_EXIT: usize = 93; 9 | pub const SYS_READ: usize = 63; 10 | pub const SYS_EXEC: usize = 221; 11 | 12 | pub fn syscall(id: usize, args: [usize; 3], _tf: &mut TrapFrame) -> isize { 13 | match id { 14 | SYS_OPEN => sys_open(args[0] as *const u8, args[1] as i32), 15 | SYS_CLOSE => sys_close(args[0] as i32), 16 | SYS_READ => unsafe { sys_read(args[0], args[1] as *mut u8, args[2]) }, 17 | SYS_WRITE => unsafe { sys_write(args[0], args[1] as *const u8, args[2]) }, 18 | SYS_EXIT => { 19 | sys_exit(args[0]); 20 | 0 21 | } 22 | SYS_EXEC => sys_exec(args[0] as *const u8), 23 | _ => { 24 | panic!("unknown syscall id {}", id); 25 | } 26 | } 27 | } 28 | 29 | fn sys_open(path: *const u8, flags: i32) -> isize { 30 | let thread = process::current_thread_mut(); 31 | let fd = thread.alloc_fd() as isize; 32 | thread.ofile[fd as usize] 33 | .as_ref() 34 | .unwrap() 35 | .lock() 36 | .open_file(unsafe { from_cstr(path) }, flags); 37 | fd 38 | } 39 | 40 | fn sys_close(fd: i32) -> isize { 41 | let thread = process::current_thread_mut(); 42 | assert!(thread.ofile[fd as usize].is_some()); 43 | thread.dealloc_fd(fd); 44 | 0 45 | } 46 | 47 | fn sys_exit(code: usize) { 48 | process::exit(code); 49 | } 50 | 51 | unsafe fn sys_read(fd: usize, base: *mut u8, len: usize) -> isize { 52 | if fd == 0 { 53 | // 如果是标准输入 54 | *base = crate::fs::stdio::STDIN.pop() as u8; 55 | 1 56 | } else { 57 | let thread = process::current_thread_mut(); 58 | assert!(thread.ofile[fd].is_some()); 59 | let mut file = thread.ofile[fd as usize].as_ref().unwrap().lock(); 60 | assert!(file.get_readable()); 61 | match file.get_fdtype() { 62 | FileDescriptorType::FdInode => { 63 | let mut offset = file.get_offset(); 64 | let s = file 65 | .inode 66 | .clone() 67 | .unwrap() 68 | .read_at(offset, core::slice::from_raw_parts_mut(base, len)) 69 | .unwrap(); 70 | offset += s; 71 | file.set_offset(offset); 72 | s as isize 73 | } 74 | _ => { 75 | panic!("fdtype not handled!"); 76 | } 77 | } 78 | } 79 | } 80 | 81 | unsafe fn sys_write(fd: usize, base: *const u8, len: usize) -> isize { 82 | if fd == 1 { 83 | assert!(len == 1); 84 | crate::io::putchar(*base as char); 85 | 1 86 | } else { 87 | let thread = process::current_thread_mut(); 88 | assert!(thread.ofile[fd].is_some()); 89 | let mut file = thread.ofile[fd as usize].as_ref().unwrap().lock(); 90 | assert!(file.get_writable()); 91 | match file.get_fdtype() { 92 | FileDescriptorType::FdInode => { 93 | let mut offset = file.get_offset(); 94 | let s = file 95 | .inode 96 | .clone() 97 | .unwrap() 98 | .write_at(offset, core::slice::from_raw_parts(base, len)) 99 | .unwrap(); 100 | offset += s; 101 | file.set_offset(offset); 102 | s as isize 103 | } 104 | _ => { 105 | panic!("fdtype not handled!"); 106 | } 107 | } 108 | } 109 | } 110 | 111 | pub unsafe fn from_cstr(s: *const u8) -> &'static str { 112 | use core::{slice, str}; 113 | let len = (0usize..).find(|&i| *s.add(i) == 0).unwrap(); 114 | str::from_utf8(slice::from_raw_parts(s, len)).unwrap() 115 | } 116 | 117 | fn sys_exec(path: *const u8) -> isize { 118 | let valid = process::execute(unsafe { from_cstr(path) }, Some(process::current_tid())); 119 | if valid { 120 | process::yield_now(); 121 | } 122 | 0 123 | } 124 | -------------------------------------------------------------------------------- /os/src/process/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::interrupt::*; 2 | use crate::process::structs::*; 3 | use crate::process::thread_pool::ThreadPool; 4 | use crate::process::Tid; 5 | use alloc::boxed::Box; 6 | use core::cell::UnsafeCell; 7 | 8 | pub struct ProcessorInner { 9 | pool: Box, 10 | idle: Box, 11 | current: Option<(Tid, Box)>, 12 | } 13 | 14 | pub struct Processor { 15 | inner: UnsafeCell>, 16 | } 17 | 18 | unsafe impl Sync for Processor {} 19 | 20 | impl Processor { 21 | pub const fn new() -> Processor { 22 | Processor { 23 | inner: UnsafeCell::new(None), 24 | } 25 | } 26 | 27 | pub fn init(&self, idle: Box, pool: Box) { 28 | unsafe { 29 | *self.inner.get() = Some(ProcessorInner { 30 | pool, 31 | idle, 32 | current: None, 33 | }); 34 | } 35 | } 36 | 37 | #[allow(clippy::mut_from_ref)] 38 | fn inner(&self) -> &mut ProcessorInner { 39 | unsafe { &mut *self.inner.get() } 40 | .as_mut() 41 | .expect("Processor is not initialized!") 42 | } 43 | 44 | pub fn add_thread(&self, thread: Box) { 45 | self.inner().pool.add(thread); 46 | } 47 | 48 | pub fn idle_main(&self) -> ! { 49 | let inner = self.inner(); 50 | disable_and_store(); 51 | 52 | loop { 53 | if let Some(thread) = inner.pool.acquire() { 54 | inner.current = Some(thread); 55 | // println!("\n>>>> will switch_to thread {} in idle_main!", inner.current.as_mut().unwrap().0); 56 | inner 57 | .idle 58 | .switch_to(&mut *inner.current.as_mut().unwrap().1); 59 | 60 | // println!("\n<<<< switch_back to idle in idle_main!"); 61 | let (tid, thread) = inner.current.take().unwrap(); 62 | inner.pool.retrieve(tid, thread); 63 | } else { 64 | enable_and_wfi(); 65 | disable_and_store(); 66 | } 67 | } 68 | } 69 | 70 | pub fn tick(&self) { 71 | let inner = self.inner(); 72 | if inner.current.is_some() && inner.pool.tick() { 73 | let flags = disable_and_store(); 74 | 75 | inner.current.as_mut().unwrap().1.switch_to(&mut inner.idle); 76 | 77 | restore(flags); 78 | } 79 | } 80 | 81 | pub fn exit(&self, code: usize) -> ! { 82 | disable_and_store(); 83 | let inner = self.inner(); 84 | let tid = inner.current.as_ref().unwrap().0; 85 | 86 | inner.pool.exit(tid); 87 | println!("thread {} exited, exit code = {}", tid, code); 88 | 89 | if let Some(wait) = inner.current.as_ref().unwrap().1.wait { 90 | inner.pool.wakeup(wait); 91 | } 92 | 93 | inner.current.as_mut().unwrap().1.switch_to(&mut inner.idle); 94 | 95 | loop {} 96 | } 97 | 98 | pub fn run(&self) { 99 | Thread::get_boot_thread().switch_to(&mut self.inner().idle); 100 | } 101 | 102 | #[allow(unused_unsafe)] 103 | pub fn yield_now(&self) { 104 | let inner = self.inner(); 105 | if inner.current.is_some() { 106 | unsafe { 107 | let flags = disable_and_store(); 108 | let tid = inner.current.as_mut().unwrap().0; 109 | let thread_info = inner.pool.threads[tid] 110 | .as_mut() 111 | .expect("thread not existed when yielding"); 112 | //let thread_info = inner.pool.get_thread_info(tid); 113 | thread_info.status = Status::Sleeping; 114 | inner 115 | .current 116 | .as_mut() 117 | .unwrap() 118 | .1 119 | .switch_to(&mut *inner.idle); 120 | 121 | restore(flags); 122 | } 123 | } 124 | } 125 | 126 | pub fn wake_up(&self, tid: Tid) { 127 | let inner = self.inner(); 128 | inner.pool.wakeup(tid); 129 | } 130 | 131 | pub fn current_tid(&self) -> usize { 132 | self.inner().current.as_mut().unwrap().0 as usize 133 | } 134 | 135 | #[allow(clippy::mut_from_ref)] 136 | pub fn current_thread_mut(&self) -> &mut Thread { 137 | self.inner().current.as_mut().unwrap().1.as_mut() 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /os/src/memory/paging.rs: -------------------------------------------------------------------------------- 1 | use crate::consts::*; 2 | use crate::memory::{access_pa_via_va, alloc_frame, dealloc_frame}; 3 | use riscv::addr::*; 4 | use riscv::asm::{sfence_vma, sfence_vma_all}; 5 | use riscv::paging::{ 6 | FrameAllocator, FrameDeallocator, Mapper, PageTable as PageTableEntryArray, PageTableEntry, 7 | PageTableFlags as EF, Rv39PageTable, 8 | }; 9 | use riscv::register::satp; 10 | 11 | pub struct PageEntry(pub &'static mut PageTableEntry, Page); 12 | 13 | impl PageEntry { 14 | pub fn update(&mut self) { 15 | unsafe { 16 | sfence_vma(0, self.1.start_address().as_usize()); 17 | } 18 | } 19 | 20 | pub fn accessed(&self) -> bool { 21 | self.0.flags().contains(EF::ACCESSED) 22 | } 23 | pub fn clear_accessed(&mut self) { 24 | self.0.flags_mut().remove(EF::ACCESSED); 25 | } 26 | 27 | pub fn dirty(&self) -> bool { 28 | self.0.flags().contains(EF::DIRTY) 29 | } 30 | pub fn clear_dirty(&mut self) { 31 | self.0.flags_mut().remove(EF::DIRTY); 32 | } 33 | 34 | pub fn writable(&self) -> bool { 35 | self.0.flags().contains(EF::WRITABLE) 36 | } 37 | pub fn set_writable(&mut self, value: bool) { 38 | self.0.flags_mut().set(EF::WRITABLE, value); 39 | } 40 | 41 | pub fn present(&self) -> bool { 42 | self.0.flags().contains(EF::VALID | EF::READABLE) 43 | } 44 | pub fn set_present(&mut self, value: bool) { 45 | self.0.flags_mut().set(EF::VALID | EF::READABLE, value); 46 | } 47 | 48 | pub fn user(&self) -> bool { 49 | self.0.flags().contains(EF::USER) 50 | } 51 | pub fn set_user(&mut self, value: bool) { 52 | self.0.flags_mut().set(EF::USER, value); 53 | } 54 | 55 | pub fn execute(&self) -> bool { 56 | self.0.flags().contains(EF::EXECUTABLE) 57 | } 58 | pub fn set_execute(&mut self, value: bool) { 59 | self.0.flags_mut().set(EF::EXECUTABLE, value); 60 | } 61 | 62 | pub fn target(&self) -> usize { 63 | self.0.addr().as_usize() 64 | } 65 | pub fn set_target(&mut self, target: usize) { 66 | let flags = self.0.flags(); 67 | let frame = Frame::of_addr(PhysAddr::new(target)); 68 | self.0.set(frame, flags); 69 | } 70 | } 71 | 72 | struct FrameAllocatorForPaging; 73 | 74 | impl FrameAllocator for FrameAllocatorForPaging { 75 | fn alloc(&mut self) -> Option { 76 | alloc_frame() 77 | } 78 | } 79 | 80 | impl FrameDeallocator for FrameAllocatorForPaging { 81 | fn dealloc(&mut self, frame: Frame) { 82 | dealloc_frame(frame) 83 | } 84 | } 85 | 86 | pub struct PageTableImpl { 87 | page_table: Rv39PageTable<'static>, 88 | root_frame: Frame, 89 | entry: Option, 90 | } 91 | 92 | impl PageTableImpl { 93 | pub fn new_bare() -> Self { 94 | let frame = alloc_frame().expect("alloc_frame failed!"); 95 | let paddr = frame.start_address().as_usize(); 96 | let table = unsafe { &mut *(access_pa_via_va(paddr) as *mut PageTableEntryArray) }; 97 | table.zero(); 98 | 99 | PageTableImpl { 100 | page_table: Rv39PageTable::new(table, PHYSICAL_MEMORY_OFFSET), 101 | root_frame: frame, 102 | entry: None, 103 | } 104 | } 105 | 106 | pub fn map(&mut self, va: usize, pa: usize) -> &mut PageEntry { 107 | let flags = EF::VALID | EF::READABLE | EF::WRITABLE; 108 | let page = Page::of_addr(VirtAddr::new(va)); 109 | let frame = Frame::of_addr(PhysAddr::new(pa)); 110 | self.page_table 111 | .map_to(page, frame, flags, &mut FrameAllocatorForPaging) 112 | .unwrap() 113 | .flush(); 114 | self.get_entry(va).expect("fail to get an entry!") 115 | } 116 | 117 | pub fn unmap(&mut self, va: usize) { 118 | let page = Page::of_addr(VirtAddr::new(va)); 119 | let (_, flush) = self.page_table.unmap(page).unwrap(); 120 | flush.flush(); 121 | } 122 | 123 | pub fn get_entry(&mut self, va: usize) -> Option<&mut PageEntry> { 124 | let page = Page::of_addr(VirtAddr::new(va)); 125 | if let Ok(e) = self.page_table.ref_entry(page) { 126 | let e = unsafe { &mut *(e as *mut PageTableEntry) }; 127 | self.entry = Some(PageEntry(e, page)); 128 | Some(self.entry.as_mut().unwrap()) 129 | } else { 130 | None 131 | } 132 | } 133 | pub fn token(&self) -> usize { 134 | self.root_frame.number() | (8 << 60) 135 | } 136 | 137 | unsafe fn set_token(token: usize) { 138 | llvm_asm!("csrw satp, $0" :: "r"(token) :: "volatile"); 139 | } 140 | 141 | fn active_token() -> usize { 142 | satp::read().bits() 143 | } 144 | 145 | fn flush_tlb() { 146 | unsafe { 147 | sfence_vma_all(); 148 | } 149 | } 150 | 151 | pub unsafe fn activate(&self) { 152 | let old_token = Self::active_token(); 153 | let new_token = self.token(); 154 | println!("switch satp from {:#x} to {:#x}", old_token, new_token); 155 | if new_token != old_token { 156 | Self::set_token(new_token); 157 | Self::flush_tlb(); 158 | } 159 | } 160 | } 161 | 162 | #[derive(Clone, Copy, PartialEq, Eq)] 163 | #[repr(C)] 164 | pub struct PageRange { 165 | start: usize, 166 | end: usize, 167 | } 168 | 169 | // 为 PageRange 实现 Iterator trait 成为可被遍历的迭代器 170 | impl Iterator for PageRange { 171 | type Item = usize; 172 | 173 | fn next(&mut self) -> Option { 174 | if self.start < self.end { 175 | let page = self.start << 12; 176 | self.start += 1; 177 | Some(page) 178 | } else { 179 | None 180 | } 181 | } 182 | } 183 | 184 | impl PageRange { 185 | pub fn new(start_addr: usize, end_addr: usize) -> Self { 186 | PageRange { 187 | start: start_addr / PAGE_SIZE, 188 | end: (end_addr - 1) / PAGE_SIZE + 1, 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /os/src/process/structs.rs: -------------------------------------------------------------------------------- 1 | use super::{ExitCode, Tid}; 2 | use crate::alloc::alloc::{alloc, dealloc, Layout}; 3 | use crate::consts::*; 4 | use crate::context::Context; 5 | use crate::fs::file::File; 6 | use crate::memory::memory_set::{attr::MemoryAttr, handler::ByFrame, MemorySet}; 7 | use alloc::boxed::Box; 8 | use alloc::sync::Arc; 9 | use riscv::register::satp; 10 | use spin::Mutex; 11 | use xmas_elf::{ 12 | header, 13 | program::{Flags, SegmentData, Type}, 14 | ElfFile, 15 | }; 16 | 17 | #[derive(Clone)] 18 | pub enum Status { 19 | Ready, 20 | Running(Tid), 21 | Sleeping, 22 | #[allow(dead_code)] 23 | Exited(ExitCode), 24 | } 25 | 26 | pub struct Thread { 27 | pub context: Context, 28 | pub kstack: KernelStack, 29 | pub wait: Option, 30 | pub ofile: [Option>>; NOFILE], 31 | } 32 | 33 | impl Thread { 34 | pub fn switch_to(&mut self, target: &mut Thread) { 35 | unsafe { 36 | self.context.switch(&mut target.context); 37 | } 38 | } 39 | 40 | pub fn new_kernel(entry: usize) -> Box { 41 | unsafe { 42 | let kstack_ = KernelStack::new(); 43 | Box::new(Thread { 44 | context: Context::new_kernel_thread(entry, kstack_.top(), satp::read().bits()), 45 | kstack: kstack_, 46 | wait: None, 47 | ofile: [None; NOFILE], 48 | }) 49 | } 50 | } 51 | 52 | pub fn get_boot_thread() -> Box { 53 | Box::new(Thread { 54 | context: Context::null(), 55 | kstack: KernelStack::new_empty(), 56 | wait: None, 57 | ofile: [None; NOFILE], 58 | }) 59 | } 60 | 61 | pub fn append_initial_arguments(&self, args: [usize; 3]) { 62 | unsafe { 63 | self.context.append_initial_arguments(args); 64 | } 65 | } 66 | 67 | pub unsafe fn new_user(data: &[u8], wait_thread: Option) -> Box { 68 | let elf = ElfFile::new(data).expect("failed to analyse elf!"); 69 | 70 | match elf.header.pt2.type_().as_type() { 71 | header::Type::Executable => { 72 | // println!("it really a executable!"); 73 | } 74 | header::Type::SharedObject => { 75 | panic!("shared object is not supported!"); 76 | } 77 | _ => { 78 | panic!("unsupported elf type!"); 79 | } 80 | } 81 | let entry_addr = elf.header.pt2.entry_point() as usize; 82 | let mut vm = elf.make_memory_set(); 83 | 84 | let ustack_top = { 85 | let (ustack_bottom, ustack_top) = 86 | (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE); 87 | vm.push( 88 | ustack_bottom, 89 | ustack_top, 90 | MemoryAttr::default().set_user(), 91 | ByFrame::new(), 92 | None, 93 | ); 94 | ustack_top 95 | }; 96 | 97 | let kstack = KernelStack::new(); 98 | 99 | let mut thread = Thread { 100 | context: Context::new_user_thread(entry_addr, ustack_top, kstack.top(), vm.token()), 101 | kstack, 102 | wait: wait_thread, 103 | ofile: [None; NOFILE], 104 | }; 105 | for i in 0..3 { 106 | thread.ofile[i] = Some(Arc::new(Mutex::new(File::default()))); 107 | } 108 | Box::new(thread) 109 | } 110 | 111 | // 分配文件描述符 112 | pub fn alloc_fd(&mut self) -> i32 { 113 | let mut fd = 0; 114 | for i in 0usize..NOFILE { 115 | if self.ofile[i].is_none() { 116 | fd = i; 117 | break; 118 | } 119 | } 120 | self.ofile[fd] = Some(Arc::new(Mutex::new(File::default()))); 121 | fd as i32 122 | } 123 | // 回收文件描述符 124 | pub fn dealloc_fd(&mut self, fd: i32) { 125 | assert!(self.ofile[fd as usize].is_some()); 126 | self.ofile[fd as usize] = None; 127 | } 128 | } 129 | 130 | pub struct KernelStack(usize); 131 | impl KernelStack { 132 | pub fn new() -> Self { 133 | let bottom = unsafe { 134 | alloc(Layout::from_size_align(KERNEL_STACK_SIZE, KERNEL_STACK_SIZE).unwrap()) as usize 135 | }; 136 | KernelStack(bottom) 137 | } 138 | pub fn new_empty() -> Self { 139 | KernelStack(0) 140 | } 141 | pub fn top(&self) -> usize { 142 | self.0 + KERNEL_STACK_SIZE 143 | } 144 | } 145 | 146 | impl Drop for KernelStack { 147 | fn drop(&mut self) { 148 | if self.0 != 0 { 149 | unsafe { 150 | dealloc( 151 | self.0 as _, 152 | Layout::from_size_align(KERNEL_STACK_SIZE, KERNEL_STACK_SIZE).unwrap(), 153 | ); 154 | } 155 | } 156 | } 157 | } 158 | 159 | trait ElfExt { 160 | fn make_memory_set(&self) -> MemorySet; 161 | } 162 | 163 | impl ElfExt for ElfFile<'_> { 164 | fn make_memory_set(&self) -> MemorySet { 165 | let mut memory_set = MemorySet::new(); 166 | for ph in self.program_iter() { 167 | if ph.get_type() != Ok(Type::Load) { 168 | continue; 169 | } 170 | let vaddr = ph.virtual_addr() as usize; 171 | let mem_size = ph.mem_size() as usize; 172 | let data = match ph.get_data(self).unwrap() { 173 | SegmentData::Undefined(data) => data, 174 | _ => unreachable!(), 175 | }; 176 | 177 | memory_set.push( 178 | vaddr, 179 | vaddr + mem_size, 180 | ph.flags().to_attr(), 181 | ByFrame::new(), 182 | Some((data.as_ptr() as usize, data.len())), 183 | ); 184 | } 185 | memory_set 186 | } 187 | } 188 | 189 | trait ToMemoryAttr { 190 | fn to_attr(&self) -> MemoryAttr; 191 | } 192 | impl ToMemoryAttr for Flags { 193 | fn to_attr(&self) -> MemoryAttr { 194 | let mut flags = MemoryAttr::default().set_user(); 195 | if self.is_execute() { 196 | flags = flags.set_execute(); 197 | } 198 | flags 199 | } 200 | } 201 | --------------------------------------------------------------------------------