├── user ├── .gitignore ├── .cargo │ └── config.toml ├── src │ ├── bin │ │ ├── infloop.rs │ │ ├── hello_world.rs │ │ ├── cmdline_args.rs │ │ ├── priv_inst.rs │ │ ├── store_fault.rs │ │ ├── priv_csr.rs │ │ ├── yield.rs │ │ ├── stack_overflow.rs │ │ ├── random_num.rs │ │ ├── run_pipe_test.rs │ │ ├── sleep_simple.rs │ │ ├── inputdev_event.rs │ │ ├── gui_tri.rs │ │ ├── barrier_fail.rs │ │ ├── sleep.rs │ │ ├── initproc.rs │ │ ├── count_lines.rs │ │ ├── filetest_simple.rs │ │ ├── forktest_simple.rs │ │ ├── forktest.rs │ │ ├── cat.rs │ │ ├── exit.rs │ │ ├── huge_write.rs │ │ ├── forktest2.rs │ │ ├── threads.rs │ │ ├── forktree.rs │ │ ├── udp.rs │ │ ├── threads_arg.rs │ │ ├── sync_sem.rs │ │ ├── fantastic_text.rs │ │ ├── pipetest.rs │ │ ├── until_timeout.rs │ │ ├── adder.rs │ │ ├── race_adder_arg.rs │ │ ├── condsync_condvar.rs │ │ ├── huge_write_mt.rs │ │ ├── condsync_sem.rs │ │ ├── adder_mutex_spin.rs │ │ ├── adder_mutex_blocking.rs │ │ ├── matrix.rs │ │ ├── gui_shape.rs │ │ ├── adder_simple_spin.rs │ │ ├── adder_simple_yield.rs │ │ ├── adder_atomic.rs │ │ ├── barrier_condvar.rs │ │ ├── peterson.rs │ │ ├── mpsc_sem.rs │ │ ├── pipe_large_test.rs │ │ ├── gui_move.rs │ │ ├── adder_peterson_yield.rs │ │ ├── adder_peterson_spin.rs │ │ ├── phil_din_mutex.rs │ │ ├── stackless_coroutine.rs │ │ └── eisenberg.rs │ ├── net.rs │ ├── lang_items.rs │ ├── linker.ld │ ├── file.rs │ ├── sync.rs │ ├── console.rs │ ├── task.rs │ ├── lib.rs │ └── io.rs ├── Cargo.toml └── Makefile ├── .dockerignore ├── os ├── src │ ├── drivers │ │ ├── bus │ │ │ ├── mod.rs │ │ │ └── virtio.rs │ │ ├── mod.rs │ │ ├── chardev │ │ │ └── mod.rs │ │ ├── block │ │ │ ├── mod.rs │ │ │ └── virtio_blk.rs │ │ ├── net │ │ │ └── mod.rs │ │ ├── gpu │ │ │ └── mod.rs │ │ ├── input │ │ │ └── mod.rs │ │ └── plic.rs │ ├── assert │ │ ├── file.bmp │ │ ├── mouse.bmp │ │ ├── desktop.bmp │ │ └── folder.bmp │ ├── sync │ │ ├── mod.rs │ │ ├── semaphore.rs │ │ ├── condvar.rs │ │ ├── mutex.rs │ │ └── up.rs │ ├── entry.asm │ ├── task │ │ ├── switch.rs │ │ ├── context.rs │ │ ├── switch.S │ │ ├── signal.rs │ │ ├── manager.rs │ │ ├── task.rs │ │ └── processor.rs │ ├── fs │ │ ├── mod.rs │ │ ├── stdio.rs │ │ └── inode.rs │ ├── sbi.rs │ ├── config.rs │ ├── mm │ │ ├── mod.rs │ │ └── heap_allocator.rs │ ├── syscall │ │ ├── input.rs │ │ ├── gui.rs │ │ ├── net.rs │ │ ├── thread.rs │ │ ├── fs.rs │ │ ├── mod.rs │ │ ├── process.rs │ │ └── sync.rs │ ├── console.rs │ ├── linker-qemu.ld │ ├── trap │ │ ├── context.rs │ │ └── trap.S │ ├── lang_items.rs │ ├── logging.rs │ ├── main.rs │ ├── timer.rs │ ├── boards │ │ └── qemu.rs │ └── net │ │ ├── udp.rs │ │ ├── tcp.rs │ │ ├── socket.rs │ │ ├── port_table.rs │ │ └── mod.rs ├── .cargo │ └── config.toml ├── build.rs ├── scripts │ └── qemu-ver-check.sh ├── Cargo.toml └── Makefile ├── easy-fs ├── .gitignore ├── src │ ├── block_dev.rs │ ├── lib.rs │ └── bitmap.rs └── Cargo.toml ├── figures └── logo.png ├── bootloader └── rustsbi-qemu.bin ├── setenv.sh ├── .gitignore ├── rust-toolchain.toml ├── dev-env-info.md ├── Makefile ├── easy-fs-fuse └── Cargo.toml ├── .vscode └── settings.json ├── .devcontainer └── devcontainer.json ├── ping.py ├── .github └── workflows │ └── doc-and-test.yml └── Dockerfile /user/.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | */* 2 | !rust-toolchain.toml -------------------------------------------------------------------------------- /os/src/drivers/bus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod virtio; 2 | -------------------------------------------------------------------------------- /easy-fs/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/figures/logo.png -------------------------------------------------------------------------------- /os/src/assert/file.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/os/src/assert/file.bmp -------------------------------------------------------------------------------- /os/src/assert/mouse.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/os/src/assert/mouse.bmp -------------------------------------------------------------------------------- /os/src/assert/desktop.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/os/src/assert/desktop.bmp -------------------------------------------------------------------------------- /os/src/assert/folder.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/os/src/assert/folder.bmp -------------------------------------------------------------------------------- /bootloader/rustsbi-qemu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcore-os/rCore-Tutorial-v3/HEAD/bootloader/rustsbi-qemu.bin -------------------------------------------------------------------------------- /setenv.sh: -------------------------------------------------------------------------------- 1 | export PATH=$(rustc --print sysroot)/bin:$PATH 2 | export RUST_SRC_PATH=$(rustc --print sysroot)/lib/rustlib/src/rust/library/ 3 | -------------------------------------------------------------------------------- /os/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64gc-unknown-none-elf" 3 | 4 | [target.riscv64gc-unknown-none-elf] 5 | rustflags = [ 6 | "-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes" 7 | ] 8 | -------------------------------------------------------------------------------- /user/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64gc-unknown-none-elf" 3 | 4 | [target.riscv64gc-unknown-none-elf] 5 | rustflags = [ 6 | "-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes" 7 | ] 8 | -------------------------------------------------------------------------------- /user/src/bin/infloop.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(clippy::empty_loop)] 4 | 5 | extern crate user_lib; 6 | 7 | #[unsafe(no_mangle)] 8 | pub fn main(_argc: usize, _argv: &[&str]) -> ! { 9 | loop {} 10 | } 11 | -------------------------------------------------------------------------------- /os/build.rs: -------------------------------------------------------------------------------- 1 | static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/"; 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=../user/src/"); 5 | println!("cargo:rerun-if-changed={}", TARGET_PATH); 6 | } 7 | -------------------------------------------------------------------------------- /user/src/bin/hello_world.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[unsafe(no_mangle)] 8 | pub fn main() -> i32 { 9 | println!("Hello world from user mode program!"); 10 | 0 11 | } 12 | -------------------------------------------------------------------------------- /easy-fs/src/block_dev.rs: -------------------------------------------------------------------------------- 1 | use core::any::Any; 2 | 3 | pub trait BlockDevice: Send + Sync + Any { 4 | fn read_block(&self, block_id: usize, buf: &mut [u8]); 5 | fn write_block(&self, block_id: usize, buf: &[u8]); 6 | fn handle_irq(&self); 7 | } 8 | -------------------------------------------------------------------------------- /os/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | mod condvar; 2 | mod mutex; 3 | mod semaphore; 4 | mod up; 5 | 6 | pub use condvar::Condvar; 7 | pub use mutex::{Mutex, MutexBlocking, MutexSpin}; 8 | pub use semaphore::Semaphore; 9 | pub use up::{UPIntrFreeCell, UPIntrRefMut}; 10 | -------------------------------------------------------------------------------- /os/src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod bus; 3 | pub mod chardev; 4 | pub mod gpu; 5 | pub mod input; 6 | pub mod net; 7 | pub mod plic; 8 | 9 | pub use block::BLOCK_DEVICE; 10 | pub use bus::*; 11 | pub use gpu::*; 12 | pub use input::*; 13 | pub use net::*; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*/* 2 | !.github/* 3 | !.vscode/settings.json 4 | !.devcontainer/devcontainer.json 5 | 6 | **/target/ 7 | **/Cargo.lock 8 | 9 | os/src/link_app.S 10 | os/src/linker.ld 11 | os/last-* 12 | os/.gdb_history 13 | os/virt.out 14 | tools/ 15 | pushall.sh 16 | .vscode/*.log 17 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | # use the nightly version of the last stable toolchain, see 4 | channel = "nightly-2025-02-18" 5 | components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] 6 | targets = ["riscv64gc-unknown-none-elf"] 7 | -------------------------------------------------------------------------------- /os/src/entry.asm: -------------------------------------------------------------------------------- 1 | .section .text.entry 2 | .globl _start 3 | _start: 4 | la sp, boot_stack_top 5 | call rust_main 6 | 7 | .section .bss.stack 8 | .globl boot_stack_lower_bound 9 | boot_stack_lower_bound: 10 | .space 4096 * 16 11 | .globl boot_stack_top 12 | boot_stack_top: 13 | -------------------------------------------------------------------------------- /user/src/net.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn connect(ip: u32, sport: u16, dport: u16) -> isize { 4 | sys_connect(ip, sport, dport) 5 | } 6 | 7 | pub fn listen(sport: u16) -> isize { 8 | sys_listen(sport) 9 | } 10 | 11 | pub fn accept(socket_fd: usize) -> isize { 12 | sys_accept(socket_fd) 13 | } 14 | -------------------------------------------------------------------------------- /dev-env-info.md: -------------------------------------------------------------------------------- 1 | # rCore-Tutorial-v3 2 | rCore-Tutorial version 3.x 3 | 4 | ## Dependency 5 | 6 | ### Binaries 7 | 8 | * rustc: 1.57.0-nightly (e1e9319d9 2021-10-14) 9 | 10 | * cargo-binutils: 0.3.3 11 | 12 | * qemu: 5.0.0 13 | 14 | * rustsbi-lib: 0.2.0-alpha.4 15 | 16 | rustsbi-qemu: d4968dd2 17 | 18 | rustsbi-k210: b689314e 19 | -------------------------------------------------------------------------------- /user/src/bin/cmdline_args.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 11 | println!("argc = {}", argc); 12 | for (i, arg) in argv.iter().enumerate() { 13 | println!("argv[{}] = {}", i, arg); 14 | } 15 | 0 16 | } 17 | -------------------------------------------------------------------------------- /user/src/bin/priv_inst.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use core::arch::asm; 8 | 9 | #[unsafe(no_mangle)] 10 | fn main() -> i32 { 11 | println!("Try to execute privileged instruction in U Mode"); 12 | println!("Kernel should kill this application!"); 13 | unsafe { 14 | asm!("sret"); 15 | } 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DOCKER_TAG ?= rcore-tutorial-v3:latest 2 | .PHONY: docker build_docker 3 | 4 | docker: 5 | docker run --rm -it -v ${PWD}:/mnt -w /mnt --name rcore-tutorial-v3 ${DOCKER_TAG} bash 6 | 7 | build_docker: 8 | docker build -t ${DOCKER_TAG} --target build . 9 | 10 | fmt: 11 | cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd .. 12 | 13 | -------------------------------------------------------------------------------- /easy-fs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | extern crate alloc; 4 | 5 | mod bitmap; 6 | mod block_cache; 7 | mod block_dev; 8 | mod efs; 9 | mod layout; 10 | mod vfs; 11 | 12 | pub const BLOCK_SZ: usize = 512; 13 | use bitmap::Bitmap; 14 | use block_cache::{block_cache_sync_all, get_block_cache}; 15 | pub use block_dev::BlockDevice; 16 | pub use efs::EasyFileSystem; 17 | use layout::*; 18 | pub use vfs::Inode; 19 | -------------------------------------------------------------------------------- /user/src/bin/store_fault.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[unsafe(no_mangle)] 8 | fn main() -> i32 { 9 | println!("Into Test store_fault, we will insert an invalid store operation..."); 10 | println!("Kernel should kill this application!"); 11 | unsafe { 12 | core::ptr::null_mut::().write_volatile(0); 13 | } 14 | 0 15 | } 16 | -------------------------------------------------------------------------------- /user/src/bin/priv_csr.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use riscv::register::sstatus::{self, SPP}; 8 | 9 | #[unsafe(no_mangle)] 10 | fn main() -> i32 { 11 | println!("Try to access privileged CSR in U Mode"); 12 | println!("Kernel should kill this application!"); 13 | unsafe { 14 | sstatus::set_spp(SPP::User); 15 | } 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /easy-fs-fuse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "easy-fs-fuse" 3 | version = "0.1.0" 4 | authors = ["Yifan Wu "] 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 | clap = "2.33.3" 11 | easy-fs = { path = "../easy-fs" } 12 | rand = "0.8.0" 13 | 14 | # [features] 15 | # board_qemu = [] 16 | # board_k210 = [] 17 | -------------------------------------------------------------------------------- /os/src/task/switch.rs: -------------------------------------------------------------------------------- 1 | use super::TaskContext; 2 | use core::arch::global_asm; 3 | 4 | global_asm!(include_str!("switch.S")); 5 | 6 | unsafe extern "C" { 7 | /// Switch to the context of `next_task_cx_ptr`, saving the current context 8 | /// in `current_task_cx_ptr`. 9 | pub unsafe fn __switch( 10 | current_task_cx_ptr: *mut TaskContext, 11 | next_task_cx_ptr: *const TaskContext, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /user/src/bin/yield.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{getpid, yield_}; 7 | 8 | #[unsafe(no_mangle)] 9 | pub fn main() -> i32 { 10 | println!("Hello, I am process {}.", getpid()); 11 | for i in 0..5 { 12 | yield_(); 13 | println!("Back in process {}, iteration {}.", getpid(), i); 14 | } 15 | println!("yield pass."); 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /os/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | mod inode; 2 | mod pipe; 3 | mod stdio; 4 | 5 | use crate::mm::UserBuffer; 6 | 7 | pub trait File: Send + Sync { 8 | fn readable(&self) -> bool; 9 | fn writable(&self) -> bool; 10 | fn read(&self, buf: UserBuffer) -> usize; 11 | fn write(&self, buf: UserBuffer) -> usize; 12 | } 13 | 14 | pub use inode::{OpenFlags, list_apps, open_file}; 15 | pub use pipe::make_pipe; 16 | pub use stdio::{Stdin, Stdout}; 17 | -------------------------------------------------------------------------------- /user/src/bin/stack_overflow.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[allow(unconditional_recursion)] 8 | fn f(depth: usize) { 9 | if depth % 10 == 0 { 10 | println!("depth = {}", depth); 11 | } 12 | f(depth + 1); 13 | } 14 | 15 | #[unsafe(no_mangle)] 16 | pub fn main() -> i32 { 17 | println!("It should trigger segmentation fault!"); 18 | f(0); 19 | 0 20 | } 21 | -------------------------------------------------------------------------------- /os/src/drivers/chardev/mod.rs: -------------------------------------------------------------------------------- 1 | mod ns16550a; 2 | 3 | use crate::board::CharDeviceImpl; 4 | use alloc::sync::Arc; 5 | use lazy_static::*; 6 | pub use ns16550a::NS16550a; 7 | 8 | pub trait CharDevice { 9 | fn init(&self); 10 | fn read(&self) -> u8; 11 | fn write(&self, ch: u8); 12 | fn handle_irq(&self); 13 | } 14 | 15 | lazy_static! { 16 | pub static ref UART: Arc = Arc::new(CharDeviceImpl::new()); 17 | } 18 | -------------------------------------------------------------------------------- /easy-fs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "easy-fs" 3 | version = "0.1.0" 4 | authors = ["Yifan Wu "] 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 | spin = "0.7.0" 11 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 12 | 13 | [profile.release] 14 | debug = true 15 | 16 | [features] 17 | board_qemu = [] 18 | board_k210 = [] -------------------------------------------------------------------------------- /os/src/sbi.rs: -------------------------------------------------------------------------------- 1 | /// use sbi call to set timer 2 | pub fn set_timer(timer: usize) { 3 | sbi_rt::set_timer(timer as _); 4 | } 5 | 6 | /// use sbi call to shutdown the kernel 7 | pub fn shutdown(failure: bool) -> ! { 8 | use sbi_rt::{NoReason, Shutdown, SystemFailure, system_reset}; 9 | if !failure { 10 | system_reset(Shutdown, NoReason); 11 | } else { 12 | system_reset(Shutdown, SystemFailure); 13 | } 14 | unreachable!() 15 | } 16 | -------------------------------------------------------------------------------- /user/src/bin/random_num.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use oorandom; 7 | 8 | #[unsafe(no_mangle)] 9 | pub fn main() -> i32 { 10 | println!("random num program!"); 11 | let seed = 4; 12 | let mut rng = oorandom::Rand32::new(seed); 13 | println!("OORandom: Random number 32bit: {}", rng.rand_i32()); 14 | println!("OORandom: Random number range: {}", rng.rand_range(1..100)); 15 | 0 16 | } 17 | -------------------------------------------------------------------------------- /os/src/config.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | 3 | pub const USER_STACK_SIZE: usize = 4096 * 2; 4 | pub const KERNEL_STACK_SIZE: usize = 4096 * 2; 5 | pub const KERNEL_HEAP_SIZE: usize = 0x100_0000; 6 | pub const PAGE_SIZE: usize = 0x1000; 7 | pub const PAGE_SIZE_BITS: usize = 0xc; 8 | 9 | pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; 10 | pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE; 11 | 12 | pub use crate::board::{CLOCK_FREQ, MEMORY_END, MMIO}; 13 | -------------------------------------------------------------------------------- /user/src/bin/run_pipe_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exec, fork, wait}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | for i in 0..5 { 12 | if fork() == 0 { 13 | exec("pipe_large_test\0", &[core::ptr::null::()]); 14 | } else { 15 | let mut _unused: i32 = 0; 16 | wait(&mut _unused); 17 | println!("Iter {} OK.", i); 18 | } 19 | } 20 | 0 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Prevent "can't find crate for `test`" error on no_std 3 | // Ref: https://github.com/rust-lang/vscode-rust/issues/729 4 | // For vscode-rust plugin users: 5 | "rust.target": "riscv64gc-unknown-none-elf", 6 | "rust.all_targets": false, 7 | // For Rust Analyzer plugin users: 8 | "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", 9 | "rust-analyzer.checkOnSave.allTargets": false, 10 | // "rust-analyzer.cargo.features": [ 11 | // "board_qemu" 12 | // ] 13 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rcore-tutorial-v3", 3 | "build": { 4 | "dockerfile": "../Dockerfile", 5 | "args": { 6 | "QEMU_VERSION": "7.0.0", 7 | "DEBIAN_FRONTEND": "noninteractive", 8 | "GDB_VERSION": "14.1" 9 | } 10 | }, 11 | "postCreateCommand": "rustup show", 12 | "customizations": { 13 | "vscode": { 14 | "extensions": [ 15 | "rust-lang.rust-analyzer", 16 | "ms-vscode.cpptools", 17 | "tamasfe.even-better-toml" 18 | ] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /os/src/task/context.rs: -------------------------------------------------------------------------------- 1 | use crate::trap::trap_return; 2 | 3 | #[repr(C)] 4 | pub struct TaskContext { 5 | ra: usize, 6 | sp: usize, 7 | s: [usize; 12], 8 | } 9 | 10 | impl TaskContext { 11 | pub fn zero_init() -> Self { 12 | Self { 13 | ra: 0, 14 | sp: 0, 15 | s: [0; 12], 16 | } 17 | } 18 | pub fn goto_trap_return(kstack_ptr: usize) -> Self { 19 | Self { 20 | ra: trap_return as usize, 21 | sp: kstack_ptr, 22 | s: [0; 12], 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ping.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | import time 4 | 5 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 6 | addr = ('localhost', 26099) 7 | sock.bind(addr) 8 | 9 | 10 | print("pinging...", file=sys.stderr) 11 | while True: 12 | buf, raddr = sock.recvfrom(4096) 13 | print("receive: " + buf.decode("utf-8")) 14 | buf = "this is a ping to port 6200!".encode('utf-8') 15 | sock.sendto(buf, ("127.0.0.1", 6200)) 16 | buf = "this is a ping to reply!".encode('utf-8') 17 | sock.sendto(buf, raddr) 18 | time.sleep(1) 19 | -------------------------------------------------------------------------------- /user/src/bin/sleep_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{get_time, sleep}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | println!("into sleep test!"); 12 | let start = get_time(); 13 | println!("current time_msec = {}", start); 14 | sleep(100); 15 | let end = get_time(); 16 | println!( 17 | "time_msec = {} after sleeping 100 ticks, delta = {}ms!", 18 | end, 19 | end - start 20 | ); 21 | println!("r_sleep passed!"); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | use super::{SignalFlags, getpid, kill}; 2 | 3 | #[panic_handler] 4 | fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { 5 | let err = panic_info.message(); 6 | if let Some(location) = panic_info.location() { 7 | println!( 8 | "Panicked at {}:{}, {}", 9 | location.file(), 10 | location.line(), 11 | err 12 | ); 13 | } else { 14 | println!("Panicked: {}", err); 15 | } 16 | kill(getpid() as usize, SignalFlags::SIGABRT.bits()); 17 | unreachable!() 18 | } 19 | -------------------------------------------------------------------------------- /user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user_lib" 3 | version = "0.1.0" 4 | authors = ["Yifan Wu "] 5 | edition = "2024" 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.6" 11 | bitflags = "1.2.1" 12 | riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } 13 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 14 | embedded-graphics = "0.7.1" 15 | oorandom ="11" 16 | virtio-input-decoder = "0.1.4" 17 | 18 | [profile.release] 19 | debug = true 20 | -------------------------------------------------------------------------------- /user/src/linker.ld: -------------------------------------------------------------------------------- 1 | 2 | OUTPUT_ARCH(riscv) 3 | ENTRY(_start) 4 | 5 | BASE_ADDRESS = 0x10000; 6 | 7 | SECTIONS 8 | { 9 | . = BASE_ADDRESS; 10 | .text : { 11 | *(.text.entry) 12 | *(.text .text.*) 13 | } 14 | . = ALIGN(4K); 15 | .rodata : { 16 | *(.rodata .rodata.*) 17 | *(.srodata .srodata.*) 18 | } 19 | . = ALIGN(4K); 20 | .data : { 21 | *(.data .data.*) 22 | *(.sdata .sdata.*) 23 | } 24 | .bss : { 25 | *(.bss .bss.*) 26 | *(.sbss .sbss.*) 27 | } 28 | /DISCARD/ : { 29 | *(.eh_frame) 30 | *(.debug*) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /user/src/bin/inputdev_event.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use user_lib::{DecodeType, Key, KeyType, event_get}; 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | println!("Input device event test"); 12 | loop { 13 | if let Some(event) = event_get() { 14 | if let Some(decoder_type) = event.decode() { 15 | println!("{:?}", decoder_type); 16 | if let DecodeType::Key(key, keytype) = decoder_type { 17 | if key == Key::Enter && keytype == KeyType::Press { 18 | break; 19 | } 20 | } 21 | } 22 | } 23 | } 24 | 0 25 | } 26 | -------------------------------------------------------------------------------- /user/src/bin/gui_tri.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use embedded_graphics::prelude::Size; 7 | use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); 12 | disp.paint_on_framebuffer(|fb| { 13 | for y in 0..VIRTGPU_YRES as usize { 14 | for x in 0..VIRTGPU_XRES as usize { 15 | let idx = (y * VIRTGPU_XRES as usize + x) * 4; 16 | fb[idx] = x as u8; 17 | fb[idx + 1] = y as u8; 18 | fb[idx + 2] = (x + y) as u8; 19 | } 20 | } 21 | }); 22 | disp.flush(); 23 | 0 24 | } 25 | -------------------------------------------------------------------------------- /user/src/bin/barrier_fail.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use user_lib::{exit, thread_create, waittid}; 10 | 11 | const THREAD_NUM: usize = 3; 12 | 13 | fn thread_fn() { 14 | for ch in 'a'..='c' { 15 | for _ in 0..300 { 16 | print!("{}", ch); 17 | } 18 | } 19 | exit(0) 20 | } 21 | 22 | #[unsafe(no_mangle)] 23 | pub fn main() -> i32 { 24 | let mut v: Vec = Vec::new(); 25 | for _ in 0..THREAD_NUM { 26 | v.push(thread_create(thread_fn as usize, 0)); 27 | } 28 | for tid in v.into_iter() { 29 | waittid(tid as usize); 30 | } 31 | println!("\nOK!"); 32 | 0 33 | } 34 | -------------------------------------------------------------------------------- /user/src/file.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | bitflags! { 4 | pub struct OpenFlags: u32 { 5 | const RDONLY = 0; 6 | const WRONLY = 1 << 0; 7 | const RDWR = 1 << 1; 8 | const CREATE = 1 << 9; 9 | const TRUNC = 1 << 10; 10 | } 11 | } 12 | 13 | pub fn dup(fd: usize) -> isize { 14 | sys_dup(fd) 15 | } 16 | pub fn open(path: &str, flags: OpenFlags) -> isize { 17 | sys_open(path, flags.bits) 18 | } 19 | pub fn close(fd: usize) -> isize { 20 | sys_close(fd) 21 | } 22 | pub fn pipe(pipe_fd: &mut [usize]) -> isize { 23 | sys_pipe(pipe_fd) 24 | } 25 | pub fn read(fd: usize, buf: &mut [u8]) -> isize { 26 | sys_read(fd, buf) 27 | } 28 | pub fn write(fd: usize, buf: &[u8]) -> isize { 29 | sys_write(fd, buf) 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, get_time, sleep, waitpid}; 8 | 9 | fn sleepy() { 10 | let time: usize = 100; 11 | for i in 0..5 { 12 | sleep(time); 13 | println!("sleep {} x {} msecs.", i + 1, time); 14 | } 15 | exit(0); 16 | } 17 | 18 | #[unsafe(no_mangle)] 19 | pub fn main() -> i32 { 20 | let current_time = get_time(); 21 | let pid = fork(); 22 | let mut exit_code: i32 = 0; 23 | if pid == 0 { 24 | sleepy(); 25 | } 26 | assert!(waitpid(pid as usize, &mut exit_code) == pid && exit_code == 0); 27 | println!("use {} msecs.", get_time() - current_time); 28 | println!("sleep pass."); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/initproc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::{exec, fork, wait, yield_}; 7 | 8 | #[unsafe(no_mangle)] 9 | fn main() -> i32 { 10 | if fork() == 0 { 11 | exec("user_shell\0", &[core::ptr::null::()]); 12 | } else { 13 | loop { 14 | let mut exit_code: i32 = 0; 15 | let pid = wait(&mut exit_code); 16 | if pid == -1 { 17 | yield_(); 18 | continue; 19 | } 20 | /* 21 | println!( 22 | "[initproc] Released a zombie process, pid={}, exit_code={}", 23 | pid, 24 | exit_code, 25 | ); 26 | */ 27 | } 28 | } 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /os/src/mm/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod frame_allocator; 3 | mod heap_allocator; 4 | mod memory_set; 5 | mod page_table; 6 | 7 | pub use address::VPNRange; 8 | pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; 9 | pub use frame_allocator::{FrameTracker, frame_alloc, frame_alloc_more, frame_dealloc}; 10 | pub use memory_set::{KERNEL_SPACE, MapArea, MapPermission, MapType, MemorySet, kernel_token}; 11 | use page_table::PTEFlags; 12 | pub use page_table::{ 13 | PageTable, PageTableEntry, UserBuffer, translated_byte_buffer, translated_ref, 14 | translated_refmut, translated_str, 15 | }; 16 | 17 | pub fn init() { 18 | heap_allocator::init_heap(); 19 | frame_allocator::init_frame_allocator(); 20 | KERNEL_SPACE.exclusive_access().activate(); 21 | } 22 | -------------------------------------------------------------------------------- /os/src/syscall/input.rs: -------------------------------------------------------------------------------- 1 | //use crate::drivers::{KEYBOARD_DEVICE,MOUSE_DEVICE,INPUT_CONDVAR,read_input_event}; 2 | use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; 3 | 4 | pub fn sys_event_get() -> isize { 5 | let kb = KEYBOARD_DEVICE.clone(); 6 | let mouse = MOUSE_DEVICE.clone(); 7 | //let input=INPUT_CONDVAR.clone(); 8 | //read_input_event() as isize 9 | if !kb.is_empty() { 10 | kb.read_event() as isize 11 | } else if !mouse.is_empty() { 12 | mouse.read_event() as isize 13 | } else { 14 | 0 15 | } 16 | } 17 | 18 | use crate::drivers::chardev::UART; 19 | 20 | /// check UART's read-buffer is empty or not 21 | pub fn sys_key_pressed() -> isize { 22 | let res = !UART.read_buffer_is_empty(); 23 | if res { 1 } else { 0 } 24 | } 25 | -------------------------------------------------------------------------------- /user/src/bin/count_lines.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::read; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main(_argc: usize, _argv: &[&str]) -> i32 { 11 | let mut buf = [0u8; 256]; 12 | let mut lines = 0usize; 13 | let mut total_size = 0usize; 14 | loop { 15 | let len = read(0, &mut buf) as usize; 16 | if len == 0 { 17 | break; 18 | } 19 | total_size += len; 20 | let string = core::str::from_utf8(&buf[..len]).unwrap(); 21 | lines += string 22 | .chars() 23 | .fold(0, |acc, c| acc + if c == '\n' { 1 } else { 0 }); 24 | } 25 | if total_size > 0 { 26 | lines += 1; 27 | } 28 | println!("{}", lines); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /os/src/task/switch.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .macro SAVE_SN n 3 | sd s\n, (\n+2)*8(a0) 4 | .endm 5 | .macro LOAD_SN n 6 | ld s\n, (\n+2)*8(a1) 7 | .endm 8 | .section .text 9 | .globl __switch 10 | __switch: 11 | # __switch( 12 | # current_task_cx_ptr: *mut TaskContext, 13 | # next_task_cx_ptr: *const TaskContext 14 | # ) 15 | # save kernel stack of current task 16 | sd sp, 8(a0) 17 | # save ra & s0~s11 of current execution 18 | sd ra, 0(a0) 19 | .set n, 0 20 | .rept 12 21 | SAVE_SN %n 22 | .set n, n + 1 23 | .endr 24 | # restore ra & s0~s11 of next execution 25 | ld ra, 0(a1) 26 | .set n, 0 27 | .rept 12 28 | LOAD_SN %n 29 | .set n, n + 1 30 | .endr 31 | # restore kernel stack of next task 32 | ld sp, 8(a1) 33 | ret 34 | 35 | -------------------------------------------------------------------------------- /os/scripts/qemu-ver-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Argument1: The filename of qemu executable, e.g. qemu-system-riscv64 4 | QEMU_PATH=$(which $1) 5 | RET=$? 6 | MINIMUM_MAJOR_VERSION=7 7 | RED='\033[0;31m' 8 | GREEN='\033[0;32m' 9 | NC='\033[0m' 10 | if [ $RET != 0 ] 11 | then 12 | echo "$1 not found" 13 | exit 1 14 | else 15 | QEMU_VERSION=$($1 --version|head -n 1|awk '{print $4}') 16 | MAJOR_VERSION=$(echo $QEMU_VERSION | awk -F '.' '{print $1}') 17 | if [ $MAJOR_VERSION -lt $MINIMUM_MAJOR_VERSION ] 18 | then 19 | echo "${RED}Error: Required major version of QEMU is ${MINIMUM_MAJOR_VERSION}, " \ 20 | "but current is ${QEMU_VERSION}.${NC}" 21 | exit 1 22 | else 23 | echo "${GREEN}QEMU version is ${QEMU_VERSION}(>=${MINIMUM_MAJOR_VERSION}), OK!${NC}" 24 | exit 0 25 | fi 26 | fi 27 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := riscv64gc-unknown-none-elf 2 | MODE := release 3 | APP_DIR := src/bin 4 | TARGET_DIR := target/$(TARGET)/$(MODE) 5 | APPS := $(wildcard $(APP_DIR)/*.rs) 6 | ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS)) 7 | BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS)) 8 | 9 | OBJDUMP := rust-objdump --arch-name=riscv64 10 | OBJCOPY := rust-objcopy --binary-architecture=riscv64 11 | CP := cp 12 | 13 | TEST ?= 14 | 15 | elf: $(APPS) 16 | @cargo build --release 17 | ifeq ($(TEST), 1) 18 | @$(CP) $(TARGET_DIR)/usertests $(TARGET_DIR)/initproc 19 | endif 20 | 21 | binary: elf 22 | @$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) 23 | 24 | build: binary 25 | 26 | clean: 27 | @cargo clean 28 | 29 | .PHONY: elf binary build clean 30 | -------------------------------------------------------------------------------- /user/src/bin/filetest_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{OpenFlags, close, open, read, write}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | let test_str = "Hello, world!"; 12 | let filea = "filea\0"; 13 | let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY); 14 | assert!(fd > 0); 15 | let fd = fd as usize; 16 | write(fd, test_str.as_bytes()); 17 | close(fd); 18 | 19 | let fd = open(filea, OpenFlags::RDONLY); 20 | assert!(fd > 0); 21 | let fd = fd as usize; 22 | let mut buffer = [0u8; 100]; 23 | let read_len = read(fd, &mut buffer) as usize; 24 | close(fd); 25 | 26 | assert_eq!(test_str, core::str::from_utf8(&buffer[..read_len]).unwrap(),); 27 | println!("file_test passed!"); 28 | 0 29 | } 30 | -------------------------------------------------------------------------------- /os/src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::drivers::chardev::CharDevice; 2 | use crate::drivers::chardev::UART; 3 | use core::fmt::{self, Write}; 4 | 5 | struct Stdout; 6 | 7 | impl Write for Stdout { 8 | fn write_str(&mut self, s: &str) -> fmt::Result { 9 | for c in s.chars() { 10 | UART.write(c as u8); 11 | } 12 | Ok(()) 13 | } 14 | } 15 | 16 | pub fn print(args: fmt::Arguments) { 17 | Stdout.write_fmt(args).unwrap(); 18 | } 19 | 20 | #[macro_export] 21 | macro_rules! print { 22 | ($fmt: literal $(, $($arg: tt)+)?) => { 23 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)) 24 | } 25 | } 26 | 27 | #[macro_export] 28 | macro_rules! println { 29 | ($fmt: literal $(, $($arg: tt)+)?) => { 30 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /user/src/bin/forktest_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{fork, getpid, wait}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | assert_eq!(wait(&mut 0i32), -1); 12 | println!("sys_wait without child process test passed!"); 13 | println!("parent start, pid = {}!", getpid()); 14 | let pid = fork(); 15 | if pid == 0 { 16 | // child process 17 | println!("hello child process!"); 18 | 100 19 | } else { 20 | // parent process 21 | let mut exit_code: i32 = 0; 22 | println!("ready waiting on parent process!"); 23 | assert_eq!(pid, wait(&mut exit_code)); 24 | assert_eq!(exit_code, 100); 25 | println!("child process pid = {}, exit code = {}", pid, exit_code); 26 | 0 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /user/src/bin/forktest.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, wait}; 8 | 9 | const MAX_CHILD: usize = 30; 10 | 11 | #[unsafe(no_mangle)] 12 | pub fn main() -> i32 { 13 | for i in 0..MAX_CHILD { 14 | let pid = fork(); 15 | if pid == 0 { 16 | println!("I am child {}", i); 17 | exit(0); 18 | } else { 19 | println!("forked child pid = {}", pid); 20 | } 21 | assert!(pid > 0); 22 | } 23 | let mut exit_code: i32 = 0; 24 | for _ in 0..MAX_CHILD { 25 | if wait(&mut exit_code) <= 0 { 26 | panic!("wait stopped early"); 27 | } 28 | } 29 | if wait(&mut exit_code) > 0 { 30 | panic!("wait got too many"); 31 | } 32 | println!("forktest pass."); 33 | 0 34 | } 35 | -------------------------------------------------------------------------------- /user/src/bin/cat.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use user_lib::{OpenFlags, close, open, read}; 9 | 10 | #[unsafe(no_mangle)] 11 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 12 | println!("argc = {}", argc); 13 | for (i, arg) in argv.iter().enumerate() { 14 | println!("argv[{}] = {}", i, arg); 15 | } 16 | assert!(argc == 2); 17 | let fd = open(argv[1], OpenFlags::RDONLY); 18 | if fd == -1 { 19 | panic!("Error occurred when opening file"); 20 | } 21 | let fd = fd as usize; 22 | let mut buf = [0u8; 256]; 23 | loop { 24 | let size = read(fd, &mut buf) as usize; 25 | if size == 0 { 26 | break; 27 | } 28 | print!("{}", core::str::from_utf8(&buf[..size]).unwrap()); 29 | } 30 | close(fd); 31 | 0 32 | } 33 | -------------------------------------------------------------------------------- /os/src/drivers/block/mod.rs: -------------------------------------------------------------------------------- 1 | mod virtio_blk; 2 | 3 | pub use virtio_blk::VirtIOBlock; 4 | 5 | use crate::board::BlockDeviceImpl; 6 | use alloc::sync::Arc; 7 | use easy_fs::BlockDevice; 8 | use lazy_static::*; 9 | 10 | lazy_static! { 11 | pub static ref BLOCK_DEVICE: Arc = Arc::new(BlockDeviceImpl::new()); 12 | } 13 | 14 | #[allow(unused)] 15 | pub fn block_device_test() { 16 | let block_device = BLOCK_DEVICE.clone(); 17 | let mut write_buffer = [0u8; 512]; 18 | let mut read_buffer = [0u8; 512]; 19 | for i in 0..512 { 20 | for byte in write_buffer.iter_mut() { 21 | *byte = i as u8; 22 | } 23 | block_device.write_block(i as usize, &write_buffer); 24 | block_device.read_block(i as usize, &mut read_buffer); 25 | assert_eq!(write_buffer, read_buffer); 26 | } 27 | println!("block device test passed!"); 28 | } 29 | -------------------------------------------------------------------------------- /os/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "os" 3 | version = "0.1.0" 4 | authors = ["Yifan Wu "] 5 | edition = "2024" 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 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 12 | buddy_system_allocator = "0.6" 13 | bitflags = "1.2.1" 14 | xmas-elf = "0.7.0" 15 | volatile = "0.3" 16 | virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } 17 | lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "db42380" } 18 | easy-fs = { path = "../easy-fs" } 19 | embedded-graphics = "0.7.1" 20 | tinybmp = "0.3.1" 21 | log = "0.4" 22 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 23 | 24 | [profile.release] 25 | debug = true 26 | -------------------------------------------------------------------------------- /user/src/bin/exit.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{exit, fork, wait, waitpid, yield_}; 7 | 8 | const MAGIC: i32 = -0x10384; 9 | 10 | #[unsafe(no_mangle)] 11 | pub fn main() -> i32 { 12 | println!("I am the parent. Forking the child..."); 13 | let pid = fork(); 14 | if pid == 0 { 15 | println!("I am the child."); 16 | for _ in 0..7 { 17 | yield_(); 18 | } 19 | exit(MAGIC); 20 | } else { 21 | println!("I am parent, fork a child pid {}", pid); 22 | } 23 | println!("I am the parent, waiting now.."); 24 | let mut xstate: i32 = 0; 25 | assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC); 26 | assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0); 27 | println!("waitpid {} ok.", pid); 28 | println!("exit pass."); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/sync.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn mutex_create() -> isize { 4 | sys_mutex_create(false) 5 | } 6 | pub fn mutex_blocking_create() -> isize { 7 | sys_mutex_create(true) 8 | } 9 | pub fn mutex_lock(mutex_id: usize) { 10 | sys_mutex_lock(mutex_id); 11 | } 12 | pub fn mutex_unlock(mutex_id: usize) { 13 | sys_mutex_unlock(mutex_id); 14 | } 15 | pub fn semaphore_create(res_count: usize) -> isize { 16 | sys_semaphore_create(res_count) 17 | } 18 | pub fn semaphore_up(sem_id: usize) { 19 | sys_semaphore_up(sem_id); 20 | } 21 | pub fn semaphore_down(sem_id: usize) { 22 | sys_semaphore_down(sem_id); 23 | } 24 | pub fn condvar_create() -> isize { 25 | sys_condvar_create() 26 | } 27 | pub fn condvar_signal(condvar_id: usize) { 28 | sys_condvar_signal(condvar_id); 29 | } 30 | pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { 31 | sys_condvar_wait(condvar_id, mutex_id); 32 | } 33 | -------------------------------------------------------------------------------- /user/src/console.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | 3 | const STDIN: usize = 0; 4 | const STDOUT: usize = 1; 5 | 6 | use super::{read, write}; 7 | 8 | struct Stdout; 9 | 10 | impl Write for Stdout { 11 | fn write_str(&mut self, s: &str) -> fmt::Result { 12 | write(STDOUT, s.as_bytes()); 13 | Ok(()) 14 | } 15 | } 16 | 17 | pub fn print(args: fmt::Arguments) { 18 | Stdout.write_fmt(args).unwrap(); 19 | } 20 | 21 | #[macro_export] 22 | macro_rules! print { 23 | ($fmt: literal $(, $($arg: tt)+)?) => { 24 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 25 | } 26 | } 27 | 28 | #[macro_export] 29 | macro_rules! println { 30 | ($fmt: literal $(, $($arg: tt)+)?) => { 31 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 32 | } 33 | } 34 | 35 | pub fn getchar() -> u8 { 36 | let mut c = [0u8; 1]; 37 | read(STDIN, &mut c); 38 | c[0] 39 | } 40 | -------------------------------------------------------------------------------- /user/src/bin/huge_write.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{OpenFlags, close, get_time, open, write}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main() -> i32 { 11 | let mut buffer = [0u8; 1024]; // 1KiB 12 | for (i, ch) in buffer.iter_mut().enumerate() { 13 | *ch = i as u8; 14 | } 15 | let f = open("testf\0", OpenFlags::CREATE | OpenFlags::WRONLY); 16 | if f < 0 { 17 | panic!("Open test file failed!"); 18 | } 19 | let f = f as usize; 20 | let start = get_time(); 21 | let size_mb = 1usize; 22 | for _ in 0..1024 * size_mb { 23 | write(f, &buffer); 24 | } 25 | close(f); 26 | let time_ms = (get_time() - start) as usize; 27 | let speed_kbs = (size_mb << 20) / time_ms; 28 | println!( 29 | "{}MiB written, time cost = {}ms, write speed = {}KiB/s", 30 | size_mb, time_ms, speed_kbs 31 | ); 32 | 0 33 | } 34 | -------------------------------------------------------------------------------- /user/src/bin/forktest2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, get_time, getpid, sleep, wait}; 8 | 9 | static NUM: usize = 30; 10 | 11 | #[unsafe(no_mangle)] 12 | pub fn main() -> i32 { 13 | for _ in 0..NUM { 14 | let pid = fork(); 15 | if pid == 0 { 16 | let current_time = get_time(); 17 | let sleep_length = 18 | (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000; 19 | println!("pid {} sleep for {} ms", getpid(), sleep_length); 20 | sleep(sleep_length as usize); 21 | println!("pid {} OK!", getpid()); 22 | exit(0); 23 | } 24 | } 25 | 26 | let mut exit_code: i32 = 0; 27 | for _ in 0..NUM { 28 | assert!(wait(&mut exit_code) > 0); 29 | assert_eq!(exit_code, 0); 30 | } 31 | assert!(wait(&mut exit_code) < 0); 32 | println!("forktest2 test passed!"); 33 | 0 34 | } 35 | -------------------------------------------------------------------------------- /os/src/task/signal.rs: -------------------------------------------------------------------------------- 1 | use bitflags::*; 2 | 3 | bitflags! { 4 | pub struct SignalFlags: u32 { 5 | const SIGINT = 1 << 2; 6 | const SIGILL = 1 << 4; 7 | const SIGABRT = 1 << 6; 8 | const SIGFPE = 1 << 8; 9 | const SIGSEGV = 1 << 11; 10 | } 11 | } 12 | 13 | impl SignalFlags { 14 | pub fn check_error(&self) -> Option<(i32, &'static str)> { 15 | if self.contains(Self::SIGINT) { 16 | Some((-2, "Killed, SIGINT=2")) 17 | } else if self.contains(Self::SIGILL) { 18 | Some((-4, "Illegal Instruction, SIGILL=4")) 19 | } else if self.contains(Self::SIGABRT) { 20 | Some((-6, "Aborted, SIGABRT=6")) 21 | } else if self.contains(Self::SIGFPE) { 22 | Some((-8, "Erroneous Arithmetic Operation, SIGFPE=8")) 23 | } else if self.contains(Self::SIGSEGV) { 24 | Some((-11, "Segmentation Fault, SIGSEGV=11")) 25 | } else { 26 | None 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /user/src/bin/threads.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec; 9 | use user_lib::{exit, thread_create, waittid}; 10 | 11 | pub fn thread_a() -> ! { 12 | for _ in 0..1000 { 13 | print!("a"); 14 | } 15 | exit(1) 16 | } 17 | 18 | pub fn thread_b() -> ! { 19 | for _ in 0..1000 { 20 | print!("b"); 21 | } 22 | exit(2) 23 | } 24 | 25 | pub fn thread_c() -> ! { 26 | for _ in 0..1000 { 27 | print!("c"); 28 | } 29 | exit(3) 30 | } 31 | 32 | #[unsafe(no_mangle)] 33 | pub fn main() -> i32 { 34 | let v = vec![ 35 | thread_create(thread_a as usize, 0), 36 | thread_create(thread_b as usize, 0), 37 | thread_create(thread_c as usize, 0), 38 | ]; 39 | for tid in v.iter() { 40 | let exit_code = waittid(*tid as usize); 41 | println!("thread#{} exited with code {}", tid, exit_code); 42 | } 43 | println!("main thread exited."); 44 | 0 45 | } 46 | -------------------------------------------------------------------------------- /os/src/linker-qemu.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x80200000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | skernel = .; 9 | 10 | stext = .; 11 | .text : { 12 | *(.text.entry) 13 | . = ALIGN(4K); 14 | strampoline = .; 15 | *(.text.trampoline); 16 | . = ALIGN(4K); 17 | *(.text .text.*) 18 | } 19 | 20 | . = ALIGN(4K); 21 | etext = .; 22 | srodata = .; 23 | .rodata : { 24 | *(.rodata .rodata.*) 25 | *(.srodata .srodata.*) 26 | } 27 | 28 | . = ALIGN(4K); 29 | erodata = .; 30 | sdata = .; 31 | .data : { 32 | *(.data .data.*) 33 | *(.sdata .sdata.*) 34 | } 35 | 36 | . = ALIGN(4K); 37 | edata = .; 38 | sbss_with_stack = .; 39 | .bss : { 40 | *(.bss.stack) 41 | sbss = .; 42 | *(.bss .bss.*) 43 | *(.sbss .sbss.*) 44 | } 45 | 46 | . = ALIGN(4K); 47 | ebss = .; 48 | ekernel = .; 49 | 50 | /DISCARD/ : { 51 | *(.eh_frame) 52 | } 53 | } -------------------------------------------------------------------------------- /os/src/trap/context.rs: -------------------------------------------------------------------------------- 1 | use riscv::register::sstatus::{self, SPP, Sstatus}; 2 | 3 | #[repr(C)] 4 | #[derive(Debug)] 5 | pub struct TrapContext { 6 | pub x: [usize; 32], 7 | pub sstatus: Sstatus, 8 | pub sepc: usize, 9 | pub kernel_satp: usize, 10 | pub kernel_sp: usize, 11 | pub trap_handler: usize, 12 | } 13 | 14 | impl TrapContext { 15 | pub fn set_sp(&mut self, sp: usize) { 16 | self.x[2] = sp; 17 | } 18 | pub fn app_init_context( 19 | entry: usize, 20 | sp: usize, 21 | kernel_satp: usize, 22 | kernel_sp: usize, 23 | trap_handler: usize, 24 | ) -> Self { 25 | let mut sstatus = sstatus::read(); 26 | // set CPU privilege to User after trapping back 27 | sstatus.set_spp(SPP::User); 28 | let mut cx = Self { 29 | x: [0; 32], 30 | sstatus, 31 | sepc: entry, 32 | kernel_satp, 33 | kernel_sp, 34 | trap_handler, 35 | }; 36 | cx.set_sp(sp); 37 | cx 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /user/src/bin/forktree.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, getpid, sleep, wait, yield_}; 8 | 9 | const DEPTH: usize = 4; 10 | 11 | fn fork_child(cur: &str, branch: char) { 12 | let mut next = [0u8; DEPTH + 1]; 13 | let l = cur.len(); 14 | if l >= DEPTH { 15 | return; 16 | } 17 | next[..l].copy_from_slice(cur.as_bytes()); 18 | next[l] = branch as u8; 19 | if fork() == 0 { 20 | fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap()); 21 | yield_(); 22 | exit(0); 23 | } 24 | } 25 | 26 | fn fork_tree(cur: &str) { 27 | println!("pid{}: {}", getpid(), cur); 28 | fork_child(cur, '0'); 29 | fork_child(cur, '1'); 30 | let mut exit_code: i32 = 0; 31 | for _ in 0..2 { 32 | wait(&mut exit_code); 33 | } 34 | } 35 | 36 | #[unsafe(no_mangle)] 37 | pub fn main() -> i32 { 38 | fork_tree(""); 39 | let mut exit_code: i32 = 0; 40 | for _ in 0..2 { 41 | wait(&mut exit_code); 42 | } 43 | sleep(3000); 44 | 0 45 | } 46 | -------------------------------------------------------------------------------- /user/src/bin/udp.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use alloc::string::String; 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | #[macro_use] 9 | extern crate alloc; 10 | 11 | use user_lib::{connect, read, write}; 12 | 13 | #[unsafe(no_mangle)] 14 | pub fn main() -> i32 { 15 | println!("udp test open!"); 16 | 17 | let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099); 18 | 19 | if udp_fd < 0 { 20 | println!("failed to create udp connection."); 21 | return -1; 22 | } 23 | 24 | let buf = "Hello rCoreOS user program!"; 25 | 26 | println!("send <{}>", buf); 27 | 28 | write(udp_fd as usize, buf.as_bytes()); 29 | 30 | println!("udp send done, waiting for reply."); 31 | 32 | let mut buf = vec![0u8; 1024]; 33 | 34 | let len = read(udp_fd as usize, &mut buf); 35 | 36 | if len < 0 { 37 | println!("can't receive udp packet"); 38 | return -1; 39 | } 40 | 41 | let recv_str = String::from_utf8_lossy(&buf[..len as usize]); 42 | 43 | println!("receive reply <{}>", recv_str); 44 | 45 | 0 46 | } 47 | -------------------------------------------------------------------------------- /os/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi::shutdown; 2 | use crate::task::current_kstack_top; 3 | use core::arch::asm; 4 | use core::panic::PanicInfo; 5 | use log::*; 6 | 7 | #[panic_handler] 8 | fn panic(info: &PanicInfo) -> ! { 9 | if let Some(location) = info.location() { 10 | error!( 11 | "[kernel] Panicked at {}:{} {}", 12 | location.file(), 13 | location.line(), 14 | info.message() 15 | ); 16 | } else { 17 | error!("[kernel] Panicked: {}", info.message()); 18 | } 19 | backtrace(); 20 | shutdown(true) 21 | } 22 | 23 | fn backtrace() { 24 | let mut fp: usize; 25 | let stop = current_kstack_top(); 26 | unsafe { 27 | asm!("mv {}, s0", out(reg) fp); 28 | } 29 | println!("---START BACKTRACE---"); 30 | for i in 0..10 { 31 | if fp == stop { 32 | break; 33 | } 34 | unsafe { 35 | println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize)); 36 | fp = *((fp - 16) as *const usize); 37 | } 38 | } 39 | println!("---END BACKTRACE---"); 40 | } 41 | -------------------------------------------------------------------------------- /user/src/bin/threads_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use user_lib::{exit, thread_create, waittid}; 10 | 11 | struct Argument { 12 | pub ch: char, 13 | pub rc: i32, 14 | } 15 | 16 | fn thread_print(arg: *const Argument) -> ! { 17 | let arg = unsafe { &*arg }; 18 | for _ in 0..1000 { 19 | print!("{}", arg.ch); 20 | } 21 | exit(arg.rc) 22 | } 23 | 24 | #[unsafe(no_mangle)] 25 | pub fn main() -> i32 { 26 | let mut v = Vec::new(); 27 | let args = [ 28 | Argument { ch: 'a', rc: 1 }, 29 | Argument { ch: 'b', rc: 2 }, 30 | Argument { ch: 'c', rc: 3 }, 31 | ]; 32 | for arg in args.iter() { 33 | v.push(thread_create( 34 | thread_print as usize, 35 | arg as *const _ as usize, 36 | )); 37 | } 38 | for tid in v.iter() { 39 | let exit_code = waittid(*tid as usize); 40 | println!("thread#{} exited with code {}", tid, exit_code); 41 | } 42 | println!("main thread exited."); 43 | 0 44 | } 45 | -------------------------------------------------------------------------------- /user/src/bin/sync_sem.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | extern crate alloc; 8 | 9 | use alloc::vec; 10 | use user_lib::exit; 11 | use user_lib::{semaphore_create, semaphore_down, semaphore_up}; 12 | use user_lib::{sleep, thread_create, waittid}; 13 | 14 | const SEM_SYNC: usize = 0; 15 | 16 | unsafe fn first() -> ! { 17 | sleep(10); 18 | println!("First work and wakeup Second"); 19 | semaphore_up(SEM_SYNC); 20 | exit(0) 21 | } 22 | 23 | unsafe fn second() -> ! { 24 | println!("Second want to continue,but need to wait first"); 25 | semaphore_down(SEM_SYNC); 26 | println!("Second can work now"); 27 | exit(0) 28 | } 29 | 30 | #[unsafe(no_mangle)] 31 | pub fn main() -> i32 { 32 | // create semaphores 33 | assert_eq!(semaphore_create(0) as usize, SEM_SYNC); 34 | // create threads 35 | let threads = vec![ 36 | thread_create(first as usize, 0), 37 | thread_create(second as usize, 0), 38 | ]; 39 | // wait for all threads to complete 40 | for thread in threads.iter() { 41 | waittid(*thread as usize); 42 | } 43 | println!("sync_sem passed!"); 44 | 0 45 | } 46 | -------------------------------------------------------------------------------- /os/src/syscall/gui.rs: -------------------------------------------------------------------------------- 1 | use crate::drivers::GPU_DEVICE; 2 | use crate::mm::{MapArea, MapPermission, MapType, PhysAddr, VirtAddr}; 3 | use crate::task::current_process; 4 | 5 | const FB_VADDR: usize = 0x10000000; 6 | 7 | pub fn sys_framebuffer() -> isize { 8 | let fb = GPU_DEVICE.get_framebuffer(); 9 | let len = fb.len(); 10 | // println!("[kernel] FrameBuffer: addr 0x{:X}, len {}", fb.as_ptr() as usize , len); 11 | let fb_start_pa = PhysAddr::from(fb.as_ptr() as usize); 12 | assert!(fb_start_pa.aligned()); 13 | let fb_start_ppn = fb_start_pa.floor(); 14 | let fb_start_vpn = VirtAddr::from(FB_VADDR).floor(); 15 | let pn_offset = fb_start_ppn.0 as isize - fb_start_vpn.0 as isize; 16 | 17 | let current_process = current_process(); 18 | let mut inner = current_process.inner_exclusive_access(); 19 | inner.memory_set.push( 20 | MapArea::new( 21 | (FB_VADDR as usize).into(), 22 | (FB_VADDR + len as usize).into(), 23 | MapType::Linear(pn_offset), 24 | MapPermission::R | MapPermission::W | MapPermission::U, 25 | ), 26 | None, 27 | ); 28 | FB_VADDR as isize 29 | } 30 | 31 | pub fn sys_framebuffer_flush() -> isize { 32 | GPU_DEVICE.flush(); 33 | 0 34 | } 35 | -------------------------------------------------------------------------------- /user/src/bin/fantastic_text.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | macro_rules! color_text { 8 | ($text:expr, $color:expr) => { 9 | format_args!("\x1b[{}m{}\x1b[0m", $color, $text) 10 | }; 11 | } 12 | 13 | #[unsafe(no_mangle)] 14 | pub fn main() -> i32 { 15 | println!( 16 | "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}", 17 | color_text!("H", 31), 18 | color_text!("e", 32), 19 | color_text!("l", 33), 20 | color_text!("l", 34), 21 | color_text!("o", 35), 22 | color_text!("R", 36), 23 | color_text!("u", 37), 24 | color_text!("s", 90), 25 | color_text!("t", 91), 26 | color_text!("u", 92), 27 | color_text!("C", 93), 28 | color_text!("o", 94), 29 | color_text!("r", 95), 30 | color_text!("e", 96), 31 | color_text!("!", 97), 32 | ); 33 | 34 | let text = 35 | "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m"; 36 | println!("\x1b[47m{}\x1b[0m", color_text!(text, 30)); 37 | for i in 31..38 { 38 | println!("{}", color_text!(text, i)); 39 | } 40 | for i in 90..98 { 41 | println!("{}", color_text!(text, i)); 42 | } 43 | 0 44 | } 45 | -------------------------------------------------------------------------------- /os/src/fs/stdio.rs: -------------------------------------------------------------------------------- 1 | use super::File; 2 | use crate::drivers::chardev::CharDevice; 3 | use crate::drivers::chardev::UART; 4 | use crate::mm::UserBuffer; 5 | 6 | pub struct Stdin; 7 | pub struct Stdout; 8 | 9 | impl File for Stdin { 10 | fn readable(&self) -> bool { 11 | true 12 | } 13 | fn writable(&self) -> bool { 14 | false 15 | } 16 | fn read(&self, mut user_buf: UserBuffer) -> usize { 17 | assert_eq!(user_buf.len(), 1); 18 | //println!("before UART.read() in Stdin::read()"); 19 | let ch = UART.read(); 20 | unsafe { 21 | user_buf.buffers[0].as_mut_ptr().write_volatile(ch); 22 | } 23 | 1 24 | } 25 | fn write(&self, _user_buf: UserBuffer) -> usize { 26 | panic!("Cannot write to stdin!"); 27 | } 28 | } 29 | 30 | impl File for Stdout { 31 | fn readable(&self) -> bool { 32 | false 33 | } 34 | fn writable(&self) -> bool { 35 | true 36 | } 37 | fn read(&self, _user_buf: UserBuffer) -> usize { 38 | panic!("Cannot read from stdout!"); 39 | } 40 | fn write(&self, user_buf: UserBuffer) -> usize { 41 | for buffer in user_buf.buffers.iter() { 42 | print!("{}", core::str::from_utf8(*buffer).unwrap()); 43 | } 44 | user_buf.len() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /user/src/bin/pipetest.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{close, fork, pipe, read, wait, write}; 8 | 9 | static STR: &str = "Hello, world!"; 10 | 11 | #[unsafe(no_mangle)] 12 | pub fn main() -> i32 { 13 | // create pipe 14 | let mut pipe_fd = [0usize; 2]; 15 | pipe(&mut pipe_fd); 16 | // read end 17 | assert_eq!(pipe_fd[0], 3); 18 | // write end 19 | assert_eq!(pipe_fd[1], 4); 20 | if fork() == 0 { 21 | // child process, read from parent 22 | // close write_end 23 | close(pipe_fd[1]); 24 | let mut buffer = [0u8; 32]; 25 | let len_read = read(pipe_fd[0], &mut buffer) as usize; 26 | // close read_end 27 | close(pipe_fd[0]); 28 | assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR); 29 | println!("Read OK, child process exited!"); 30 | 0 31 | } else { 32 | // parent process, write to child 33 | // close read end 34 | close(pipe_fd[0]); 35 | assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize); 36 | // close write end 37 | close(pipe_fd[1]); 38 | let mut child_exit_code: i32 = 0; 39 | wait(&mut child_exit_code); 40 | assert_eq!(child_exit_code, 0); 41 | println!("pipetest passed!"); 42 | 0 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /os/src/drivers/net/mod.rs: -------------------------------------------------------------------------------- 1 | use core::any::Any; 2 | 3 | use crate::drivers::virtio::VirtioHal; 4 | use crate::sync::UPIntrFreeCell; 5 | use alloc::sync::Arc; 6 | use lazy_static::*; 7 | use virtio_drivers::{VirtIOHeader, VirtIONet}; 8 | 9 | const VIRTIO8: usize = 0x10004000; 10 | 11 | lazy_static! { 12 | pub static ref NET_DEVICE: Arc = Arc::new(VirtIONetWrapper::new()); 13 | } 14 | 15 | pub trait NetDevice: Send + Sync + Any { 16 | fn transmit(&self, data: &[u8]); 17 | fn receive(&self, data: &mut [u8]) -> usize; 18 | } 19 | 20 | pub struct VirtIONetWrapper(UPIntrFreeCell>); 21 | 22 | impl NetDevice for VirtIONetWrapper { 23 | fn transmit(&self, data: &[u8]) { 24 | self.0 25 | .exclusive_access() 26 | .send(data) 27 | .expect("can't send data") 28 | } 29 | 30 | fn receive(&self, data: &mut [u8]) -> usize { 31 | self.0 32 | .exclusive_access() 33 | .recv(data) 34 | .expect("can't receive data") 35 | } 36 | } 37 | 38 | impl VirtIONetWrapper { 39 | pub fn new() -> Self { 40 | unsafe { 41 | let virtio = VirtIONet::::new(&mut *(VIRTIO8 as *mut VirtIOHeader)) 42 | .expect("can't create net device by virtio"); 43 | VirtIONetWrapper(UPIntrFreeCell::new(virtio)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /os/src/mm/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | use crate::config::KERNEL_HEAP_SIZE; 2 | use buddy_system_allocator::LockedHeap; 3 | use core::ptr::addr_of_mut; 4 | 5 | #[global_allocator] 6 | static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 7 | 8 | #[alloc_error_handler] 9 | pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { 10 | panic!("Heap allocation error, layout = {:?}", layout); 11 | } 12 | 13 | static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 14 | 15 | pub fn init_heap() { 16 | unsafe { 17 | HEAP_ALLOCATOR 18 | .lock() 19 | .init(addr_of_mut!(HEAP_SPACE) as usize, KERNEL_HEAP_SIZE); 20 | } 21 | } 22 | 23 | #[allow(unused)] 24 | pub fn heap_test() { 25 | use alloc::boxed::Box; 26 | use alloc::vec::Vec; 27 | unsafe extern "C" { 28 | safe fn sbss(); 29 | safe fn ebss(); 30 | } 31 | let bss_range = sbss as usize..ebss as usize; 32 | let a = Box::new(5); 33 | assert_eq!(*a, 5); 34 | assert!(bss_range.contains(&(a.as_ref() as *const _ as usize))); 35 | drop(a); 36 | let mut v: Vec = Vec::new(); 37 | for i in 0..500 { 38 | v.push(i); 39 | } 40 | for (i, val) in v.iter().take(500).enumerate() { 41 | assert_eq!(*val, i); 42 | } 43 | assert!(bss_range.contains(&(v.as_ptr() as usize))); 44 | drop(v); 45 | println!("heap_test passed!"); 46 | } 47 | -------------------------------------------------------------------------------- /os/src/sync/semaphore.rs: -------------------------------------------------------------------------------- 1 | use crate::sync::UPIntrFreeCell; 2 | use crate::task::{TaskControlBlock, block_current_and_run_next, current_task, wakeup_task}; 3 | use alloc::{collections::VecDeque, sync::Arc}; 4 | 5 | pub struct Semaphore { 6 | pub inner: UPIntrFreeCell, 7 | } 8 | 9 | pub struct SemaphoreInner { 10 | pub count: isize, 11 | pub wait_queue: VecDeque>, 12 | } 13 | 14 | impl Semaphore { 15 | pub fn new(res_count: usize) -> Self { 16 | Self { 17 | inner: unsafe { 18 | UPIntrFreeCell::new(SemaphoreInner { 19 | count: res_count as isize, 20 | wait_queue: VecDeque::new(), 21 | }) 22 | }, 23 | } 24 | } 25 | 26 | pub fn up(&self) { 27 | let mut inner = self.inner.exclusive_access(); 28 | inner.count += 1; 29 | if inner.count <= 0 { 30 | if let Some(task) = inner.wait_queue.pop_front() { 31 | wakeup_task(task); 32 | } 33 | } 34 | } 35 | 36 | pub fn down(&self) { 37 | let mut inner = self.inner.exclusive_access(); 38 | inner.count -= 1; 39 | if inner.count < 0 { 40 | inner.wait_queue.push_back(current_task().unwrap()); 41 | drop(inner); 42 | block_current_and_run_next(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /os/src/logging.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | 本模块利用 log crate 为你提供了日志功能,使用方式见 main.rs. 4 | 5 | */ 6 | 7 | use log::{self, Level, LevelFilter, Log, Metadata, Record}; 8 | 9 | struct SimpleLogger; 10 | 11 | impl Log for SimpleLogger { 12 | fn enabled(&self, _metadata: &Metadata) -> bool { 13 | true 14 | } 15 | fn log(&self, record: &Record) { 16 | if !self.enabled(record.metadata()) { 17 | return; 18 | } 19 | let color = match record.level() { 20 | Level::Error => 31, // Red 21 | Level::Warn => 93, // BrightYellow 22 | Level::Info => 34, // Blue 23 | Level::Debug => 32, // Green 24 | Level::Trace => 90, // BrightBlack 25 | }; 26 | println!( 27 | "\u{1B}[{}m[{:>5}] {}\u{1B}[0m", 28 | color, 29 | record.level(), 30 | record.args(), 31 | ); 32 | } 33 | fn flush(&self) {} 34 | } 35 | 36 | pub fn init() { 37 | static LOGGER: SimpleLogger = SimpleLogger; 38 | log::set_logger(&LOGGER).unwrap(); 39 | log::set_max_level(match option_env!("LOG") { 40 | Some("ERROR") => LevelFilter::Error, 41 | Some("WARN") => LevelFilter::Warn, 42 | Some("INFO") => LevelFilter::Info, 43 | Some("DEBUG") => LevelFilter::Debug, 44 | Some("TRACE") => LevelFilter::Trace, 45 | _ => LevelFilter::Info, 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /os/src/drivers/bus/virtio.rs: -------------------------------------------------------------------------------- 1 | use crate::mm::{ 2 | FrameTracker, PageTable, PhysAddr, PhysPageNum, StepByOne, VirtAddr, frame_alloc_more, 3 | frame_dealloc, kernel_token, 4 | }; 5 | use crate::sync::UPIntrFreeCell; 6 | use alloc::vec::Vec; 7 | use lazy_static::*; 8 | use virtio_drivers::Hal; 9 | 10 | lazy_static! { 11 | static ref QUEUE_FRAMES: UPIntrFreeCell> = 12 | unsafe { UPIntrFreeCell::new(Vec::new()) }; 13 | } 14 | 15 | pub struct VirtioHal; 16 | 17 | impl Hal for VirtioHal { 18 | fn dma_alloc(pages: usize) -> usize { 19 | let trakcers = frame_alloc_more(pages); 20 | let ppn_base = trakcers.as_ref().unwrap().last().unwrap().ppn; 21 | QUEUE_FRAMES 22 | .exclusive_access() 23 | .append(&mut trakcers.unwrap()); 24 | let pa: PhysAddr = ppn_base.into(); 25 | pa.0 26 | } 27 | 28 | fn dma_dealloc(pa: usize, pages: usize) -> i32 { 29 | let pa = PhysAddr::from(pa); 30 | let mut ppn_base: PhysPageNum = pa.into(); 31 | for _ in 0..pages { 32 | frame_dealloc(ppn_base); 33 | ppn_base.step(); 34 | } 35 | 0 36 | } 37 | 38 | fn phys_to_virt(addr: usize) -> usize { 39 | addr 40 | } 41 | 42 | fn virt_to_phys(vaddr: usize) -> usize { 43 | PageTable::from_token(kernel_token()) 44 | .translate_va(VirtAddr::from(vaddr)) 45 | .unwrap() 46 | .0 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /user/src/bin/until_timeout.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{SignalFlags, exec, fork, get_time, kill, waitpid, waitpid_nb}; 8 | 9 | #[unsafe(no_mangle)] 10 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 11 | assert_eq!(argc, 3, "argc must be 3!"); 12 | let timeout_ms = argv[2] 13 | .parse::() 14 | .expect("Error when parsing timeout!"); 15 | let pid = fork() as usize; 16 | if pid == 0 { 17 | if exec(argv[1], &[core::ptr::null::()]) != 0 { 18 | println!("Error when executing '{}'", argv[1]); 19 | return -4; 20 | } 21 | } else { 22 | let start_time = get_time(); 23 | let mut child_exited = false; 24 | let mut exit_code: i32 = 0; 25 | loop { 26 | if get_time() - start_time > timeout_ms { 27 | break; 28 | } 29 | if waitpid_nb(pid, &mut exit_code) as usize == pid { 30 | child_exited = true; 31 | println!( 32 | "child exited in {}ms, exit_code = {}", 33 | get_time() - start_time, 34 | exit_code, 35 | ); 36 | } 37 | } 38 | if !child_exited { 39 | println!("child has run for {}ms, kill it!", timeout_ms); 40 | kill(pid, SignalFlags::SIGINT.bits()); 41 | assert_eq!(waitpid(pid, &mut exit_code) as usize, pid); 42 | println!("exit code of the child is {}", exit_code); 43 | } 44 | } 45 | 0 46 | } 47 | -------------------------------------------------------------------------------- /user/src/bin/adder.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use user_lib::{exit, get_time, thread_create, waittid}; 11 | 12 | static mut A: usize = 0; 13 | const PER_THREAD_DEFAULT: usize = 10000; 14 | const THREAD_COUNT_DEFAULT: usize = 16; 15 | static mut PER_THREAD: usize = 0; 16 | 17 | fn critical_section(t: &mut usize) { 18 | let a = addr_of_mut!(A); 19 | let cur = unsafe { a.read_volatile() }; 20 | for _ in 0..500 { 21 | *t = (*t) * (*t) % 10007; 22 | } 23 | unsafe { 24 | a.write_volatile(cur + 1); 25 | } 26 | } 27 | 28 | fn f() -> ! { 29 | let mut t = 2usize; 30 | for _ in 0..unsafe { PER_THREAD } { 31 | critical_section(&mut t); 32 | } 33 | exit(t as i32) 34 | } 35 | 36 | #[unsafe(no_mangle)] 37 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 38 | let mut thread_count = THREAD_COUNT_DEFAULT; 39 | let mut per_thread = PER_THREAD_DEFAULT; 40 | if argc >= 2 { 41 | thread_count = argv[1].parse().unwrap(); 42 | if argc >= 3 { 43 | per_thread = argv[2].parse().unwrap(); 44 | } 45 | } 46 | unsafe { 47 | PER_THREAD = per_thread; 48 | } 49 | let start = get_time(); 50 | let mut v = Vec::new(); 51 | for _ in 0..thread_count { 52 | v.push(thread_create(f as usize, 0) as usize); 53 | } 54 | for tid in v.into_iter() { 55 | waittid(tid); 56 | } 57 | println!("time cost is {}ms", get_time() - start); 58 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 59 | 0 60 | } 61 | -------------------------------------------------------------------------------- /user/src/bin/race_adder_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use crate::alloc::string::ToString; 9 | use alloc::vec::Vec; 10 | use core::ptr::addr_of_mut; 11 | use user_lib::{exit, get_time, thread_create, waittid}; 12 | 13 | static mut A: usize = 0; 14 | const PER_THREAD: usize = 1000; 15 | const THREAD_COUNT: usize = 16; 16 | 17 | fn f(count: usize) -> ! { 18 | let mut t = 2usize; 19 | for _ in 0..PER_THREAD { 20 | let a = addr_of_mut!(A); 21 | let cur = unsafe { a.read_volatile() }; 22 | for _ in 0..count { 23 | t = t * t % 10007; 24 | } 25 | unsafe { 26 | a.write_volatile(cur + 1); 27 | } 28 | } 29 | exit(t as i32) 30 | } 31 | 32 | #[unsafe(no_mangle)] 33 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 34 | let count: usize; 35 | if argc == 1 { 36 | count = THREAD_COUNT; 37 | } else if argc == 2 { 38 | count = argv[1].to_string().parse::().unwrap(); 39 | } else { 40 | println!( 41 | "ERROR in argv, argc is {}, argv[0] {} , argv[1] {} , argv[2] {}", 42 | argc, argv[0], argv[1], argv[2] 43 | ); 44 | exit(-1); 45 | } 46 | 47 | let start = get_time(); 48 | let mut v = Vec::new(); 49 | for _ in 0..THREAD_COUNT { 50 | v.push(thread_create(f as usize, count) as usize); 51 | } 52 | let mut time_cost = Vec::new(); 53 | for tid in v.iter() { 54 | time_cost.push(waittid(*tid)); 55 | } 56 | println!("time cost is {}ms", get_time() - start); 57 | assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); 58 | 0 59 | } 60 | -------------------------------------------------------------------------------- /user/src/bin/condsync_condvar.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | extern crate alloc; 8 | 9 | use alloc::vec; 10 | use user_lib::exit; 11 | use user_lib::{ 12 | condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, 13 | }; 14 | use user_lib::{sleep, thread_create, waittid}; 15 | 16 | static mut A: usize = 0; 17 | 18 | const CONDVAR_ID: usize = 0; 19 | const MUTEX_ID: usize = 0; 20 | 21 | fn first() -> ! { 22 | sleep(10); 23 | println!("First work, Change A --> 1 and wakeup Second"); 24 | mutex_lock(MUTEX_ID); 25 | unsafe { 26 | A = 1; 27 | } 28 | condvar_signal(CONDVAR_ID); 29 | mutex_unlock(MUTEX_ID); 30 | exit(0) 31 | } 32 | 33 | fn second() -> ! { 34 | println!("Second want to continue,but need to wait A=1"); 35 | mutex_lock(MUTEX_ID); 36 | while unsafe { A } == 0 { 37 | println!("Second: A is {}", unsafe { A }); 38 | condvar_wait(CONDVAR_ID, MUTEX_ID); 39 | } 40 | println!("A is {}, Second can work now", unsafe { A }); 41 | mutex_unlock(MUTEX_ID); 42 | exit(0) 43 | } 44 | 45 | #[unsafe(no_mangle)] 46 | pub fn main() -> i32 { 47 | // create condvar & mutex 48 | assert_eq!(condvar_create() as usize, CONDVAR_ID); 49 | assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); 50 | // create threads 51 | let threads = vec![ 52 | thread_create(first as usize, 0), 53 | thread_create(second as usize, 0), 54 | ]; 55 | // wait for all threads to complete 56 | for thread in threads.iter() { 57 | waittid(*thread as usize); 58 | } 59 | println!("test_condvar passed!"); 60 | 0 61 | } 62 | -------------------------------------------------------------------------------- /os/src/sync/condvar.rs: -------------------------------------------------------------------------------- 1 | use crate::sync::{Mutex, UPIntrFreeCell}; 2 | use crate::task::{ 3 | TaskContext, TaskControlBlock, block_current_and_run_next, block_current_task, current_task, 4 | wakeup_task, 5 | }; 6 | use alloc::{collections::VecDeque, sync::Arc}; 7 | 8 | pub struct Condvar { 9 | pub inner: UPIntrFreeCell, 10 | } 11 | 12 | pub struct CondvarInner { 13 | pub wait_queue: VecDeque>, 14 | } 15 | 16 | impl Condvar { 17 | pub fn new() -> Self { 18 | Self { 19 | inner: unsafe { 20 | UPIntrFreeCell::new(CondvarInner { 21 | wait_queue: VecDeque::new(), 22 | }) 23 | }, 24 | } 25 | } 26 | 27 | pub fn signal(&self) { 28 | let mut inner = self.inner.exclusive_access(); 29 | if let Some(task) = inner.wait_queue.pop_front() { 30 | wakeup_task(task); 31 | } 32 | } 33 | 34 | /* 35 | pub fn wait(&self) { 36 | let mut inner = self.inner.exclusive_access(); 37 | inner.wait_queue.push_back(current_task().unwrap()); 38 | drop(inner); 39 | block_current_and_run_next(); 40 | } 41 | */ 42 | 43 | pub fn wait_no_sched(&self) -> *mut TaskContext { 44 | self.inner.exclusive_session(|inner| { 45 | inner.wait_queue.push_back(current_task().unwrap()); 46 | }); 47 | block_current_task() 48 | } 49 | 50 | pub fn wait_with_mutex(&self, mutex: Arc) { 51 | mutex.unlock(); 52 | self.inner.exclusive_session(|inner| { 53 | inner.wait_queue.push_back(current_task().unwrap()); 54 | }); 55 | block_current_and_run_next(); 56 | mutex.lock(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /user/src/bin/huge_write_mt.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::{fmt::format, vec::Vec}; 9 | use user_lib::{OpenFlags, close, get_time, gettid, open, write}; 10 | use user_lib::{exit, thread_create, waittid}; 11 | 12 | fn worker(size_kib: usize) { 13 | let mut buffer = [0u8; 1024]; // 1KiB 14 | for (i, ch) in buffer.iter_mut().enumerate() { 15 | *ch = i as u8; 16 | } 17 | let filename = format(format_args!("testf{}\0", gettid())); 18 | let f = open(filename.as_str(), OpenFlags::CREATE | OpenFlags::WRONLY); 19 | if f < 0 { 20 | panic!("Open test file failed!"); 21 | } 22 | let f = f as usize; 23 | for _ in 0..size_kib { 24 | write(f, &buffer); 25 | } 26 | close(f); 27 | exit(0) 28 | } 29 | 30 | #[unsafe(no_mangle)] 31 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 32 | assert_eq!(argc, 2, "wrong argument"); 33 | let size_mb = 1usize; 34 | let size_kb = size_mb << 10; 35 | let workers = argv[1].parse::().expect("wrong argument"); 36 | assert!(workers >= 1 && size_kb % workers == 0, "wrong argument"); 37 | 38 | let start = get_time(); 39 | 40 | let mut v = Vec::new(); 41 | let size_mb = 1usize; 42 | for _ in 0..workers { 43 | v.push(thread_create(worker as usize, size_kb / workers)); 44 | } 45 | for tid in v.iter() { 46 | assert_eq!(0, waittid(*tid as usize)); 47 | } 48 | 49 | let time_ms = (get_time() - start) as usize; 50 | let speed_kbs = size_kb * 1000 / time_ms; 51 | println!( 52 | "{}MiB written by {} threads, time cost = {}ms, write speed = {}KiB/s", 53 | size_mb, workers, time_ms, speed_kbs 54 | ); 55 | 0 56 | } 57 | -------------------------------------------------------------------------------- /os/src/syscall/net.rs: -------------------------------------------------------------------------------- 1 | use crate::net::port_table::{PortFd, accept, listen, port_acceptable}; 2 | use crate::net::udp::UDP; 3 | use crate::net::{IPv4, net_interrupt_handler}; 4 | use crate::task::{current_process, current_task, current_trap_cx}; 5 | use alloc::sync::Arc; 6 | 7 | // just support udp 8 | pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { 9 | let process = current_process(); 10 | let mut inner = process.inner_exclusive_access(); 11 | let fd = inner.alloc_fd(); 12 | let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); 13 | inner.fd_table[fd] = Some(Arc::new(udp_node)); 14 | fd as isize 15 | } 16 | 17 | // listen a port 18 | pub fn sys_listen(port: u16) -> isize { 19 | match listen(port) { 20 | Some(port_index) => { 21 | let process = current_process(); 22 | let mut inner = process.inner_exclusive_access(); 23 | let fd = inner.alloc_fd(); 24 | let port_fd = PortFd::new(port_index); 25 | inner.fd_table[fd] = Some(Arc::new(port_fd)); 26 | 27 | // NOTICE: this return the port index, not the fd 28 | port_index as isize 29 | } 30 | None => -1, 31 | } 32 | } 33 | 34 | // accept a tcp connection 35 | pub fn sys_accept(port_index: usize) -> isize { 36 | println!("accepting port {}", port_index); 37 | 38 | let task = current_task().unwrap(); 39 | accept(port_index, task); 40 | // block_current_and_run_next(); 41 | 42 | // NOTICE: There does not have interrupt handler, just call it munually. 43 | loop { 44 | net_interrupt_handler(); 45 | 46 | if !port_acceptable(port_index) { 47 | break; 48 | } 49 | } 50 | 51 | let cx = current_trap_cx(); 52 | cx.x[10] as isize 53 | } 54 | -------------------------------------------------------------------------------- /user/src/bin/condsync_sem.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | extern crate alloc; 8 | 9 | use alloc::vec; 10 | use user_lib::exit; 11 | use user_lib::{ 12 | mutex_blocking_create, mutex_lock, mutex_unlock, semaphore_create, semaphore_down, semaphore_up, 13 | }; 14 | use user_lib::{sleep, thread_create, waittid}; 15 | 16 | static mut A: usize = 0; 17 | 18 | const SEM_ID: usize = 0; 19 | const MUTEX_ID: usize = 0; 20 | 21 | fn first() -> ! { 22 | sleep(10); 23 | println!("First work, Change A --> 1 and wakeup Second"); 24 | mutex_lock(MUTEX_ID); 25 | unsafe { 26 | A = 1; 27 | } 28 | semaphore_up(SEM_ID); 29 | mutex_unlock(MUTEX_ID); 30 | exit(0) 31 | } 32 | fn second() -> ! { 33 | println!("Second want to continue,but need to wait A=1"); 34 | loop { 35 | mutex_lock(MUTEX_ID); 36 | if unsafe { A } == 0 { 37 | println!("Second: A is {}", unsafe { A }); 38 | mutex_unlock(MUTEX_ID); 39 | semaphore_down(SEM_ID); 40 | } else { 41 | mutex_unlock(MUTEX_ID); 42 | break; 43 | } 44 | } 45 | println!("A is {}, Second can work now", unsafe { A }); 46 | exit(0) 47 | } 48 | 49 | #[unsafe(no_mangle)] 50 | pub fn main() -> i32 { 51 | // create semaphore & mutex 52 | assert_eq!(semaphore_create(0) as usize, SEM_ID); 53 | assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); 54 | // create threads 55 | let threads = vec![ 56 | thread_create(first as usize, 0), 57 | thread_create(second as usize, 0), 58 | ]; 59 | // wait for all threads to complete 60 | for thread in threads.iter() { 61 | waittid(*thread as usize); 62 | } 63 | println!("test_condvar passed!"); 64 | 0 65 | } 66 | -------------------------------------------------------------------------------- /user/src/bin/adder_mutex_spin.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use user_lib::{exit, get_time, thread_create, waittid}; 11 | use user_lib::{mutex_create, mutex_lock, mutex_unlock}; 12 | 13 | static mut A: usize = 0; 14 | const PER_THREAD_DEFAULT: usize = 10000; 15 | const THREAD_COUNT_DEFAULT: usize = 16; 16 | static mut PER_THREAD: usize = 0; 17 | 18 | fn critical_section(t: &mut usize) { 19 | let a = addr_of_mut!(A); 20 | let cur = unsafe { a.read_volatile() }; 21 | for _ in 0..500 { 22 | *t = (*t) * (*t) % 10007; 23 | } 24 | unsafe { 25 | a.write_volatile(cur + 1); 26 | } 27 | } 28 | 29 | fn f() -> ! { 30 | let mut t = 2usize; 31 | for _ in 0..unsafe { PER_THREAD } { 32 | mutex_lock(0); 33 | critical_section(&mut t); 34 | mutex_unlock(0); 35 | } 36 | exit(t as i32) 37 | } 38 | 39 | #[unsafe(no_mangle)] 40 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 41 | let mut thread_count = THREAD_COUNT_DEFAULT; 42 | let mut per_thread = PER_THREAD_DEFAULT; 43 | if argc >= 2 { 44 | thread_count = argv[1].parse().unwrap(); 45 | if argc >= 3 { 46 | per_thread = argv[2].parse().unwrap(); 47 | } 48 | } 49 | unsafe { 50 | PER_THREAD = per_thread; 51 | } 52 | 53 | let start = get_time(); 54 | assert_eq!(mutex_create(), 0); 55 | let mut v = Vec::new(); 56 | for _ in 0..thread_count { 57 | v.push(thread_create(f as usize, 0) as usize); 58 | } 59 | for tid in v.into_iter() { 60 | waittid(tid); 61 | } 62 | println!("time cost is {}ms", get_time() - start); 63 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 64 | 0 65 | } 66 | -------------------------------------------------------------------------------- /user/src/bin/adder_mutex_blocking.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use user_lib::{exit, get_time, thread_create, waittid}; 11 | use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; 12 | 13 | static mut A: usize = 0; 14 | const PER_THREAD_DEFAULT: usize = 10000; 15 | const THREAD_COUNT_DEFAULT: usize = 16; 16 | static mut PER_THREAD: usize = 0; 17 | 18 | fn critical_section(t: &mut usize) { 19 | let a = addr_of_mut!(A); 20 | let cur = unsafe { a.read_volatile() }; 21 | for _ in 0..500 { 22 | *t = (*t) * (*t) % 10007; 23 | } 24 | unsafe { 25 | a.write_volatile(cur + 1); 26 | } 27 | } 28 | 29 | fn f() -> ! { 30 | let mut t = 2usize; 31 | for _ in 0..unsafe { PER_THREAD } { 32 | mutex_lock(0); 33 | critical_section(&mut t); 34 | mutex_unlock(0); 35 | } 36 | exit(t as i32) 37 | } 38 | 39 | #[unsafe(no_mangle)] 40 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 41 | let mut thread_count = THREAD_COUNT_DEFAULT; 42 | let mut per_thread = PER_THREAD_DEFAULT; 43 | if argc >= 2 { 44 | thread_count = argv[1].parse().unwrap(); 45 | if argc >= 3 { 46 | per_thread = argv[2].parse().unwrap(); 47 | } 48 | } 49 | unsafe { 50 | PER_THREAD = per_thread; 51 | } 52 | 53 | let start = get_time(); 54 | assert_eq!(mutex_blocking_create(), 0); 55 | let mut v = Vec::new(); 56 | for _ in 0..thread_count { 57 | v.push(thread_create(f as usize, 0) as usize); 58 | } 59 | for tid in v.into_iter() { 60 | waittid(tid); 61 | } 62 | println!("time cost is {}ms", get_time() - start); 63 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 64 | 0 65 | } 66 | -------------------------------------------------------------------------------- /user/src/bin/matrix.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(clippy::needless_range_loop)] 4 | 5 | #[macro_use] 6 | extern crate user_lib; 7 | 8 | use user_lib::{exit, fork, get_time, getpid, wait, yield_}; 9 | 10 | static NUM: usize = 30; 11 | const N: usize = 10; 12 | static P: i32 = 10007; 13 | type Arr = [[i32; N]; N]; 14 | 15 | fn work(times: isize) { 16 | let mut a: Arr = Default::default(); 17 | let mut b: Arr = Default::default(); 18 | let mut c: Arr = Default::default(); 19 | for i in 0..N { 20 | for j in 0..N { 21 | a[i][j] = 1; 22 | b[i][j] = 1; 23 | } 24 | } 25 | yield_(); 26 | println!("pid {} is running ({} times)!.", getpid(), times); 27 | for _ in 0..times { 28 | for i in 0..N { 29 | for j in 0..N { 30 | c[i][j] = 0; 31 | for k in 0..N { 32 | c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P; 33 | } 34 | } 35 | } 36 | for i in 0..N { 37 | for j in 0..N { 38 | a[i][j] = c[i][j]; 39 | b[i][j] = c[i][j]; 40 | } 41 | } 42 | } 43 | println!("pid {} done!.", getpid()); 44 | exit(0); 45 | } 46 | 47 | #[unsafe(no_mangle)] 48 | pub fn main() -> i32 { 49 | for _ in 0..NUM { 50 | let pid = fork(); 51 | if pid == 0 { 52 | let current_time = get_time(); 53 | let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000; 54 | work(times * 10); 55 | } 56 | } 57 | 58 | println!("fork ok."); 59 | 60 | let mut exit_code: i32 = 0; 61 | for _ in 0..NUM { 62 | if wait(&mut exit_code) < 0 { 63 | panic!("wait failed."); 64 | } 65 | } 66 | assert!(wait(&mut exit_code) < 0); 67 | println!("matrix passed."); 68 | 0 69 | } 70 | -------------------------------------------------------------------------------- /user/src/bin/gui_shape.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | extern crate user_lib; 6 | 7 | use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; 8 | 9 | use embedded_graphics::pixelcolor::Rgb888; 10 | use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size}; 11 | use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle, Triangle}; 12 | 13 | const INIT_X: i32 = 80; 14 | const INIT_Y: i32 = 400; 15 | const RECT_SIZE: u32 = 150; 16 | 17 | pub struct DrawingBoard { 18 | disp: Display, 19 | latest_pos: Point, 20 | } 21 | 22 | impl DrawingBoard { 23 | pub fn new() -> Self { 24 | Self { 25 | disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), 26 | latest_pos: Point::new(INIT_X, INIT_Y), 27 | } 28 | } 29 | fn paint(&mut self) { 30 | Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) 31 | .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10)) 32 | .draw(&mut self.disp) 33 | .ok(); 34 | Circle::new(self.latest_pos + Point::new(-70, -300), 150) 35 | .into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE)) 36 | .draw(&mut self.disp) 37 | .ok(); 38 | Triangle::new( 39 | self.latest_pos + Point::new(0, 150), 40 | self.latest_pos + Point::new(80, 200), 41 | self.latest_pos + Point::new(-120, 300), 42 | ) 43 | .into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10)) 44 | .draw(&mut self.disp) 45 | .ok(); 46 | } 47 | } 48 | 49 | #[unsafe(no_mangle)] 50 | pub fn main() -> i32 { 51 | let mut board = DrawingBoard::new(); 52 | let _ = board.disp.clear(Rgb888::BLACK).unwrap(); 53 | for _ in 0..5 { 54 | board.latest_pos.x += RECT_SIZE as i32 + 20; 55 | board.paint(); 56 | } 57 | board.disp.flush(); 58 | 0 59 | } 60 | -------------------------------------------------------------------------------- /user/src/bin/adder_simple_spin.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use user_lib::{exit, get_time, thread_create, waittid}; 11 | 12 | static mut A: usize = 0; 13 | static mut OCCUPIED: bool = false; 14 | const PER_THREAD_DEFAULT: usize = 10000; 15 | const THREAD_COUNT_DEFAULT: usize = 16; 16 | static mut PER_THREAD: usize = 0; 17 | 18 | fn critical_section(t: &mut usize) { 19 | let a = addr_of_mut!(A); 20 | let cur = unsafe { a.read_volatile() }; 21 | for _ in 0..500 { 22 | *t = (*t) * (*t) % 10007; 23 | } 24 | unsafe { 25 | a.write_volatile(cur + 1); 26 | } 27 | } 28 | 29 | fn lock() { 30 | while vload!(OCCUPIED) {} 31 | unsafe { 32 | OCCUPIED = true; 33 | } 34 | } 35 | 36 | fn unlock() { 37 | unsafe { 38 | OCCUPIED = false; 39 | } 40 | } 41 | 42 | fn f() -> ! { 43 | let mut t = 2usize; 44 | for _ in 0..unsafe { PER_THREAD } { 45 | lock(); 46 | critical_section(&mut t); 47 | unlock(); 48 | } 49 | exit(t as i32) 50 | } 51 | 52 | #[unsafe(no_mangle)] 53 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 54 | let mut thread_count = THREAD_COUNT_DEFAULT; 55 | let mut per_thread = PER_THREAD_DEFAULT; 56 | if argc >= 2 { 57 | thread_count = argv[1].parse().unwrap(); 58 | if argc >= 3 { 59 | per_thread = argv[2].parse().unwrap(); 60 | } 61 | } 62 | unsafe { 63 | PER_THREAD = per_thread; 64 | } 65 | let start = get_time(); 66 | let mut v = Vec::new(); 67 | for _ in 0..thread_count { 68 | v.push(thread_create(f as usize, 0) as usize); 69 | } 70 | for tid in v.into_iter() { 71 | waittid(tid); 72 | } 73 | println!("time cost is {}ms", get_time() - start); 74 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 75 | 0 76 | } 77 | -------------------------------------------------------------------------------- /os/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | 5 | //use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE, INPUT_CONDVAR}; 6 | use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; 7 | extern crate alloc; 8 | 9 | #[macro_use] 10 | extern crate bitflags; 11 | 12 | use log::*; 13 | 14 | #[path = "boards/qemu.rs"] 15 | mod board; 16 | 17 | #[macro_use] 18 | mod console; 19 | mod config; 20 | mod drivers; 21 | mod fs; 22 | mod lang_items; 23 | mod logging; 24 | mod mm; 25 | mod net; 26 | mod sbi; 27 | mod sync; 28 | mod syscall; 29 | mod task; 30 | mod timer; 31 | mod trap; 32 | 33 | use crate::drivers::chardev::CharDevice; 34 | use crate::drivers::chardev::UART; 35 | 36 | core::arch::global_asm!(include_str!("entry.asm")); 37 | 38 | fn clear_bss() { 39 | unsafe extern "C" { 40 | safe fn sbss(); 41 | safe fn ebss(); 42 | } 43 | unsafe { 44 | core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize) 45 | .fill(0); 46 | } 47 | } 48 | 49 | use lazy_static::*; 50 | use sync::UPIntrFreeCell; 51 | 52 | lazy_static! { 53 | pub static ref DEV_NON_BLOCKING_ACCESS: UPIntrFreeCell = 54 | unsafe { UPIntrFreeCell::new(false) }; 55 | } 56 | 57 | #[unsafe(no_mangle)] 58 | pub fn rust_main() -> ! { 59 | clear_bss(); 60 | logging::init(); 61 | mm::init(); 62 | UART.init(); 63 | info!("KERN: init gpu"); 64 | let _gpu = GPU_DEVICE.clone(); 65 | info!("KERN: init keyboard"); 66 | let _keyboard = KEYBOARD_DEVICE.clone(); 67 | info!("KERN: init mouse"); 68 | let _mouse = MOUSE_DEVICE.clone(); 69 | info!("KERN: init trap"); 70 | trap::init(); 71 | trap::enable_timer_interrupt(); 72 | timer::set_next_trigger(); 73 | board::device_init(); 74 | fs::list_apps(); 75 | task::add_initproc(); 76 | *DEV_NON_BLOCKING_ACCESS.exclusive_access() = true; 77 | task::run_tasks(); 78 | panic!("Unreachable in rust_main!"); 79 | } 80 | -------------------------------------------------------------------------------- /user/src/bin/adder_simple_yield.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use user_lib::{exit, get_time, thread_create, waittid, yield_}; 11 | 12 | static mut A: usize = 0; 13 | static mut OCCUPIED: bool = false; 14 | const PER_THREAD_DEFAULT: usize = 10000; 15 | const THREAD_COUNT_DEFAULT: usize = 16; 16 | static mut PER_THREAD: usize = 0; 17 | 18 | fn critical_section(t: &mut usize) { 19 | let a = addr_of_mut!(A); 20 | let cur = unsafe { a.read_volatile() }; 21 | for _ in 0..500 { 22 | *t = (*t) * (*t) % 10007; 23 | } 24 | unsafe { 25 | a.write_volatile(cur + 1); 26 | } 27 | } 28 | 29 | fn lock() { 30 | unsafe { 31 | while OCCUPIED { 32 | yield_(); 33 | } 34 | OCCUPIED = true; 35 | } 36 | } 37 | 38 | fn unlock() { 39 | unsafe { 40 | OCCUPIED = false; 41 | } 42 | } 43 | 44 | fn f() -> ! { 45 | let mut t = 2usize; 46 | for _ in 0..unsafe { PER_THREAD } { 47 | lock(); 48 | critical_section(&mut t); 49 | unlock(); 50 | } 51 | exit(t as i32) 52 | } 53 | 54 | #[unsafe(no_mangle)] 55 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 56 | let mut thread_count = THREAD_COUNT_DEFAULT; 57 | let mut per_thread = PER_THREAD_DEFAULT; 58 | if argc >= 2 { 59 | thread_count = argv[1].parse().unwrap(); 60 | if argc >= 3 { 61 | per_thread = argv[2].parse().unwrap(); 62 | } 63 | } 64 | unsafe { 65 | PER_THREAD = per_thread; 66 | } 67 | let start = get_time(); 68 | let mut v = Vec::new(); 69 | for _ in 0..thread_count { 70 | v.push(thread_create(f as usize, 0) as usize); 71 | } 72 | for tid in v.into_iter() { 73 | waittid(tid); 74 | } 75 | println!("time cost is {}ms", get_time() - start); 76 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 77 | 0 78 | } 79 | -------------------------------------------------------------------------------- /os/src/task/manager.rs: -------------------------------------------------------------------------------- 1 | use super::{ProcessControlBlock, TaskControlBlock, TaskStatus}; 2 | use crate::sync::UPIntrFreeCell; 3 | use alloc::collections::{BTreeMap, VecDeque}; 4 | use alloc::sync::Arc; 5 | use lazy_static::*; 6 | 7 | pub struct TaskManager { 8 | ready_queue: VecDeque>, 9 | } 10 | 11 | /// A simple FIFO scheduler. 12 | impl TaskManager { 13 | pub fn new() -> Self { 14 | Self { 15 | ready_queue: VecDeque::new(), 16 | } 17 | } 18 | pub fn add(&mut self, task: Arc) { 19 | self.ready_queue.push_back(task); 20 | } 21 | pub fn fetch(&mut self) -> Option> { 22 | self.ready_queue.pop_front() 23 | } 24 | } 25 | 26 | lazy_static! { 27 | pub static ref TASK_MANAGER: UPIntrFreeCell = 28 | unsafe { UPIntrFreeCell::new(TaskManager::new()) }; 29 | pub static ref PID2PCB: UPIntrFreeCell>> = 30 | unsafe { UPIntrFreeCell::new(BTreeMap::new()) }; 31 | } 32 | 33 | pub fn add_task(task: Arc) { 34 | TASK_MANAGER.exclusive_access().add(task); 35 | } 36 | 37 | pub fn wakeup_task(task: Arc) { 38 | let mut task_inner = task.inner_exclusive_access(); 39 | task_inner.task_status = TaskStatus::Ready; 40 | drop(task_inner); 41 | add_task(task); 42 | } 43 | 44 | pub fn fetch_task() -> Option> { 45 | TASK_MANAGER.exclusive_access().fetch() 46 | } 47 | 48 | pub fn pid2process(pid: usize) -> Option> { 49 | let map = PID2PCB.exclusive_access(); 50 | map.get(&pid).map(Arc::clone) 51 | } 52 | 53 | pub fn insert_into_pid2process(pid: usize, process: Arc) { 54 | PID2PCB.exclusive_access().insert(pid, process); 55 | } 56 | 57 | pub fn remove_from_pid2process(pid: usize) { 58 | let mut map = PID2PCB.exclusive_access(); 59 | if map.remove(&pid).is_none() { 60 | panic!("cannot find pid {} in pid2task!", pid); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /user/src/task.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn exit(exit_code: i32) -> ! { 4 | sys_exit(exit_code); 5 | } 6 | pub fn yield_() -> isize { 7 | sys_yield() 8 | } 9 | pub fn get_time() -> isize { 10 | sys_get_time() 11 | } 12 | pub fn getpid() -> isize { 13 | sys_getpid() 14 | } 15 | pub fn fork() -> isize { 16 | sys_fork() 17 | } 18 | pub fn exec(path: &str, args: &[*const u8]) -> isize { 19 | sys_exec(path, args) 20 | } 21 | 22 | pub fn wait(exit_code: &mut i32) -> isize { 23 | loop { 24 | match sys_waitpid(-1, exit_code as *mut _) { 25 | -2 => { 26 | yield_(); 27 | } 28 | // -1 or a real pid 29 | exit_pid => return exit_pid, 30 | } 31 | } 32 | } 33 | 34 | pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { 35 | loop { 36 | match sys_waitpid(pid as isize, exit_code as *mut _) { 37 | -2 => { 38 | yield_(); 39 | } 40 | // -1 or a real pid 41 | exit_pid => return exit_pid, 42 | } 43 | } 44 | } 45 | 46 | pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { 47 | sys_waitpid(pid as isize, exit_code as *mut _) 48 | } 49 | 50 | bitflags! { 51 | pub struct SignalFlags: i32 { 52 | const SIGINT = 1 << 2; 53 | const SIGILL = 1 << 4; 54 | const SIGABRT = 1 << 6; 55 | const SIGFPE = 1 << 8; 56 | const SIGSEGV = 1 << 11; 57 | } 58 | } 59 | 60 | pub fn kill(pid: usize, signal: i32) -> isize { 61 | sys_kill(pid, signal) 62 | } 63 | 64 | pub fn sleep(sleep_ms: usize) { 65 | sys_sleep(sleep_ms); 66 | } 67 | 68 | pub fn thread_create(entry: usize, arg: usize) -> isize { 69 | sys_thread_create(entry, arg) 70 | } 71 | pub fn gettid() -> isize { 72 | sys_gettid() 73 | } 74 | pub fn waittid(tid: usize) -> isize { 75 | loop { 76 | match sys_waittid(tid) { 77 | -2 => { 78 | yield_(); 79 | } 80 | exit_code => return exit_code, 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /user/src/bin/adder_atomic.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::ptr::addr_of_mut; 10 | use core::sync::atomic::{AtomicBool, Ordering}; 11 | use user_lib::{exit, get_time, thread_create, waittid, yield_}; 12 | 13 | static mut A: usize = 0; 14 | static OCCUPIED: AtomicBool = AtomicBool::new(false); 15 | const PER_THREAD_DEFAULT: usize = 10000; 16 | const THREAD_COUNT_DEFAULT: usize = 16; 17 | static mut PER_THREAD: usize = 0; 18 | 19 | fn critical_section(t: &mut usize) { 20 | let a = addr_of_mut!(A); 21 | let cur = unsafe { a.read_volatile() }; 22 | for _ in 0..500 { 23 | *t = (*t) * (*t) % 10007; 24 | } 25 | unsafe { 26 | a.write_volatile(cur + 1); 27 | } 28 | } 29 | 30 | fn lock() { 31 | while OCCUPIED 32 | .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) 33 | .is_err() 34 | { 35 | yield_(); 36 | } 37 | } 38 | 39 | fn unlock() { 40 | OCCUPIED.store(false, Ordering::Relaxed); 41 | } 42 | 43 | fn f() -> ! { 44 | let mut t = 2usize; 45 | for _ in 0..unsafe { PER_THREAD } { 46 | lock(); 47 | critical_section(&mut t); 48 | unlock(); 49 | } 50 | exit(t as i32) 51 | } 52 | 53 | #[unsafe(no_mangle)] 54 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 55 | let mut thread_count = THREAD_COUNT_DEFAULT; 56 | let mut per_thread = PER_THREAD_DEFAULT; 57 | if argc >= 2 { 58 | thread_count = argv[1].parse().unwrap(); 59 | if argc >= 3 { 60 | per_thread = argv[2].parse().unwrap(); 61 | } 62 | } 63 | unsafe { 64 | PER_THREAD = per_thread; 65 | } 66 | let start = get_time(); 67 | let mut v = Vec::new(); 68 | for _ in 0..thread_count { 69 | v.push(thread_create(f as usize, 0) as usize); 70 | } 71 | for tid in v.into_iter() { 72 | waittid(tid); 73 | } 74 | println!("time cost is {}ms", get_time() - start); 75 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 76 | 0 77 | } 78 | -------------------------------------------------------------------------------- /os/src/timer.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | 3 | use crate::config::CLOCK_FREQ; 4 | use crate::sbi::set_timer; 5 | use crate::sync::UPIntrFreeCell; 6 | use crate::task::{TaskControlBlock, wakeup_task}; 7 | use alloc::collections::BinaryHeap; 8 | use alloc::sync::Arc; 9 | use lazy_static::*; 10 | use riscv::register::time; 11 | 12 | const TICKS_PER_SEC: usize = 100; 13 | const MSEC_PER_SEC: usize = 1000; 14 | 15 | pub fn get_time() -> usize { 16 | time::read() 17 | } 18 | 19 | pub fn get_time_ms() -> usize { 20 | time::read() / (CLOCK_FREQ / MSEC_PER_SEC) 21 | } 22 | 23 | pub fn set_next_trigger() { 24 | set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC); 25 | } 26 | 27 | pub struct TimerCondVar { 28 | pub expire_ms: usize, 29 | pub task: Arc, 30 | } 31 | 32 | impl PartialEq for TimerCondVar { 33 | fn eq(&self, other: &Self) -> bool { 34 | self.expire_ms == other.expire_ms 35 | } 36 | } 37 | impl Eq for TimerCondVar {} 38 | impl PartialOrd for TimerCondVar { 39 | fn partial_cmp(&self, other: &Self) -> Option { 40 | let a = -(self.expire_ms as isize); 41 | let b = -(other.expire_ms as isize); 42 | Some(a.cmp(&b)) 43 | } 44 | } 45 | 46 | impl Ord for TimerCondVar { 47 | fn cmp(&self, other: &Self) -> Ordering { 48 | self.partial_cmp(other).unwrap() 49 | } 50 | } 51 | 52 | lazy_static! { 53 | static ref TIMERS: UPIntrFreeCell> = 54 | unsafe { UPIntrFreeCell::new(BinaryHeap::::new()) }; 55 | } 56 | 57 | pub fn add_timer(expire_ms: usize, task: Arc) { 58 | let mut timers = TIMERS.exclusive_access(); 59 | timers.push(TimerCondVar { expire_ms, task }); 60 | } 61 | 62 | pub fn check_timer() { 63 | let current_ms = get_time_ms(); 64 | TIMERS.exclusive_session(|timers| { 65 | while let Some(timer) = timers.peek() { 66 | if timer.expire_ms <= current_ms { 67 | wakeup_task(Arc::clone(&timer.task)); 68 | timers.pop(); 69 | } else { 70 | break; 71 | } 72 | } 73 | }); 74 | } 75 | -------------------------------------------------------------------------------- /os/src/boards/qemu.rs: -------------------------------------------------------------------------------- 1 | pub const CLOCK_FREQ: usize = 12500000; 2 | pub const MEMORY_END: usize = 0x8800_0000; 3 | 4 | pub const MMIO: &[(usize, usize)] = &[ 5 | (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine 6 | (0x2000000, 0x10000), // core local interrupter (CLINT) 7 | (0xc000000, 0x210000), // VIRT_PLIC in virt machine 8 | (0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine 9 | ]; 10 | 11 | pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock; 12 | pub type CharDeviceImpl = crate::drivers::chardev::NS16550a; 13 | 14 | pub const VIRT_PLIC: usize = 0xC00_0000; 15 | pub const VIRT_UART: usize = 0x1000_0000; 16 | #[allow(unused)] 17 | pub const VIRTGPU_XRES: u32 = 1280; 18 | #[allow(unused)] 19 | pub const VIRTGPU_YRES: u32 = 800; 20 | 21 | use crate::drivers::block::BLOCK_DEVICE; 22 | use crate::drivers::chardev::{CharDevice, UART}; 23 | use crate::drivers::plic::{IntrTargetPriority, PLIC}; 24 | use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; 25 | 26 | pub fn device_init() { 27 | use riscv::register::sie; 28 | let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; 29 | let hart_id: usize = 0; 30 | let supervisor = IntrTargetPriority::Supervisor; 31 | let machine = IntrTargetPriority::Machine; 32 | plic.set_threshold(hart_id, supervisor, 0); 33 | plic.set_threshold(hart_id, machine, 1); 34 | //irq nums: 5 keyboard, 6 mouse, 8 block, 10 uart 35 | for intr_src_id in [5usize, 6, 8, 10] { 36 | plic.enable(hart_id, supervisor, intr_src_id); 37 | plic.set_priority(intr_src_id, 1); 38 | } 39 | unsafe { 40 | sie::set_sext(); 41 | } 42 | } 43 | 44 | pub fn irq_handler() { 45 | let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; 46 | let intr_src_id = plic.claim(0, IntrTargetPriority::Supervisor); 47 | match intr_src_id { 48 | 5 => KEYBOARD_DEVICE.handle_irq(), 49 | 6 => MOUSE_DEVICE.handle_irq(), 50 | 8 => BLOCK_DEVICE.handle_irq(), 51 | 10 => UART.handle_irq(), 52 | _ => panic!("unsupported IRQ {}", intr_src_id), 53 | } 54 | plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); 55 | } 56 | -------------------------------------------------------------------------------- /user/src/bin/barrier_condvar.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | 8 | use alloc::vec::Vec; 9 | use core::cell::UnsafeCell; 10 | use lazy_static::*; 11 | use user_lib::{ 12 | condvar_create, condvar_signal, condvar_wait, exit, mutex_create, mutex_lock, mutex_unlock, 13 | thread_create, waittid, 14 | }; 15 | 16 | const THREAD_NUM: usize = 3; 17 | 18 | struct Barrier { 19 | mutex_id: usize, 20 | condvar_id: usize, 21 | count: UnsafeCell, 22 | } 23 | 24 | impl Barrier { 25 | pub fn new() -> Self { 26 | Self { 27 | mutex_id: mutex_create() as usize, 28 | condvar_id: condvar_create() as usize, 29 | count: UnsafeCell::new(0), 30 | } 31 | } 32 | pub fn block(&self) { 33 | mutex_lock(self.mutex_id); 34 | let count = self.count.get(); 35 | // SAFETY: Here, the accesses of the count is in the 36 | // critical section protected by the mutex. 37 | unsafe { 38 | *count = *count + 1; 39 | } 40 | if unsafe { *count } == THREAD_NUM { 41 | condvar_signal(self.condvar_id); 42 | } else { 43 | condvar_wait(self.condvar_id, self.mutex_id); 44 | condvar_signal(self.condvar_id); 45 | } 46 | mutex_unlock(self.mutex_id); 47 | } 48 | } 49 | 50 | unsafe impl Sync for Barrier {} 51 | 52 | lazy_static! { 53 | static ref BARRIER_AB: Barrier = Barrier::new(); 54 | static ref BARRIER_BC: Barrier = Barrier::new(); 55 | } 56 | 57 | fn thread_fn() { 58 | for _ in 0..300 { 59 | print!("a"); 60 | } 61 | BARRIER_AB.block(); 62 | for _ in 0..300 { 63 | print!("b"); 64 | } 65 | BARRIER_BC.block(); 66 | for _ in 0..300 { 67 | print!("c"); 68 | } 69 | exit(0) 70 | } 71 | 72 | #[unsafe(no_mangle)] 73 | pub fn main() -> i32 { 74 | let mut v: Vec = Vec::new(); 75 | for _ in 0..THREAD_NUM { 76 | v.push(thread_create(thread_fn as usize, 0)); 77 | } 78 | for tid in v.into_iter() { 79 | waittid(tid as usize); 80 | } 81 | println!("\nOK!"); 82 | 0 83 | } 84 | -------------------------------------------------------------------------------- /os/src/drivers/gpu/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::drivers::bus::virtio::VirtioHal; 2 | use crate::sync::UPIntrFreeCell; 3 | use alloc::{sync::Arc, vec::Vec}; 4 | use core::any::Any; 5 | use embedded_graphics::pixelcolor::Rgb888; 6 | use tinybmp::Bmp; 7 | use virtio_drivers::{VirtIOGpu, VirtIOHeader}; 8 | const VIRTIO7: usize = 0x10007000; 9 | pub trait GpuDevice: Send + Sync + Any { 10 | fn get_framebuffer(&self) -> &mut [u8]; 11 | fn flush(&self); 12 | } 13 | 14 | lazy_static::lazy_static!( 15 | pub static ref GPU_DEVICE: Arc = Arc::new(VirtIOGpuWrapper::new()); 16 | ); 17 | 18 | pub struct VirtIOGpuWrapper { 19 | gpu: UPIntrFreeCell>, 20 | fb: &'static [u8], 21 | } 22 | static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp"); 23 | impl VirtIOGpuWrapper { 24 | pub fn new() -> Self { 25 | unsafe { 26 | let mut virtio = 27 | VirtIOGpu::::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap(); 28 | 29 | let fbuffer = virtio.setup_framebuffer().unwrap(); 30 | let len = fbuffer.len(); 31 | let ptr = fbuffer.as_mut_ptr(); 32 | let fb = core::slice::from_raw_parts_mut(ptr, len); 33 | 34 | let bmp = Bmp::::from_slice(BMP_DATA).unwrap(); 35 | let raw = bmp.as_raw(); 36 | let mut b = Vec::new(); 37 | for i in raw.image_data().chunks(3) { 38 | let mut v = i.to_vec(); 39 | b.append(&mut v); 40 | if i == [255, 255, 255] { 41 | b.push(0x0) 42 | } else { 43 | b.push(0xff) 44 | } 45 | } 46 | virtio.setup_cursor(b.as_slice(), 50, 50, 50, 50).unwrap(); 47 | 48 | Self { 49 | gpu: UPIntrFreeCell::new(virtio), 50 | fb, 51 | } 52 | } 53 | } 54 | } 55 | 56 | impl GpuDevice for VirtIOGpuWrapper { 57 | fn flush(&self) { 58 | self.gpu.exclusive_access().flush().unwrap(); 59 | } 60 | fn get_framebuffer(&self) -> &mut [u8] { 61 | unsafe { 62 | let ptr = self.fb.as_ptr() as *const _ as *mut u8; 63 | core::slice::from_raw_parts_mut(ptr, self.fb.len()) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /user/src/bin/peterson.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | extern crate core; 8 | 9 | use alloc::vec::Vec; 10 | use core::sync::atomic::{AtomicUsize, Ordering}; 11 | use user_lib::{exit, sleep, thread_create, waittid}; 12 | const N: usize = 1000; 13 | 14 | static mut TURN: usize = 0; 15 | static mut FLAG: [bool; 2] = [false; 2]; 16 | static GUARD: AtomicUsize = AtomicUsize::new(0); 17 | 18 | fn critical_test_enter() { 19 | assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); 20 | } 21 | 22 | fn critical_test_claim() { 23 | assert_eq!(GUARD.load(Ordering::SeqCst), 1); 24 | } 25 | 26 | fn critical_test_exit() { 27 | assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); 28 | } 29 | 30 | fn peterson_enter_critical(id: usize, peer_id: usize) { 31 | // println!("Thread[{}] try enter", id); 32 | vstore!(FLAG[id], true); 33 | vstore!(TURN, peer_id); 34 | memory_fence!(); 35 | while vload!(FLAG[peer_id]) && vload!(TURN) == peer_id { 36 | // println!("Thread[{}] enter fail", id); 37 | sleep(1); 38 | // println!("Thread[{}] retry enter", id); 39 | } 40 | // println!("Thread[{}] enter", id); 41 | } 42 | 43 | fn peterson_exit_critical(id: usize) { 44 | vstore!(FLAG[id], false); 45 | // println!("Thread[{}] exit", id); 46 | } 47 | 48 | pub fn thread_fn(id: usize) -> ! { 49 | // println!("Thread[{}] init.", id); 50 | let peer_id: usize = id ^ 1; 51 | for iter in 0..N { 52 | if iter % 10 == 0 { 53 | println!("[{}] it={}", id, iter); 54 | } 55 | peterson_enter_critical(id, peer_id); 56 | critical_test_enter(); 57 | for _ in 0..3 { 58 | critical_test_claim(); 59 | sleep(2); 60 | } 61 | critical_test_exit(); 62 | peterson_exit_critical(id); 63 | } 64 | exit(0) 65 | } 66 | 67 | #[unsafe(no_mangle)] 68 | pub fn main() -> i32 { 69 | let mut v = Vec::new(); 70 | v.push(thread_create(thread_fn as usize, 0)); 71 | // v.push(thread_create(thread_fn as usize, 1)); 72 | for tid in v.iter() { 73 | let exit_code = waittid(*tid as usize); 74 | assert_eq!(exit_code, 0, "thread conflict happened!"); 75 | println!("thread#{} exited with code {}", tid, exit_code); 76 | } 77 | println!("main thread exited."); 78 | 0 79 | } 80 | -------------------------------------------------------------------------------- /user/src/bin/mpsc_sem.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(clippy::println_empty_string)] 4 | 5 | #[macro_use] 6 | extern crate user_lib; 7 | 8 | extern crate alloc; 9 | 10 | use alloc::vec::Vec; 11 | use user_lib::exit; 12 | use user_lib::{semaphore_create, semaphore_down, semaphore_up}; 13 | use user_lib::{thread_create, waittid}; 14 | 15 | const SEM_MUTEX: usize = 0; 16 | const SEM_EMPTY: usize = 1; 17 | const SEM_AVAIL: usize = 2; 18 | const BUFFER_SIZE: usize = 8; 19 | static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; 20 | static mut FRONT: usize = 0; 21 | static mut TAIL: usize = 0; 22 | const PRODUCER_COUNT: usize = 4; 23 | const NUMBER_PER_PRODUCER: usize = 100; 24 | 25 | fn producer(id: *const usize) -> ! { 26 | unsafe { 27 | let id = *id; 28 | for _ in 0..NUMBER_PER_PRODUCER { 29 | semaphore_down(SEM_EMPTY); 30 | semaphore_down(SEM_MUTEX); 31 | BUFFER[TAIL] = id; 32 | TAIL = (TAIL + 1) % BUFFER_SIZE; 33 | semaphore_up(SEM_MUTEX); 34 | semaphore_up(SEM_AVAIL); 35 | } 36 | } 37 | exit(0) 38 | } 39 | 40 | fn consumer() -> ! { 41 | unsafe { 42 | for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { 43 | semaphore_down(SEM_AVAIL); 44 | semaphore_down(SEM_MUTEX); 45 | print!("{} ", BUFFER[FRONT]); 46 | FRONT = (FRONT + 1) % BUFFER_SIZE; 47 | semaphore_up(SEM_MUTEX); 48 | semaphore_up(SEM_EMPTY); 49 | } 50 | } 51 | println!(""); 52 | exit(0) 53 | } 54 | 55 | #[unsafe(no_mangle)] 56 | pub fn main() -> i32 { 57 | // create semaphores 58 | assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); 59 | assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); 60 | assert_eq!(semaphore_create(0) as usize, SEM_AVAIL); 61 | // create threads 62 | let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); 63 | let mut threads = Vec::new(); 64 | for i in 0..PRODUCER_COUNT { 65 | threads.push(thread_create( 66 | producer as usize, 67 | &ids.as_slice()[i] as *const _ as usize, 68 | )); 69 | } 70 | threads.push(thread_create(consumer as usize, 0)); 71 | // wait for all threads to complete 72 | for thread in threads.iter() { 73 | waittid(*thread as usize); 74 | } 75 | println!("mpsc_sem passed!"); 76 | 0 77 | } 78 | -------------------------------------------------------------------------------- /easy-fs/src/bitmap.rs: -------------------------------------------------------------------------------- 1 | use super::{get_block_cache, BlockDevice, BLOCK_SZ}; 2 | use alloc::sync::Arc; 3 | 4 | type BitmapBlock = [u64; 64]; 5 | 6 | const BLOCK_BITS: usize = BLOCK_SZ * 8; 7 | 8 | pub struct Bitmap { 9 | start_block_id: usize, 10 | blocks: usize, 11 | } 12 | 13 | /// Return (block_pos, bits64_pos, inner_pos) 14 | fn decomposition(mut bit: usize) -> (usize, usize, usize) { 15 | let block_pos = bit / BLOCK_BITS; 16 | bit %= BLOCK_BITS; 17 | (block_pos, bit / 64, bit % 64) 18 | } 19 | 20 | impl Bitmap { 21 | pub fn new(start_block_id: usize, blocks: usize) -> Self { 22 | Self { 23 | start_block_id, 24 | blocks, 25 | } 26 | } 27 | 28 | pub fn alloc(&self, block_device: &Arc) -> Option { 29 | for block_id in 0..self.blocks { 30 | let pos = get_block_cache( 31 | block_id + self.start_block_id as usize, 32 | Arc::clone(block_device), 33 | ) 34 | .lock() 35 | .modify(0, |bitmap_block: &mut BitmapBlock| { 36 | if let Some((bits64_pos, inner_pos)) = bitmap_block 37 | .iter() 38 | .enumerate() 39 | .find(|(_, bits64)| **bits64 != u64::MAX) 40 | .map(|(bits64_pos, bits64)| (bits64_pos, bits64.trailing_ones() as usize)) 41 | { 42 | // modify cache 43 | bitmap_block[bits64_pos] |= 1u64 << inner_pos; 44 | Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize) 45 | } else { 46 | None 47 | } 48 | }); 49 | if pos.is_some() { 50 | return pos; 51 | } 52 | } 53 | None 54 | } 55 | 56 | pub fn dealloc(&self, block_device: &Arc, bit: usize) { 57 | let (block_pos, bits64_pos, inner_pos) = decomposition(bit); 58 | get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device)) 59 | .lock() 60 | .modify(0, |bitmap_block: &mut BitmapBlock| { 61 | assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0); 62 | bitmap_block[bits64_pos] -= 1u64 << inner_pos; 63 | }); 64 | } 65 | 66 | pub fn maximum(&self) -> usize { 67 | self.blocks * BLOCK_BITS 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /os/src/task/task.rs: -------------------------------------------------------------------------------- 1 | use super::id::TaskUserRes; 2 | use super::{KernelStack, ProcessControlBlock, TaskContext, kstack_alloc}; 3 | use crate::trap::TrapContext; 4 | use crate::{ 5 | mm::PhysPageNum, 6 | sync::{UPIntrFreeCell, UPIntrRefMut}, 7 | }; 8 | use alloc::sync::{Arc, Weak}; 9 | 10 | pub struct TaskControlBlock { 11 | // immutable 12 | pub process: Weak, 13 | pub kstack: KernelStack, 14 | // mutable 15 | pub inner: UPIntrFreeCell, 16 | } 17 | 18 | impl TaskControlBlock { 19 | pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, TaskControlBlockInner> { 20 | self.inner.exclusive_access() 21 | } 22 | 23 | pub fn get_user_token(&self) -> usize { 24 | let process = self.process.upgrade().unwrap(); 25 | let inner = process.inner_exclusive_access(); 26 | inner.memory_set.token() 27 | } 28 | } 29 | 30 | pub struct TaskControlBlockInner { 31 | pub res: Option, 32 | pub trap_cx_ppn: PhysPageNum, 33 | pub task_cx: TaskContext, 34 | pub task_status: TaskStatus, 35 | pub exit_code: Option, 36 | } 37 | 38 | impl TaskControlBlockInner { 39 | pub fn get_trap_cx(&self) -> &'static mut TrapContext { 40 | self.trap_cx_ppn.get_mut() 41 | } 42 | 43 | #[allow(unused)] 44 | fn get_status(&self) -> TaskStatus { 45 | self.task_status 46 | } 47 | } 48 | 49 | impl TaskControlBlock { 50 | pub fn new( 51 | process: Arc, 52 | ustack_base: usize, 53 | alloc_user_res: bool, 54 | ) -> Self { 55 | let res = TaskUserRes::new(Arc::clone(&process), ustack_base, alloc_user_res); 56 | let trap_cx_ppn = res.trap_cx_ppn(); 57 | let kstack = kstack_alloc(); 58 | let kstack_top = kstack.get_top(); 59 | Self { 60 | process: Arc::downgrade(&process), 61 | kstack, 62 | inner: unsafe { 63 | UPIntrFreeCell::new(TaskControlBlockInner { 64 | res: Some(res), 65 | trap_cx_ppn, 66 | task_cx: TaskContext::goto_trap_return(kstack_top), 67 | task_status: TaskStatus::Ready, 68 | exit_code: None, 69 | }) 70 | }, 71 | } 72 | } 73 | } 74 | 75 | #[derive(Copy, Clone, PartialEq)] 76 | pub enum TaskStatus { 77 | Ready, 78 | Running, 79 | Blocked, 80 | } 81 | -------------------------------------------------------------------------------- /user/src/bin/pipe_large_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | extern crate alloc; 8 | 9 | use alloc::format; 10 | use user_lib::{close, fork, get_time, pipe, read, wait, write}; 11 | 12 | const LENGTH: usize = 3000; 13 | #[unsafe(no_mangle)] 14 | pub fn main() -> i32 { 15 | // create pipes 16 | // parent write to child 17 | let mut down_pipe_fd = [0usize; 2]; 18 | // child write to parent 19 | let mut up_pipe_fd = [0usize; 2]; 20 | pipe(&mut down_pipe_fd); 21 | pipe(&mut up_pipe_fd); 22 | let mut random_str = [0u8; LENGTH]; 23 | if fork() == 0 { 24 | // close write end of down pipe 25 | close(down_pipe_fd[1]); 26 | // close read end of up pipe 27 | close(up_pipe_fd[0]); 28 | assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH); 29 | close(down_pipe_fd[0]); 30 | let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); 31 | println!("sum = {}(child)", sum); 32 | let sum_str = format!("{}", sum); 33 | write(up_pipe_fd[1], sum_str.as_bytes()); 34 | close(up_pipe_fd[1]); 35 | println!("Child process exited!"); 36 | 0 37 | } else { 38 | // close read end of down pipe 39 | close(down_pipe_fd[0]); 40 | // close write end of up pipe 41 | close(up_pipe_fd[1]); 42 | // generate a long random string 43 | for ch in random_str.iter_mut() { 44 | *ch = get_time() as u8; 45 | } 46 | // send it 47 | assert_eq!( 48 | write(down_pipe_fd[1], &random_str) as usize, 49 | random_str.len() 50 | ); 51 | // close write end of down pipe 52 | close(down_pipe_fd[1]); 53 | // calculate sum(parent) 54 | let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); 55 | println!("sum = {}(parent)", sum); 56 | // recv sum(child) 57 | let mut child_result = [0u8; 32]; 58 | let result_len = read(up_pipe_fd[0], &mut child_result) as usize; 59 | close(up_pipe_fd[0]); 60 | // check 61 | assert_eq!( 62 | sum, 63 | str::parse::(core::str::from_utf8(&child_result[..result_len]).unwrap()) 64 | .unwrap() 65 | ); 66 | let mut _unused: i32 = 0; 67 | wait(&mut _unused); 68 | println!("pipe_large_test passed!"); 69 | 0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /os/src/sync/mutex.rs: -------------------------------------------------------------------------------- 1 | use super::UPIntrFreeCell; 2 | use crate::task::TaskControlBlock; 3 | use crate::task::{block_current_and_run_next, suspend_current_and_run_next}; 4 | use crate::task::{current_task, wakeup_task}; 5 | use alloc::{collections::VecDeque, sync::Arc}; 6 | 7 | pub trait Mutex: Sync + Send { 8 | fn lock(&self); 9 | fn unlock(&self); 10 | } 11 | 12 | pub struct MutexSpin { 13 | locked: UPIntrFreeCell, 14 | } 15 | 16 | impl MutexSpin { 17 | pub fn new() -> Self { 18 | Self { 19 | locked: unsafe { UPIntrFreeCell::new(false) }, 20 | } 21 | } 22 | } 23 | 24 | impl Mutex for MutexSpin { 25 | fn lock(&self) { 26 | loop { 27 | let mut locked = self.locked.exclusive_access(); 28 | if *locked { 29 | drop(locked); 30 | suspend_current_and_run_next(); 31 | continue; 32 | } else { 33 | *locked = true; 34 | return; 35 | } 36 | } 37 | } 38 | 39 | fn unlock(&self) { 40 | let mut locked = self.locked.exclusive_access(); 41 | *locked = false; 42 | } 43 | } 44 | 45 | pub struct MutexBlocking { 46 | inner: UPIntrFreeCell, 47 | } 48 | 49 | pub struct MutexBlockingInner { 50 | locked: bool, 51 | wait_queue: VecDeque>, 52 | } 53 | 54 | impl MutexBlocking { 55 | pub fn new() -> Self { 56 | Self { 57 | inner: unsafe { 58 | UPIntrFreeCell::new(MutexBlockingInner { 59 | locked: false, 60 | wait_queue: VecDeque::new(), 61 | }) 62 | }, 63 | } 64 | } 65 | } 66 | 67 | impl Mutex for MutexBlocking { 68 | fn lock(&self) { 69 | let mut mutex_inner = self.inner.exclusive_access(); 70 | if mutex_inner.locked { 71 | mutex_inner.wait_queue.push_back(current_task().unwrap()); 72 | drop(mutex_inner); 73 | block_current_and_run_next(); 74 | } else { 75 | mutex_inner.locked = true; 76 | } 77 | } 78 | 79 | fn unlock(&self) { 80 | let mut mutex_inner = self.inner.exclusive_access(); 81 | assert!(mutex_inner.locked); 82 | if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { 83 | wakeup_task(waking_task); 84 | } else { 85 | mutex_inner.locked = false; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /.github/workflows/doc-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build Rust Doc And Run tests 2 | 3 | on: [push] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | rust_toolchain: nightly-2025-02-18 8 | 9 | jobs: 10 | build-doc: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: ${{ env.rust_toolchain }} 18 | components: rust-src, llvm-tools-preview 19 | target: riscv64gc-unknown-none-elf 20 | - name: Build doc 21 | run: cd os && cargo doc --no-deps --verbose 22 | - name: Deploy to Github Pages 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc 27 | destination_dir: ${{ github.ref_name }} 28 | 29 | run-tests: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: actions-rs/toolchain@v1 34 | with: 35 | profile: minimal 36 | toolchain: ${{ env.rust_toolchain }} 37 | components: rust-src, llvm-tools-preview 38 | target: riscv64gc-unknown-none-elf 39 | - uses: actions-rs/install@v0.1 40 | with: 41 | crate: cargo-binutils 42 | version: latest 43 | use-tool-cache: true 44 | - name: Cache QEMU 45 | uses: actions/cache@v3 46 | with: 47 | path: qemu-7.0.0 48 | key: qemu-7.0.0-x86_64-riscv64 49 | - name: Install QEMU 50 | run: | 51 | sudo apt-get update 52 | sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ 53 | gawk build-essential bison flex texinfo gperf libtool patchutils bc \ 54 | zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev libslirp-dev \ 55 | python3 python3-pip ninja-build -y 56 | if [ ! -d qemu-7.0.0 ]; then 57 | wget https://download.qemu.org/qemu-7.0.0.tar.xz 58 | tar -xf qemu-7.0.0.tar.xz 59 | cd qemu-7.0.0 60 | ./configure --target-list=riscv64-softmmu 61 | make -j 62 | else 63 | cd qemu-7.0.0 64 | fi 65 | sudo make install 66 | qemu-system-riscv64 --version 67 | 68 | - name: Run usertests 69 | run: cd os && make run TEST=1 70 | timeout-minutes: 10 71 | 72 | -------------------------------------------------------------------------------- /user/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(linkage)] 3 | #![feature(alloc_error_handler)] 4 | 5 | #[macro_use] 6 | pub mod console; 7 | mod file; 8 | mod io; 9 | mod lang_items; 10 | mod net; 11 | mod sync; 12 | mod syscall; 13 | mod task; 14 | 15 | extern crate alloc; 16 | #[macro_use] 17 | extern crate bitflags; 18 | 19 | use core::ptr::addr_of_mut; 20 | 21 | use alloc::vec::Vec; 22 | use buddy_system_allocator::LockedHeap; 23 | pub use file::*; 24 | pub use io::*; 25 | pub use net::*; 26 | pub use sync::*; 27 | use syscall::*; 28 | pub use task::*; 29 | 30 | const USER_HEAP_SIZE: usize = 32768; 31 | 32 | static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; 33 | 34 | #[global_allocator] 35 | static HEAP: LockedHeap = LockedHeap::empty(); 36 | 37 | #[alloc_error_handler] 38 | pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { 39 | panic!("Heap allocation error, layout = {:?}", layout); 40 | } 41 | 42 | #[unsafe(no_mangle)] 43 | #[unsafe(link_section = ".text.entry")] 44 | pub extern "C" fn _start(argc: usize, argv: usize) -> ! { 45 | unsafe { 46 | HEAP.lock() 47 | .init(addr_of_mut!(HEAP_SPACE) as usize, USER_HEAP_SIZE); 48 | } 49 | let mut v: Vec<&'static str> = Vec::new(); 50 | for i in 0..argc { 51 | let str_start = 52 | unsafe { ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() }; 53 | let len = (0usize..) 54 | .find(|i| unsafe { ((str_start + *i) as *const u8).read_volatile() == 0 }) 55 | .unwrap(); 56 | v.push( 57 | core::str::from_utf8(unsafe { 58 | core::slice::from_raw_parts(str_start as *const u8, len) 59 | }) 60 | .unwrap(), 61 | ); 62 | } 63 | exit(main(argc, v.as_slice())); 64 | } 65 | 66 | #[linkage = "weak"] 67 | #[unsafe(no_mangle)] 68 | fn main(_argc: usize, _argv: &[&str]) -> i32 { 69 | panic!("Cannot find main!"); 70 | } 71 | 72 | #[macro_export] 73 | macro_rules! vstore { 74 | ($var: expr, $value: expr) => { 75 | // unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } 76 | unsafe { 77 | core::ptr::write_volatile(core::ptr::addr_of_mut!($var), $value); 78 | } 79 | }; 80 | } 81 | 82 | #[macro_export] 83 | macro_rules! vload { 84 | ($var: expr) => { 85 | // unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } 86 | unsafe { core::ptr::read_volatile(core::ptr::addr_of!($var)) } 87 | }; 88 | } 89 | 90 | #[macro_export] 91 | macro_rules! memory_fence { 92 | () => { 93 | core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst) 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /user/src/bin/gui_move.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | extern crate user_lib; 6 | 7 | use user_lib::console::getchar; 8 | use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; 9 | 10 | use embedded_graphics::draw_target::DrawTarget; 11 | use embedded_graphics::pixelcolor::Rgb888; 12 | use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; 13 | use embedded_graphics::primitives::Primitive; 14 | use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; 15 | 16 | const INIT_X: i32 = 640; 17 | const INIT_Y: i32 = 400; 18 | const RECT_SIZE: u32 = 40; 19 | 20 | pub struct DrawingBoard { 21 | disp: Display, 22 | latest_pos: Point, 23 | } 24 | 25 | impl DrawingBoard { 26 | pub fn new() -> Self { 27 | Self { 28 | disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), 29 | latest_pos: Point::new(INIT_X, INIT_Y), 30 | } 31 | } 32 | fn paint(&mut self) { 33 | Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) 34 | .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1)) 35 | .draw(&mut self.disp) 36 | .ok(); 37 | } 38 | fn unpaint(&mut self) { 39 | Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) 40 | .into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1)) 41 | .draw(&mut self.disp) 42 | .ok(); 43 | } 44 | pub fn move_rect(&mut self, dx: i32, dy: i32) { 45 | let new_x = self.latest_pos.x + dx; 46 | let new_y = self.latest_pos.y + dy; 47 | let r = (RECT_SIZE / 2) as i32; 48 | if new_x > r 49 | && new_x + r < (VIRTGPU_XRES as i32) 50 | && new_y > r 51 | && new_y + r < (VIRTGPU_YRES as i32) 52 | { 53 | self.unpaint(); 54 | self.latest_pos.x = new_x; 55 | self.latest_pos.y = new_y; 56 | self.paint(); 57 | } 58 | } 59 | } 60 | 61 | const LF: u8 = 0x0au8; 62 | const CR: u8 = 0x0du8; 63 | #[unsafe(no_mangle)] 64 | pub fn main() -> i32 { 65 | let mut board = DrawingBoard::new(); 66 | let _ = board.disp.clear(Rgb888::BLACK).unwrap(); 67 | board.disp.flush(); 68 | loop { 69 | let c = getchar(); 70 | if c == LF || c == CR { 71 | break; 72 | } 73 | let mut moved = true; 74 | match c { 75 | b'w' => board.move_rect(0, -10), 76 | b'a' => board.move_rect(-10, 0), 77 | b's' => board.move_rect(0, 10), 78 | b'd' => board.move_rect(10, 0), 79 | _ => moved = false, 80 | } 81 | if moved { 82 | board.disp.flush(); 83 | } 84 | } 85 | 0 86 | } 87 | -------------------------------------------------------------------------------- /os/src/drivers/input/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::drivers::bus::virtio::VirtioHal; 2 | use crate::sync::{Condvar, UPIntrFreeCell}; 3 | use crate::task::schedule; 4 | use alloc::collections::VecDeque; 5 | use alloc::sync::Arc; 6 | use core::any::Any; 7 | use virtio_drivers::{VirtIOHeader, VirtIOInput}; 8 | 9 | const VIRTIO5: usize = 0x10005000; 10 | const VIRTIO6: usize = 0x10006000; 11 | 12 | struct VirtIOInputInner { 13 | virtio_input: VirtIOInput<'static, VirtioHal>, 14 | events: VecDeque, 15 | } 16 | 17 | struct VirtIOInputWrapper { 18 | inner: UPIntrFreeCell, 19 | condvar: Condvar, 20 | } 21 | 22 | pub trait InputDevice: Send + Sync + Any { 23 | fn read_event(&self) -> u64; 24 | fn handle_irq(&self); 25 | fn is_empty(&self) -> bool; 26 | } 27 | 28 | lazy_static::lazy_static!( 29 | pub static ref KEYBOARD_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO5)); 30 | pub static ref MOUSE_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO6)); 31 | ); 32 | 33 | impl VirtIOInputWrapper { 34 | pub fn new(addr: usize) -> Self { 35 | let inner = VirtIOInputInner { 36 | virtio_input: unsafe { 37 | VirtIOInput::::new(&mut *(addr as *mut VirtIOHeader)).unwrap() 38 | }, 39 | events: VecDeque::new(), 40 | }; 41 | Self { 42 | inner: unsafe { UPIntrFreeCell::new(inner) }, 43 | condvar: Condvar::new(), 44 | } 45 | } 46 | } 47 | 48 | impl InputDevice for VirtIOInputWrapper { 49 | fn is_empty(&self) -> bool { 50 | self.inner.exclusive_access().events.is_empty() 51 | } 52 | 53 | fn read_event(&self) -> u64 { 54 | loop { 55 | let mut inner = self.inner.exclusive_access(); 56 | if let Some(event) = inner.events.pop_front() { 57 | return event; 58 | } else { 59 | let task_cx_ptr = self.condvar.wait_no_sched(); 60 | drop(inner); 61 | schedule(task_cx_ptr); 62 | } 63 | } 64 | } 65 | 66 | fn handle_irq(&self) { 67 | let mut count = 0; 68 | let mut result = 0; 69 | self.inner.exclusive_session(|inner| { 70 | inner.virtio_input.ack_interrupt(); 71 | while let Some(event) = inner.virtio_input.pop_pending_event() { 72 | count += 1; 73 | result = (event.event_type as u64) << 48 74 | | (event.code as u64) << 32 75 | | (event.value) as u64; 76 | inner.events.push_back(result); 77 | } 78 | }); 79 | if count > 0 { 80 | self.condvar.signal(); 81 | }; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /os/src/trap/trap.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .macro SAVE_GP n 3 | sd x\n, \n*8(sp) 4 | .endm 5 | .macro LOAD_GP n 6 | ld x\n, \n*8(sp) 7 | .endm 8 | .section .text.trampoline 9 | .globl __alltraps 10 | .globl __restore 11 | .globl __alltraps_k 12 | .globl __restore_k 13 | .align 2 14 | __alltraps: 15 | csrrw sp, sscratch, sp 16 | # now sp->*TrapContext in user space, sscratch->user stack 17 | # save other general purpose registers 18 | sd x1, 1*8(sp) 19 | # skip sp(x2), we will save it later 20 | sd x3, 3*8(sp) 21 | # skip tp(x4), application does not use it 22 | # save x5~x31 23 | .set n, 5 24 | .rept 27 25 | SAVE_GP %n 26 | .set n, n+1 27 | .endr 28 | # we can use t0/t1/t2 freely, because they have been saved in TrapContext 29 | csrr t0, sstatus 30 | csrr t1, sepc 31 | sd t0, 32*8(sp) 32 | sd t1, 33*8(sp) 33 | # read user stack from sscratch and save it in TrapContext 34 | csrr t2, sscratch 35 | sd t2, 2*8(sp) 36 | # load kernel_satp into t0 37 | ld t0, 34*8(sp) 38 | # load trap_handler into t1 39 | ld t1, 36*8(sp) 40 | # move to kernel_sp 41 | ld sp, 35*8(sp) 42 | # switch to kernel space 43 | csrw satp, t0 44 | sfence.vma 45 | # jump to trap_handler 46 | jr t1 47 | 48 | __restore: 49 | # a0: *TrapContext in user space(Constant); a1: user space token 50 | # switch to user space 51 | csrw satp, a1 52 | sfence.vma 53 | csrw sscratch, a0 54 | mv sp, a0 55 | # now sp points to TrapContext in user space, start restoring based on it 56 | # restore sstatus/sepc 57 | ld t0, 32*8(sp) 58 | ld t1, 33*8(sp) 59 | csrw sstatus, t0 60 | csrw sepc, t1 61 | # restore general purpose registers except x0/sp/tp 62 | ld x1, 1*8(sp) 63 | ld x3, 3*8(sp) 64 | .set n, 5 65 | .rept 27 66 | LOAD_GP %n 67 | .set n, n+1 68 | .endr 69 | # back to user stack 70 | ld sp, 2*8(sp) 71 | sret 72 | 73 | .align 2 74 | __alltraps_k: 75 | addi sp, sp, -34*8 76 | sd x1, 1*8(sp) 77 | sd x3, 3*8(sp) 78 | .set n, 5 79 | .rept 27 80 | SAVE_GP %n 81 | .set n, n+1 82 | .endr 83 | csrr t0, sstatus 84 | csrr t1, sepc 85 | sd t0, 32*8(sp) 86 | sd t1, 33*8(sp) 87 | mv a0, sp 88 | csrr t2, sscratch 89 | jalr t2 90 | 91 | __restore_k: 92 | ld t0, 32*8(sp) 93 | ld t1, 33*8(sp) 94 | csrw sstatus, t0 95 | csrw sepc, t1 96 | ld x1, 1*8(sp) 97 | ld x3, 3*8(sp) 98 | .set n, 5 99 | .rept 27 100 | LOAD_GP %n 101 | .set n, n+1 102 | .endr 103 | addi sp, sp, 34*8 104 | sret 105 | -------------------------------------------------------------------------------- /os/src/net/udp.rs: -------------------------------------------------------------------------------- 1 | use super::LOSE_NET_STACK; 2 | use super::NET_DEVICE; 3 | use super::net_interrupt_handler; 4 | use super::socket::{add_socket, pop_data, remove_socket}; 5 | use crate::fs::File; 6 | use alloc::vec; 7 | use lose_net_stack::IPv4; 8 | use lose_net_stack::MacAddress; 9 | use lose_net_stack::packets::udp::UDPPacket; 10 | 11 | pub struct UDP { 12 | pub target: IPv4, 13 | pub sport: u16, 14 | pub dport: u16, 15 | pub socket_index: usize, 16 | } 17 | 18 | impl UDP { 19 | pub fn new(target: IPv4, sport: u16, dport: u16) -> Self { 20 | let index = add_socket(target, sport, dport).expect("can't add socket"); 21 | 22 | Self { 23 | target, 24 | sport, 25 | dport, 26 | socket_index: index, 27 | } 28 | } 29 | } 30 | 31 | impl File for UDP { 32 | fn readable(&self) -> bool { 33 | true 34 | } 35 | 36 | fn writable(&self) -> bool { 37 | true 38 | } 39 | 40 | fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { 41 | loop { 42 | if let Some(data) = pop_data(self.socket_index) { 43 | let data_len = data.len(); 44 | let mut left = 0; 45 | for i in 0..buf.buffers.len() { 46 | let buffer_i_len = buf.buffers[i].len().min(data_len - left); 47 | 48 | buf.buffers[i][..buffer_i_len] 49 | .copy_from_slice(&data[left..(left + buffer_i_len)]); 50 | 51 | left += buffer_i_len; 52 | if left == data_len { 53 | break; 54 | } 55 | } 56 | return left; 57 | } else { 58 | net_interrupt_handler(); 59 | } 60 | } 61 | } 62 | 63 | fn write(&self, buf: crate::mm::UserBuffer) -> usize { 64 | let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); 65 | 66 | let mut data = vec![0u8; buf.len()]; 67 | 68 | let mut left = 0; 69 | for i in 0..buf.buffers.len() { 70 | data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); 71 | left += buf.buffers[i].len(); 72 | } 73 | 74 | let len = data.len(); 75 | 76 | let udp_packet = UDPPacket::new( 77 | lose_net_stack.ip, 78 | lose_net_stack.mac, 79 | self.sport, 80 | self.target, 81 | MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 82 | self.dport, 83 | len, 84 | data.as_ref(), 85 | ); 86 | NET_DEVICE.transmit(&udp_packet.build_data()); 87 | len 88 | } 89 | } 90 | 91 | impl Drop for UDP { 92 | fn drop(&mut self) { 93 | remove_socket(self.socket_index) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /user/src/bin/adder_peterson_yield.rs: -------------------------------------------------------------------------------- 1 | //! It only works on a single CPU! 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | extern crate alloc; 9 | 10 | use alloc::vec::Vec; 11 | use core::{ 12 | ptr::addr_of_mut, 13 | sync::atomic::{Ordering, compiler_fence}, 14 | }; 15 | use user_lib::{exit, get_time, thread_create, waittid, yield_}; 16 | 17 | static mut A: usize = 0; 18 | static mut FLAG: [bool; 2] = [false; 2]; 19 | static mut TURN: usize = 0; 20 | const PER_THREAD_DEFAULT: usize = 2000; 21 | const THREAD_COUNT_DEFAULT: usize = 2; 22 | static mut PER_THREAD: usize = 0; 23 | 24 | fn critical_section(t: &mut usize) { 25 | let a = addr_of_mut!(A); 26 | let cur = unsafe { a.read_volatile() }; 27 | for _ in 0..500 { 28 | *t = (*t) * (*t) % 10007; 29 | } 30 | unsafe { 31 | a.write_volatile(cur + 1); 32 | } 33 | } 34 | 35 | fn lock(id: usize) { 36 | unsafe { 37 | FLAG[id] = true; 38 | let j = 1 - id; 39 | TURN = j; 40 | // Tell the compiler not to reorder memory operations 41 | // across this fence. 42 | compiler_fence(Ordering::SeqCst); 43 | while FLAG[j] && TURN == j { 44 | yield_(); 45 | } 46 | } 47 | } 48 | 49 | fn unlock(id: usize) { 50 | unsafe { 51 | FLAG[id] = false; 52 | } 53 | } 54 | 55 | fn f(id: usize) -> ! { 56 | let mut t = 2usize; 57 | for _iter in 0..unsafe { PER_THREAD } { 58 | lock(id); 59 | critical_section(&mut t); 60 | unlock(id); 61 | } 62 | exit(t as i32) 63 | } 64 | 65 | #[unsafe(no_mangle)] 66 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 67 | let mut thread_count = THREAD_COUNT_DEFAULT; 68 | let mut per_thread = PER_THREAD_DEFAULT; 69 | if argc >= 2 { 70 | thread_count = argv[1].parse().unwrap(); 71 | if argc >= 3 { 72 | per_thread = argv[2].parse().unwrap(); 73 | } 74 | } 75 | unsafe { 76 | PER_THREAD = per_thread; 77 | } 78 | 79 | // uncomment this if you want to check the assembly 80 | // println!( 81 | // "addr: lock={:#x}, unlock={:#x}", 82 | // lock as usize, 83 | // unlock as usize 84 | // ); 85 | 86 | let start = get_time(); 87 | let mut v = Vec::new(); 88 | assert_eq!( 89 | thread_count, 2, 90 | "Peterson works when there are only 2 threads." 91 | ); 92 | for id in 0..thread_count { 93 | v.push(thread_create(f as usize, id) as usize); 94 | } 95 | let mut time_cost = Vec::new(); 96 | for tid in v.iter() { 97 | time_cost.push(waittid(*tid)); 98 | } 99 | println!("time cost is {}ms", get_time() - start); 100 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 101 | 0 102 | } 103 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # Stage 1 Build QEMU 4 | # - https://www.qemu.org/download/ 5 | # - https://wiki.qemu.org/Hosts/Linux#Building_QEMU_for_Linux 6 | # - https://wiki.qemu.org/Documentation/Platforms/RISCV 7 | 8 | FROM ubuntu:20.04 as build_qemu 9 | 10 | ARG QEMU_VERSION=7.0.0 11 | 12 | RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ 13 | sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ 14 | apt-get update && \ 15 | DEBIAN_FRONTEND=noninteractive apt-get install -y wget build-essential libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build 16 | 17 | RUN wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz && \ 18 | tar xf qemu-${QEMU_VERSION}.tar.xz && \ 19 | cd qemu-${QEMU_VERSION} && \ 20 | ./configure --target-list=riscv64-softmmu,riscv64-linux-user && \ 21 | make -j$(nproc) && \ 22 | make install 23 | 24 | # Stage 2 Set Lab Environment 25 | FROM ubuntu:20.04 as build 26 | 27 | WORKDIR /tmp 28 | 29 | # 2.0. Install general tools 30 | RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ 31 | apt-get update && \ 32 | DEBIAN_FRONTEND=noninteractive apt-get install -y jq curl git python3 wget build-essential \ 33 | # qemu dependency 34 | libglib2.0-0 libfdt1 libpixman-1-0 zlib1g \ 35 | # gdb 36 | gdb-multiarch 37 | 38 | # 2.1. Copy qemu 39 | COPY --from=build_qemu /usr/local/bin/* /usr/local/bin 40 | 41 | # 2.2. Install Rust 42 | # - https://www.rust-lang.org/tools/install 43 | ENV RUSTUP_HOME=/usr/local/rustup \ 44 | CARGO_HOME=/usr/local/cargo \ 45 | PATH=/usr/local/cargo/bin:$PATH \ 46 | RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static \ 47 | RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup 48 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ 49 | sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly 50 | 51 | # 2.3. Build env for labs 52 | # See os/Makefile `env:` for example. 53 | # This avoids having to wait for these steps each time using a new container. 54 | COPY rust-toolchain.toml rust-toolchain.toml 55 | RUN rustup target add riscv64gc-unknown-none-elf && \ 56 | cargo install toml-cli cargo-binutils && \ 57 | RUST_VERSION=$(toml get -r rust-toolchain.toml toolchain.channel) && \ 58 | Components=$(toml get -r rust-toolchain.toml toolchain.components | jq -r 'join(" ")') && \ 59 | rustup install $RUST_VERSION && \ 60 | rustup component add --toolchain $RUST_VERSION $Components 61 | 62 | # 2.4. Set GDB 63 | RUN ln -s /usr/bin/gdb-multiarch /usr/bin/riscv64-unknown-elf-gdb 64 | 65 | # Stage 3 Sanity checking 66 | FROM build as test 67 | RUN qemu-system-riscv64 --version && \ 68 | qemu-riscv64 --version && \ 69 | rustup --version && \ 70 | cargo --version && \ 71 | rustc --version && \ 72 | riscv64-unknown-elf-gdb --version -------------------------------------------------------------------------------- /os/src/syscall/thread.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | mm::kernel_token, 3 | task::{TaskControlBlock, add_task, current_task}, 4 | trap::{TrapContext, trap_handler}, 5 | }; 6 | use alloc::sync::Arc; 7 | 8 | pub fn sys_thread_create(entry: usize, arg: usize) -> isize { 9 | let task = current_task().unwrap(); 10 | let process = task.process.upgrade().unwrap(); 11 | // create a new thread 12 | let new_task = Arc::new(TaskControlBlock::new( 13 | Arc::clone(&process), 14 | task.inner_exclusive_access() 15 | .res 16 | .as_ref() 17 | .unwrap() 18 | .ustack_base, 19 | true, 20 | )); 21 | // add new task to scheduler 22 | add_task(Arc::clone(&new_task)); 23 | let new_task_inner = new_task.inner_exclusive_access(); 24 | let new_task_res = new_task_inner.res.as_ref().unwrap(); 25 | let new_task_tid = new_task_res.tid; 26 | let mut process_inner = process.inner_exclusive_access(); 27 | // add new thread to current process 28 | let tasks = &mut process_inner.tasks; 29 | while tasks.len() < new_task_tid + 1 { 30 | tasks.push(None); 31 | } 32 | tasks[new_task_tid] = Some(Arc::clone(&new_task)); 33 | let new_task_trap_cx = new_task_inner.get_trap_cx(); 34 | *new_task_trap_cx = TrapContext::app_init_context( 35 | entry, 36 | new_task_res.ustack_top(), 37 | kernel_token(), 38 | new_task.kstack.get_top(), 39 | trap_handler as usize, 40 | ); 41 | (*new_task_trap_cx).x[10] = arg; 42 | new_task_tid as isize 43 | } 44 | 45 | pub fn sys_gettid() -> isize { 46 | current_task() 47 | .unwrap() 48 | .inner_exclusive_access() 49 | .res 50 | .as_ref() 51 | .unwrap() 52 | .tid as isize 53 | } 54 | 55 | /// thread does not exist, return -1 56 | /// thread has not exited yet, return -2 57 | /// otherwise, return thread's exit code 58 | pub fn sys_waittid(tid: usize) -> i32 { 59 | let task = current_task().unwrap(); 60 | let process = task.process.upgrade().unwrap(); 61 | let task_inner = task.inner_exclusive_access(); 62 | let mut process_inner = process.inner_exclusive_access(); 63 | // a thread cannot wait for itself 64 | if task_inner.res.as_ref().unwrap().tid == tid { 65 | return -1; 66 | } 67 | let mut exit_code: Option = None; 68 | let waited_task = process_inner.tasks[tid].as_ref(); 69 | if let Some(waited_task) = waited_task { 70 | if let Some(waited_exit_code) = waited_task.inner_exclusive_access().exit_code { 71 | exit_code = Some(waited_exit_code); 72 | } 73 | } else { 74 | // waited thread does not exist 75 | return -1; 76 | } 77 | if let Some(exit_code) = exit_code { 78 | // dealloc the exited thread 79 | process_inner.tasks[tid] = None; 80 | exit_code 81 | } else { 82 | // waited thread has not exited 83 | -2 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /user/src/bin/adder_peterson_spin.rs: -------------------------------------------------------------------------------- 1 | //! It only works on a single CPU! 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | extern crate alloc; 9 | 10 | use alloc::vec::Vec; 11 | use core::ptr::addr_of_mut; 12 | use core::sync::atomic::{Ordering, compiler_fence}; 13 | use user_lib::{exit, get_time, thread_create, waittid}; 14 | 15 | static mut A: usize = 0; 16 | static mut FLAG: [bool; 2] = [false; 2]; 17 | static mut TURN: usize = 0; 18 | const PER_THREAD_DEFAULT: usize = 2000; 19 | const THREAD_COUNT_DEFAULT: usize = 2; 20 | static mut PER_THREAD: usize = 0; 21 | 22 | fn critical_section(t: &mut usize) { 23 | let a = addr_of_mut!(A); 24 | let cur = unsafe { a.read_volatile() }; 25 | for _ in 0..500 { 26 | *t = (*t) * (*t) % 10007; 27 | } 28 | unsafe { 29 | a.write_volatile(cur + 1); 30 | } 31 | } 32 | 33 | fn lock(id: usize) { 34 | unsafe { 35 | FLAG[id] = true; 36 | let j = 1 - id; 37 | TURN = j; 38 | // Tell the compiler not to reorder memory operations 39 | // across this fence. 40 | compiler_fence(Ordering::SeqCst); 41 | // Why do we need to use volatile_read here? 42 | // Otherwise the compiler will assume that they will never 43 | // be changed on this thread. Thus, they will be accessed 44 | // only once! 45 | while vload!(FLAG[j]) && vload!(TURN) == j {} 46 | } 47 | } 48 | 49 | fn unlock(id: usize) { 50 | unsafe { 51 | FLAG[id] = false; 52 | } 53 | } 54 | 55 | fn f(id: usize) -> ! { 56 | let mut t = 2usize; 57 | for _iter in 0..unsafe { PER_THREAD } { 58 | lock(id); 59 | critical_section(&mut t); 60 | unlock(id); 61 | } 62 | exit(t as i32) 63 | } 64 | 65 | #[unsafe(no_mangle)] 66 | pub fn main(argc: usize, argv: &[&str]) -> i32 { 67 | let mut thread_count = THREAD_COUNT_DEFAULT; 68 | let mut per_thread = PER_THREAD_DEFAULT; 69 | if argc >= 2 { 70 | thread_count = argv[1].parse().unwrap(); 71 | if argc >= 3 { 72 | per_thread = argv[2].parse().unwrap(); 73 | } 74 | } 75 | unsafe { 76 | PER_THREAD = per_thread; 77 | } 78 | 79 | // uncomment this if you want to check the assembly 80 | // println!( 81 | // "addr: lock={:#x}, unlock={:#x}", 82 | // lock as usize, 83 | // unlock as usize 84 | // ); 85 | let start = get_time(); 86 | let mut v = Vec::new(); 87 | assert_eq!( 88 | thread_count, 2, 89 | "Peterson works when there are only 2 threads." 90 | ); 91 | for id in 0..thread_count { 92 | v.push(thread_create(f as usize, id) as usize); 93 | } 94 | let mut time_cost = Vec::new(); 95 | for tid in v.iter() { 96 | time_cost.push(waittid(*tid)); 97 | } 98 | println!("time cost is {}ms", get_time() - start); 99 | assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); 100 | 0 101 | } 102 | -------------------------------------------------------------------------------- /os/src/drivers/block/virtio_blk.rs: -------------------------------------------------------------------------------- 1 | use super::BlockDevice; 2 | use crate::DEV_NON_BLOCKING_ACCESS; 3 | use crate::drivers::bus::virtio::VirtioHal; 4 | use crate::sync::{Condvar, UPIntrFreeCell}; 5 | use crate::task::schedule; 6 | use alloc::collections::BTreeMap; 7 | use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader}; 8 | 9 | #[allow(unused)] 10 | const VIRTIO0: usize = 0x10008000; 11 | 12 | pub struct VirtIOBlock { 13 | virtio_blk: UPIntrFreeCell>, 14 | condvars: BTreeMap, 15 | } 16 | 17 | impl BlockDevice for VirtIOBlock { 18 | fn read_block(&self, block_id: usize, buf: &mut [u8]) { 19 | let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); 20 | if nb { 21 | let mut resp = BlkResp::default(); 22 | let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { 23 | let token = unsafe { blk.read_block_nb(block_id, buf, &mut resp).unwrap() }; 24 | self.condvars.get(&token).unwrap().wait_no_sched() 25 | }); 26 | schedule(task_cx_ptr); 27 | assert_eq!( 28 | resp.status(), 29 | RespStatus::Ok, 30 | "Error when reading VirtIOBlk" 31 | ); 32 | } else { 33 | self.virtio_blk 34 | .exclusive_access() 35 | .read_block(block_id, buf) 36 | .expect("Error when reading VirtIOBlk"); 37 | } 38 | } 39 | fn write_block(&self, block_id: usize, buf: &[u8]) { 40 | let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); 41 | if nb { 42 | let mut resp = BlkResp::default(); 43 | let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { 44 | let token = unsafe { blk.write_block_nb(block_id, buf, &mut resp).unwrap() }; 45 | self.condvars.get(&token).unwrap().wait_no_sched() 46 | }); 47 | schedule(task_cx_ptr); 48 | assert_eq!( 49 | resp.status(), 50 | RespStatus::Ok, 51 | "Error when writing VirtIOBlk" 52 | ); 53 | } else { 54 | self.virtio_blk 55 | .exclusive_access() 56 | .write_block(block_id, buf) 57 | .expect("Error when writing VirtIOBlk"); 58 | } 59 | } 60 | fn handle_irq(&self) { 61 | self.virtio_blk.exclusive_session(|blk| { 62 | while let Ok(token) = blk.pop_used() { 63 | self.condvars.get(&token).unwrap().signal(); 64 | } 65 | }); 66 | } 67 | } 68 | 69 | impl VirtIOBlock { 70 | pub fn new() -> Self { 71 | let virtio_blk = unsafe { 72 | UPIntrFreeCell::new( 73 | VirtIOBlk::::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), 74 | ) 75 | }; 76 | let mut condvars = BTreeMap::new(); 77 | let channels = virtio_blk.exclusive_access().virt_queue_size(); 78 | for i in 0..channels { 79 | let condvar = Condvar::new(); 80 | condvars.insert(i, condvar); 81 | } 82 | Self { 83 | virtio_blk, 84 | condvars, 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /user/src/io.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use embedded_graphics::pixelcolor::Rgb888; 3 | use embedded_graphics::prelude::{RgbColor, Size}; 4 | use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; 5 | use virtio_input_decoder::Decoder; 6 | pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse}; 7 | 8 | pub const VIRTGPU_XRES: u32 = 1280; 9 | pub const VIRTGPU_YRES: u32 = 800; 10 | pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize; 11 | 12 | pub fn framebuffer() -> isize { 13 | sys_framebuffer() 14 | } 15 | pub fn framebuffer_flush() -> isize { 16 | sys_framebuffer_flush() 17 | } 18 | 19 | pub struct Display { 20 | pub size: Size, 21 | pub fb: &'static mut [u8], 22 | } 23 | 24 | impl Display { 25 | pub fn new(size: Size) -> Self { 26 | let fb_ptr = framebuffer() as *mut u8; 27 | let fb = unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) }; 28 | Self { size, fb } 29 | } 30 | pub fn framebuffer(&mut self) -> &mut [u8] { 31 | self.fb 32 | } 33 | pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) { 34 | p(self.framebuffer()); 35 | } 36 | pub fn flush(&self) { 37 | framebuffer_flush(); 38 | } 39 | } 40 | 41 | impl OriginDimensions for Display { 42 | fn size(&self) -> Size { 43 | self.size 44 | } 45 | } 46 | 47 | impl DrawTarget for Display { 48 | type Color = Rgb888; 49 | 50 | type Error = core::convert::Infallible; 51 | 52 | fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> 53 | where 54 | I: IntoIterator>, 55 | { 56 | pixels.into_iter().for_each(|px| { 57 | let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x) as usize * 4; 58 | if idx + 2 >= self.fb.len() { 59 | return; 60 | } 61 | self.fb[idx] = px.1.b(); 62 | self.fb[idx + 1] = px.1.g(); 63 | self.fb[idx + 2] = px.1.r(); 64 | }); 65 | Ok(()) 66 | } 67 | } 68 | 69 | pub fn event_get() -> Option { 70 | let raw_value = sys_event_get(); 71 | if raw_value == 0 { 72 | None 73 | } else { 74 | Some((raw_value as u64).into()) 75 | } 76 | } 77 | 78 | pub fn key_pressed() -> bool { 79 | if sys_key_pressed() == 1 { true } else { false } 80 | } 81 | 82 | #[repr(C)] 83 | pub struct InputEvent { 84 | pub event_type: u16, 85 | pub code: u16, 86 | pub value: u32, 87 | } 88 | 89 | impl From for InputEvent { 90 | fn from(mut v: u64) -> Self { 91 | let value = v as u32; 92 | v >>= 32; 93 | let code = v as u16; 94 | v >>= 16; 95 | let event_type = v as u16; 96 | Self { 97 | event_type, 98 | code, 99 | value, 100 | } 101 | } 102 | } 103 | 104 | impl InputEvent { 105 | pub fn decode(&self) -> Option { 106 | Decoder::decode( 107 | self.event_type as usize, 108 | self.code as usize, 109 | self.value as usize, 110 | ) 111 | .ok() 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /os/Makefile: -------------------------------------------------------------------------------- 1 | # Building 2 | TARGET := riscv64gc-unknown-none-elf 3 | MODE := release 4 | KERNEL_ELF := target/$(TARGET)/$(MODE)/os 5 | KERNEL_BIN := $(KERNEL_ELF).bin 6 | DISASM_TMP := target/$(TARGET)/$(MODE)/asm 7 | FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img 8 | APPS := ../user/src/bin/* 9 | 10 | # BOARD 11 | BOARD := qemu 12 | SBI ?= rustsbi 13 | BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin 14 | 15 | # GUI 16 | GUI ?= off 17 | ifeq ($(GUI), off) 18 | GUI_OPTION := -display none 19 | endif 20 | 21 | # Building mode argument 22 | ifeq ($(MODE), release) 23 | MODE_ARG := --release 24 | endif 25 | 26 | # KERNEL ENTRY 27 | KERNEL_ENTRY_PA := 0x80200000 28 | 29 | # Binutils 30 | OBJDUMP := rust-objdump --arch-name=riscv64 31 | OBJCOPY := rust-objcopy --binary-architecture=riscv64 32 | 33 | # Disassembly 34 | DISASM ?= -x 35 | 36 | # Run usertests or usershell 37 | TEST ?= 38 | 39 | build: env $(KERNEL_BIN) fs-img 40 | 41 | env: 42 | (rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET) 43 | cargo install cargo-binutils 44 | rustup component add rust-src 45 | rustup component add llvm-tools-preview 46 | 47 | $(KERNEL_BIN): kernel 48 | @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ 49 | 50 | fs-img: $(APPS) 51 | @cd ../user && make build TEST=$(TEST) 52 | @rm -f $(FS_IMG) 53 | @cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/ 54 | 55 | $(APPS): 56 | 57 | kernel: 58 | @echo Platform: $(BOARD) 59 | @cp src/linker-$(BOARD).ld src/linker.ld 60 | @cargo build --release 61 | @rm src/linker.ld 62 | 63 | clean: 64 | @cargo clean 65 | 66 | disasm: kernel 67 | @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) | less 68 | 69 | disasm-vim: kernel 70 | @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP) 71 | @nvim $(DISASM_TMP) 72 | @rm $(DISASM_TMP) 73 | 74 | run: run-inner 75 | 76 | QEMU_ARGS := -machine virt \ 77 | -bios $(BOOTLOADER) \ 78 | -serial stdio \ 79 | $(GUI_OPTION) \ 80 | -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ 81 | -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ 82 | -device virtio-blk-device,drive=x0 \ 83 | -device virtio-gpu-device \ 84 | -device virtio-keyboard-device \ 85 | -device virtio-mouse-device \ 86 | -device virtio-net-device,netdev=net0 \ 87 | -netdev user,id=net0,hostfwd=udp::6200-:2000,hostfwd=tcp::6201-:80 88 | 89 | fdt: 90 | @qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out 91 | fdtdump virt.out 92 | 93 | QEMU_NAME := qemu-system-riscv64 94 | qemu-version-check: 95 | @sh scripts/qemu-ver-check.sh $(QEMU_NAME) 96 | 97 | run-inner: qemu-version-check build 98 | @qemu-system-riscv64 $(QEMU_ARGS) 99 | 100 | debug: qemu-version-check build 101 | @tmux new-session -d \ 102 | "qemu-system-riscv64 $(QEMU_ARGS) -s -S" && \ 103 | tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ 104 | tmux -2 attach-session -d 105 | 106 | gdbserver: qemu-version-check build 107 | @qemu-system-riscv64 $(QEMU_ARGS) -s -S 108 | 109 | gdbclient: 110 | @riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234' 111 | 112 | .PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient fdt qemu-version-check 113 | -------------------------------------------------------------------------------- /os/src/net/tcp.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec; 2 | use lose_net_stack::IPv4; 3 | use lose_net_stack::MacAddress; 4 | use lose_net_stack::TcpFlags; 5 | use lose_net_stack::packets::tcp::TCPPacket; 6 | 7 | use crate::{drivers::NET_DEVICE, fs::File}; 8 | 9 | use super::socket::get_s_a_by_index; 10 | use super::{ 11 | LOSE_NET_STACK, net_interrupt_handler, 12 | socket::{add_socket, pop_data, remove_socket}, 13 | }; 14 | 15 | // add tcp packet info to this structure 16 | pub struct TCP { 17 | pub target: IPv4, 18 | pub sport: u16, 19 | pub dport: u16, 20 | #[allow(unused)] 21 | pub seq: u32, 22 | #[allow(unused)] 23 | pub ack: u32, 24 | pub socket_index: usize, 25 | } 26 | 27 | impl TCP { 28 | pub fn new(target: IPv4, sport: u16, dport: u16, seq: u32, ack: u32) -> Self { 29 | let index = add_socket(target, sport, dport).expect("can't add socket"); 30 | 31 | Self { 32 | target, 33 | sport, 34 | dport, 35 | seq, 36 | ack, 37 | socket_index: index, 38 | } 39 | } 40 | } 41 | 42 | impl File for TCP { 43 | fn readable(&self) -> bool { 44 | true 45 | } 46 | 47 | fn writable(&self) -> bool { 48 | true 49 | } 50 | 51 | fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { 52 | loop { 53 | if let Some(data) = pop_data(self.socket_index) { 54 | let data_len = data.len(); 55 | let mut left = 0; 56 | for i in 0..buf.buffers.len() { 57 | let buffer_i_len = buf.buffers[i].len().min(data_len - left); 58 | 59 | buf.buffers[i][..buffer_i_len] 60 | .copy_from_slice(&data[left..(left + buffer_i_len)]); 61 | 62 | left += buffer_i_len; 63 | if left == data_len { 64 | break; 65 | } 66 | } 67 | return left; 68 | } else { 69 | net_interrupt_handler(); 70 | } 71 | } 72 | } 73 | 74 | fn write(&self, buf: crate::mm::UserBuffer) -> usize { 75 | let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); 76 | 77 | let mut data = vec![0u8; buf.len()]; 78 | 79 | let mut left = 0; 80 | for i in 0..buf.buffers.len() { 81 | data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); 82 | left += buf.buffers[i].len(); 83 | } 84 | 85 | let len = data.len(); 86 | 87 | // get sock and sequence 88 | let (ack, seq) = get_s_a_by_index(self.socket_index).map_or((0, 0), |x| x); 89 | 90 | let tcp_packet = TCPPacket { 91 | source_ip: lose_net_stack.ip, 92 | source_mac: lose_net_stack.mac, 93 | source_port: self.sport, 94 | dest_ip: self.target, 95 | dest_mac: MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 96 | dest_port: self.dport, 97 | data_len: len, 98 | seq, 99 | ack, 100 | flags: TcpFlags::A, 101 | win: 65535, 102 | urg: 0, 103 | data: data.as_ref(), 104 | }; 105 | NET_DEVICE.transmit(&tcp_packet.build_data()); 106 | len 107 | } 108 | } 109 | 110 | impl Drop for TCP { 111 | fn drop(&mut self) { 112 | remove_socket(self.socket_index) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /os/src/syscall/fs.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::{OpenFlags, make_pipe, open_file}; 2 | use crate::mm::{UserBuffer, translated_byte_buffer, translated_refmut, translated_str}; 3 | use crate::task::{current_process, current_user_token}; 4 | use alloc::sync::Arc; 5 | 6 | pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { 7 | let token = current_user_token(); 8 | let process = current_process(); 9 | let inner = process.inner_exclusive_access(); 10 | if fd >= inner.fd_table.len() { 11 | return -1; 12 | } 13 | if let Some(file) = &inner.fd_table[fd] { 14 | if !file.writable() { 15 | return -1; 16 | } 17 | let file = file.clone(); 18 | // release current task TCB manually to avoid multi-borrow 19 | drop(inner); 20 | file.write(UserBuffer::new(translated_byte_buffer(token, buf, len))) as isize 21 | } else { 22 | -1 23 | } 24 | } 25 | 26 | pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { 27 | let token = current_user_token(); 28 | let process = current_process(); 29 | let inner = process.inner_exclusive_access(); 30 | if fd >= inner.fd_table.len() { 31 | return -1; 32 | } 33 | if let Some(file) = &inner.fd_table[fd] { 34 | let file = file.clone(); 35 | if !file.readable() { 36 | return -1; 37 | } 38 | // release current task TCB manually to avoid multi-borrow 39 | drop(inner); 40 | file.read(UserBuffer::new(translated_byte_buffer(token, buf, len))) as isize 41 | } else { 42 | -1 43 | } 44 | } 45 | 46 | pub fn sys_open(path: *const u8, flags: u32) -> isize { 47 | let process = current_process(); 48 | let token = current_user_token(); 49 | let path = translated_str(token, path); 50 | if let Some(inode) = open_file(path.as_str(), OpenFlags::from_bits(flags).unwrap()) { 51 | let mut inner = process.inner_exclusive_access(); 52 | let fd = inner.alloc_fd(); 53 | inner.fd_table[fd] = Some(inode); 54 | fd as isize 55 | } else { 56 | -1 57 | } 58 | } 59 | 60 | pub fn sys_close(fd: usize) -> isize { 61 | let process = current_process(); 62 | let mut inner = process.inner_exclusive_access(); 63 | if fd >= inner.fd_table.len() { 64 | return -1; 65 | } 66 | if inner.fd_table[fd].is_none() { 67 | return -1; 68 | } 69 | inner.fd_table[fd].take(); 70 | 0 71 | } 72 | 73 | pub fn sys_pipe(pipe: *mut usize) -> isize { 74 | let process = current_process(); 75 | let token = current_user_token(); 76 | let mut inner = process.inner_exclusive_access(); 77 | let (pipe_read, pipe_write) = make_pipe(); 78 | let read_fd = inner.alloc_fd(); 79 | inner.fd_table[read_fd] = Some(pipe_read); 80 | let write_fd = inner.alloc_fd(); 81 | inner.fd_table[write_fd] = Some(pipe_write); 82 | *translated_refmut(token, pipe) = read_fd; 83 | *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd; 84 | 0 85 | } 86 | 87 | pub fn sys_dup(fd: usize) -> isize { 88 | let process = current_process(); 89 | let mut inner = process.inner_exclusive_access(); 90 | if fd >= inner.fd_table.len() { 91 | return -1; 92 | } 93 | if inner.fd_table[fd].is_none() { 94 | return -1; 95 | } 96 | let new_fd = inner.alloc_fd(); 97 | inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); 98 | new_fd as isize 99 | } 100 | -------------------------------------------------------------------------------- /os/src/task/processor.rs: -------------------------------------------------------------------------------- 1 | use super::__switch; 2 | use super::{ProcessControlBlock, TaskContext, TaskControlBlock}; 3 | use super::{TaskStatus, fetch_task}; 4 | use crate::sync::UPIntrFreeCell; 5 | use crate::trap::TrapContext; 6 | use alloc::sync::Arc; 7 | use core::arch::asm; 8 | use lazy_static::*; 9 | 10 | pub struct Processor { 11 | current: Option>, 12 | idle_task_cx: TaskContext, 13 | } 14 | 15 | impl Processor { 16 | pub fn new() -> Self { 17 | Self { 18 | current: None, 19 | idle_task_cx: TaskContext::zero_init(), 20 | } 21 | } 22 | fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext { 23 | &mut self.idle_task_cx as *mut _ 24 | } 25 | pub fn take_current(&mut self) -> Option> { 26 | self.current.take() 27 | } 28 | pub fn current(&self) -> Option> { 29 | self.current.as_ref().map(Arc::clone) 30 | } 31 | } 32 | 33 | lazy_static! { 34 | pub static ref PROCESSOR: UPIntrFreeCell = 35 | unsafe { UPIntrFreeCell::new(Processor::new()) }; 36 | } 37 | 38 | pub fn run_tasks() { 39 | loop { 40 | let mut processor = PROCESSOR.exclusive_access(); 41 | if let Some(task) = fetch_task() { 42 | let idle_task_cx_ptr = processor.get_idle_task_cx_ptr(); 43 | // access coming task TCB exclusively 44 | let next_task_cx_ptr = task.inner.exclusive_session(|task_inner| { 45 | task_inner.task_status = TaskStatus::Running; 46 | &task_inner.task_cx as *const TaskContext 47 | }); 48 | processor.current = Some(task); 49 | // release processor manually 50 | drop(processor); 51 | unsafe { 52 | __switch(idle_task_cx_ptr, next_task_cx_ptr); 53 | } 54 | } else { 55 | println!("no tasks available in run_tasks"); 56 | } 57 | } 58 | } 59 | 60 | pub fn take_current_task() -> Option> { 61 | PROCESSOR.exclusive_access().take_current() 62 | } 63 | 64 | pub fn current_task() -> Option> { 65 | PROCESSOR.exclusive_access().current() 66 | } 67 | 68 | pub fn current_process() -> Arc { 69 | current_task().unwrap().process.upgrade().unwrap() 70 | } 71 | 72 | pub fn current_user_token() -> usize { 73 | let task = current_task().unwrap(); 74 | task.get_user_token() 75 | } 76 | 77 | pub fn current_trap_cx() -> &'static mut TrapContext { 78 | current_task() 79 | .unwrap() 80 | .inner_exclusive_access() 81 | .get_trap_cx() 82 | } 83 | 84 | pub fn current_trap_cx_user_va() -> usize { 85 | current_task() 86 | .unwrap() 87 | .inner_exclusive_access() 88 | .res 89 | .as_ref() 90 | .unwrap() 91 | .trap_cx_user_va() 92 | } 93 | 94 | pub fn current_kstack_top() -> usize { 95 | if let Some(task) = current_task() { 96 | task.kstack.get_top() 97 | } else { 98 | let mut boot_stack_top; 99 | unsafe { asm!("la {},boot_stack_top",out(reg) boot_stack_top) }; 100 | boot_stack_top 101 | } 102 | // current_task().unwrap().kstack.get_top() 103 | } 104 | 105 | pub fn schedule(switched_task_cx_ptr: *mut TaskContext) { 106 | let idle_task_cx_ptr = 107 | PROCESSOR.exclusive_session(|processor| processor.get_idle_task_cx_ptr()); 108 | unsafe { 109 | __switch(switched_task_cx_ptr, idle_task_cx_ptr); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /user/src/bin/phil_din_mutex.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(clippy::println_empty_string)] 4 | 5 | #[macro_use] 6 | extern crate user_lib; 7 | extern crate alloc; 8 | 9 | use alloc::vec::Vec; 10 | use user_lib::{exit, get_time, sleep}; 11 | use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; 12 | use user_lib::{thread_create, waittid}; 13 | 14 | const N: usize = 5; 15 | const ROUND: usize = 4; 16 | // A round: think -> wait for forks -> eat 17 | const GRAPH_SCALE: usize = 100; 18 | 19 | fn get_time_u() -> usize { 20 | get_time() as usize 21 | } 22 | 23 | // Time unit: ms 24 | const ARR: [[usize; ROUND * 2]; N] = [ 25 | [700, 800, 1000, 400, 500, 600, 200, 400], 26 | [300, 600, 200, 700, 1000, 100, 300, 600], 27 | [500, 200, 900, 200, 400, 600, 1200, 400], 28 | [500, 1000, 600, 500, 800, 600, 200, 900], 29 | [600, 100, 600, 600, 200, 500, 600, 200], 30 | ]; 31 | static mut THINK: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; 32 | static mut EAT: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; 33 | 34 | fn philosopher_dining_problem(id: *const usize) { 35 | let id = unsafe { *id }; 36 | let left = id; 37 | let right = if id == N - 1 { 0 } else { id + 1 }; 38 | let min = if left < right { left } else { right }; 39 | let max = left + right - min; 40 | for round in 0..ROUND { 41 | // thinking 42 | unsafe { 43 | THINK[id][2 * round] = get_time_u(); 44 | } 45 | sleep(ARR[id][2 * round]); 46 | unsafe { 47 | THINK[id][2 * round + 1] = get_time_u(); 48 | } 49 | // wait for forks 50 | mutex_lock(min); 51 | mutex_lock(max); 52 | // eating 53 | unsafe { 54 | EAT[id][2 * round] = get_time_u(); 55 | } 56 | sleep(ARR[id][2 * round + 1]); 57 | unsafe { 58 | EAT[id][2 * round + 1] = get_time_u(); 59 | } 60 | mutex_unlock(max); 61 | mutex_unlock(min); 62 | } 63 | exit(0) 64 | } 65 | 66 | #[unsafe(no_mangle)] 67 | pub fn main() -> i32 { 68 | let mut v = Vec::new(); 69 | let ids: Vec<_> = (0..N).collect(); 70 | let start = get_time_u(); 71 | for i in 0..N { 72 | assert_eq!(mutex_blocking_create(), i as isize); 73 | v.push(thread_create( 74 | philosopher_dining_problem as usize, 75 | &ids.as_slice()[i] as *const _ as usize, 76 | )); 77 | } 78 | for tid in v.iter() { 79 | waittid(*tid as usize); 80 | } 81 | let time_cost = get_time_u() - start; 82 | println!("time cost = {}", time_cost); 83 | println!("'-' -> THINKING; 'x' -> EATING; ' ' -> WAITING "); 84 | for id in (0..N).into_iter().chain(0..=0) { 85 | print!("#{}:", id); 86 | for j in 0..time_cost / GRAPH_SCALE { 87 | let current_time = j * GRAPH_SCALE + start; 88 | if (0..ROUND).any(|round| unsafe { 89 | let start_thinking = THINK[id][2 * round]; 90 | let end_thinking = THINK[id][2 * round + 1]; 91 | start_thinking <= current_time && current_time <= end_thinking 92 | }) { 93 | print!("-"); 94 | } else if (0..ROUND).any(|round| unsafe { 95 | let start_eating = EAT[id][2 * round]; 96 | let end_eating = EAT[id][2 * round + 1]; 97 | start_eating <= current_time && current_time <= end_eating 98 | }) { 99 | print!("x"); 100 | } else { 101 | print!(" "); 102 | }; 103 | } 104 | println!(""); 105 | } 106 | 0 107 | } 108 | -------------------------------------------------------------------------------- /user/src/bin/stackless_coroutine.rs: -------------------------------------------------------------------------------- 1 | // https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/ 2 | // https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86 3 | #![no_std] 4 | #![no_main] 5 | 6 | use core::future::Future; 7 | use core::pin::Pin; 8 | use core::task::{Context, Poll}; 9 | use core::task::{RawWaker, RawWakerVTable, Waker}; 10 | 11 | extern crate alloc; 12 | use alloc::collections::VecDeque; 13 | 14 | use alloc::boxed::Box; 15 | 16 | #[macro_use] 17 | extern crate user_lib; 18 | 19 | enum State { 20 | Halted, 21 | Running, 22 | } 23 | 24 | struct Task { 25 | state: State, 26 | } 27 | 28 | impl Task { 29 | fn waiter<'a>(&'a mut self) -> Waiter<'a> { 30 | Waiter { task: self } 31 | } 32 | } 33 | 34 | struct Waiter<'a> { 35 | task: &'a mut Task, 36 | } 37 | 38 | impl<'a> Future for Waiter<'a> { 39 | type Output = (); 40 | 41 | fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll { 42 | match self.task.state { 43 | State::Halted => { 44 | self.task.state = State::Running; 45 | Poll::Ready(()) 46 | } 47 | State::Running => { 48 | self.task.state = State::Halted; 49 | Poll::Pending 50 | } 51 | } 52 | } 53 | } 54 | 55 | struct Executor { 56 | tasks: VecDeque>>>, 57 | } 58 | 59 | impl Executor { 60 | fn new() -> Self { 61 | Executor { 62 | tasks: VecDeque::new(), 63 | } 64 | } 65 | 66 | fn push(&mut self, closure: C) 67 | where 68 | F: Future + 'static, 69 | C: FnOnce(Task) -> F, 70 | { 71 | let task = Task { 72 | state: State::Running, 73 | }; 74 | self.tasks.push_back(Box::pin(closure(task))); 75 | } 76 | 77 | fn run(&mut self) { 78 | let waker = create_waker(); 79 | let mut context = Context::from_waker(&waker); 80 | 81 | while let Some(mut task) = self.tasks.pop_front() { 82 | match task.as_mut().poll(&mut context) { 83 | Poll::Pending => { 84 | self.tasks.push_back(task); 85 | } 86 | Poll::Ready(()) => {} 87 | } 88 | } 89 | } 90 | } 91 | 92 | pub fn create_waker() -> Waker { 93 | // Safety: The waker points to a vtable with functions that do nothing. Doing 94 | // nothing is memory-safe. 95 | unsafe { Waker::from_raw(RAW_WAKER) } 96 | } 97 | 98 | const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE); 99 | const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); 100 | 101 | unsafe fn clone(_: *const ()) -> RawWaker { 102 | RAW_WAKER 103 | } 104 | unsafe fn wake(_: *const ()) {} 105 | unsafe fn wake_by_ref(_: *const ()) {} 106 | unsafe fn drop(_: *const ()) {} 107 | 108 | #[unsafe(no_mangle)] 109 | pub fn main() -> i32 { 110 | println!("stackless coroutine Begin.."); 111 | let mut exec = Executor::new(); 112 | println!(" Create futures"); 113 | for instance in 1..=3 { 114 | exec.push(move |mut task| async move { 115 | println!(" Task {}: begin state", instance); 116 | task.waiter().await; 117 | println!(" Task {}: next state", instance); 118 | task.waiter().await; 119 | println!(" Task {}: end state", instance); 120 | }); 121 | } 122 | 123 | println!(" Running"); 124 | exec.run(); 125 | println!(" Done"); 126 | println!("stackless coroutine PASSED"); 127 | 128 | 0 129 | } 130 | -------------------------------------------------------------------------------- /os/src/net/socket.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::VecDeque; 2 | use alloc::vec::Vec; 3 | use lazy_static::lazy_static; 4 | use lose_net_stack::IPv4; 5 | 6 | use crate::sync::UPIntrFreeCell; 7 | 8 | // TODO: specify the protocol, TCP or UDP 9 | pub struct Socket { 10 | pub raddr: IPv4, // remote address 11 | pub lport: u16, // local port 12 | pub rport: u16, // rempote port 13 | pub buffers: VecDeque>, // datas 14 | pub seq: u32, 15 | pub ack: u32, 16 | } 17 | 18 | lazy_static! { 19 | static ref SOCKET_TABLE: UPIntrFreeCell>> = 20 | unsafe { UPIntrFreeCell::new(Vec::new()) }; 21 | } 22 | 23 | /// get the seq and ack by socket index 24 | pub fn get_s_a_by_index(index: usize) -> Option<(u32, u32)> { 25 | let socket_table = SOCKET_TABLE.exclusive_access(); 26 | 27 | assert!(index < socket_table.len()); 28 | 29 | socket_table.get(index).map_or(None, |x| match x { 30 | Some(x) => Some((x.seq, x.ack)), 31 | None => None, 32 | }) 33 | } 34 | 35 | pub fn set_s_a_by_index(index: usize, seq: u32, ack: u32) { 36 | let mut socket_table = SOCKET_TABLE.exclusive_access(); 37 | 38 | assert!(socket_table.len() > index); 39 | assert!(socket_table[index].is_some()); 40 | 41 | let sock = socket_table[index].as_mut().unwrap(); 42 | 43 | sock.ack = ack; 44 | sock.seq = seq; 45 | } 46 | 47 | pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { 48 | let socket_table = SOCKET_TABLE.exclusive_access(); 49 | for i in 0..socket_table.len() { 50 | let sock = &socket_table[i]; 51 | if sock.is_none() { 52 | continue; 53 | } 54 | 55 | let sock = sock.as_ref().unwrap(); 56 | if sock.raddr == raddr && sock.lport == lport && sock.rport == rport { 57 | return Some(i); 58 | } 59 | } 60 | None 61 | } 62 | 63 | pub fn add_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { 64 | if get_socket(raddr, lport, rport).is_some() { 65 | return None; 66 | } 67 | 68 | let mut socket_table = SOCKET_TABLE.exclusive_access(); 69 | let mut index = usize::MAX; 70 | for i in 0..socket_table.len() { 71 | if socket_table[i].is_none() { 72 | index = i; 73 | break; 74 | } 75 | } 76 | 77 | let socket = Socket { 78 | raddr, 79 | lport, 80 | rport, 81 | buffers: VecDeque::new(), 82 | seq: 0, 83 | ack: 0, 84 | }; 85 | 86 | if index == usize::MAX { 87 | socket_table.push(Some(socket)); 88 | Some(socket_table.len() - 1) 89 | } else { 90 | socket_table[index] = Some(socket); 91 | Some(index) 92 | } 93 | } 94 | 95 | pub fn remove_socket(index: usize) { 96 | let mut socket_table = SOCKET_TABLE.exclusive_access(); 97 | 98 | assert!(socket_table.len() > index); 99 | 100 | socket_table[index] = None; 101 | } 102 | 103 | pub fn push_data(index: usize, data: Vec) { 104 | let mut socket_table = SOCKET_TABLE.exclusive_access(); 105 | 106 | assert!(socket_table.len() > index); 107 | assert!(socket_table[index].is_some()); 108 | 109 | socket_table[index] 110 | .as_mut() 111 | .unwrap() 112 | .buffers 113 | .push_back(data); 114 | } 115 | 116 | pub fn pop_data(index: usize) -> Option> { 117 | let mut socket_table = SOCKET_TABLE.exclusive_access(); 118 | 119 | assert!(socket_table.len() > index); 120 | assert!(socket_table[index].is_some()); 121 | 122 | socket_table[index].as_mut().unwrap().buffers.pop_front() 123 | } 124 | -------------------------------------------------------------------------------- /os/src/syscall/mod.rs: -------------------------------------------------------------------------------- 1 | const SYSCALL_DUP: usize = 24; 2 | const SYSCALL_CONNECT: usize = 29; 3 | const SYSCALL_LISTEN: usize = 30; 4 | const SYSCALL_ACCEPT: usize = 31; 5 | const SYSCALL_OPEN: usize = 56; 6 | const SYSCALL_CLOSE: usize = 57; 7 | const SYSCALL_PIPE: usize = 59; 8 | const SYSCALL_READ: usize = 63; 9 | const SYSCALL_WRITE: usize = 64; 10 | const SYSCALL_EXIT: usize = 93; 11 | const SYSCALL_SLEEP: usize = 101; 12 | const SYSCALL_YIELD: usize = 124; 13 | const SYSCALL_KILL: usize = 129; 14 | const SYSCALL_GET_TIME: usize = 169; 15 | const SYSCALL_GETPID: usize = 172; 16 | const SYSCALL_FORK: usize = 220; 17 | const SYSCALL_EXEC: usize = 221; 18 | const SYSCALL_WAITPID: usize = 260; 19 | const SYSCALL_THREAD_CREATE: usize = 1000; 20 | const SYSCALL_GETTID: usize = 1001; 21 | const SYSCALL_WAITTID: usize = 1002; 22 | const SYSCALL_MUTEX_CREATE: usize = 1010; 23 | const SYSCALL_MUTEX_LOCK: usize = 1011; 24 | const SYSCALL_MUTEX_UNLOCK: usize = 1012; 25 | const SYSCALL_SEMAPHORE_CREATE: usize = 1020; 26 | const SYSCALL_SEMAPHORE_UP: usize = 1021; 27 | const SYSCALL_SEMAPHORE_DOWN: usize = 1022; 28 | const SYSCALL_CONDVAR_CREATE: usize = 1030; 29 | const SYSCALL_CONDVAR_SIGNAL: usize = 1031; 30 | const SYSCALL_CONDVAR_WAIT: usize = 1032; 31 | const SYSCALL_FRAMEBUFFER: usize = 2000; 32 | const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; 33 | const SYSCALL_EVENT_GET: usize = 3000; 34 | const SYSCALL_KEY_PRESSED: usize = 3001; 35 | 36 | mod fs; 37 | mod gui; 38 | mod input; 39 | mod net; 40 | mod process; 41 | mod sync; 42 | mod thread; 43 | 44 | use fs::*; 45 | use gui::*; 46 | use input::*; 47 | use net::*; 48 | use process::*; 49 | use sync::*; 50 | use thread::*; 51 | 52 | pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { 53 | match syscall_id { 54 | SYSCALL_DUP => sys_dup(args[0]), 55 | SYSCALL_CONNECT => sys_connect(args[0] as _, args[1] as _, args[2] as _), 56 | SYSCALL_LISTEN => sys_listen(args[0] as _), 57 | SYSCALL_ACCEPT => sys_accept(args[0] as _), 58 | SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), 59 | SYSCALL_CLOSE => sys_close(args[0]), 60 | SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), 61 | SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]), 62 | SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), 63 | SYSCALL_EXIT => sys_exit(args[0] as i32), 64 | SYSCALL_SLEEP => sys_sleep(args[0]), 65 | SYSCALL_YIELD => sys_yield(), 66 | SYSCALL_KILL => sys_kill(args[0], args[1] as u32), 67 | SYSCALL_GET_TIME => sys_get_time(), 68 | SYSCALL_GETPID => sys_getpid(), 69 | SYSCALL_FORK => sys_fork(), 70 | SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), 71 | SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), 72 | SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]), 73 | SYSCALL_GETTID => sys_gettid(), 74 | SYSCALL_WAITTID => sys_waittid(args[0]) as isize, 75 | SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1), 76 | SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]), 77 | SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]), 78 | SYSCALL_SEMAPHORE_CREATE => sys_semaphore_create(args[0]), 79 | SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]), 80 | SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]), 81 | SYSCALL_CONDVAR_CREATE => sys_condvar_create(), 82 | SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]), 83 | SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]), 84 | SYSCALL_FRAMEBUFFER => sys_framebuffer(), 85 | SYSCALL_FRAMEBUFFER_FLUSH => sys_framebuffer_flush(), 86 | SYSCALL_EVENT_GET => sys_event_get(), 87 | SYSCALL_KEY_PRESSED => sys_key_pressed(), 88 | _ => panic!("Unsupported syscall_id: {}", syscall_id), 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /os/src/sync/up.rs: -------------------------------------------------------------------------------- 1 | use core::cell::{RefCell, RefMut, UnsafeCell}; 2 | use core::ops::{Deref, DerefMut}; 3 | use lazy_static::*; 4 | use riscv::register::sstatus; 5 | 6 | /* 7 | /// Wrap a static data structure inside it so that we are 8 | /// able to access it without any `unsafe`. 9 | /// 10 | /// We should only use it in uniprocessor. 11 | /// 12 | /// In order to get mutable reference of inner data, call 13 | /// `exclusive_access`. 14 | pub struct UPSafeCell { 15 | /// inner data 16 | inner: RefCell, 17 | } 18 | 19 | unsafe impl Sync for UPSafeCell {} 20 | 21 | impl UPSafeCell { 22 | /// User is responsible to guarantee that inner struct is only used in 23 | /// uniprocessor. 24 | pub unsafe fn new(value: T) -> Self { 25 | Self { 26 | inner: RefCell::new(value), 27 | } 28 | } 29 | /// Panic if the data has been borrowed. 30 | pub fn exclusive_access(&self) -> RefMut<'_, T> { 31 | self.inner.borrow_mut() 32 | } 33 | } 34 | */ 35 | 36 | pub struct UPSafeCellRaw { 37 | inner: UnsafeCell, 38 | } 39 | 40 | unsafe impl Sync for UPSafeCellRaw {} 41 | 42 | impl UPSafeCellRaw { 43 | pub unsafe fn new(value: T) -> Self { 44 | Self { 45 | inner: UnsafeCell::new(value), 46 | } 47 | } 48 | pub fn get_mut(&self) -> &mut T { 49 | unsafe { &mut (*self.inner.get()) } 50 | } 51 | } 52 | 53 | pub struct IntrMaskingInfo { 54 | nested_level: usize, 55 | sie_before_masking: bool, 56 | } 57 | 58 | lazy_static! { 59 | static ref INTR_MASKING_INFO: UPSafeCellRaw = 60 | unsafe { UPSafeCellRaw::new(IntrMaskingInfo::new()) }; 61 | } 62 | 63 | impl IntrMaskingInfo { 64 | pub fn new() -> Self { 65 | Self { 66 | nested_level: 0, 67 | sie_before_masking: false, 68 | } 69 | } 70 | 71 | pub fn enter(&mut self) { 72 | let sie = sstatus::read().sie(); 73 | unsafe { 74 | sstatus::clear_sie(); 75 | } 76 | if self.nested_level == 0 { 77 | self.sie_before_masking = sie; 78 | } 79 | self.nested_level += 1; 80 | } 81 | 82 | pub fn exit(&mut self) { 83 | self.nested_level -= 1; 84 | if self.nested_level == 0 && self.sie_before_masking { 85 | unsafe { 86 | sstatus::set_sie(); 87 | } 88 | } 89 | } 90 | } 91 | 92 | pub struct UPIntrFreeCell { 93 | /// inner data 94 | inner: RefCell, 95 | } 96 | 97 | unsafe impl Sync for UPIntrFreeCell {} 98 | 99 | pub struct UPIntrRefMut<'a, T>(Option>); 100 | 101 | impl UPIntrFreeCell { 102 | pub unsafe fn new(value: T) -> Self { 103 | Self { 104 | inner: RefCell::new(value), 105 | } 106 | } 107 | 108 | /// Panic if the data has been borrowed. 109 | pub fn exclusive_access(&self) -> UPIntrRefMut<'_, T> { 110 | INTR_MASKING_INFO.get_mut().enter(); 111 | UPIntrRefMut(Some(self.inner.borrow_mut())) 112 | } 113 | 114 | pub fn exclusive_session(&self, f: F) -> V 115 | where 116 | F: FnOnce(&mut T) -> V, 117 | { 118 | let mut inner = self.exclusive_access(); 119 | f(inner.deref_mut()) 120 | } 121 | } 122 | 123 | impl<'a, T> Drop for UPIntrRefMut<'a, T> { 124 | fn drop(&mut self) { 125 | self.0 = None; 126 | INTR_MASKING_INFO.get_mut().exit(); 127 | } 128 | } 129 | 130 | impl<'a, T> Deref for UPIntrRefMut<'a, T> { 131 | type Target = T; 132 | fn deref(&self) -> &Self::Target { 133 | self.0.as_ref().unwrap().deref() 134 | } 135 | } 136 | impl<'a, T> DerefMut for UPIntrRefMut<'a, T> { 137 | fn deref_mut(&mut self) -> &mut Self::Target { 138 | self.0.as_mut().unwrap().deref_mut() 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /os/src/syscall/process.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::{OpenFlags, open_file}; 2 | use crate::mm::{translated_ref, translated_refmut, translated_str}; 3 | use crate::task::{ 4 | SignalFlags, current_process, current_task, current_user_token, exit_current_and_run_next, 5 | pid2process, suspend_current_and_run_next, 6 | }; 7 | use crate::timer::get_time_ms; 8 | use alloc::string::String; 9 | use alloc::sync::Arc; 10 | use alloc::vec::Vec; 11 | 12 | pub fn sys_exit(exit_code: i32) -> ! { 13 | exit_current_and_run_next(exit_code); 14 | panic!("Unreachable in sys_exit!"); 15 | } 16 | 17 | pub fn sys_yield() -> isize { 18 | suspend_current_and_run_next(); 19 | 0 20 | } 21 | 22 | pub fn sys_get_time() -> isize { 23 | get_time_ms() as isize 24 | } 25 | 26 | pub fn sys_getpid() -> isize { 27 | current_task().unwrap().process.upgrade().unwrap().getpid() as isize 28 | } 29 | 30 | pub fn sys_fork() -> isize { 31 | let current_process = current_process(); 32 | let new_process = current_process.fork(); 33 | let new_pid = new_process.getpid(); 34 | // modify trap context of new_task, because it returns immediately after switching 35 | let new_process_inner = new_process.inner_exclusive_access(); 36 | let task = new_process_inner.tasks[0].as_ref().unwrap(); 37 | let trap_cx = task.inner_exclusive_access().get_trap_cx(); 38 | // we do not have to move to next instruction since we have done it before 39 | // for child process, fork returns 0 40 | trap_cx.x[10] = 0; 41 | new_pid as isize 42 | } 43 | 44 | pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { 45 | let token = current_user_token(); 46 | let path = translated_str(token, path); 47 | let mut args_vec: Vec = Vec::new(); 48 | loop { 49 | let arg_str_ptr = *translated_ref(token, args); 50 | if arg_str_ptr == 0 { 51 | break; 52 | } 53 | args_vec.push(translated_str(token, arg_str_ptr as *const u8)); 54 | unsafe { 55 | args = args.add(1); 56 | } 57 | } 58 | if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) { 59 | let all_data = app_inode.read_all(); 60 | let process = current_process(); 61 | let argc = args_vec.len(); 62 | process.exec(all_data.as_slice(), args_vec); 63 | // return argc because cx.x[10] will be covered with it later 64 | argc as isize 65 | } else { 66 | -1 67 | } 68 | } 69 | 70 | /// If there is not a child process whose pid is same as given, return -1. 71 | /// Else if there is a child process but it is still running, return -2. 72 | pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { 73 | let process = current_process(); 74 | // find a child process 75 | 76 | let mut inner = process.inner_exclusive_access(); 77 | if !inner 78 | .children 79 | .iter() 80 | .any(|p| pid == -1 || pid as usize == p.getpid()) 81 | { 82 | return -1; 83 | // ---- release current PCB 84 | } 85 | let pair = inner.children.iter().enumerate().find(|(_, p)| { 86 | // ++++ temporarily access child PCB exclusively 87 | p.inner_exclusive_access().is_zombie && (pid == -1 || pid as usize == p.getpid()) 88 | // ++++ release child PCB 89 | }); 90 | if let Some((idx, _)) = pair { 91 | let child = inner.children.remove(idx); 92 | // confirm that child will be deallocated after being removed from children list 93 | assert_eq!(Arc::strong_count(&child), 1); 94 | let found_pid = child.getpid(); 95 | // ++++ temporarily access child PCB exclusively 96 | let exit_code = child.inner_exclusive_access().exit_code; 97 | // ++++ release child PCB 98 | *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code; 99 | found_pid as isize 100 | } else { 101 | -2 102 | } 103 | // ---- release current PCB automatically 104 | } 105 | 106 | pub fn sys_kill(pid: usize, signal: u32) -> isize { 107 | if let Some(process) = pid2process(pid) { 108 | if let Some(flag) = SignalFlags::from_bits(signal) { 109 | process.inner_exclusive_access().signals |= flag; 110 | 0 111 | } else { 112 | -1 113 | } 114 | } else { 115 | -1 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /os/src/fs/inode.rs: -------------------------------------------------------------------------------- 1 | use super::File; 2 | use crate::drivers::BLOCK_DEVICE; 3 | use crate::mm::UserBuffer; 4 | use crate::sync::UPIntrFreeCell; 5 | use alloc::sync::Arc; 6 | use alloc::vec::Vec; 7 | use bitflags::*; 8 | use easy_fs::{EasyFileSystem, Inode}; 9 | use lazy_static::*; 10 | 11 | pub struct OSInode { 12 | readable: bool, 13 | writable: bool, 14 | inner: UPIntrFreeCell, 15 | } 16 | 17 | pub struct OSInodeInner { 18 | offset: usize, 19 | inode: Arc, 20 | } 21 | 22 | impl OSInode { 23 | pub fn new(readable: bool, writable: bool, inode: Arc) -> Self { 24 | Self { 25 | readable, 26 | writable, 27 | inner: unsafe { UPIntrFreeCell::new(OSInodeInner { offset: 0, inode }) }, 28 | } 29 | } 30 | pub fn read_all(&self) -> Vec { 31 | let mut inner = self.inner.exclusive_access(); 32 | let mut buffer = [0u8; 512]; 33 | let mut v: Vec = Vec::new(); 34 | loop { 35 | let len = inner.inode.read_at(inner.offset, &mut buffer); 36 | if len == 0 { 37 | break; 38 | } 39 | inner.offset += len; 40 | v.extend_from_slice(&buffer[..len]); 41 | } 42 | v 43 | } 44 | } 45 | 46 | lazy_static! { 47 | pub static ref ROOT_INODE: Arc = { 48 | let efs = EasyFileSystem::open(BLOCK_DEVICE.clone()); 49 | Arc::new(EasyFileSystem::root_inode(&efs)) 50 | }; 51 | } 52 | 53 | pub fn list_apps() { 54 | println!("/**** APPS ****"); 55 | for app in ROOT_INODE.ls() { 56 | println!("{}", app); 57 | } 58 | println!("**************/") 59 | } 60 | 61 | bitflags! { 62 | pub struct OpenFlags: u32 { 63 | const RDONLY = 0; 64 | const WRONLY = 1 << 0; 65 | const RDWR = 1 << 1; 66 | const CREATE = 1 << 9; 67 | const TRUNC = 1 << 10; 68 | } 69 | } 70 | 71 | impl OpenFlags { 72 | /// Do not check validity for simplicity 73 | /// Return (readable, writable) 74 | pub fn read_write(&self) -> (bool, bool) { 75 | if self.is_empty() { 76 | (true, false) 77 | } else if self.contains(Self::WRONLY) { 78 | (false, true) 79 | } else { 80 | (true, true) 81 | } 82 | } 83 | } 84 | 85 | pub fn open_file(name: &str, flags: OpenFlags) -> Option> { 86 | let (readable, writable) = flags.read_write(); 87 | if flags.contains(OpenFlags::CREATE) { 88 | if let Some(inode) = ROOT_INODE.find(name) { 89 | // clear size 90 | inode.clear(); 91 | Some(Arc::new(OSInode::new(readable, writable, inode))) 92 | } else { 93 | // create file 94 | ROOT_INODE 95 | .create(name) 96 | .map(|inode| Arc::new(OSInode::new(readable, writable, inode))) 97 | } 98 | } else { 99 | ROOT_INODE.find(name).map(|inode| { 100 | if flags.contains(OpenFlags::TRUNC) { 101 | inode.clear(); 102 | } 103 | Arc::new(OSInode::new(readable, writable, inode)) 104 | }) 105 | } 106 | } 107 | 108 | impl File for OSInode { 109 | fn readable(&self) -> bool { 110 | self.readable 111 | } 112 | fn writable(&self) -> bool { 113 | self.writable 114 | } 115 | fn read(&self, mut buf: UserBuffer) -> usize { 116 | let mut inner = self.inner.exclusive_access(); 117 | let mut total_read_size = 0usize; 118 | for slice in buf.buffers.iter_mut() { 119 | let read_size = inner.inode.read_at(inner.offset, *slice); 120 | if read_size == 0 { 121 | break; 122 | } 123 | inner.offset += read_size; 124 | total_read_size += read_size; 125 | } 126 | total_read_size 127 | } 128 | fn write(&self, buf: UserBuffer) -> usize { 129 | let mut inner = self.inner.exclusive_access(); 130 | let mut total_write_size = 0usize; 131 | for slice in buf.buffers.iter() { 132 | let write_size = inner.inode.write_at(inner.offset, *slice); 133 | assert_eq!(write_size, slice.len()); 134 | inner.offset += write_size; 135 | total_write_size += write_size; 136 | } 137 | total_write_size 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /os/src/net/port_table.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | use lazy_static::lazy_static; 3 | use lose_net_stack::packets::tcp::TCPPacket; 4 | 5 | use crate::fs::File; 6 | use crate::sync::UPIntrFreeCell; 7 | use crate::task::TaskControlBlock; 8 | 9 | use super::tcp::TCP; 10 | 11 | pub struct Port { 12 | pub port: u16, 13 | pub receivable: bool, 14 | pub schedule: Option>, 15 | } 16 | 17 | lazy_static! { 18 | static ref LISTEN_TABLE: UPIntrFreeCell>> = 19 | unsafe { UPIntrFreeCell::new(Vec::new()) }; 20 | } 21 | 22 | pub fn listen(port: u16) -> Option { 23 | let mut listen_table = LISTEN_TABLE.exclusive_access(); 24 | let mut index = usize::MAX; 25 | for i in 0..listen_table.len() { 26 | if listen_table[i].is_none() { 27 | index = i; 28 | break; 29 | } 30 | } 31 | 32 | let listen_port = Port { 33 | port, 34 | receivable: false, 35 | schedule: None, 36 | }; 37 | 38 | if index == usize::MAX { 39 | listen_table.push(Some(listen_port)); 40 | Some(listen_table.len() - 1) 41 | } else { 42 | listen_table[index] = Some(listen_port); 43 | Some(index) 44 | } 45 | } 46 | 47 | // can accept request 48 | pub fn accept(listen_index: usize, task: Arc) { 49 | let mut listen_table = LISTEN_TABLE.exclusive_access(); 50 | assert!(listen_index < listen_table.len()); 51 | let listen_port = listen_table[listen_index].as_mut(); 52 | assert!(listen_port.is_some()); 53 | let listen_port = listen_port.unwrap(); 54 | listen_port.receivable = true; 55 | listen_port.schedule = Some(task); 56 | } 57 | 58 | pub fn port_acceptable(listen_index: usize) -> bool { 59 | let mut listen_table = LISTEN_TABLE.exclusive_access(); 60 | assert!(listen_index < listen_table.len()); 61 | 62 | let listen_port = listen_table[listen_index].as_mut(); 63 | listen_port.map_or(false, |x| x.receivable) 64 | } 65 | 66 | // check whether it can accept request 67 | pub fn check_accept(port: u16, tcp_packet: &TCPPacket) -> Option<()> { 68 | LISTEN_TABLE.exclusive_session(|listen_table| { 69 | let mut listen_ports: Vec<&mut Option> = listen_table 70 | .iter_mut() 71 | .filter(|x| match x { 72 | Some(t) => t.port == port && t.receivable == true, 73 | None => false, 74 | }) 75 | .collect(); 76 | if listen_ports.len() == 0 { 77 | None 78 | } else { 79 | let listen_port = listen_ports[0].as_mut().unwrap(); 80 | let task = listen_port.schedule.clone().unwrap(); 81 | // wakeup_task(Arc::clone(&listen_port.schedule.clone().unwrap())); 82 | listen_port.schedule = None; 83 | listen_port.receivable = false; 84 | 85 | accept_connection(port, tcp_packet, task); 86 | Some(()) 87 | } 88 | }) 89 | } 90 | 91 | pub fn accept_connection(_port: u16, tcp_packet: &TCPPacket, task: Arc) { 92 | let process = task.process.upgrade().unwrap(); 93 | let mut inner = process.inner_exclusive_access(); 94 | let fd = inner.alloc_fd(); 95 | 96 | let tcp_socket = TCP::new( 97 | tcp_packet.source_ip, 98 | tcp_packet.dest_port, 99 | tcp_packet.source_port, 100 | tcp_packet.seq, 101 | tcp_packet.ack, 102 | ); 103 | 104 | inner.fd_table[fd] = Some(Arc::new(tcp_socket)); 105 | 106 | let cx = task.inner_exclusive_access().get_trap_cx(); 107 | cx.x[10] = fd; 108 | } 109 | 110 | // store in the fd_table, delete the listen table when close the application. 111 | pub struct PortFd(usize); 112 | 113 | impl PortFd { 114 | pub fn new(port_index: usize) -> Self { 115 | PortFd(port_index) 116 | } 117 | } 118 | 119 | impl Drop for PortFd { 120 | fn drop(&mut self) { 121 | LISTEN_TABLE.exclusive_access()[self.0] = None 122 | } 123 | } 124 | 125 | impl File for PortFd { 126 | fn readable(&self) -> bool { 127 | false 128 | } 129 | 130 | fn writable(&self) -> bool { 131 | false 132 | } 133 | 134 | fn read(&self, _buf: crate::mm::UserBuffer) -> usize { 135 | 0 136 | } 137 | 138 | fn write(&self, _buf: crate::mm::UserBuffer) -> usize { 139 | 0 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /user/src/bin/eisenberg.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate alloc; 7 | extern crate core; 8 | 9 | use alloc::vec::Vec; 10 | use core::sync::atomic::{AtomicUsize, Ordering}; 11 | use user_lib::{exit, sleep, thread_create, waittid}; 12 | 13 | const N: usize = 2; 14 | const THREAD_NUM: usize = 10; 15 | 16 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 17 | enum FlagState { 18 | Out, 19 | Want, 20 | In, 21 | } 22 | 23 | static mut TURN: usize = 0; 24 | static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM]; 25 | 26 | static GUARD: AtomicUsize = AtomicUsize::new(0); 27 | 28 | fn critical_test_enter() { 29 | assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); 30 | } 31 | 32 | fn critical_test_claim() { 33 | assert_eq!(GUARD.load(Ordering::SeqCst), 1); 34 | } 35 | 36 | fn critical_test_exit() { 37 | assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); 38 | } 39 | 40 | fn eisenberg_enter_critical(id: usize) { 41 | /* announce that we want to enter */ 42 | loop { 43 | println!("Thread[{}] try enter", id); 44 | vstore!(FLAG[id], FlagState::Want); 45 | loop { 46 | /* check if any with higher priority is `Want` or `In` */ 47 | let mut prior_thread: Option = None; 48 | let turn = vload!(TURN); 49 | let ring_id = if id < turn { id + THREAD_NUM } else { id }; 50 | // FLAG.iter() may lead to some errors, use for-loop instead 51 | for i in turn..ring_id { 52 | if vload!(FLAG[i % THREAD_NUM]) != FlagState::Out { 53 | prior_thread = Some(i % THREAD_NUM); 54 | break; 55 | } 56 | } 57 | if prior_thread.is_none() { 58 | break; 59 | } 60 | println!( 61 | "Thread[{}]: prior thread {} exist, sleep and retry", 62 | id, 63 | prior_thread.unwrap() 64 | ); 65 | sleep(1); 66 | } 67 | /* now tentatively claim the resource */ 68 | vstore!(FLAG[id], FlagState::In); 69 | /* enforce the order of `claim` and `conflict check`*/ 70 | memory_fence!(); 71 | /* check if anthor thread is also `In`, which imply a conflict*/ 72 | let mut conflict = false; 73 | for i in 0..THREAD_NUM { 74 | if i != id && vload!(FLAG[i]) == FlagState::In { 75 | conflict = true; 76 | } 77 | } 78 | if !conflict { 79 | break; 80 | } 81 | println!("Thread[{}]: CONFLECT!", id); 82 | /* no need to sleep */ 83 | } 84 | /* clain the trun */ 85 | vstore!(TURN, id); 86 | println!("Thread[{}] enter", id); 87 | } 88 | 89 | fn eisenberg_exit_critical(id: usize) { 90 | /* find next one who wants to enter and give the turn to it*/ 91 | let mut next = id; 92 | let ring_id = id + THREAD_NUM; 93 | for i in (id + 1)..ring_id { 94 | let idx = i % THREAD_NUM; 95 | if vload!(FLAG[idx]) == FlagState::Want { 96 | next = idx; 97 | break; 98 | } 99 | } 100 | vstore!(TURN, next); 101 | /* All done */ 102 | vstore!(FLAG[id], FlagState::Out); 103 | println!("Thread[{}] exit, give turn to {}", id, next); 104 | } 105 | 106 | pub fn thread_fn(id: usize) -> ! { 107 | println!("Thread[{}] init.", id); 108 | for _ in 0..N { 109 | eisenberg_enter_critical(id); 110 | critical_test_enter(); 111 | for _ in 0..3 { 112 | critical_test_claim(); 113 | sleep(2); 114 | } 115 | critical_test_exit(); 116 | eisenberg_exit_critical(id); 117 | } 118 | exit(0) 119 | } 120 | 121 | #[unsafe(no_mangle)] 122 | pub fn main() -> i32 { 123 | let mut v = Vec::new(); 124 | // TODO: really shuffle 125 | assert_eq!(THREAD_NUM, 10); 126 | let shuffle: [usize; 10] = [0, 7, 4, 6, 2, 9, 8, 1, 3, 5]; 127 | for i in 0..THREAD_NUM { 128 | v.push(thread_create(thread_fn as usize, shuffle[i])); 129 | } 130 | for tid in v.iter() { 131 | let exit_code = waittid(*tid as usize); 132 | assert_eq!(exit_code, 0, "thread conflict happened!"); 133 | println!("thread#{} exited with code {}", tid, exit_code); 134 | } 135 | println!("main thread exited."); 136 | 0 137 | } 138 | -------------------------------------------------------------------------------- /os/src/drivers/plic.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::upper_case_acronyms)] 2 | pub struct PLIC { 3 | base_addr: usize, 4 | } 5 | 6 | #[derive(Copy, Clone)] 7 | pub enum IntrTargetPriority { 8 | Machine = 0, 9 | Supervisor = 1, 10 | } 11 | 12 | impl IntrTargetPriority { 13 | pub fn supported_number() -> usize { 14 | 2 15 | } 16 | } 17 | 18 | impl PLIC { 19 | fn priority_ptr(&self, intr_source_id: usize) -> *mut u32 { 20 | assert!(intr_source_id > 0 && intr_source_id <= 132); 21 | (self.base_addr + intr_source_id * 4) as *mut u32 22 | } 23 | fn hart_id_with_priority(hart_id: usize, target_priority: IntrTargetPriority) -> usize { 24 | let priority_num = IntrTargetPriority::supported_number(); 25 | hart_id * priority_num + target_priority as usize 26 | } 27 | fn enable_ptr( 28 | &self, 29 | hart_id: usize, 30 | target_priority: IntrTargetPriority, 31 | intr_source_id: usize, 32 | ) -> (*mut u32, usize) { 33 | let id = Self::hart_id_with_priority(hart_id, target_priority); 34 | let (reg_id, reg_shift) = (intr_source_id / 32, intr_source_id % 32); 35 | ( 36 | (self.base_addr + 0x2000 + 0x80 * id + 0x4 * reg_id) as *mut u32, 37 | reg_shift, 38 | ) 39 | } 40 | fn threshold_ptr_of_hart_with_priority( 41 | &self, 42 | hart_id: usize, 43 | target_priority: IntrTargetPriority, 44 | ) -> *mut u32 { 45 | let id = Self::hart_id_with_priority(hart_id, target_priority); 46 | (self.base_addr + 0x20_0000 + 0x1000 * id) as *mut u32 47 | } 48 | fn claim_comp_ptr_of_hart_with_priority( 49 | &self, 50 | hart_id: usize, 51 | target_priority: IntrTargetPriority, 52 | ) -> *mut u32 { 53 | let id = Self::hart_id_with_priority(hart_id, target_priority); 54 | (self.base_addr + 0x20_0004 + 0x1000 * id) as *mut u32 55 | } 56 | pub unsafe fn new(base_addr: usize) -> Self { 57 | Self { base_addr } 58 | } 59 | pub fn set_priority(&mut self, intr_source_id: usize, priority: u32) { 60 | assert!(priority < 8); 61 | unsafe { 62 | self.priority_ptr(intr_source_id).write_volatile(priority); 63 | } 64 | } 65 | #[allow(unused)] 66 | pub fn get_priority(&mut self, intr_source_id: usize) -> u32 { 67 | unsafe { self.priority_ptr(intr_source_id).read_volatile() & 7 } 68 | } 69 | pub fn enable( 70 | &mut self, 71 | hart_id: usize, 72 | target_priority: IntrTargetPriority, 73 | intr_source_id: usize, 74 | ) { 75 | let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); 76 | unsafe { 77 | reg_ptr.write_volatile(reg_ptr.read_volatile() | 1 << shift); 78 | } 79 | } 80 | #[allow(unused)] 81 | pub fn disable( 82 | &mut self, 83 | hart_id: usize, 84 | target_priority: IntrTargetPriority, 85 | intr_source_id: usize, 86 | ) { 87 | let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); 88 | unsafe { 89 | reg_ptr.write_volatile(reg_ptr.read_volatile() & (!(1u32 << shift))); 90 | } 91 | } 92 | pub fn set_threshold( 93 | &mut self, 94 | hart_id: usize, 95 | target_priority: IntrTargetPriority, 96 | threshold: u32, 97 | ) { 98 | assert!(threshold < 8); 99 | let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); 100 | unsafe { 101 | threshold_ptr.write_volatile(threshold); 102 | } 103 | } 104 | #[allow(unused)] 105 | pub fn get_threshold(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { 106 | let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); 107 | unsafe { threshold_ptr.read_volatile() & 7 } 108 | } 109 | pub fn claim(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { 110 | let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); 111 | unsafe { claim_comp_ptr.read_volatile() } 112 | } 113 | pub fn complete( 114 | &mut self, 115 | hart_id: usize, 116 | target_priority: IntrTargetPriority, 117 | completion: u32, 118 | ) { 119 | let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); 120 | unsafe { 121 | claim_comp_ptr.write_volatile(completion); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /os/src/syscall/sync.rs: -------------------------------------------------------------------------------- 1 | use crate::sync::{Condvar, Mutex, MutexBlocking, MutexSpin, Semaphore}; 2 | use crate::task::{block_current_and_run_next, current_process, current_task}; 3 | use crate::timer::{add_timer, get_time_ms}; 4 | use alloc::sync::Arc; 5 | 6 | pub fn sys_sleep(ms: usize) -> isize { 7 | let expire_ms = get_time_ms() + ms; 8 | let task = current_task().unwrap(); 9 | add_timer(expire_ms, task); 10 | block_current_and_run_next(); 11 | 0 12 | } 13 | 14 | pub fn sys_mutex_create(blocking: bool) -> isize { 15 | let process = current_process(); 16 | let mutex: Option> = if !blocking { 17 | Some(Arc::new(MutexSpin::new())) 18 | } else { 19 | Some(Arc::new(MutexBlocking::new())) 20 | }; 21 | let mut process_inner = process.inner_exclusive_access(); 22 | if let Some(id) = process_inner 23 | .mutex_list 24 | .iter() 25 | .enumerate() 26 | .find(|(_, item)| item.is_none()) 27 | .map(|(id, _)| id) 28 | { 29 | process_inner.mutex_list[id] = mutex; 30 | id as isize 31 | } else { 32 | process_inner.mutex_list.push(mutex); 33 | process_inner.mutex_list.len() as isize - 1 34 | } 35 | } 36 | 37 | pub fn sys_mutex_lock(mutex_id: usize) -> isize { 38 | let process = current_process(); 39 | let process_inner = process.inner_exclusive_access(); 40 | let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); 41 | drop(process_inner); 42 | drop(process); 43 | mutex.lock(); 44 | 0 45 | } 46 | 47 | pub fn sys_mutex_unlock(mutex_id: usize) -> isize { 48 | let process = current_process(); 49 | let process_inner = process.inner_exclusive_access(); 50 | let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); 51 | drop(process_inner); 52 | drop(process); 53 | mutex.unlock(); 54 | 0 55 | } 56 | 57 | pub fn sys_semaphore_create(res_count: usize) -> isize { 58 | let process = current_process(); 59 | let mut process_inner = process.inner_exclusive_access(); 60 | let id = if let Some(id) = process_inner 61 | .semaphore_list 62 | .iter() 63 | .enumerate() 64 | .find(|(_, item)| item.is_none()) 65 | .map(|(id, _)| id) 66 | { 67 | process_inner.semaphore_list[id] = Some(Arc::new(Semaphore::new(res_count))); 68 | id 69 | } else { 70 | process_inner 71 | .semaphore_list 72 | .push(Some(Arc::new(Semaphore::new(res_count)))); 73 | process_inner.semaphore_list.len() - 1 74 | }; 75 | id as isize 76 | } 77 | 78 | pub fn sys_semaphore_up(sem_id: usize) -> isize { 79 | let process = current_process(); 80 | let process_inner = process.inner_exclusive_access(); 81 | let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); 82 | drop(process_inner); 83 | sem.up(); 84 | 0 85 | } 86 | 87 | pub fn sys_semaphore_down(sem_id: usize) -> isize { 88 | let process = current_process(); 89 | let process_inner = process.inner_exclusive_access(); 90 | let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); 91 | drop(process_inner); 92 | sem.down(); 93 | 0 94 | } 95 | 96 | pub fn sys_condvar_create() -> isize { 97 | let process = current_process(); 98 | let mut process_inner = process.inner_exclusive_access(); 99 | let id = if let Some(id) = process_inner 100 | .condvar_list 101 | .iter() 102 | .enumerate() 103 | .find(|(_, item)| item.is_none()) 104 | .map(|(id, _)| id) 105 | { 106 | process_inner.condvar_list[id] = Some(Arc::new(Condvar::new())); 107 | id 108 | } else { 109 | process_inner 110 | .condvar_list 111 | .push(Some(Arc::new(Condvar::new()))); 112 | process_inner.condvar_list.len() - 1 113 | }; 114 | id as isize 115 | } 116 | 117 | pub fn sys_condvar_signal(condvar_id: usize) -> isize { 118 | let process = current_process(); 119 | let process_inner = process.inner_exclusive_access(); 120 | let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); 121 | drop(process_inner); 122 | condvar.signal(); 123 | 0 124 | } 125 | 126 | pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { 127 | let process = current_process(); 128 | let process_inner = process.inner_exclusive_access(); 129 | let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); 130 | let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); 131 | drop(process_inner); 132 | condvar.wait_with_mutex(mutex); 133 | 0 134 | } 135 | -------------------------------------------------------------------------------- /os/src/net/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod port_table; 2 | pub mod socket; 3 | pub mod tcp; 4 | pub mod udp; 5 | 6 | pub use lose_net_stack::IPv4; 7 | 8 | use alloc::{sync::Arc, vec}; 9 | use lose_net_stack::{LoseStack, MacAddress, TcpFlags, results::Packet}; 10 | 11 | use crate::{ 12 | drivers::NET_DEVICE, 13 | net::socket::{get_socket, push_data}, 14 | sync::UPIntrFreeCell, 15 | }; 16 | 17 | use self::{port_table::check_accept, socket::set_s_a_by_index}; 18 | 19 | pub struct NetStack(UPIntrFreeCell); 20 | 21 | impl NetStack { 22 | pub fn new() -> Self { 23 | unsafe { 24 | NetStack(UPIntrFreeCell::new(LoseStack::new( 25 | IPv4::new(10, 0, 2, 15), 26 | MacAddress::new([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]), 27 | ))) 28 | } 29 | } 30 | } 31 | 32 | lazy_static::lazy_static! { 33 | static ref LOSE_NET_STACK: Arc = Arc::new(NetStack::new()); 34 | } 35 | 36 | pub fn net_interrupt_handler() { 37 | let mut recv_buf = vec![0u8; 1024]; 38 | 39 | let len = NET_DEVICE.receive(&mut recv_buf); 40 | 41 | let packet = LOSE_NET_STACK 42 | .0 43 | .exclusive_access() 44 | .analysis(&recv_buf[..len]); 45 | 46 | // println!("[kernel] receive a packet"); 47 | // hexdump(&recv_buf[..len]); 48 | 49 | match packet { 50 | Packet::ARP(arp_packet) => { 51 | let lose_stack = LOSE_NET_STACK.0.exclusive_access(); 52 | let reply_packet = arp_packet 53 | .reply_packet(lose_stack.ip, lose_stack.mac) 54 | .expect("can't build reply"); 55 | let reply_data = reply_packet.build_data(); 56 | NET_DEVICE.transmit(&reply_data) 57 | } 58 | 59 | Packet::UDP(udp_packet) => { 60 | let target = udp_packet.source_ip; 61 | let lport = udp_packet.dest_port; 62 | let rport = udp_packet.source_port; 63 | 64 | if let Some(socket_index) = get_socket(target, lport, rport) { 65 | push_data(socket_index, udp_packet.data.to_vec()); 66 | } 67 | } 68 | 69 | Packet::TCP(tcp_packet) => { 70 | let target = tcp_packet.source_ip; 71 | let lport = tcp_packet.dest_port; 72 | let rport = tcp_packet.source_port; 73 | let flags = tcp_packet.flags; 74 | 75 | if flags.contains(TcpFlags::S) { 76 | // if it has a port to accept, then response the request 77 | if check_accept(lport, &tcp_packet).is_some() { 78 | let mut reply_packet = tcp_packet.ack(); 79 | reply_packet.flags = TcpFlags::S | TcpFlags::A; 80 | NET_DEVICE.transmit(&reply_packet.build_data()); 81 | } 82 | return; 83 | } else if tcp_packet.flags.contains(TcpFlags::F) { 84 | // tcp disconnected 85 | let reply_packet = tcp_packet.ack(); 86 | NET_DEVICE.transmit(&reply_packet.build_data()); 87 | 88 | let mut end_packet = reply_packet.ack(); 89 | end_packet.flags |= TcpFlags::F; 90 | NET_DEVICE.transmit(&end_packet.build_data()); 91 | } else if tcp_packet.flags.contains(TcpFlags::A) && tcp_packet.data_len == 0 { 92 | return; 93 | } 94 | 95 | if let Some(socket_index) = get_socket(target, lport, rport) { 96 | push_data(socket_index, tcp_packet.data.to_vec()); 97 | set_s_a_by_index(socket_index, tcp_packet.seq, tcp_packet.ack); 98 | } 99 | } 100 | _ => {} 101 | } 102 | } 103 | 104 | #[allow(unused)] 105 | pub fn hexdump(data: &[u8]) { 106 | const PRELAND_WIDTH: usize = 70; 107 | println!("[kernel] {:-^1$}", " hexdump ", PRELAND_WIDTH); 108 | for offset in (0..data.len()).step_by(16) { 109 | print!("[kernel] "); 110 | for i in 0..16 { 111 | if offset + i < data.len() { 112 | print!("{:02x} ", data[offset + i]); 113 | } else { 114 | print!("{:02} ", ""); 115 | } 116 | } 117 | 118 | print!("{:>6}", ' '); 119 | 120 | for i in 0..16 { 121 | if offset + i < data.len() { 122 | let c = data[offset + i]; 123 | if c >= 0x20 && c <= 0x7e { 124 | print!("{}", c as char); 125 | } else { 126 | print!("."); 127 | } 128 | } else { 129 | print!("{:02} ", ""); 130 | } 131 | } 132 | 133 | println!(""); 134 | } 135 | println!("[kernel] {:-^1$}", " hexdump end ", PRELAND_WIDTH); 136 | } 137 | --------------------------------------------------------------------------------