├── 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