├── .cargo └── config.toml ├── .github └── workflows │ └── workflow.yml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── ch1-lab ├── Cargo.toml ├── README.md ├── build.rs └── src │ └── main.rs ├── ch1 ├── Cargo.toml ├── README.md ├── build.rs └── src │ └── main.rs ├── ch2 ├── Cargo.toml ├── README.md ├── build.rs └── src │ └── main.rs ├── ch3 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── main.rs │ └── task.rs ├── ch4 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── main.rs │ └── process.rs ├── ch5 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── main.rs │ ├── process.rs │ └── processor.rs ├── ch6 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── fs.rs │ ├── main.rs │ ├── process.rs │ ├── processor.rs │ └── virtio_block.rs ├── ch7 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── fs.rs │ ├── main.rs │ ├── process.rs │ ├── processor.rs │ └── virtio_block.rs ├── ch8 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── fs.rs │ ├── main.rs │ ├── process.rs │ ├── processor.rs │ └── virtio_block.rs ├── console ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── docs └── design │ ├── 20220814-crate-types.md │ └── 20220823-kpti.md ├── easy-fs ├── .gitignore ├── Cargo.toml └── src │ ├── bitmap.rs │ ├── block_cache.rs │ ├── block_dev.rs │ ├── efs.rs │ ├── file.rs │ ├── layout.rs │ ├── lib.rs │ └── vfs.rs ├── kernel-alloc ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── kernel-context ├── Cargo.toml └── src │ ├── foreign │ ├── mod.rs │ └── multislot_portal.rs │ └── lib.rs ├── kernel-vm ├── Cargo.toml └── src │ ├── lib.rs │ └── space │ ├── mapper.rs │ ├── mod.rs │ └── visitor.rs ├── linker ├── Cargo.toml ├── README.md └── src │ ├── app.rs │ └── lib.rs ├── rust-toolchain.toml ├── rustsbi-qemu.bin ├── signal-defs ├── Cargo.toml └── src │ └── lib.rs ├── signal-impl ├── Cargo.toml └── src │ ├── default_action.rs │ ├── lib.rs │ └── signal_set.rs ├── signal ├── Cargo.toml └── src │ ├── lib.rs │ └── signal_result.rs ├── sync ├── Cargo.toml └── src │ ├── condvar.rs │ ├── lib.rs │ ├── mutex.rs │ ├── semaphore.rs │ └── up.rs ├── syscall ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── io.rs │ ├── kernel │ └── mod.rs │ ├── lib.rs │ ├── syscall.h.in │ ├── time.rs │ └── user.rs ├── task-manage ├── Cargo.toml ├── README.md └── src │ ├── id.rs │ ├── lib.rs │ ├── manager.rs │ ├── proc_manage.rs │ ├── proc_rel.rs │ ├── proc_thread_rel.rs │ ├── scheduler.rs │ └── thread_manager.rs ├── user ├── Cargo.toml ├── README.md ├── build.rs ├── cases.toml └── src │ ├── bin │ ├── 00hello_world.rs │ ├── 01store_fault.rs │ ├── 02power.rs │ ├── 03priv_inst.rs │ ├── 04priv_csr.rs │ ├── 05write_a.rs │ ├── 06write_b.rs │ ├── 07write_c.rs │ ├── 08power_3.rs │ ├── 09power_5.rs │ ├── 10power_7.rs │ ├── 11sleep.rs │ ├── 12forktest.rs │ ├── 13forktree.rs │ ├── 14forktest2.rs │ ├── 15matrix.rs │ ├── cat_filea.rs │ ├── filetest_simple.rs │ ├── initproc.rs │ ├── mpsc_sem.rs │ ├── race_adder_mutex_blocking.rs │ ├── sig_ctrlc.rs │ ├── sig_simple.rs │ ├── sig_simple2.rs │ ├── sig_tests.rs │ ├── sync_sem.rs │ ├── test_condvar.rs │ ├── threads.rs │ ├── threads_arg.rs │ └── user_shell.rs │ ├── heap.rs │ └── lib.rs └── xtask ├── Cargo.toml └── src ├── fs_pack.rs ├── main.rs └── user.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --release --" 3 | make = "xtask make" 4 | asm = "xtask asm" 5 | qemu = "xtask qemu" 6 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | make: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Build syscall 16 | uses: actions-rs/cargo@v1 17 | with: 18 | command: build 19 | args: --package syscall 20 | 21 | - name: Check format 22 | uses: actions-rs/cargo@v1 23 | with: 24 | command: fmt 25 | args: --all --check 26 | 27 | - name: Make every chapter 28 | run: | 29 | cargo make --ch 1 30 | cargo make --ch 2 31 | cargo make --ch 3 32 | cargo make --ch 4 33 | cargo make --ch 5 34 | cargo make --ch 6 35 | cargo make --ch 7 36 | cargo make --ch 8 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.*/* 2 | !**/.cargo/* 3 | !/.github/* 4 | !/.vscode/settings.json 5 | 6 | /target 7 | *.asm 8 | *.bin 9 | !/rustsbi-qemu.bin 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", 3 | // Prevent "can't find crate for `test`" error on no_std 4 | // Ref: https://github.com/rust-lang/vscode-rust/issues/729 5 | "rust-analyzer.checkOnSave.enable": false 6 | } 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "xtask", 4 | "ch1", 5 | "ch1-lab", 6 | "ch2", 7 | "ch3", 8 | "ch4", 9 | "ch5", 10 | "ch6", 11 | "ch7", 12 | "ch8", 13 | "user", 14 | "linker", 15 | "console", 16 | "syscall", 17 | "kernel-context", 18 | "kernel-alloc", 19 | "kernel-vm", 20 | "task-manage", 21 | "easy-fs", 22 | "signal-defs", 23 | "signal", 24 | "signal-impl", 25 | "sync", 26 | ] 27 | default-members = ["xtask"] 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 |  DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2022 YdrMaster 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 模块化的 rCore-Tutorial 2 | 3 | [![CI](https://github.com/YdrMaster/rCore-Tutorial-in-single-workspace/actions/workflows/workflow.yml/badge.svg?branch=main)](https://github.com/YdrMaster/rCore-Tutorial-in-single-workspace/actions) 4 | [![issue](https://img.shields.io/github/issues/YdrMaster/rCore-Tutorial-in-single-workspace)](https://github.com/YdrMaster/rCore-Tutorial-in-single-workspace/issues) 5 | ![license](https://img.shields.io/github/license/YdrMaster/rCore-Tutorial-in-single-workspace) 6 | 7 | - 只需要维护一个分支! 8 | - 支持 Windows 直接调试! 9 | 10 | > - 需要安装 Rust 环境和 Windows Qemu 11 | > - 当然 Linux 也支持,其实就是跨平台 12 | 13 | ## 目录 14 | 15 | - [宗旨](#宗旨) 16 | - [史书的另一种形态](#史书的另一种形态) 17 | - [协作政策](#协作政策) 18 | - [运行系统](#运行系统) 19 | - [设计稿和报告](#设计稿和报告) 20 | - [进度](#进度) 21 | 22 | ## 宗旨 23 | 24 | 本项目目标是实现一个存在于单独仓库、单独分支的完整操作系统教程。 25 | 26 | “一个操作系统教程”定义为由一系列章节组成的,用于学习操作系统的文本和代码资料。用户可按照自然的顺序阅读这些章节,逐步了解操作系统的发展史和原理性的技术细节。因此,在本文的后续部分,将用户称为学生,将使用过程称为学习。 27 | 28 | 我假设学生在学习本教程前对 Rust 语言有充分的了解,因为我希望在编写时尽量使用 Rust 带来的方便,不必受受众的掣肘。 29 | 30 | 我希望,学生在学习本教程时可以专注于 Rust 和操作系统本身。因此,教程不要求用户精通操作 git,`git clone` 只是获取教程的手段之一,与下载 zip 或从他人处拷贝没有本质区别(当然,版本管理总是有益的);并且会尽量跨平台,为此,可能需要在构建系统中增加一些平台相关的操作。 31 | 32 | 本教程**并不期望在章节设置或教学内容上有所创新**,也不是为了实验任何新的内核技术,rCore-Tutorial-v3 足够优秀。本项目仅关注**教程的构建过程和学生的学习体验**(换句话说,如果为了这两点,修改章节设置和教学内容也可以接受)。在此基础之上,我希望尽量使用 Rust 的机制减少“构造一个操作系统”的代码量,以及使得库构建规范化、可迁移。 33 | 34 | ### 史书的另一种形态 35 | 36 | rCore-Tutorial-v3 的教材部分别出心裁地为每一章取了一种古生物的名字,将操作系统的发展史和动物的演化史联系起来,为常常枯燥的教材带来了新奇的趣味。随着每个章节,学生可以了解到一个现代内核,是如何从蒙昧年代一步步发展来的。 37 | 38 | 阅读教材会带来快乐——收获知识、进步的快乐;阅读历史也会带来快乐——带入那些天才人物、风云故事的快乐。然而,一旦将教材与一部真正的史书相比,就会发现我们似乎缺了什么。从古至今、由简至繁——这是一部编年史。编年史直白、清晰,利于建立概念,然而却不利于理解一个复杂的人物。对于操作系统来说,就是不利于理解一个真正的现代内核子系统的细节。子系统的功能常常限于相关其他子系统尚不存在而无法完整介绍;而增加一个子系统时,又需要修改以前的子系统来适应新部件的功能。复杂的系统,要么不得不多次修改,要么只能放在后面的章节,这导致教程书可以是极好的入门,但却无法常读常新。 39 | 40 | 幸好,历史已经告诉我们另外的选择。编年史是历史研究的重要工具,但堪称“史家之绝唱”的还是纪传体。每次将视角聚焦到一个重要的人物,将他的经历作为线索,往往使历史更丰满。或许我们需要另一部纪传体作品,通过无序的介绍内核的各个子模块,为离开了蹒跚学步的内核研究者带来一部更有效的工具书。这样一本工具书,必然天然需要模块化——模块就是章节本身。 41 | 42 | 本项目仍然聚焦一个更好的 Tutorial。但其模块化的经验有助于另外的作品。如果有这样一个项目,可以称为 rCore-Manual,它是一本手册,供操作系统研究者随时翻阅。正如 rCore-Tutorial 不需要介绍原始的 rCore,它们是 rCore 的精神续作。 43 | 44 | ### 协作政策 45 | 46 | 本项目**不是、也无意成为相关工作的典范**。如果你: 47 | 48 | - 认为项目的操作非常合适,欢迎共同开发; 49 | - 你不喜欢某些更新,请发起讨论,或从任何提交分叉; 50 | - 认同项目的目标但不认同其设计,可以创建己的仓库; 51 | 52 | 因此,本项目使用 [WTFPL](LICENSE) 开源,如果你不喜欢,可以改名字或在你的分叉里直接删除它,这都是它本身支持的。 53 | 54 | 另外,本项目具有实验性。这意味着它随时可能发生设计变更、破坏性重构、移动、废弃或删除。 55 | 56 | ## 运行系统 57 | 58 | - `cargo qemu --ch ` 59 | 60 | 在 qemu 运行第 `n` 章的操作系统。 61 | 62 | 可选参数: 63 | 64 | - `--lab` 只对 ch1 有效,执行 ch1-lab 65 | - `--features ` 只对 ch3 有效,传入 features=coop 66 | 67 | ## 设计稿和报告 68 | 69 | - [crate 分类](docs/design/20220814-crate-types.md) 70 | - [内核地址隔离](docs/design/20220823-kpti.md) 71 | - [3.5 章进展报告](https://github.com/YdrMaster/slides/blob/main/20220824-%E6%A8%A1%E5%9D%97%E5%8C%96%E8%BF%9B%E5%B1%95.pdf) 72 | 73 | ## 进度 74 | 75 | - [x] [§1](ch1/README.md)([实验参考](ch1-lab/README.md)) 76 | - [x] [§2](ch2/README.md) 77 | - [x] [§3](ch3/README.md) 78 | - [x] [§4](ch4/README.md) 79 | - [x] [§5](ch5/README.md) by [ZFL](https://github.com/zflcs) 80 | - [x] [§6](ch6/README.md) by [TKF](https://github.com/tkf2019) 81 | - [x] [§7](ch7/README.md) by [BHY](https://github.com/scPointer) 82 | - [x] [§8](ch8/README.md) by [ZFL](https://github.com/zflcs) 83 | - [ ] §9 84 | -------------------------------------------------------------------------------- /ch1-lab/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch1-lab" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | rcore-console = { path = "../console" } 10 | -------------------------------------------------------------------------------- /ch1-lab/README.md: -------------------------------------------------------------------------------- 1 | # 第一章实验 2 | 3 | 第一章实验的示例,展示如何依赖 `rcore_console` crate。 4 | 5 | 在 [Cargo.toml](Cargo.toml#L9) 里添加: 6 | 7 | ```toml 8 | rcore_console = { path = "../rcore_console"} 9 | ``` 10 | 11 | 在 [main.rs](src/main.rs#L38) 里初始化: 12 | 13 | ```rust 14 | rcore_console::init_console(&Console); 15 | ``` 16 | 17 | 后续的章节都可以这样依赖 `rcore_console`。 18 | -------------------------------------------------------------------------------- /ch1-lab/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, LINKER).unwrap(); 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 8 | } 9 | 10 | const LINKER: &[u8] = b" 11 | OUTPUT_ARCH(riscv) 12 | SECTIONS { 13 | .text 0x80200000 : { 14 | *(.text.entry) 15 | *(.text .text.*) 16 | } 17 | .rodata : { 18 | *(.rodata .rodata.*) 19 | *(.srodata .srodata.*) 20 | } 21 | .data : { 22 | *(.data .data.*) 23 | *(.sdata .sdata.*) 24 | } 25 | .bss : { 26 | *(.bss.uninit) 27 | *(.bss .bss.*) 28 | *(.sbss .sbss.*) 29 | } 30 | }"; 31 | -------------------------------------------------------------------------------- /ch1-lab/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | #[macro_use] 7 | extern crate rcore_console; 8 | 9 | use sbi_rt::*; 10 | 11 | /// Supervisor 汇编入口。 12 | /// 13 | /// 设置栈并跳转到 Rust。 14 | #[naked] 15 | #[no_mangle] 16 | #[link_section = ".text.entry"] 17 | unsafe extern "C" fn _start() -> ! { 18 | const STACK_SIZE: usize = 4096; 19 | 20 | #[link_section = ".bss.uninit"] 21 | static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; 22 | 23 | core::arch::asm!( 24 | "la sp, {stack} + {stack_size}", 25 | "j {main}", 26 | stack_size = const STACK_SIZE, 27 | stack = sym STACK, 28 | main = sym rust_main, 29 | options(noreturn), 30 | ) 31 | } 32 | 33 | /// 使用 `console` 输出的 Supervisor 裸机程序。 34 | /// 35 | /// 测试各种日志和输出后关机。 36 | extern "C" fn rust_main() -> ! { 37 | // 初始化 `console` 38 | rcore_console::init_console(&Console); 39 | // 设置日志级别 40 | rcore_console::set_log_level(option_env!("LOG")); 41 | // 测试各种打印 42 | rcore_console::test_log(); 43 | 44 | system_reset(Shutdown, NoReason); 45 | unreachable!() 46 | } 47 | 48 | /// 将传给 `console` 的控制台对象。 49 | /// 50 | /// 这是一个 Unit struct,它不需要空间。否则需要传一个 static 对象。 51 | struct Console; 52 | 53 | /// 为 `Console` 实现 `console::Console` trait。 54 | impl rcore_console::Console for Console { 55 | fn put_char(&self, c: u8) { 56 | #[allow(deprecated)] 57 | legacy::console_putchar(c as _); 58 | } 59 | } 60 | 61 | /// Rust 异常处理函数,以异常方式关机。 62 | #[panic_handler] 63 | fn panic(info: &core::panic::PanicInfo) -> ! { 64 | println!("{info}"); 65 | system_reset(Shutdown, SystemFailure); 66 | loop {} 67 | } 68 | -------------------------------------------------------------------------------- /ch1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch1" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | -------------------------------------------------------------------------------- /ch1/README.md: -------------------------------------------------------------------------------- 1 | # 第一章 2 | 3 | 第一章旨在展示一个尽量简单的**特权态裸机应用程序**: 4 | 5 | - 只有[一个文件](src/main.rs); 6 | - 链接脚本在 [build.rs](build.rs),以免增加依赖; 7 | - 只依赖 [*sbi-rt*](https://crates.io/crates/sbi-rt) 以获得封装好的 SBI 调用; 8 | - 这个程序被 SEE 引导,工作在 S 态; 9 | - 这个程序不需要环境: 10 | - 从汇编进入并为 Rust 准备栈; 11 | - 依赖 SBI 提供的 `legacy::console_putchar` 打印 `Hello, world!`; 12 | - 依赖 SBI 提供的 `system_reset` 调用关机; 13 | 14 | 它不配被称作一个操作系统,因为它没有操作(硬件),也不构造(执行用户程序的)系统; 15 | 16 | ## sbi-rt 17 | 18 | 这个库就是 kernel 的 libc。 19 | 20 | 它根据 [SBI 标准](https://github.com/riscv-non-isa/riscv-sbi-doc)封装了一系列函数,通过 `ecall` 命令调用 SBI 提供的响应功能。本章需要使用 `legacy::console_putchar` 向控制台打印字符,以及 `system_reset` 在程序运行完后关机。 21 | 22 | ## 定制链接脚本 23 | 24 | build.rs 的用法见[文档](https://doc.rust-lang.org/cargo/reference/build-scripts.html)。这个定制的链接脚本是特殊的: 25 | 26 | ```ld 27 | OUTPUT_ARCH(riscv) 28 | SECTIONS { 29 | .text 0x80200000 : { 30 | *(.text.entry) 31 | *(.text .text.*) 32 | } 33 | .rodata : { 34 | *(.rodata .rodata.*) 35 | *(.srodata .srodata.*) 36 | } 37 | .data : { 38 | *(.data .data.*) 39 | *(.sdata .sdata.*) 40 | } 41 | .bss : { 42 | *(.bss.uninit) 43 | *(.bss .bss.*) 44 | *(.sbss .sbss.*) 45 | } 46 | } 47 | ``` 48 | 49 | 1. 为了被引导,它的 `.text` 在最前面。一般是 `.rodata` 在最前面。`.text` 的最前面是 `.text.entry`,有且只有一个汇编入口放在这个节,实现引导; 50 | 2. 正常情况下,裸机应用程序需要清除自己的 `.bss` 节,所以需要定义全局符号以便动态定位 `.bss`。但这一章的程序并不依赖 清空的 `.bss`,所以没有导出符号。`.bss` 本身仍然需要,因为栈会放在里面。 51 | 52 | ## 工作流程解读 53 | 54 | 1. SBI 初始化完成后,将固定跳转到 0x8020_0000 地址; 55 | 2. 根据链接脚本,汇编入口函数被放置在这个地址。它叫做 `_start`,这个名字是特殊的!GNU LD 及兼容其脚本的链接器会将这个名字认为是默认的入口,否则需要指定。这个函数是一个 rust 裸函数([`#[naked]`](https://github.com/rust-lang/rust/issues/90957)),编译器不会为它添加任何序言和尾声,因此可以在没有栈的情况下执行。它将栈指针指向预留的栈空间,然后跳转到 `rust_main` 函数; 56 | 3. `rust_main` 函数在一个最简单的循环打印调用 sbi 打印 `Hello, world!` 字符串,然后关机。 57 | -------------------------------------------------------------------------------- /ch1/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, LINKER).unwrap(); 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 8 | } 9 | 10 | const LINKER: &[u8] = b" 11 | OUTPUT_ARCH(riscv) 12 | SECTIONS { 13 | .text 0x80200000 : { 14 | *(.text.entry) 15 | *(.text .text.*) 16 | } 17 | .rodata : { 18 | *(.rodata .rodata.*) 19 | *(.srodata .srodata.*) 20 | } 21 | .data : { 22 | *(.data .data.*) 23 | *(.sdata .sdata.*) 24 | } 25 | .bss : { 26 | *(.bss.uninit) 27 | *(.bss .bss.*) 28 | *(.sbss .sbss.*) 29 | } 30 | }"; 31 | -------------------------------------------------------------------------------- /ch1/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | /// Supervisor 汇编入口。 7 | /// 8 | /// 设置栈并跳转到 Rust。 9 | #[naked] 10 | #[no_mangle] 11 | #[link_section = ".text.entry"] 12 | unsafe extern "C" fn _start() -> ! { 13 | const STACK_SIZE: usize = 4096; 14 | 15 | #[link_section = ".bss.uninit"] 16 | static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; 17 | 18 | core::arch::asm!( 19 | "la sp, {stack} + {stack_size}", 20 | "j {main}", 21 | stack_size = const STACK_SIZE, 22 | stack = sym STACK, 23 | main = sym rust_main, 24 | options(noreturn), 25 | ) 26 | } 27 | 28 | /// 非常简单的 Supervisor 裸机程序。 29 | /// 30 | /// 打印 `Hello, World!`,然后关机。 31 | extern "C" fn rust_main() -> ! { 32 | use sbi_rt::*; 33 | for c in b"Hello, world!" { 34 | #[allow(deprecated)] 35 | legacy::console_putchar(*c as _); 36 | } 37 | system_reset(Shutdown, NoReason); 38 | unreachable!() 39 | } 40 | 41 | /// Rust 异常处理函数,以异常方式关机。 42 | #[panic_handler] 43 | fn panic(_: &core::panic::PanicInfo) -> ! { 44 | use sbi_rt::*; 45 | system_reset(Shutdown, SystemFailure); 46 | loop {} 47 | } 48 | -------------------------------------------------------------------------------- /ch2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch2" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | riscv = "0.10.1" 10 | 11 | linker = { path = "../linker" } 12 | rcore-console = { path = "../console" } 13 | kernel-context = { path = "../kernel-context" } 14 | syscall = { path = "../syscall", features = ["kernel"] } 15 | 16 | [build-dependencies] 17 | linker = { path = "../linker" } 18 | -------------------------------------------------------------------------------- /ch2/README.md: -------------------------------------------------------------------------------- 1 | # 第二章 2 | 3 | 第二章实现的操作系统被称为“邓氏鱼”。邓氏鱼是这样一种生物:在它生存的年代,它最大、最强,非常有效的颌、眼睛和尾巴提供了充分强大的咬合力、视力和游泳能力,这些结构在泥盆纪都堪称先进。然而,在泥盆纪末灭绝事件中,以邓氏鱼为代表的所有盾皮鱼类都迅速而彻底地灭绝了。在今天的海洋里,我们没有发现什么与之在演化上接近的生物。演化就是这样短视。一时非常有效的设计,可能随着环境微妙的一点改变就完全不适用了,设计也是一样。 4 | 5 | 因此,在迈向更复杂的系统之前,就让我们任性地实现一个保持简单的单道批处理系统吧。如你所见,第二章依然只有一个 [main.rs](src/main.rs) 文件,而且算上注释也不到 150 行(当然,它使用了更多的依赖)。重点关注 [`syscall`](src/main.rs#L29) 和 [`Context`](src/main.rs#L36) 的用法,这就是邓氏鱼的大脑和脊椎,这些真正成功的结构是不会随着某些物种的灭绝而消失的。 6 | 7 | > **注意** 如果修改了用户程序,需要 `cargo clean` 重新编译才能刷新,不知道怎么优化。 8 | 9 | ## 实验 10 | 11 | 提高 `write` 系统调用安全性的实验只需要在第二章代码上做一点修改,没必要再做一个新的 bin crate 了。 12 | -------------------------------------------------------------------------------- /ch2/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch2/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | #[macro_use] 7 | extern crate rcore_console; 8 | 9 | use impls::{Console, SyscallContext}; 10 | use kernel_context::LocalContext; 11 | use rcore_console::log; 12 | use riscv::register::*; 13 | use sbi_rt::*; 14 | use syscall::{Caller, SyscallId}; 15 | 16 | // 用户程序内联进来。 17 | core::arch::global_asm!(include_str!(env!("APP_ASM"))); 18 | // 定义内核入口。 19 | linker::boot0!(rust_main; stack = 4 * 4096); 20 | 21 | extern "C" fn rust_main() -> ! { 22 | // bss 段清零 23 | unsafe { linker::KernelLayout::locate().zero_bss() }; 24 | // 初始化 `console` 25 | rcore_console::init_console(&Console); 26 | rcore_console::set_log_level(option_env!("LOG")); 27 | rcore_console::test_log(); 28 | // 初始化 syscall 29 | syscall::init_io(&SyscallContext); 30 | syscall::init_process(&SyscallContext); 31 | // 批处理 32 | for (i, app) in linker::AppMeta::locate().iter().enumerate() { 33 | let app_base = app.as_ptr() as usize; 34 | log::info!("load app{i} to {app_base:#x}"); 35 | // 初始化上下文 36 | let mut ctx = LocalContext::user(app_base); 37 | // 设置用户栈 38 | let mut user_stack = [0usize; 256]; 39 | *ctx.sp_mut() = user_stack.as_mut_ptr() as usize + core::mem::size_of_val(&user_stack); 40 | // 执行应用程序 41 | loop { 42 | unsafe { ctx.execute() }; 43 | 44 | use scause::{Exception, Trap}; 45 | match scause::read().cause() { 46 | Trap::Exception(Exception::UserEnvCall) => { 47 | use SyscallResult::*; 48 | match handle_syscall(&mut ctx) { 49 | Done => continue, 50 | Exit(code) => log::info!("app{i} exit with code {code}"), 51 | Error(id) => log::error!("app{i} call an unsupported syscall {}", id.0), 52 | } 53 | } 54 | trap => log::error!("app{i} was killed because of {trap:?}"), 55 | } 56 | // 清除指令缓存 57 | unsafe { core::arch::asm!("fence.i") }; 58 | break; 59 | } 60 | println!(); 61 | } 62 | 63 | system_reset(Shutdown, NoReason); 64 | unreachable!() 65 | } 66 | 67 | /// Rust 异常处理函数,以异常方式关机。 68 | #[panic_handler] 69 | fn panic(info: &core::panic::PanicInfo) -> ! { 70 | println!("{info}"); 71 | system_reset(Shutdown, SystemFailure); 72 | loop {} 73 | } 74 | 75 | enum SyscallResult { 76 | Done, 77 | Exit(usize), 78 | Error(SyscallId), 79 | } 80 | 81 | /// 处理系统调用,返回是否应该终止程序。 82 | fn handle_syscall(ctx: &mut LocalContext) -> SyscallResult { 83 | use syscall::{SyscallId as Id, SyscallResult as Ret}; 84 | 85 | let id = ctx.a(7).into(); 86 | let args = [ctx.a(0), ctx.a(1), ctx.a(2), ctx.a(3), ctx.a(4), ctx.a(5)]; 87 | match syscall::handle(Caller { entity: 0, flow: 0 }, id, args) { 88 | Ret::Done(ret) => match id { 89 | Id::EXIT => SyscallResult::Exit(ctx.a(0)), 90 | _ => { 91 | *ctx.a_mut(0) = ret as _; 92 | ctx.move_next(); 93 | SyscallResult::Done 94 | } 95 | }, 96 | Ret::Unsupported(id) => SyscallResult::Error(id), 97 | } 98 | } 99 | 100 | /// 各种接口库的实现 101 | mod impls { 102 | use syscall::{STDDEBUG, STDOUT}; 103 | 104 | pub struct Console; 105 | 106 | impl rcore_console::Console for Console { 107 | #[inline] 108 | fn put_char(&self, c: u8) { 109 | #[allow(deprecated)] 110 | sbi_rt::legacy::console_putchar(c as _); 111 | } 112 | } 113 | 114 | pub struct SyscallContext; 115 | 116 | impl syscall::IO for SyscallContext { 117 | fn write(&self, _caller: syscall::Caller, fd: usize, buf: usize, count: usize) -> isize { 118 | match fd { 119 | STDOUT | STDDEBUG => { 120 | print!("{}", unsafe { 121 | core::str::from_utf8_unchecked(core::slice::from_raw_parts( 122 | buf as *const u8, 123 | count, 124 | )) 125 | }); 126 | count as _ 127 | } 128 | _ => { 129 | rcore_console::log::error!("unsupported fd: {fd}"); 130 | -1 131 | } 132 | } 133 | } 134 | } 135 | 136 | impl syscall::Process for SyscallContext { 137 | #[inline] 138 | fn exit(&self, _caller: syscall::Caller, _status: usize) -> isize { 139 | 0 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /ch3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch3" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | riscv = "0.10.1" 10 | 11 | linker = { path = "../linker" } 12 | rcore-console = { path = "../console" } 13 | kernel-context = { path = "../kernel-context" } 14 | syscall = { path = "../syscall", features = ["kernel"] } 15 | 16 | [build-dependencies] 17 | linker = { path = "../linker" } 18 | 19 | [features] 20 | coop = [] 21 | -------------------------------------------------------------------------------- /ch3/README.md: -------------------------------------------------------------------------------- 1 | # 第三章 2 | -------------------------------------------------------------------------------- /ch3/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch3/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | mod task; 7 | 8 | #[macro_use] 9 | extern crate rcore_console; 10 | 11 | use impls::{Console, SyscallContext}; 12 | use rcore_console::log; 13 | use riscv::register::*; 14 | use sbi_rt::*; 15 | use task::TaskControlBlock; 16 | 17 | // 应用程序内联进来。 18 | core::arch::global_asm!(include_str!(env!("APP_ASM"))); 19 | // 应用程序数量。 20 | const APP_CAPACITY: usize = 32; 21 | // 定义内核入口。 22 | linker::boot0!(rust_main; stack = (APP_CAPACITY + 2) * 4096); 23 | 24 | extern "C" fn rust_main() -> ! { 25 | // bss 段清零 26 | unsafe { linker::KernelLayout::locate().zero_bss() }; 27 | // 初始化 `console` 28 | rcore_console::init_console(&Console); 29 | rcore_console::set_log_level(option_env!("LOG")); 30 | rcore_console::test_log(); 31 | // 初始化 syscall 32 | syscall::init_io(&SyscallContext); 33 | syscall::init_process(&SyscallContext); 34 | syscall::init_scheduling(&SyscallContext); 35 | syscall::init_clock(&SyscallContext); 36 | // 任务控制块 37 | let mut tcbs = [TaskControlBlock::ZERO; APP_CAPACITY]; 38 | let mut index_mod = 0; 39 | // 初始化 40 | for (i, app) in linker::AppMeta::locate().iter().enumerate() { 41 | let entry = app.as_ptr() as usize; 42 | log::info!("load app{i} to {entry:#x}"); 43 | tcbs[i].init(entry); 44 | index_mod += 1; 45 | } 46 | println!(); 47 | // 打开中断 48 | unsafe { sie::set_stimer() }; 49 | // 多道执行 50 | let mut remain = index_mod; 51 | let mut i = 0usize; 52 | while remain > 0 { 53 | let tcb = &mut tcbs[i]; 54 | if !tcb.finish { 55 | loop { 56 | #[cfg(not(feature = "coop"))] 57 | sbi_rt::set_timer(time::read64() + 12500); 58 | unsafe { tcb.execute() }; 59 | 60 | use scause::*; 61 | let finish = match scause::read().cause() { 62 | Trap::Interrupt(Interrupt::SupervisorTimer) => { 63 | sbi_rt::set_timer(u64::MAX); 64 | log::trace!("app{i} timeout"); 65 | false 66 | } 67 | Trap::Exception(Exception::UserEnvCall) => { 68 | use task::SchedulingEvent as Event; 69 | match tcb.handle_syscall() { 70 | Event::None => continue, 71 | Event::Exit(code) => { 72 | log::info!("app{i} exit with code {code}"); 73 | true 74 | } 75 | Event::Yield => { 76 | log::debug!("app{i} yield"); 77 | false 78 | } 79 | Event::UnsupportedSyscall(id) => { 80 | log::error!("app{i} call an unsupported syscall {}", id.0); 81 | true 82 | } 83 | } 84 | } 85 | Trap::Exception(e) => { 86 | log::error!("app{i} was killed by {e:?}"); 87 | true 88 | } 89 | Trap::Interrupt(ir) => { 90 | log::error!("app{i} was killed by an unexpected interrupt {ir:?}"); 91 | true 92 | } 93 | }; 94 | if finish { 95 | tcb.finish = true; 96 | remain -= 1; 97 | } 98 | break; 99 | } 100 | } 101 | i = (i + 1) % index_mod; 102 | } 103 | system_reset(Shutdown, NoReason); 104 | unreachable!() 105 | } 106 | 107 | /// Rust 异常处理函数,以异常方式关机。 108 | #[panic_handler] 109 | fn panic(info: &core::panic::PanicInfo) -> ! { 110 | println!("{info}"); 111 | system_reset(Shutdown, SystemFailure); 112 | loop {} 113 | } 114 | 115 | /// 各种接口库的实现 116 | mod impls { 117 | use syscall::*; 118 | 119 | pub struct Console; 120 | 121 | impl rcore_console::Console for Console { 122 | #[inline] 123 | fn put_char(&self, c: u8) { 124 | #[allow(deprecated)] 125 | sbi_rt::legacy::console_putchar(c as _); 126 | } 127 | } 128 | 129 | pub struct SyscallContext; 130 | 131 | impl IO for SyscallContext { 132 | #[inline] 133 | fn write(&self, _caller: syscall::Caller, fd: usize, buf: usize, count: usize) -> isize { 134 | match fd { 135 | STDOUT | STDDEBUG => { 136 | print!("{}", unsafe { 137 | core::str::from_utf8_unchecked(core::slice::from_raw_parts( 138 | buf as *const u8, 139 | count, 140 | )) 141 | }); 142 | count as _ 143 | } 144 | _ => { 145 | rcore_console::log::error!("unsupported fd: {fd}"); 146 | -1 147 | } 148 | } 149 | } 150 | } 151 | 152 | impl Process for SyscallContext { 153 | #[inline] 154 | fn exit(&self, _caller: syscall::Caller, _status: usize) -> isize { 155 | 0 156 | } 157 | } 158 | 159 | impl Scheduling for SyscallContext { 160 | #[inline] 161 | fn sched_yield(&self, _caller: syscall::Caller) -> isize { 162 | 0 163 | } 164 | } 165 | 166 | impl Clock for SyscallContext { 167 | #[inline] 168 | fn clock_gettime(&self, _caller: syscall::Caller, clock_id: ClockId, tp: usize) -> isize { 169 | match clock_id { 170 | ClockId::CLOCK_MONOTONIC => { 171 | let time = riscv::register::time::read() * 10000 / 125; 172 | *unsafe { &mut *(tp as *mut TimeSpec) } = TimeSpec { 173 | tv_sec: time / 1_000_000_000, 174 | tv_nsec: time % 1_000_000_000, 175 | }; 176 | 0 177 | } 178 | _ => -1, 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /ch3/src/task.rs: -------------------------------------------------------------------------------- 1 | use kernel_context::LocalContext; 2 | use syscall::{Caller, SyscallId}; 3 | 4 | /// 任务控制块。 5 | /// 6 | /// 包含任务的上下文、状态和资源。 7 | pub struct TaskControlBlock { 8 | ctx: LocalContext, 9 | pub finish: bool, 10 | stack: [usize; 256], 11 | } 12 | 13 | /// 调度事件。 14 | pub enum SchedulingEvent { 15 | None, 16 | Yield, 17 | Exit(usize), 18 | UnsupportedSyscall(SyscallId), 19 | } 20 | 21 | impl TaskControlBlock { 22 | pub const ZERO: Self = Self { 23 | ctx: LocalContext::empty(), 24 | finish: false, 25 | stack: [0; 256], 26 | }; 27 | 28 | /// 初始化一个任务。 29 | pub fn init(&mut self, entry: usize) { 30 | self.stack.fill(0); 31 | self.finish = false; 32 | self.ctx = LocalContext::user(entry); 33 | *self.ctx.sp_mut() = self.stack.as_ptr() as usize + core::mem::size_of_val(&self.stack); 34 | } 35 | 36 | /// 执行此任务。 37 | #[inline] 38 | pub unsafe fn execute(&mut self) { 39 | self.ctx.execute(); 40 | } 41 | 42 | /// 处理系统调用,返回是否应该终止程序。 43 | pub fn handle_syscall(&mut self) -> SchedulingEvent { 44 | use syscall::{SyscallId as Id, SyscallResult as Ret}; 45 | use SchedulingEvent as Event; 46 | 47 | let id = self.ctx.a(7).into(); 48 | let args = [ 49 | self.ctx.a(0), 50 | self.ctx.a(1), 51 | self.ctx.a(2), 52 | self.ctx.a(3), 53 | self.ctx.a(4), 54 | self.ctx.a(5), 55 | ]; 56 | match syscall::handle(Caller { entity: 0, flow: 0 }, id, args) { 57 | Ret::Done(ret) => match id { 58 | Id::EXIT => Event::Exit(self.ctx.a(0)), 59 | Id::SCHED_YIELD => { 60 | *self.ctx.a_mut(0) = ret as _; 61 | self.ctx.move_next(); 62 | Event::Yield 63 | } 64 | _ => { 65 | *self.ctx.a_mut(0) = ret as _; 66 | self.ctx.move_next(); 67 | Event::None 68 | } 69 | }, 70 | Ret::Unsupported(_) => Event::UnsupportedSyscall(id), 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ch4/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch4" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | xmas-elf = "0.8.0" 10 | riscv = "0.10.1" 11 | 12 | linker = { path = "../linker" } 13 | rcore-console = { path = "../console" } 14 | kernel-context = { path = "../kernel-context", features = ["foreign"] } 15 | kernel-alloc = { path = "../kernel-alloc" } 16 | kernel-vm = { path = "../kernel-vm" } 17 | syscall = { path = "../syscall", features = ["kernel"] } 18 | 19 | [build-dependencies] 20 | linker = { path = "../linker" } 21 | -------------------------------------------------------------------------------- /ch4/README.md: -------------------------------------------------------------------------------- 1 | # 第四章 2 | -------------------------------------------------------------------------------- /ch4/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch4/src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::Sv39Manager; 2 | use alloc::alloc::alloc_zeroed; 3 | use core::{alloc::Layout, str::FromStr}; 4 | use kernel_context::{foreign::ForeignContext, LocalContext}; 5 | use kernel_vm::{ 6 | page_table::{MmuMeta, Sv39, VAddr, VmFlags, PPN, VPN}, 7 | AddressSpace, 8 | }; 9 | use rcore_console::log; 10 | use xmas_elf::{ 11 | header::{self, HeaderPt2, Machine}, 12 | program, ElfFile, 13 | }; 14 | 15 | /// 进程。 16 | pub struct Process { 17 | pub context: ForeignContext, 18 | pub address_space: AddressSpace, 19 | } 20 | 21 | impl Process { 22 | pub fn new(elf: ElfFile) -> Option { 23 | let entry = match elf.header.pt2 { 24 | HeaderPt2::Header64(pt2) 25 | if pt2.type_.as_type() == header::Type::Executable 26 | && pt2.machine.as_machine() == Machine::RISC_V => 27 | { 28 | pt2.entry_point as usize 29 | } 30 | _ => None?, 31 | }; 32 | 33 | const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS; 34 | const PAGE_MASK: usize = PAGE_SIZE - 1; 35 | 36 | let mut address_space = AddressSpace::new(); 37 | for program in elf.program_iter() { 38 | if !matches!(program.get_type(), Ok(program::Type::Load)) { 39 | continue; 40 | } 41 | 42 | let off_file = program.offset() as usize; 43 | let len_file = program.file_size() as usize; 44 | let off_mem = program.virtual_addr() as usize; 45 | let end_mem = off_mem + program.mem_size() as usize; 46 | assert_eq!(off_file & PAGE_MASK, off_mem & PAGE_MASK); 47 | 48 | let mut flags: [u8; 5] = *b"U___V"; 49 | if program.flags().is_execute() { 50 | flags[1] = b'X'; 51 | } 52 | if program.flags().is_write() { 53 | flags[2] = b'W'; 54 | } 55 | if program.flags().is_read() { 56 | flags[3] = b'R'; 57 | } 58 | address_space.map( 59 | VAddr::new(off_mem).floor()..VAddr::new(end_mem).ceil(), 60 | &elf.input[off_file..][..len_file], 61 | off_mem & PAGE_MASK, 62 | VmFlags::from_str(unsafe { core::str::from_utf8_unchecked(&flags) }).unwrap(), 63 | ); 64 | } 65 | let stack = unsafe { 66 | alloc_zeroed(Layout::from_size_align_unchecked( 67 | 2 << Sv39::PAGE_BITS, 68 | 1 << Sv39::PAGE_BITS, 69 | )) 70 | }; 71 | address_space.map_extern( 72 | VPN::new((1 << 26) - 2)..VPN::new(1 << 26), 73 | PPN::new(stack as usize >> Sv39::PAGE_BITS), 74 | VmFlags::build_from_str("U_WRV"), 75 | ); 76 | 77 | log::info!("process entry = {:#x}", entry); 78 | 79 | let mut context = LocalContext::user(entry); 80 | let satp = (8 << 60) | address_space.root_ppn().val(); 81 | *context.sp_mut() = 1 << 38; 82 | Some(Self { 83 | context: ForeignContext { context, satp }, 84 | address_space, 85 | }) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ch5/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch5" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["zflcs <1491657576@qq.com>"] 6 | 7 | [dependencies] 8 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 9 | xmas-elf = "0.8.0" 10 | riscv = "0.10.1" 11 | spin = "0.9" 12 | 13 | linker = { path = "../linker" } 14 | rcore-console = { path = "../console" } 15 | kernel-context = { path = "../kernel-context", features = ["foreign"] } 16 | kernel-alloc = { path = "../kernel-alloc" } 17 | kernel-vm = { path = "../kernel-vm" } 18 | syscall = { path = "../syscall", features = ["kernel"] } 19 | rcore-task-manage = { path = "../task-manage", features = ["proc"] } 20 | 21 | [build-dependencies] 22 | linker = { path = "../linker" } 23 | -------------------------------------------------------------------------------- /ch5/README.md: -------------------------------------------------------------------------------- 1 | # 第五章 2 | 3 | #### 目前基本上实现了进程管理 4 | #### 存在的问题 5 | * `exit_code`:因为进程不存在内核栈,`exit` 进入内核之后,会直接删除 `PCB`,目前是直接写死的 333 6 | * `wait` 系统调用:等待任意的子进程结束,但是由于子进程 `exit` 之后会直接删除,父子关系也会直接断开,所以 `wait` 系统调用的语义产生了变化,导致 `fork` 相关的测例均不能通过 7 | -------------------------------------------------------------------------------- /ch5/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch5/src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::{map_portal, Sv39Manager}; 2 | use alloc::alloc::alloc_zeroed; 3 | use core::alloc::Layout; 4 | use core::str::FromStr; 5 | use kernel_context::{foreign::ForeignContext, LocalContext}; 6 | use kernel_vm::{ 7 | page_table::{MmuMeta, Sv39, VAddr, VmFlags, PPN, VPN}, 8 | AddressSpace, 9 | }; 10 | use rcore_task_manage::ProcId; 11 | use xmas_elf::{ 12 | header::{self, HeaderPt2, Machine}, 13 | program, ElfFile, 14 | }; 15 | 16 | /// 进程。 17 | pub struct Process { 18 | /// 不可变 19 | pub pid: ProcId, 20 | /// 可变 21 | pub context: ForeignContext, 22 | pub address_space: AddressSpace, 23 | } 24 | 25 | impl Process { 26 | pub fn exec(&mut self, elf: ElfFile) { 27 | let proc = Process::from_elf(elf).unwrap(); 28 | self.address_space = proc.address_space; 29 | self.context = proc.context; 30 | } 31 | 32 | pub fn fork(&mut self) -> Option { 33 | // 子进程 pid 34 | let pid = ProcId::new(); 35 | // 复制父进程地址空间 36 | let parent_addr_space = &self.address_space; 37 | let mut address_space: AddressSpace = AddressSpace::new(); 38 | parent_addr_space.cloneself(&mut address_space); 39 | map_portal(&address_space); 40 | // 复制父进程上下文 41 | let context = self.context.context.clone(); 42 | let satp = (8 << 60) | address_space.root_ppn().val(); 43 | let foreign_ctx = ForeignContext { context, satp }; 44 | Some(Self { 45 | pid, 46 | context: foreign_ctx, 47 | address_space, 48 | }) 49 | } 50 | 51 | pub fn from_elf(elf: ElfFile) -> Option { 52 | let entry = match elf.header.pt2 { 53 | HeaderPt2::Header64(pt2) 54 | if pt2.type_.as_type() == header::Type::Executable 55 | && pt2.machine.as_machine() == Machine::RISC_V => 56 | { 57 | pt2.entry_point as usize 58 | } 59 | _ => None?, 60 | }; 61 | 62 | const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS; 63 | const PAGE_MASK: usize = PAGE_SIZE - 1; 64 | 65 | let mut address_space = AddressSpace::new(); 66 | for program in elf.program_iter() { 67 | if !matches!(program.get_type(), Ok(program::Type::Load)) { 68 | continue; 69 | } 70 | 71 | let off_file = program.offset() as usize; 72 | let len_file = program.file_size() as usize; 73 | let off_mem = program.virtual_addr() as usize; 74 | let end_mem = off_mem + program.mem_size() as usize; 75 | assert_eq!(off_file & PAGE_MASK, off_mem & PAGE_MASK); 76 | 77 | let mut flags: [u8; 5] = *b"U___V"; 78 | if program.flags().is_execute() { 79 | flags[1] = b'X'; 80 | } 81 | if program.flags().is_write() { 82 | flags[2] = b'W'; 83 | } 84 | if program.flags().is_read() { 85 | flags[3] = b'R'; 86 | } 87 | address_space.map( 88 | VAddr::new(off_mem).floor()..VAddr::new(end_mem).ceil(), 89 | &elf.input[off_file..][..len_file], 90 | off_mem & PAGE_MASK, 91 | VmFlags::from_str(unsafe { core::str::from_utf8_unchecked(&flags) }).unwrap(), 92 | ); 93 | } 94 | // 映射用户栈 95 | let stack = unsafe { 96 | alloc_zeroed(Layout::from_size_align_unchecked( 97 | 2 << Sv39::PAGE_BITS, 98 | 1 << Sv39::PAGE_BITS, 99 | )) 100 | }; 101 | address_space.map_extern( 102 | VPN::new((1 << 26) - 2)..VPN::new(1 << 26), 103 | PPN::new(stack as usize >> Sv39::PAGE_BITS), 104 | VmFlags::build_from_str("U_WRV"), 105 | ); 106 | // 映射异界传送门 107 | map_portal(&address_space); 108 | 109 | let mut context = LocalContext::user(entry); 110 | let satp = (8 << 60) | address_space.root_ppn().val(); 111 | *context.sp_mut() = 1 << 38; 112 | Some(Self { 113 | pid: ProcId::new(), 114 | context: ForeignContext { context, satp }, 115 | address_space, 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /ch5/src/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::process::Process; 2 | use alloc::collections::{BTreeMap, VecDeque}; 3 | use rcore_task_manage::{Manage, PManager, ProcId, Schedule}; 4 | 5 | pub static mut PROCESSOR: PManager = PManager::new(); 6 | 7 | /// 任务管理器 8 | /// `tasks` 中保存所有的任务实体 9 | /// `ready_queue` 删除任务的实体 10 | pub struct ProcManager { 11 | tasks: BTreeMap, 12 | ready_queue: VecDeque, 13 | } 14 | 15 | impl ProcManager { 16 | /// 新建任务管理器 17 | pub fn new() -> Self { 18 | Self { 19 | tasks: BTreeMap::new(), 20 | ready_queue: VecDeque::new(), 21 | } 22 | } 23 | } 24 | 25 | impl Manage for ProcManager { 26 | /// 插入一个新任务 27 | #[inline] 28 | fn insert(&mut self, id: ProcId, task: Process) { 29 | self.tasks.insert(id, task); 30 | } 31 | /// 根据 id 获取对应的任务 32 | #[inline] 33 | fn get_mut(&mut self, id: ProcId) -> Option<&mut Process> { 34 | self.tasks.get_mut(&id) 35 | } 36 | /// 删除任务实体 37 | #[inline] 38 | fn delete(&mut self, id: ProcId) { 39 | self.tasks.remove(&id); 40 | } 41 | } 42 | 43 | impl Schedule for ProcManager { 44 | /// 添加 id 进入调度队列 45 | fn add(&mut self, id: ProcId) { 46 | self.ready_queue.push_back(id); 47 | } 48 | /// 从调度队列中取出 id 49 | fn fetch(&mut self) -> Option { 50 | self.ready_queue.pop_front() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch6" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["tkf2019 "] 6 | 7 | [dependencies] 8 | virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } 9 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 10 | xmas-elf = "0.8.0" 11 | riscv = "0.10.1" 12 | spin = "0.9" 13 | 14 | linker = { path = "../linker" } 15 | rcore-console = { path = "../console" } 16 | kernel-context = { path = "../kernel-context", features = ["foreign"] } 17 | kernel-alloc = { path = "../kernel-alloc" } 18 | kernel-vm = { path = "../kernel-vm" } 19 | syscall = { path = "../syscall", features = ["kernel"] } 20 | rcore-task-manage = { path = "../task-manage", features = ["proc"] } 21 | easy-fs = { path = "../easy-fs" } 22 | 23 | [build-dependencies] 24 | linker = { path = "../linker" } 25 | -------------------------------------------------------------------------------- /ch6/README.md: -------------------------------------------------------------------------------- 1 | # 第六章 2 | 3 | 目前的进度: 4 | 5 | - 在 `xtask` 中对 `easy-fs` 进行封装制作镜像,在 QEMU 中添加虚拟块设备 6 | - 在 `ch6` 框架下利用现有模块重新实现 `VirtIO` 驱动,需要用到内核地址空间及当前的内存分配机制 7 | - 修复内核栈溢出等关于内存的问题 8 | - 增加下述接口并重现第五章的功能,移除 `loader`,通过 `easy-fs` 加载程序并执行 9 | - 完成 `IO` 系统调用 `read/write/open/close`,通过原来的 `filetest_simple` 和 `cat_filea` 测试 10 | 11 | 总结: 12 | 13 | - `easy-fs` 关于模块化的设计已经相对比较完善了,主要是如何将其接入现有框架 14 | - 还需要继续讨论内存分配和回收的问题 15 | - `fuse` 的设计对于文件系统模块化有很好的启发作用 16 | - 现在的 `driver` 耦合性还比较强,还需要进一步完善 17 | - 多级目录参考实现,目前还是只实现了 `root_inode` 下的扁平目录 18 | 19 | ## EasyFS 20 | 21 | ### Block 22 | 23 | ```rust 24 | pub trait BlockDevice: Send + Sync + Any { 25 | fn read_block(&self, block_id: usize, buf: &mut [u8]); 26 | fn write_block(&self, block_id: usize, buf: &[u8]); 27 | fn handle_irq(&self); 28 | } 29 | ``` 30 | 31 | - 需要外部提供块设备驱动,对块缓存层暴露根据块号读写块设备的接口。 32 | - 这个部分不需要改动,目前已经可以和块设备驱动模块进行交互,具备灵活性和泛用性。 33 | 34 | ```rust 35 | pub struct BlockCache { 36 | cache: Vec, 37 | block_id: usize, 38 | block_device: Arc, 39 | modified: bool, 40 | } 41 | ``` 42 | 43 | - 缓冲区的生命周期结束后其占用的内存空间会被回收,同时根据脏位判断是否要写回块设备。 44 | - 这个部分可以考虑不同的查找和替换算法,目前 `BlockCacheManager` 默认实现了 `get_block_cache` 方法;实现其他算法的关键在于使用的数据结构,可以利用 Rust 的泛型机制。 45 | 46 | ### Layout 47 | 48 | ```txt 49 | +------------+--------------+-------+-------------+------+ 50 | | SuperBlock | Inode Bitmap | Inode | Data Bitmap | Data | 51 | +------------+--------------+-------+-------------+------+ 52 | ``` 53 | 54 | - 往年留给同学们的实验是硬链接,需要修改文件系统内的 Inode 结构,增加持久化的链接数量信息。模块化的设计不太容易支持这种结构本身的修改,所以如果保留该实验题目,应该预先给出完整的结构体,不要求同学们修改这一部分。 55 | - 目前大部分实现不需要改动,与 `BlockCacheManager` 的交互已经很好地屏蔽了内部信息。 56 | 57 | ### VFS 58 | 59 | 改动的主体,需要对暴露的接口进行修改和完善来实现更好的抽象。 60 | 61 | ```rust 62 | pub struct Inode { 63 | block_id: usize, 64 | block_offset: usize, 65 | fs: Arc>, 66 | block_device: Arc, 67 | } 68 | ``` 69 | 70 | - `Inode`结构是暴露给上层的,可以让调用者对文件和目录进行直接操作。 71 | - 目前的文件系统默认是扁平化的设计,无法访问多级目录,为了增加灵活性,可以将一部分查找接口暴露给上层进行定义和使用: 72 | 73 | ```rust 74 | pub trait FSManager { 75 | /// Open a file 76 | fn open(&self, path: &str, flags: OpenFlags) -> Option>; 77 | 78 | /// Find a file 79 | fn find(&self, path: &str) -> Option>; 80 | 81 | /// Create a hard link to source file 82 | fn link(&self, src: &str, dst: &str) -> isize; 83 | 84 | /// Remove a hard link 85 | fn unlink(&self, path: &str) -> isize; 86 | 87 | /// List inodes under the target directory 88 | fn readdir(&self, path: &str) -> Option>; 89 | } 90 | ``` 91 | 92 | - 内核可以根据提供的接口自行定义按路径查找的逻辑,`easy-fs` 的实现中,已经给出了 `Inode` 的大部分操作,列举如下: 93 | 94 | ```rust 95 | impl Inode { 96 | /// Create a vfs inode 97 | pub fn new( 98 | block_id: u32, 99 | block_offset: usize, 100 | fs: Arc>, 101 | block_device: Arc, 102 | ) -> Self; 103 | 104 | /// Call a function over a disk inode to read it 105 | fn read_disk_inode(&self, f: impl FnOnce(&DiskInode) -> V) -> V; 106 | 107 | /// Call a function over a disk inode to modify it 108 | fn modify_disk_inode(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V; 109 | 110 | /// Find inode under a disk inode by name 111 | fn find_inode_id(&self, name: &str, disk_inode: &DiskInode) -> Option; 112 | 113 | /// Find inode under current inode by name 114 | pub fn find(&self, name: &str) -> Option>; 115 | 116 | /// Increase the size of a disk inode 117 | fn increase_size( 118 | &self, 119 | new_size: u32, 120 | disk_inode: &mut DiskInode, 121 | fs: &mut MutexGuard, 122 | ); 123 | 124 | /// Create inode under current inode by name. 125 | /// Attention: use find previously to ensure the new file not existing. 126 | pub fn create(&self, name: &str) -> Option>; 127 | 128 | /// List inodes by id under current inode 129 | pub fn readdir(&self) -> Vec; 130 | 131 | /// Read data from current inode 132 | pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize; 133 | 134 | /// Write data to current inode 135 | pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize; 136 | 137 | /// Clear the data in current inode 138 | pub fn clear(&self); 139 | } 140 | ``` 141 | 142 | - 内核对 `FileHandle` 进行维护时,可以自行实现路径到 `FileHandle` 的映射缓存(参考 Linux 相关代码 143 | -------------------------------------------------------------------------------- /ch6/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch6/src/fs.rs: -------------------------------------------------------------------------------- 1 | use crate::virtio_block::BLOCK_DEVICE; 2 | use alloc::{string::String, sync::Arc, vec::Vec}; 3 | use easy_fs::{EasyFileSystem, FSManager, FileHandle, Inode, OpenFlags}; 4 | use spin::Lazy; 5 | 6 | pub static FS: Lazy = Lazy::new(|| FileSystem { 7 | root: EasyFileSystem::root_inode(&EasyFileSystem::open(BLOCK_DEVICE.clone())), 8 | }); 9 | 10 | pub struct FileSystem { 11 | root: Inode, 12 | } 13 | 14 | impl FSManager for FileSystem { 15 | fn open(&self, path: &str, flags: OpenFlags) -> Option> { 16 | let (readable, writable) = flags.read_write(); 17 | if flags.contains(OpenFlags::CREATE) { 18 | if let Some(inode) = self.find(path) { 19 | // Clear size 20 | inode.clear(); 21 | Some(Arc::new(FileHandle::new(readable, writable, inode))) 22 | } else { 23 | // Create new file 24 | self.root 25 | .create(path) 26 | .map(|new_inode| Arc::new(FileHandle::new(readable, writable, new_inode))) 27 | } 28 | } else { 29 | self.find(path).map(|inode| { 30 | if flags.contains(OpenFlags::TRUNC) { 31 | inode.clear(); 32 | } 33 | Arc::new(FileHandle::new(readable, writable, inode)) 34 | }) 35 | } 36 | } 37 | 38 | fn find(&self, path: &str) -> Option> { 39 | self.root.find(path) 40 | } 41 | 42 | fn readdir(&self, _path: &str) -> Option> { 43 | Some(self.root.readdir()) 44 | } 45 | 46 | fn link(&self, _src: &str, _dst: &str) -> isize { 47 | unimplemented!() 48 | } 49 | 50 | fn unlink(&self, _path: &str) -> isize { 51 | unimplemented!() 52 | } 53 | } 54 | 55 | pub fn read_all(fd: Arc) -> Vec { 56 | let mut offset = 0usize; 57 | let mut buffer = [0u8; 512]; 58 | let mut v: Vec = Vec::new(); 59 | if let Some(inode) = &fd.inode { 60 | loop { 61 | let len = inode.read_at(offset, &mut buffer); 62 | if len == 0 { 63 | break; 64 | } 65 | offset += len; 66 | v.extend_from_slice(&buffer[..len]); 67 | } 68 | } 69 | v 70 | } 71 | -------------------------------------------------------------------------------- /ch6/src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::{map_portal, Sv39Manager}; 2 | use alloc::{alloc::alloc_zeroed, vec::Vec}; 3 | use core::{alloc::Layout, str::FromStr}; 4 | use easy_fs::FileHandle; 5 | use kernel_context::{foreign::ForeignContext, LocalContext}; 6 | use kernel_vm::{ 7 | page_table::{MmuMeta, Sv39, VAddr, VmFlags, PPN, VPN}, 8 | AddressSpace, 9 | }; 10 | use rcore_task_manage::ProcId; 11 | use spin::Mutex; 12 | use xmas_elf::{ 13 | header::{self, HeaderPt2, Machine}, 14 | program, ElfFile, 15 | }; 16 | 17 | /// 进程。 18 | pub struct Process { 19 | /// 不可变 20 | pub pid: ProcId, 21 | /// 可变 22 | pub context: ForeignContext, 23 | pub address_space: AddressSpace, 24 | /// 文件描述符表 25 | pub fd_table: Vec>>, 26 | } 27 | 28 | impl Process { 29 | pub fn exec(&mut self, elf: ElfFile) { 30 | let proc = Process::from_elf(elf).unwrap(); 31 | self.address_space = proc.address_space; 32 | self.context = proc.context; 33 | } 34 | 35 | pub fn fork(&mut self) -> Option { 36 | // 子进程 pid 37 | let pid = ProcId::new(); 38 | // 复制父进程地址空间 39 | let parent_addr_space = &self.address_space; 40 | let mut address_space: AddressSpace = AddressSpace::new(); 41 | parent_addr_space.cloneself(&mut address_space); 42 | map_portal(&address_space); 43 | // 复制父进程上下文 44 | let context = self.context.context.clone(); 45 | let satp = (8 << 60) | address_space.root_ppn().val(); 46 | let foreign_ctx = ForeignContext { context, satp }; 47 | // 复制父进程文件符描述表 48 | let mut new_fd_table: Vec>> = Vec::new(); 49 | for fd in self.fd_table.iter_mut() { 50 | if let Some(file) = fd { 51 | new_fd_table.push(Some(Mutex::new(file.get_mut().clone()))); 52 | } else { 53 | new_fd_table.push(None); 54 | } 55 | } 56 | Some(Self { 57 | pid, 58 | context: foreign_ctx, 59 | address_space, 60 | fd_table: new_fd_table, 61 | }) 62 | } 63 | 64 | pub fn from_elf(elf: ElfFile) -> Option { 65 | let entry = match elf.header.pt2 { 66 | HeaderPt2::Header64(pt2) 67 | if pt2.type_.as_type() == header::Type::Executable 68 | && pt2.machine.as_machine() == Machine::RISC_V => 69 | { 70 | pt2.entry_point as usize 71 | } 72 | _ => None?, 73 | }; 74 | 75 | const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS; 76 | const PAGE_MASK: usize = PAGE_SIZE - 1; 77 | 78 | let mut address_space = AddressSpace::new(); 79 | for program in elf.program_iter() { 80 | if !matches!(program.get_type(), Ok(program::Type::Load)) { 81 | continue; 82 | } 83 | 84 | let off_file = program.offset() as usize; 85 | let len_file = program.file_size() as usize; 86 | let off_mem = program.virtual_addr() as usize; 87 | let end_mem = off_mem + program.mem_size() as usize; 88 | assert_eq!(off_file & PAGE_MASK, off_mem & PAGE_MASK); 89 | 90 | let mut flags: [u8; 5] = *b"U___V"; 91 | if program.flags().is_execute() { 92 | flags[1] = b'X'; 93 | } 94 | if program.flags().is_write() { 95 | flags[2] = b'W'; 96 | } 97 | if program.flags().is_read() { 98 | flags[3] = b'R'; 99 | } 100 | address_space.map( 101 | VAddr::new(off_mem).floor()..VAddr::new(end_mem).ceil(), 102 | &elf.input[off_file..][..len_file], 103 | off_mem & PAGE_MASK, 104 | VmFlags::from_str(unsafe { core::str::from_utf8_unchecked(&flags) }).unwrap(), 105 | ); 106 | } 107 | // 映射用户栈 108 | let stack = unsafe { 109 | alloc_zeroed(Layout::from_size_align_unchecked( 110 | 2 << Sv39::PAGE_BITS, 111 | 1 << Sv39::PAGE_BITS, 112 | )) 113 | }; 114 | address_space.map_extern( 115 | VPN::new((1 << 26) - 2)..VPN::new(1 << 26), 116 | PPN::new(stack as usize >> Sv39::PAGE_BITS), 117 | VmFlags::build_from_str("U_WRV"), 118 | ); 119 | // 映射异界传送门 120 | map_portal(&address_space); 121 | 122 | let mut context = LocalContext::user(entry); 123 | let satp = (8 << 60) | address_space.root_ppn().val(); 124 | *context.sp_mut() = 1 << 38; 125 | Some(Self { 126 | pid: ProcId::new(), 127 | context: ForeignContext { context, satp }, 128 | address_space, 129 | fd_table: vec![ 130 | // Stdin 131 | Some(Mutex::new(FileHandle::empty(true, false))), 132 | // Stdout 133 | Some(Mutex::new(FileHandle::empty(false, true))), 134 | ], 135 | }) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ch6/src/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::process::Process; 2 | use alloc::collections::{BTreeMap, VecDeque}; 3 | use rcore_task_manage::{Manage, PManager, ProcId, Schedule}; 4 | 5 | pub static mut PROCESSOR: PManager = PManager::new(); 6 | 7 | /// 任务管理器 8 | /// `tasks` 中保存所有的任务实体 9 | /// `ready_queue` 删除任务的实体 10 | pub struct ProcManager { 11 | tasks: BTreeMap, 12 | ready_queue: VecDeque, 13 | } 14 | 15 | impl ProcManager { 16 | /// 新建任务管理器 17 | pub fn new() -> Self { 18 | Self { 19 | tasks: BTreeMap::new(), 20 | ready_queue: VecDeque::new(), 21 | } 22 | } 23 | } 24 | 25 | impl Manage for ProcManager { 26 | /// 插入一个新任务 27 | #[inline] 28 | fn insert(&mut self, id: ProcId, task: Process) { 29 | self.tasks.insert(id, task); 30 | } 31 | /// 根据 id 获取对应的任务 32 | #[inline] 33 | fn get_mut(&mut self, id: ProcId) -> Option<&mut Process> { 34 | self.tasks.get_mut(&id) 35 | } 36 | /// 删除任务实体 37 | #[inline] 38 | fn delete(&mut self, id: ProcId) { 39 | self.tasks.remove(&id); 40 | } 41 | } 42 | 43 | impl Schedule for ProcManager { 44 | /// 添加 id 进入调度队列 45 | fn add(&mut self, id: ProcId) { 46 | self.ready_queue.push_back(id); 47 | } 48 | /// 从调度队列中取出 id 49 | fn fetch(&mut self) -> Option { 50 | self.ready_queue.pop_front() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch6/src/virtio_block.rs: -------------------------------------------------------------------------------- 1 | use crate::KERNEL_SPACE; 2 | use alloc::{ 3 | alloc::{alloc_zeroed, dealloc}, 4 | sync::Arc, 5 | }; 6 | use core::{alloc::Layout, ptr::NonNull}; 7 | use easy_fs::BlockDevice; 8 | use kernel_vm::page_table::{MmuMeta, Sv39, VAddr, VmFlags}; 9 | use spin::{Lazy, Mutex}; 10 | use virtio_drivers::{Hal, VirtIOBlk, VirtIOHeader}; 11 | 12 | const VIRTIO0: usize = 0x10001000; 13 | 14 | pub static BLOCK_DEVICE: Lazy> = Lazy::new(|| { 15 | Arc::new(unsafe { 16 | VirtIOBlock(Mutex::new( 17 | VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), 18 | )) 19 | }) 20 | }); 21 | 22 | struct VirtIOBlock(Mutex>); 23 | 24 | impl BlockDevice for VirtIOBlock { 25 | fn read_block(&self, block_id: usize, buf: &mut [u8]) { 26 | self.0 27 | .lock() 28 | .read_block(block_id, buf) 29 | .expect("Error when reading VirtIOBlk"); 30 | } 31 | fn write_block(&self, block_id: usize, buf: &[u8]) { 32 | self.0 33 | .lock() 34 | .write_block(block_id, buf) 35 | .expect("Error when writing VirtIOBlk"); 36 | } 37 | } 38 | 39 | struct VirtioHal; 40 | 41 | impl Hal for VirtioHal { 42 | fn dma_alloc(pages: usize) -> usize { 43 | // warn!("dma_alloc"); 44 | unsafe { 45 | alloc_zeroed(Layout::from_size_align_unchecked( 46 | pages << Sv39::PAGE_BITS, 47 | 1 << Sv39::PAGE_BITS, 48 | )) as _ 49 | } 50 | } 51 | 52 | fn dma_dealloc(paddr: usize, pages: usize) -> i32 { 53 | // warn!("dma_dealloc"); 54 | unsafe { 55 | dealloc( 56 | paddr as _, 57 | Layout::from_size_align_unchecked(pages << Sv39::PAGE_BITS, 1 << Sv39::PAGE_BITS), 58 | ) 59 | } 60 | 0 61 | } 62 | 63 | fn phys_to_virt(paddr: usize) -> usize { 64 | // warn!("p2v"); 65 | paddr 66 | } 67 | 68 | fn virt_to_phys(vaddr: usize) -> usize { 69 | // warn!("v2p"); 70 | const VALID: VmFlags = VmFlags::build_from_str("__V"); 71 | let ptr: NonNull = unsafe { 72 | KERNEL_SPACE 73 | .assume_init_ref() 74 | .translate(VAddr::new(vaddr), VALID) 75 | .unwrap() 76 | }; 77 | ptr.as_ptr() as usize 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ch7/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch7" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["scPointer "] 6 | 7 | [dependencies] 8 | virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } 9 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 10 | xmas-elf = "0.8.0" 11 | riscv = "0.10.1" 12 | spin = "0.9" 13 | 14 | linker = { path = "../linker" } 15 | rcore-console = { path = "../console" } 16 | kernel-context = { path = "../kernel-context", features = ["foreign"] } 17 | kernel-alloc = { path = "../kernel-alloc" } 18 | kernel-vm = { path = "../kernel-vm" } 19 | syscall = { path = "../syscall", features = ["kernel"] } 20 | rcore-task-manage = { path = "../task-manage", features = ["proc"] } 21 | easy-fs = { path = "../easy-fs" } 22 | signal = { path = "../signal" } 23 | signal-impl = { path = "../signal-impl" } 24 | 25 | [build-dependencies] 26 | linker = { path = "../linker" } 27 | -------------------------------------------------------------------------------- /ch7/README.md: -------------------------------------------------------------------------------- 1 | # 第七章 2 | 3 | 已提供信号部分的用户库接口及测例,但内核没有相关实现 4 | 5 | ## 主体实现 6 | 7 | 目前 `/ch7` 模块复制自 `/ch6`,已经有了基本的信号实现。 8 | 9 | 在 `/xtask/src/user.rs` 和 `/user/cases.toml` 中加入了第七章相关信息。 10 | 11 | ### 新增模块 12 | 13 | 目前新增加了四个模块: 14 | 15 | - `signal-defs` 模块。包含每个信号标号的具体含义 `enum SignalNo`和信号处理函数的定义 `struct SignalAction`。`syscall`模块需要它们,而信号的接口模块`signal`也需要它们。**考虑到 `syscall` 和 `signal` 谁依赖谁都不太合适,所以单独拆了一个非常简单的 `signal-defs` 出来,专门存放用户程序和内核都需要使用的信号定义** 16 | 17 | - `signal`模块。信号模块的定义,主要提供`pub trait Signal` 18 | 19 | - `signal-impl`模块。一个信号模块的参考实现。 20 | 21 | - `ch7`模块。第七章的主线代码。 22 | 23 | 它们(和部分已有模块)的依赖关系如下: 24 | 25 | ```mermaid 26 | graph TD; 27 | ch7-->signal-impl 28 | ch7-->signal 29 | ch7-->syscall 30 | user-->syscall 31 | signal-impl-->signal 32 | signal-->signal-defs 33 | syscall-->signal-defs 34 | signal-defs-->numeric-enum-macro 35 | ``` 36 | 37 | 其中 `numeric-enum-macro` 是一个外部库,提供了 `enum` 和整数类型之间的表达和转换关系。 38 | 39 | ## 用户测例 40 | 41 | 添加测例:`sig_ctrlc` `sig_simple` `sig_simple2` `sig_tests`,其中 `sig_ctrlc` `sig_simple` `sig_simple2` 可通过。 42 | 43 | 目前仅添加了信号相关的测例,后续还会加入其他测例。 44 | 45 | (本项目与原 `rCore-Tutorial-v3`对用户程序的部分接口有所不同,因此引入时会修改部分代码) 46 | 47 | ## 信号部分 48 | 49 | 目前已在 `/syscall/src/user.rs` 添加用户库对应需要的 syscall: 50 | 51 | - `kill` 发送信号 52 | - `sigaction` 设置信号处理函数 53 | - `sigprocmask` 修改信号掩码 54 | - `sigreturn` 从信号处理函数中返回 55 | 56 | 并添加 `/signal-defs`,包含一些用户程序和内核通用的信号标号和处理函数定义。 57 | 58 | > 这里 `SignalAction::mask` 使用 `usize` 而非 `i32`,是为了兼容将来可能会有的标号在 `[32,64)` 之间的实时信号。 59 | > 60 | > 这里信号标号使用 `SignalNo`,是为了与上面的 `mask` 区分,提示用户程序在 `kill()` 和 `sigaction()` 中应使用信号的标号,而在 `sigprocmask` 中应使用信号的掩码 61 | 62 | ### 额外添加的 syscall 和代码 63 | 64 | 由于信号模块依赖一些前面章节的 syscall,但它们还没有实现,所以这里也添加和修改了一些信号之外的 syscall 和代码: 65 | 66 | - 添加 `syscall: getpid`,应属于第五章。 67 | 68 | - 添加 `/user/src/lib.rs: sleep(period_ms: usize)` ,应属于第三章。这里为了适应用户程序,还在 `/syscall/lib/time.rs` 中添加了从毫秒数(`usize`)转换为 `TimeSpec`的方法 69 | 70 | - 添加 `/ch7/src/exit_process.rs` 这个方法实际上是原来的 `sys_exit` 。因为“进程退出”这一事件除了由 `sys_exit` 触发,也可能由信号触发,因此现在把这个过程抽离出来,其他事情发生时也会 71 | 72 | ## 后续工作的依赖问题 73 | 74 | 前面章节还有一些工作没有完成,而第七章的部分内容恰好对这些没完成的部分有依赖,因此目前功能还不够全面: 75 | 76 | - 缺少时钟中断,所以还不支持暂停应用的信号 77 | 78 | - 缺少 trait File,目前所有文件都来自fs,而stdin/stdout 是靠特判实现的,所以pipe还需要等一会 79 | 80 | - 第五章实现中,进程状态简化了,现在没法保存和获取进程退出时的 exit_code,wait 的非负数返回值只能使 333。但是信号相关的部分测例 `sig_tests`是需要检测退出时的返回值的。 81 | 82 | - 之前的rCore-Tutorial是有“用户程序阻塞在内核态”的机制的,比如说暂停的信号和pipe,即可能出现 83 | 84 | ```rust 85 | loop { 86 | 87 |     if ...... 88 |     suspend_current_and_run_next(); 89 | 90 | } 91 | ``` 92 | 93 | 的情况,但是现在的 `kernel-context` 似乎不支持这个机制了。这样上述的功能都需要重新考虑如何实现 94 | 95 | (顺便一提,本来 `stdin` 的串口输入也是需要这个机制的,但现在似乎整个内核会阻塞在`sbi_rt::legacy::console_getchar()` 上,把问题绕过去了) -------------------------------------------------------------------------------- /ch7/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch7/src/fs.rs: -------------------------------------------------------------------------------- 1 | use crate::virtio_block::BLOCK_DEVICE; 2 | use alloc::{string::String, sync::Arc, vec::Vec}; 3 | use easy_fs::{EasyFileSystem, FSManager, FileHandle, Inode, OpenFlags}; 4 | use spin::Lazy; 5 | 6 | pub static FS: Lazy = Lazy::new(|| FileSystem { 7 | root: EasyFileSystem::root_inode(&EasyFileSystem::open(BLOCK_DEVICE.clone())), 8 | }); 9 | 10 | pub struct FileSystem { 11 | root: Inode, 12 | } 13 | 14 | impl FSManager for FileSystem { 15 | fn open(&self, path: &str, flags: OpenFlags) -> Option> { 16 | let (readable, writable) = flags.read_write(); 17 | if flags.contains(OpenFlags::CREATE) { 18 | if let Some(inode) = self.find(path) { 19 | // Clear size 20 | inode.clear(); 21 | Some(Arc::new(FileHandle::new(readable, writable, inode))) 22 | } else { 23 | // Create new file 24 | self.root 25 | .create(path) 26 | .map(|new_inode| Arc::new(FileHandle::new(readable, writable, new_inode))) 27 | } 28 | } else { 29 | self.find(path).map(|inode| { 30 | if flags.contains(OpenFlags::TRUNC) { 31 | inode.clear(); 32 | } 33 | Arc::new(FileHandle::new(readable, writable, inode)) 34 | }) 35 | } 36 | } 37 | 38 | fn find(&self, path: &str) -> Option> { 39 | self.root.find(path) 40 | } 41 | 42 | fn readdir(&self, _path: &str) -> Option> { 43 | Some(self.root.readdir()) 44 | } 45 | 46 | fn link(&self, _src: &str, _dst: &str) -> isize { 47 | unimplemented!() 48 | } 49 | 50 | fn unlink(&self, _path: &str) -> isize { 51 | unimplemented!() 52 | } 53 | } 54 | 55 | pub fn read_all(fd: Arc) -> Vec { 56 | let mut offset = 0usize; 57 | let mut buffer = [0u8; 512]; 58 | let mut v: Vec = Vec::new(); 59 | if let Some(inode) = &fd.inode { 60 | loop { 61 | let len = inode.read_at(offset, &mut buffer); 62 | if len == 0 { 63 | break; 64 | } 65 | offset += len; 66 | v.extend_from_slice(&buffer[..len]); 67 | } 68 | } 69 | v 70 | } 71 | -------------------------------------------------------------------------------- /ch7/src/process.rs: -------------------------------------------------------------------------------- 1 | use crate::{map_portal, Sv39Manager}; 2 | use alloc::{alloc::alloc_zeroed, boxed::Box, vec::Vec}; 3 | use core::{alloc::Layout, str::FromStr}; 4 | use easy_fs::FileHandle; 5 | use kernel_context::{foreign::ForeignContext, LocalContext}; 6 | use kernel_vm::{ 7 | page_table::{MmuMeta, Sv39, VAddr, VmFlags, PPN, VPN}, 8 | AddressSpace, 9 | }; 10 | use rcore_task_manage::ProcId; 11 | use signal::Signal; 12 | use signal_impl::SignalImpl; 13 | use spin::Mutex; 14 | use xmas_elf::{ 15 | header::{self, HeaderPt2, Machine}, 16 | program, ElfFile, 17 | }; 18 | 19 | /// 进程。 20 | pub struct Process { 21 | /// 不可变 22 | pub pid: ProcId, 23 | /// 可变 24 | pub context: ForeignContext, 25 | pub address_space: AddressSpace, 26 | 27 | /// 文件描述符表 28 | pub fd_table: Vec>>, 29 | 30 | /// 信号模块 31 | pub signal: Box, 32 | } 33 | 34 | impl Process { 35 | pub fn exec(&mut self, elf: ElfFile) { 36 | let proc = Process::from_elf(elf).unwrap(); 37 | self.address_space = proc.address_space; 38 | self.context = proc.context; 39 | } 40 | 41 | pub fn fork(&mut self) -> Option { 42 | // 子进程 pid 43 | let pid = ProcId::new(); 44 | // 复制父进程地址空间 45 | let parent_addr_space = &self.address_space; 46 | let mut address_space: AddressSpace = AddressSpace::new(); 47 | parent_addr_space.cloneself(&mut address_space); 48 | map_portal(&address_space); 49 | // 复制父进程上下文 50 | let context = self.context.context.clone(); 51 | let satp = (8 << 60) | address_space.root_ppn().val(); 52 | let foreign_ctx = ForeignContext { context, satp }; 53 | // 复制父进程文件符描述表 54 | let mut new_fd_table: Vec>> = Vec::new(); 55 | for fd in self.fd_table.iter_mut() { 56 | if let Some(file) = fd { 57 | new_fd_table.push(Some(Mutex::new(file.get_mut().clone()))); 58 | } else { 59 | new_fd_table.push(None); 60 | } 61 | } 62 | Some(Self { 63 | pid, 64 | context: foreign_ctx, 65 | address_space, 66 | fd_table: new_fd_table, 67 | signal: self.signal.from_fork(), 68 | }) 69 | } 70 | 71 | pub fn from_elf(elf: ElfFile) -> Option { 72 | let entry = match elf.header.pt2 { 73 | HeaderPt2::Header64(pt2) 74 | if pt2.type_.as_type() == header::Type::Executable 75 | && pt2.machine.as_machine() == Machine::RISC_V => 76 | { 77 | pt2.entry_point as usize 78 | } 79 | _ => None?, 80 | }; 81 | 82 | const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS; 83 | const PAGE_MASK: usize = PAGE_SIZE - 1; 84 | 85 | let mut address_space = AddressSpace::new(); 86 | for program in elf.program_iter() { 87 | if !matches!(program.get_type(), Ok(program::Type::Load)) { 88 | continue; 89 | } 90 | 91 | let off_file = program.offset() as usize; 92 | let len_file = program.file_size() as usize; 93 | let off_mem = program.virtual_addr() as usize; 94 | let end_mem = off_mem + program.mem_size() as usize; 95 | assert_eq!(off_file & PAGE_MASK, off_mem & PAGE_MASK); 96 | 97 | let mut flags: [u8; 5] = *b"U___V"; 98 | if program.flags().is_execute() { 99 | flags[1] = b'X'; 100 | } 101 | if program.flags().is_write() { 102 | flags[2] = b'W'; 103 | } 104 | if program.flags().is_read() { 105 | flags[3] = b'R'; 106 | } 107 | address_space.map( 108 | VAddr::new(off_mem).floor()..VAddr::new(end_mem).ceil(), 109 | &elf.input[off_file..][..len_file], 110 | off_mem & PAGE_MASK, 111 | VmFlags::from_str(unsafe { core::str::from_utf8_unchecked(&flags) }).unwrap(), 112 | ); 113 | } 114 | // 映射用户栈 115 | let stack = unsafe { 116 | alloc_zeroed(Layout::from_size_align_unchecked( 117 | 2 << Sv39::PAGE_BITS, 118 | 1 << Sv39::PAGE_BITS, 119 | )) 120 | }; 121 | address_space.map_extern( 122 | VPN::new((1 << 26) - 2)..VPN::new(1 << 26), 123 | PPN::new(stack as usize >> Sv39::PAGE_BITS), 124 | VmFlags::build_from_str("U_WRV"), 125 | ); 126 | // 映射异界传送门 127 | map_portal(&address_space); 128 | 129 | let mut context = LocalContext::user(entry); 130 | let satp = (8 << 60) | address_space.root_ppn().val(); 131 | *context.sp_mut() = 1 << 38; 132 | Some(Self { 133 | pid: ProcId::new(), 134 | context: ForeignContext { context, satp }, 135 | address_space, 136 | fd_table: vec![ 137 | // Stdin 138 | Some(Mutex::new(FileHandle::empty(true, false))), 139 | // Stdout 140 | Some(Mutex::new(FileHandle::empty(false, true))), 141 | ], 142 | signal: Box::new(SignalImpl::new()), 143 | }) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /ch7/src/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::process::Process; 2 | use alloc::collections::{BTreeMap, VecDeque}; 3 | use rcore_task_manage::{Manage, PManager, ProcId, Schedule}; 4 | 5 | pub static mut PROCESSOR: PManager = PManager::new(); 6 | 7 | /// 任务管理器 8 | /// `tasks` 中保存所有的任务实体 9 | /// `ready_queue` 删除任务的实体 10 | pub struct ProcManager { 11 | tasks: BTreeMap, 12 | ready_queue: VecDeque, 13 | } 14 | 15 | impl ProcManager { 16 | /// 新建任务管理器 17 | pub fn new() -> Self { 18 | Self { 19 | tasks: BTreeMap::new(), 20 | ready_queue: VecDeque::new(), 21 | } 22 | } 23 | } 24 | 25 | impl Manage for ProcManager { 26 | /// 插入一个新任务 27 | #[inline] 28 | fn insert(&mut self, id: ProcId, task: Process) { 29 | self.tasks.insert(id, task); 30 | } 31 | /// 根据 id 获取对应的任务 32 | #[inline] 33 | fn get_mut(&mut self, id: ProcId) -> Option<&mut Process> { 34 | self.tasks.get_mut(&id) 35 | } 36 | /// 删除任务实体 37 | #[inline] 38 | fn delete(&mut self, id: ProcId) { 39 | self.tasks.remove(&id); 40 | } 41 | } 42 | 43 | impl Schedule for ProcManager { 44 | /// 添加 id 进入调度队列 45 | fn add(&mut self, id: ProcId) { 46 | self.ready_queue.push_back(id); 47 | } 48 | /// 从调度队列中取出 id 49 | fn fetch(&mut self) -> Option { 50 | self.ready_queue.pop_front() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch7/src/virtio_block.rs: -------------------------------------------------------------------------------- 1 | use crate::KERNEL_SPACE; 2 | use alloc::{ 3 | alloc::{alloc_zeroed, dealloc}, 4 | sync::Arc, 5 | }; 6 | use core::{alloc::Layout, ptr::NonNull}; 7 | use easy_fs::BlockDevice; 8 | use kernel_vm::page_table::{MmuMeta, Sv39, VAddr, VmFlags}; 9 | use spin::{Lazy, Mutex}; 10 | use virtio_drivers::{Hal, VirtIOBlk, VirtIOHeader}; 11 | 12 | const VIRTIO0: usize = 0x10001000; 13 | 14 | pub static BLOCK_DEVICE: Lazy> = Lazy::new(|| { 15 | Arc::new(unsafe { 16 | VirtIOBlock(Mutex::new( 17 | VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), 18 | )) 19 | }) 20 | }); 21 | 22 | struct VirtIOBlock(Mutex>); 23 | 24 | impl BlockDevice for VirtIOBlock { 25 | fn read_block(&self, block_id: usize, buf: &mut [u8]) { 26 | self.0 27 | .lock() 28 | .read_block(block_id, buf) 29 | .expect("Error when reading VirtIOBlk"); 30 | } 31 | fn write_block(&self, block_id: usize, buf: &[u8]) { 32 | self.0 33 | .lock() 34 | .write_block(block_id, buf) 35 | .expect("Error when writing VirtIOBlk"); 36 | } 37 | } 38 | 39 | struct VirtioHal; 40 | 41 | impl Hal for VirtioHal { 42 | fn dma_alloc(pages: usize) -> usize { 43 | // warn!("dma_alloc"); 44 | unsafe { 45 | alloc_zeroed(Layout::from_size_align_unchecked( 46 | pages << Sv39::PAGE_BITS, 47 | 1 << Sv39::PAGE_BITS, 48 | )) as _ 49 | } 50 | } 51 | 52 | fn dma_dealloc(paddr: usize, pages: usize) -> i32 { 53 | // warn!("dma_dealloc"); 54 | unsafe { 55 | dealloc( 56 | paddr as _, 57 | Layout::from_size_align_unchecked(pages << Sv39::PAGE_BITS, 1 << Sv39::PAGE_BITS), 58 | ) 59 | } 60 | 0 61 | } 62 | 63 | fn phys_to_virt(paddr: usize) -> usize { 64 | // warn!("p2v"); 65 | paddr 66 | } 67 | 68 | fn virt_to_phys(vaddr: usize) -> usize { 69 | // warn!("v2p"); 70 | const VALID: VmFlags = VmFlags::build_from_str("__V"); 71 | let ptr: NonNull = unsafe { 72 | KERNEL_SPACE 73 | .assume_init_ref() 74 | .translate(VAddr::new(vaddr), VALID) 75 | .unwrap() 76 | }; 77 | ptr.as_ptr() as usize 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ch8/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ch8" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["zflcs <1491657576@qq.com>"] 6 | 7 | [dependencies] 8 | virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } 9 | sbi-rt = { version = "0.0.2", features = ["legacy"] } 10 | xmas-elf = "0.8.0" 11 | riscv = "0.10.1" 12 | spin = "0.9" 13 | 14 | linker = { path = "../linker" } 15 | rcore-console = { path = "../console" } 16 | kernel-context = { path = "../kernel-context", features = ["foreign"] } 17 | kernel-alloc = { path = "../kernel-alloc" } 18 | kernel-vm = { path = "../kernel-vm" } 19 | syscall = { path = "../syscall", features = ["kernel"] } 20 | rcore-task-manage = { path = "../task-manage", features = ["thread"] } 21 | easy-fs = { path = "../easy-fs" } 22 | signal = { path = "../signal" } 23 | signal-impl = { path = "../signal-impl" } 24 | sync = { path = "../sync" } 25 | 26 | [build-dependencies] 27 | linker = { path = "../linker" } 28 | -------------------------------------------------------------------------------- /ch8/README.md: -------------------------------------------------------------------------------- 1 | # 第八章 2 | -------------------------------------------------------------------------------- /ch8/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, linker::SCRIPT).unwrap(); 6 | 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-env-changed=LOG"); 9 | println!("cargo:rerun-if-env-changed=APP_ASM"); 10 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 11 | } 12 | -------------------------------------------------------------------------------- /ch8/src/fs.rs: -------------------------------------------------------------------------------- 1 | use crate::virtio_block::BLOCK_DEVICE; 2 | use alloc::{string::String, sync::Arc, vec::Vec}; 3 | use easy_fs::{EasyFileSystem, FSManager, FileHandle, Inode, OpenFlags}; 4 | use spin::Lazy; 5 | 6 | pub static FS: Lazy = Lazy::new(|| FileSystem { 7 | root: EasyFileSystem::root_inode(&EasyFileSystem::open(BLOCK_DEVICE.clone())), 8 | }); 9 | 10 | pub struct FileSystem { 11 | root: Inode, 12 | } 13 | 14 | impl FSManager for FileSystem { 15 | fn open(&self, path: &str, flags: OpenFlags) -> Option> { 16 | let (readable, writable) = flags.read_write(); 17 | if flags.contains(OpenFlags::CREATE) { 18 | if let Some(inode) = self.find(path) { 19 | // Clear size 20 | inode.clear(); 21 | Some(Arc::new(FileHandle::new(readable, writable, inode))) 22 | } else { 23 | // Create new file 24 | self.root 25 | .create(path) 26 | .map(|new_inode| Arc::new(FileHandle::new(readable, writable, new_inode))) 27 | } 28 | } else { 29 | self.find(path).map(|inode| { 30 | if flags.contains(OpenFlags::TRUNC) { 31 | inode.clear(); 32 | } 33 | Arc::new(FileHandle::new(readable, writable, inode)) 34 | }) 35 | } 36 | } 37 | 38 | fn find(&self, path: &str) -> Option> { 39 | self.root.find(path) 40 | } 41 | 42 | fn readdir(&self, _path: &str) -> Option> { 43 | Some(self.root.readdir()) 44 | } 45 | 46 | fn link(&self, _src: &str, _dst: &str) -> isize { 47 | unimplemented!() 48 | } 49 | 50 | fn unlink(&self, _path: &str) -> isize { 51 | unimplemented!() 52 | } 53 | } 54 | 55 | pub fn read_all(fd: Arc) -> Vec { 56 | let mut offset = 0usize; 57 | let mut buffer = [0u8; 512]; 58 | let mut v: Vec = Vec::new(); 59 | if let Some(inode) = &fd.inode { 60 | loop { 61 | let len = inode.read_at(offset, &mut buffer); 62 | if len == 0 { 63 | break; 64 | } 65 | offset += len; 66 | v.extend_from_slice(&buffer[..len]); 67 | } 68 | } 69 | v 70 | } 71 | -------------------------------------------------------------------------------- /ch8/src/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::process::{Process, Thread}; 2 | use alloc::collections::{BTreeMap, VecDeque}; 3 | use rcore_task_manage::{Manage, PThreadManager, ProcId, Schedule, ThreadId}; 4 | 5 | pub static mut PROCESSOR: PThreadManager = 6 | PThreadManager::new(); 7 | 8 | /// 任务管理器 9 | /// `tasks` 中保存所有的任务实体 10 | /// `ready_queue` 删除任务的实体 11 | pub struct ThreadManager { 12 | tasks: BTreeMap, 13 | ready_queue: VecDeque, 14 | } 15 | 16 | impl ThreadManager { 17 | /// 新建任务管理器 18 | pub fn new() -> Self { 19 | Self { 20 | tasks: BTreeMap::new(), 21 | ready_queue: VecDeque::new(), 22 | } 23 | } 24 | } 25 | 26 | impl Manage for ThreadManager { 27 | /// 插入一个新任务 28 | #[inline] 29 | fn insert(&mut self, id: ThreadId, task: Thread) { 30 | self.tasks.insert(id, task); 31 | } 32 | /// 根据 id 获取对应的任务 33 | #[inline] 34 | fn get_mut(&mut self, id: ThreadId) -> Option<&mut Thread> { 35 | self.tasks.get_mut(&id) 36 | } 37 | /// 删除任务实体 38 | #[inline] 39 | fn delete(&mut self, id: ThreadId) { 40 | self.tasks.remove(&id); 41 | } 42 | } 43 | 44 | impl Schedule for ThreadManager { 45 | /// 添加 id 进入调度队列 46 | fn add(&mut self, id: ThreadId) { 47 | self.ready_queue.push_back(id); 48 | } 49 | /// 从调度队列中取出 id 50 | fn fetch(&mut self) -> Option { 51 | self.ready_queue.pop_front() 52 | } 53 | } 54 | 55 | /// 进程管理器 56 | /// `procs` 中保存所有的进程实体 57 | pub struct ProcManager { 58 | procs: BTreeMap, 59 | } 60 | 61 | impl ProcManager { 62 | /// 新建进程管理器 63 | pub fn new() -> Self { 64 | Self { 65 | procs: BTreeMap::new(), 66 | } 67 | } 68 | } 69 | 70 | impl Manage for ProcManager { 71 | /// 插入一个新任务 72 | #[inline] 73 | fn insert(&mut self, id: ProcId, item: Process) { 74 | self.procs.insert(id, item); 75 | } 76 | /// 根据 id 获取对应的任务 77 | #[inline] 78 | fn get_mut(&mut self, id: ProcId) -> Option<&mut Process> { 79 | self.procs.get_mut(&id) 80 | } 81 | /// 删除任务实体 82 | #[inline] 83 | fn delete(&mut self, id: ProcId) { 84 | self.procs.remove(&id); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /ch8/src/virtio_block.rs: -------------------------------------------------------------------------------- 1 | use crate::KERNEL_SPACE; 2 | use alloc::{ 3 | alloc::{alloc_zeroed, dealloc}, 4 | sync::Arc, 5 | }; 6 | use core::{alloc::Layout, ptr::NonNull}; 7 | use easy_fs::BlockDevice; 8 | use kernel_vm::page_table::{MmuMeta, Sv39, VAddr, VmFlags}; 9 | use spin::{Lazy, Mutex}; 10 | use virtio_drivers::{Hal, VirtIOBlk, VirtIOHeader}; 11 | 12 | const VIRTIO0: usize = 0x10001000; 13 | 14 | pub static BLOCK_DEVICE: Lazy> = Lazy::new(|| { 15 | Arc::new(unsafe { 16 | VirtIOBlock(Mutex::new( 17 | VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), 18 | )) 19 | }) 20 | }); 21 | 22 | struct VirtIOBlock(Mutex>); 23 | 24 | impl BlockDevice for VirtIOBlock { 25 | fn read_block(&self, block_id: usize, buf: &mut [u8]) { 26 | self.0 27 | .lock() 28 | .read_block(block_id, buf) 29 | .expect("Error when reading VirtIOBlk"); 30 | } 31 | fn write_block(&self, block_id: usize, buf: &[u8]) { 32 | self.0 33 | .lock() 34 | .write_block(block_id, buf) 35 | .expect("Error when writing VirtIOBlk"); 36 | } 37 | } 38 | 39 | struct VirtioHal; 40 | 41 | impl Hal for VirtioHal { 42 | fn dma_alloc(pages: usize) -> usize { 43 | // warn!("dma_alloc"); 44 | unsafe { 45 | alloc_zeroed(Layout::from_size_align_unchecked( 46 | pages << Sv39::PAGE_BITS, 47 | 1 << Sv39::PAGE_BITS, 48 | )) as _ 49 | } 50 | } 51 | 52 | fn dma_dealloc(paddr: usize, pages: usize) -> i32 { 53 | // warn!("dma_dealloc"); 54 | unsafe { 55 | dealloc( 56 | paddr as _, 57 | Layout::from_size_align_unchecked(pages << Sv39::PAGE_BITS, 1 << Sv39::PAGE_BITS), 58 | ) 59 | } 60 | 0 61 | } 62 | 63 | fn phys_to_virt(paddr: usize) -> usize { 64 | // warn!("p2v"); 65 | paddr 66 | } 67 | 68 | fn virt_to_phys(vaddr: usize) -> usize { 69 | // warn!("v2p"); 70 | const VALID: VmFlags = VmFlags::build_from_str("__V"); 71 | let ptr: NonNull = unsafe { 72 | KERNEL_SPACE 73 | .assume_init_ref() 74 | .translate(VAddr::new(vaddr), VALID) 75 | .unwrap() 76 | }; 77 | ptr.as_ptr() as usize 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /console/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rcore-console" 3 | description = "Provides `print!`, `println!` and `log::Log` with customizable implementations." 4 | version = "0.0.0" 5 | edition = "2021" 6 | authors = ["YdrMaster "] 7 | repository = "https://github.com/YdrMaster/rCore-Tutorial-in-single-workspace/console" 8 | documentation = "https://docs.rs/rcore-console" 9 | license = "WTFPL" 10 | readme = "README.md" 11 | keywords = ["rcore", "console"] 12 | categories = ["no-std"] 13 | 14 | [dependencies] 15 | log = "0.4.17" 16 | spin = "0.9" 17 | -------------------------------------------------------------------------------- /console/README.md: -------------------------------------------------------------------------------- 1 | # 控制台(输出) 2 | 3 | [![Latest version](https://img.shields.io/crates/v/rcore-console.svg)](https://crates.io/crates/rcore-console) 4 | [![Documentation](https://docs.rs/rcore-console/badge.svg)](https://docs.rs/rcore-console) 5 | ![license](https://img.shields.io/github/license/YdrMaster/rCore-Tutorial-in-single-workspace) 6 | 7 | 提供可定制实现的 `print!`、`println!` 和 `log::Log`。日志只有基本的彩色功能。 8 | -------------------------------------------------------------------------------- /console/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 提供可定制实现的 `print!`、`println!` 和 `log::Log`。 2 | 3 | #![no_std] 4 | #![deny(warnings, missing_docs)] 5 | 6 | use core::{ 7 | fmt::{self, Write}, 8 | str::FromStr, 9 | }; 10 | use spin::Once; 11 | 12 | /// 向用户提供 `log`。 13 | pub extern crate log; 14 | 15 | /// 这个接口定义了向控制台“输出”这件事。 16 | pub trait Console: Sync { 17 | /// 向控制台放置一个字符。 18 | fn put_char(&self, c: u8); 19 | 20 | /// 向控制台放置一个字符串。 21 | /// 22 | /// 如果使用了锁,覆盖这个实现以免反复获取和释放锁。 23 | #[inline] 24 | fn put_str(&self, s: &str) { 25 | for c in s.bytes() { 26 | self.put_char(c); 27 | } 28 | } 29 | } 30 | 31 | /// 库找到输出的方法:保存一个对象引用,这是一种单例。 32 | static CONSOLE: Once<&'static dyn Console> = Once::new(); 33 | 34 | /// 用户调用这个函数设置输出的方法。 35 | pub fn init_console(console: &'static dyn Console) { 36 | CONSOLE.call_once(|| console); 37 | log::set_logger(&Logger).unwrap(); 38 | } 39 | 40 | /// 根据环境变量设置日志级别。 41 | pub fn set_log_level(env: Option<&str>) { 42 | use log::LevelFilter as Lv; 43 | log::set_max_level(env.and_then(|s| Lv::from_str(s).ok()).unwrap_or(Lv::Trace)); 44 | } 45 | 46 | /// 打印一些测试信息。 47 | pub fn test_log() { 48 | println!( 49 | r" 50 | ______ __ 51 | / ____/___ ____ _________ / /__ 52 | / / / __ \/ __ \/ ___/ __ \/ / _ \ 53 | / /___/ /_/ / / / (__ ) /_/ / / __/ 54 | \____/\____/_/ /_/____/\____/_/\___/ 55 | ====================================" 56 | ); 57 | log::trace!("LOG TEST >> Hello, world!"); 58 | log::debug!("LOG TEST >> Hello, world!"); 59 | log::info!("LOG TEST >> Hello, world!"); 60 | log::warn!("LOG TEST >> Hello, world!"); 61 | log::error!("LOG TEST >> Hello, world!"); 62 | println!(); 63 | } 64 | 65 | /// 打印。 66 | /// 67 | /// 给宏用的,用户不会直接调它。 68 | #[doc(hidden)] 69 | #[inline] 70 | pub fn _print(args: fmt::Arguments) { 71 | Logger.write_fmt(args).unwrap(); 72 | } 73 | 74 | /// 格式化打印。 75 | #[macro_export] 76 | macro_rules! print { 77 | ($($arg:tt)*) => { 78 | $crate::_print(core::format_args!($($arg)*)); 79 | } 80 | } 81 | 82 | /// 格式化打印并换行。 83 | #[macro_export] 84 | macro_rules! println { 85 | () => ($crate::print!("\n")); 86 | ($($arg:tt)*) => {{ 87 | $crate::_print(core::format_args!($($arg)*)); 88 | $crate::println!(); 89 | }} 90 | } 91 | 92 | /// 这个 Unit struct 是 `core::fmt` 要求的。 93 | struct Logger; 94 | 95 | /// 实现 [`Write`] trait,格式化的基础。 96 | impl Write for Logger { 97 | #[inline] 98 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 99 | let _ = CONSOLE.get().unwrap().put_str(s); 100 | Ok(()) 101 | } 102 | } 103 | 104 | /// 实现 `log::Log` trait,提供分级日志。 105 | impl log::Log for Logger { 106 | #[inline] 107 | fn enabled(&self, _metadata: &log::Metadata) -> bool { 108 | true 109 | } 110 | 111 | #[inline] 112 | fn log(&self, record: &log::Record) { 113 | use log::Level::*; 114 | let color_code: u8 = match record.level() { 115 | Error => 31, 116 | Warn => 93, 117 | Info => 34, 118 | Debug => 32, 119 | Trace => 90, 120 | }; 121 | println!( 122 | "\x1b[{color_code}m[{:>5}] {}\x1b[0m", 123 | record.level(), 124 | record.args(), 125 | ); 126 | } 127 | 128 | fn flush(&self) {} 129 | } 130 | -------------------------------------------------------------------------------- /docs/design/20220814-crate-types.md: -------------------------------------------------------------------------------- 1 | # crate 类型 2 | 3 | 对项目中用到的和设计的 crates 分类如下: 4 | 5 | - `[[bin]]` 6 | - `ch_` 7 | - `xtask` 8 | - `[[lib]]` 9 | - 封装 10 | - 接口 11 | - 实现 12 | 13 | ## `[[bin]]` 14 | 15 | `ch_` 是章节 crate,每个章节都能编译出一个独立的、适用于 qemu-virt 的内核。 16 | 17 | `xtask` 是整个项目的构建工具。 18 | 19 | ## `[[lib]]` 20 | 21 | **封装**、**接口**和**实现**是一个库 crate可能具有的标签,一个 crate 可能具有多个标签。 22 | 23 | 封装类型是对底层细节的直接封装。这种库的核心指标是无开销抽象,必须充分暴露底层细节,并且不能引入不必要的时间和空间复杂度。封装库常常是根据某种成文规范实现的库,类似 [*riscv*](https://crates.io/crates/riscv),在这种情况下,库应该指出其遵循的规范文本的获取方式,注明其实现的规范版本和不一致/不完整之处。 24 | 25 | 接口类型通常提供至少一个 trait 定义和一组相关结构体。每个 trait 会定义了一类对象的接口,从而将接口与实现分离,类似 [*log*](https://crates.io/crates/log) 和 [*rustsbi*](https://crates.io/crates/rustsbi)。实现库则依赖接口库实现这些 trait,典型的是各种 logger 和 allocator。 26 | -------------------------------------------------------------------------------- /docs/design/20220823-kpti.md: -------------------------------------------------------------------------------- 1 | # 切换不同地址空间上的任务 2 | 3 | 经过总结,切换控制流有 3 种形式,开销从小到大分别是: 4 | 5 | 1. 函数调用(无栈协程) 6 | 2. 线程切换(相同地址空间上下文切换) 7 | 3. 进程切换(不同地址空间上下文切换) 8 | 9 | 内核将用不同的流程应对三种切换。 10 | 11 | 1. 函数调用设置好参数直接 `call`,如果是协程,将在挂起点返回; 12 | 2. 线程控制流支持抢断,因此需要保存通用寄存器; 13 | > 为什么必须在汇编里保存/恢复 `sstatus` 和 `sepc`?我忽然不那么确定了。 14 | 3. 进程切换除了保存通用寄存器外还需要切换地址空间; 15 | > 本质上说,切换地址空间和切换进程控制流并不耦合;但如果地址空间都变了,就切换个协程,虽然理论上是可能的,但实在没这个必要; 16 | 17 | > 这里还有个特殊情况:支持抢占的协程。如果想要达到这类任务的极限性能,需要另一个抢占协程执行器。这就是另一个话题了。 18 | 19 | 本文尝试描述一种中转代理机制,将是否切换地址空间的切换在内核上统一起来。 20 | 21 | 切换地址空间和不切换最大的区别其实不在于多几条 csr 指令操作了一下根页表,而在于切换地址空间这个操作本身必须发生在两个地址空间的公共部分。这给代码带来了极大的不便: 22 | 23 | 1. 保存/恢复通用寄存器的代码必须被两个地址空间映射; 24 | 2. 保存寄存器的位置/要恢复的寄存器的值必须在两个地址空间映射; 25 | 26 | 为了获得起码的可用性,这个要在两个地址空间映射的位置不可能是它的物理位置,只能是一个正常内核和用户程序都不可能碰到的位置,也就是虚地址空间高地址的高处。所以上面的代码必须是位置无关(PIC)的,并且这里的代码不可能太复杂,这意味着我们不可能把整个调度器都放上去(不然为什么不干脆把整个内核映射到用户空间去呢)。所以我得出一个必然的结论:**跨地址空间的切换流程必须独立于正常流程以外**。 27 | 28 | 接下来的设计就是顺理成章的了: 29 | 30 | 1. 每个硬件线程(HART)有一个内部调度器,负责选择什么任务可以占用自己的计算时间; 31 | 2. 如果调度器选中的任务不需要切换地址空间,直接切换上去; 32 | 3. 如果调度器发现选中的任务需要切换地址空间,将其上下文拷贝到公共地址空间上,然后跳到公共地址空间上的切换函数。切换函数负责按照上下文的描述切换地址空间再切换任务; 33 | 4. 切换函数同时将 `stvec` 也改到公共地址空间上的某处,陷入时切换通用寄存器后先恢复地址空间再返回到调度器; 34 | 5. 由于每个 HART 都需要一个同时具有 `XWR` 权限的页,它们没法映射同一个页;所以它们应该在各自初始化自己的公共页,并把通用寄存器切换函数拷贝上去;这个操作可以在第一个这样的任务调度到时懒加载; 35 | 6. 无论是懒加载还是跳上去,都可以封装在进程切换函数内部,调度器没必要积极地处理这种复杂性; 36 | 7. 为了尽量减少需要拷贝到公共地址空间的数据,可以将公共地址空间上的切换函数视作一个线程。这样大部分通用寄存器都被保存在调度器自己的地址空间上了,切换函数没必要全部保存; 37 | 8. 既然跨地址空间的切换函数需要修改 `stvec`,那么不跨的也需要;虽然实际上调度器知道 `stvec` 是不是需要重置,但重置一下实在是太快了,以至于调度器没必要告诉切换函数。重置甚至快于一次判断,这是一个可以接受的额外开销; 38 | 39 | --- 40 | 41 | 分割线以下是旧的版本,很有趣所以保留了。 42 | 43 | --- 44 | 45 | > \# 内核地址隔离 46 | > 47 | > **原理** 先切换到中转地址空间上的单页表微型内核,再从这个微型内核引导用户态。 48 | > 49 | > ## 设计 50 | > 51 | > 本文描述了一种设计,目的是实现单页表内核和双页表内核的一致性。位于不同地址空间的任务被传递给一个公共地址空间(称为中转地址空间)上的任务执行器(称为异世界执行器),由这个执行器中转执行。执行器在主内核上表现为一个屏蔽中断的内核线程(即一个正常的任务)。 52 | > 53 | > 执行器、微型内核、内核线程,是同一个结构从三个不同角度的描述。执行器是功能上的描述,一个任务上下文被传递给它,由它代理执行;微型内核是结构上的描述,它有内聚的、静态的分段:代码段、(共享)数据段和栈;内核线程是它在主内核中的表现,它可以用一个上下文描述,并通过这个上下文进行调度。 54 | > 55 | > ### 执行器内存布局 56 | > 57 | > 为了方便映射到多个地址空间中,形成中转地址空间,这个内存布局应该被放进一个物理页里。如果放不下,考虑多个连续页或一个大页。 58 | > 59 | > | 内容 60 | > | - 61 | > | 共享用户上下文 62 | > | `execute` 63 | > | `trap` 64 | > | 中转内核入口 65 | > | 中转内核栈 66 | > 67 | > ### 执行器编译 68 | > 69 | > 中转内核在编译时是主内核的一部分。主内核应该在自己的链接脚本上留一个页,然后将执行器代码段直接链接到页中间的一个位置,保证页开头到执行器控制流入口之间足够容纳上表描述的内容。 70 | > 71 | > ### 执行器初始化 72 | > 73 | > 以 `RustForeignExecutor` 为例,执行器初始化时,主内核拷贝 `execute` 和 `trap` 到执行器页代码段,并初始化一个执行器上下文,内容包括: 74 | > 75 | > - `sp` = 执行器栈顶(如果中转页在最高虚页上,是 0) 76 | > - `a0` = 执行器页基地址/共享用户上下文基地址 77 | > - `a1` = `execute` 地址 78 | > - `a2` = `trap` 地址 79 | > - `sepc` = 控制流入口 80 | > - `sstatus` = 特权态屏蔽中断 81 | > 82 | > > 以上所有地址为中转地址空间的虚地址。 83 | > 84 | > ### 执行用户程序 85 | > 86 | > 1. 主内核填写共享用户上下文,内容包括基本任务上下文和用户根页表; 87 | > 2. 主内核执行中转内核上下文,切换到中转内核线程 88 | > 3. 中转内核切换上下文 CSR:`satp`、`stvec`、`sscratch` 89 | > - 初次进入时还要将 `a0`、`a1`、`a2` 保存在栈上 90 | > - 完成时到达用户空间 91 | > 4. 执行共享用户上下文 92 | > 93 | > ### 处理用户陷入 94 | > 95 | > 1. 用户陷入到中转内核的 `trap` 96 | > - 用户上下文已被保存在共享上下文区域 97 | > 2. 中转内核切换上下文 CSR:`satp`、`stvec`、`sscratch` 98 | > - 完成时回到内核空间 99 | > 3. 模拟中断操作设置 `sepc` 100 | > 4. 直接跳转内核的 `stvec` 回到主内核 101 | > 102 | > ## 实现 103 | > 104 | > ### 定位上下文切换例程 105 | > 106 | > 上下文切换例程是手写内联汇编实现的,可以人工保证它们可重定位(不用 `la` 就行了),以便拷贝到任意位置使用。 107 | > 可以通过找结尾指令在运行时定位这些函数,并拷贝到其他位置,以 `execute` 为例: 108 | > 109 | > ```rust 110 | > /// 通过寻找结尾的指令在运行时定位一个函数。 111 | > unsafe fn locate_function(entry: usize, key: [u16; N]) -> &'static [u8] { 112 | > use core::{mem::size_of, slice::from_raw_parts}; 113 | > let entry = entry as *const u16; 114 | > for len in 1.. { 115 | > let ptr = entry.add(len); 116 | > if key == from_raw_parts(ptr, key.len()) { 117 | > return from_raw_parts(entry.cast(), size_of::() * (len + key.len())); 118 | > } 119 | > } 120 | > unreachable!() 121 | > } 122 | > 123 | > /// 运行时定位 `locate` 函数。 124 | > #[inline] 125 | > fn locate_execute() -> &'static [u8] { 126 | > // sret + unimp 127 | > unsafe { locate_function(execute as _, [0x0073, 0x1020, 0x0000]) } 128 | > } 129 | > ``` 130 | > 131 | > ### 中转内核布局 132 | > 133 | > 直接用一个 `#[repr(C)]` 结构体定义中转内核布局: 134 | > 135 | > ```rust 136 | > /// 中转内核布局。 137 | > #[repr(C)] 138 | > pub struct TransitKernel { 139 | > /// 共享任务上下文。 140 | > pub shared_context: ForeignContext, 141 | > /// `execute` 的拷贝。 142 | > /// 143 | > /// 512 Bytes,4 字节对齐。 144 | > pub execute_copy: [u32; 128], 145 | > /// `trap` 的拷贝。 146 | > /// 147 | > /// 512 Bytes,4 字节对齐。 148 | > pub trap_copy: [u32; 128], 149 | > // 中转内核控制流,直接链接进来。 150 | > // pub main: [u32; 512], 151 | > // 页上其余部分用作栈,运行时设置。 152 | > // pub stack: [u8], 153 | > } 154 | > 155 | > /// 位于不同地址空间的任务上下文。 156 | > #[repr(C)] 157 | > pub struct ForeignContext { 158 | > /// `satp` 寄存器值指定地址空间。 159 | > pub satp: usize, 160 | > /// 正常的任务上下文。 161 | > pub context: Context, 162 | > } 163 | > 164 | > /// 中转内核控制流。 165 | > #[inline(never)] 166 | > #[link_section = ".transit.entry"] 167 | > pub extern "C" fn transit_main( 168 | > _ctx: &'static mut ForeignContext, 169 | > _execute_copy: unsafe extern "C" fn(), 170 | > _trap_copy: unsafe extern "C" fn(), 171 | > ) { 172 | > todo!() 173 | > } 174 | > ``` 175 | > 176 | > `execute` 和 `trap` 会在运行时定位并拷贝到结构体。 177 | > 178 | > ### 执行器的运行时初始化 179 | > 180 | > 定位 `execute` 和 `trap`,并拷贝到执行器页。 181 | > 182 | > ```rust 183 | > pub unsafe fn init(&mut self) { 184 | > use core::mem::size_of_val; 185 | > 186 | > // sret + unimp 187 | > let execute = locate_function(crate::execute as _, [0x0073, 0x1020, 0x0000]); 188 | > assert!( 189 | > size_of_val(&self.execute_copy) >= execute.len(), 190 | > "`execute_copy` is too small in transit kernel" 191 | > ); 192 | > self.execute_copy 193 | > .as_mut_ptr() 194 | > .cast::() 195 | > .copy_from_nonoverlapping(execute.as_ptr(), execute.len()); 196 | > 197 | > // ret + unimp 198 | > let trap = locate_function(crate::trap as _, [0x8082, 0x0000]); 199 | > assert!( 200 | > size_of_val(&self.trap_copy) >= trap.len(), 201 | > "`trap_copy` is too small in transit kernel" 202 | > ); 203 | > self.trap_copy 204 | > .as_mut_ptr() 205 | > .cast::() 206 | > .copy_from_nonoverlapping(trap.as_ptr(), trap.len()); 207 | > } 208 | > ``` 209 | -------------------------------------------------------------------------------- /easy-fs/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /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 | bitflags = "1.2.1" 12 | -------------------------------------------------------------------------------- /easy-fs/src/bitmap.rs: -------------------------------------------------------------------------------- 1 | use super::{get_block_cache, BlockDevice, BLOCK_SZ}; 2 | use alloc::sync::Arc; 3 | /// A bitmap block 4 | type BitmapBlock = [u64; 64]; 5 | /// Number of bits in a block 6 | const BLOCK_BITS: usize = BLOCK_SZ * 8; 7 | /// A bitmap 8 | pub struct Bitmap { 9 | start_block_id: usize, 10 | blocks: usize, 11 | } 12 | 13 | /// Decompose bits into (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 | /// A new bitmap from start block id and number of blocks 22 | pub fn new(start_block_id: usize, blocks: usize) -> Self { 23 | Self { 24 | start_block_id, 25 | blocks, 26 | } 27 | } 28 | /// Allocate a new block from a block device 29 | pub fn alloc(&self, block_device: &Arc) -> Option { 30 | for block_id in 0..self.blocks { 31 | let pos = get_block_cache( 32 | block_id + self.start_block_id as usize, 33 | Arc::clone(block_device), 34 | ) 35 | .lock() 36 | .modify(0, |bitmap_block: &mut BitmapBlock| { 37 | if let Some((bits64_pos, inner_pos)) = bitmap_block 38 | .iter() 39 | .enumerate() 40 | .find(|(_, bits64)| **bits64 != u64::MAX) 41 | .map(|(bits64_pos, bits64)| (bits64_pos, bits64.trailing_ones() as usize)) 42 | { 43 | // modify cache 44 | bitmap_block[bits64_pos] |= 1u64 << inner_pos; 45 | Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize) 46 | } else { 47 | None 48 | } 49 | }); 50 | if pos.is_some() { 51 | return pos; 52 | } 53 | } 54 | None 55 | } 56 | /// Deallocate a block 57 | pub fn dealloc(&self, block_device: &Arc, bit: usize) { 58 | let (block_pos, bits64_pos, inner_pos) = decomposition(bit); 59 | get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device)) 60 | .lock() 61 | .modify(0, |bitmap_block: &mut BitmapBlock| { 62 | assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0); 63 | bitmap_block[bits64_pos] -= 1u64 << inner_pos; 64 | }); 65 | } 66 | /// Get the max number of allocatable blocks 67 | pub fn maximum(&self) -> usize { 68 | self.blocks * BLOCK_BITS 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /easy-fs/src/block_cache.rs: -------------------------------------------------------------------------------- 1 | use super::{BlockDevice, BLOCK_SZ}; 2 | use alloc::{collections::VecDeque, sync::Arc}; 3 | use spin::{Lazy, Mutex}; 4 | 5 | /// Cached block inside memory 6 | pub struct BlockCache { 7 | /// cached block data 8 | cache: [u8; BLOCK_SZ], 9 | /// underlying block id 10 | block_id: usize, 11 | /// underlying block device 12 | block_device: Arc, 13 | /// whether the block is dirty 14 | modified: bool, 15 | } 16 | 17 | impl BlockCache { 18 | /// Load a new BlockCache from disk. 19 | pub fn new(block_id: usize, block_device: Arc) -> Self { 20 | let mut cache = [0u8; BLOCK_SZ]; 21 | block_device.read_block(block_id, &mut cache); 22 | Self { 23 | cache, 24 | block_id, 25 | block_device, 26 | modified: false, 27 | } 28 | } 29 | /// Get the address of an offset inside the cached block data 30 | fn addr_of_offset(&self, offset: usize) -> usize { 31 | &self.cache[offset] as *const _ as usize 32 | } 33 | 34 | pub fn get_ref(&self, offset: usize) -> &T 35 | where 36 | T: Sized, 37 | { 38 | let type_size = core::mem::size_of::(); 39 | assert!(offset + type_size <= BLOCK_SZ); 40 | let addr = self.addr_of_offset(offset); 41 | unsafe { &*(addr as *const T) } 42 | } 43 | 44 | pub fn get_mut(&mut self, offset: usize) -> &mut T 45 | where 46 | T: Sized, 47 | { 48 | let type_size = core::mem::size_of::(); 49 | assert!(offset + type_size <= BLOCK_SZ); 50 | self.modified = true; 51 | let addr = self.addr_of_offset(offset); 52 | unsafe { &mut *(addr as *mut T) } 53 | } 54 | 55 | pub fn read(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V { 56 | f(self.get_ref(offset)) 57 | } 58 | 59 | pub fn modify(&mut self, offset: usize, f: impl FnOnce(&mut T) -> V) -> V { 60 | f(self.get_mut(offset)) 61 | } 62 | 63 | pub fn sync(&mut self) { 64 | if self.modified { 65 | self.modified = false; 66 | self.block_device.write_block(self.block_id, &self.cache); 67 | } 68 | } 69 | } 70 | 71 | impl Drop for BlockCache { 72 | fn drop(&mut self) { 73 | self.sync() 74 | } 75 | } 76 | /// Use a block cache of 16 blocks 77 | const BLOCK_CACHE_SIZE: usize = 16; 78 | 79 | pub struct BlockCacheManager { 80 | queue: VecDeque<(usize, Arc>)>, 81 | } 82 | 83 | impl BlockCacheManager { 84 | pub fn new() -> Self { 85 | Self { 86 | queue: VecDeque::new(), 87 | } 88 | } 89 | 90 | pub fn get_block_cache( 91 | &mut self, 92 | block_id: usize, 93 | block_device: Arc, 94 | ) -> Arc> { 95 | if let Some(pair) = self.queue.iter().find(|pair| pair.0 == block_id) { 96 | Arc::clone(&pair.1) 97 | } else { 98 | // substitute 99 | if self.queue.len() == BLOCK_CACHE_SIZE { 100 | // from front to tail 101 | if let Some((idx, _)) = self 102 | .queue 103 | .iter() 104 | .enumerate() 105 | .find(|(_, pair)| Arc::strong_count(&pair.1) == 1) 106 | { 107 | self.queue.drain(idx..=idx); 108 | } else { 109 | panic!("Run out of BlockCache!"); 110 | } 111 | } 112 | // load block into mem and push back 113 | let block_cache = Arc::new(Mutex::new(BlockCache::new( 114 | block_id, 115 | Arc::clone(&block_device), 116 | ))); 117 | self.queue.push_back((block_id, Arc::clone(&block_cache))); 118 | block_cache 119 | } 120 | } 121 | } 122 | 123 | /// The global block cache manager 124 | pub static BLOCK_CACHE_MANAGER: Lazy> = 125 | Lazy::new(|| Mutex::new(BlockCacheManager::new())); 126 | 127 | /// Get the block cache corresponding to the given block id and block device 128 | pub fn get_block_cache( 129 | block_id: usize, 130 | block_device: Arc, 131 | ) -> Arc> { 132 | BLOCK_CACHE_MANAGER 133 | .lock() 134 | .get_block_cache(block_id, block_device) 135 | } 136 | /// Sync all block cache to block device 137 | pub fn block_cache_sync_all() { 138 | let manager = BLOCK_CACHE_MANAGER.lock(); 139 | for (_, cache) in manager.queue.iter() { 140 | cache.lock().sync(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /easy-fs/src/block_dev.rs: -------------------------------------------------------------------------------- 1 | use core::any::Any; 2 | /// Trait for block devices 3 | /// which reads and writes data in the unit of blocks 4 | pub trait BlockDevice: Send + Sync + Any { 5 | ///Read data form block to buffer 6 | fn read_block(&self, block_id: usize, buf: &mut [u8]); 7 | ///Write data from buffer to block 8 | fn write_block(&self, block_id: usize, buf: &[u8]); 9 | } 10 | -------------------------------------------------------------------------------- /easy-fs/src/file.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use alloc::sync::Arc; 3 | use alloc::vec::Vec; 4 | use bitflags::*; 5 | 6 | use crate::Inode; 7 | 8 | ///Array of u8 slice that user communicate with os 9 | pub struct UserBuffer { 10 | ///U8 vec 11 | pub buffers: Vec<&'static mut [u8]>, 12 | } 13 | 14 | impl UserBuffer { 15 | ///Create a `UserBuffer` by parameter 16 | pub fn new(buffers: Vec<&'static mut [u8]>) -> Self { 17 | Self { buffers } 18 | } 19 | ///Length of `UserBuffer` 20 | pub fn len(&self) -> usize { 21 | let mut total: usize = 0; 22 | for b in self.buffers.iter() { 23 | total += b.len(); 24 | } 25 | total 26 | } 27 | } 28 | 29 | bitflags! { 30 | /// Open file flags 31 | pub struct OpenFlags: u32 { 32 | ///Read only 33 | const RDONLY = 0; 34 | ///Write only 35 | const WRONLY = 1 << 0; 36 | ///Read & Write 37 | const RDWR = 1 << 1; 38 | ///Allow create 39 | const CREATE = 1 << 9; 40 | ///Clear file and return an empty one 41 | const TRUNC = 1 << 10; 42 | } 43 | } 44 | 45 | impl OpenFlags { 46 | /// Do not check validity for simplicity 47 | /// Return (readable, writable) 48 | pub fn read_write(&self) -> (bool, bool) { 49 | if self.is_empty() { 50 | (true, false) 51 | } else if self.contains(Self::WRONLY) { 52 | (false, true) 53 | } else { 54 | (true, true) 55 | } 56 | } 57 | } 58 | 59 | /// Cached file metadata in memory 60 | #[derive(Clone)] 61 | pub struct FileHandle { 62 | /// FileSystem Inode 63 | pub inode: Option>, 64 | /// Open options: able to read 65 | pub read: bool, 66 | /// Open options: able to write 67 | pub write: bool, 68 | /// Current offset 69 | pub offset: usize, 70 | // TODO: CH7 71 | // /// Specify if this is pipe 72 | // pub pipe: bool, 73 | } 74 | 75 | impl FileHandle { 76 | pub fn new(read: bool, write: bool, inode: Arc) -> Self { 77 | Self { 78 | inode: Some(inode), 79 | read, 80 | write, 81 | offset: 0, 82 | } 83 | } 84 | 85 | pub fn empty(read: bool, write: bool) -> Self { 86 | Self { 87 | inode: None, 88 | read, 89 | write, 90 | offset: 0, 91 | } 92 | } 93 | } 94 | 95 | impl FileHandle { 96 | pub fn readable(&self) -> bool { 97 | self.read 98 | } 99 | 100 | pub fn writable(&self) -> bool { 101 | self.write 102 | } 103 | 104 | pub fn read(&mut self, mut buf: UserBuffer) -> isize { 105 | let mut total_read_size: usize = 0; 106 | if let Some(inode) = &self.inode { 107 | for slice in buf.buffers.iter_mut() { 108 | let read_size = inode.read_at(self.offset, *slice); 109 | if read_size == 0 { 110 | break; 111 | } 112 | self.offset += read_size; 113 | total_read_size += read_size; 114 | } 115 | total_read_size as _ 116 | } else { 117 | -1 118 | } 119 | } 120 | 121 | pub fn write(&mut self, buf: UserBuffer) -> isize { 122 | let mut total_write_size: usize = 0; 123 | if let Some(inode) = &self.inode { 124 | for slice in buf.buffers.iter() { 125 | let write_size = inode.write_at(self.offset, *slice); 126 | assert_eq!(write_size, slice.len()); 127 | self.offset += write_size; 128 | total_write_size += write_size; 129 | } 130 | total_write_size as _ 131 | } else { 132 | -1 133 | } 134 | } 135 | } 136 | 137 | pub trait FSManager { 138 | /// Open a file 139 | fn open(&self, path: &str, flags: OpenFlags) -> Option>; 140 | 141 | /// Find a file 142 | fn find(&self, path: &str) -> Option>; 143 | 144 | /// Create a hard link to source file 145 | fn link(&self, src: &str, dst: &str) -> isize; 146 | 147 | /// Remove a hard link 148 | fn unlink(&self, path: &str) -> isize; 149 | 150 | /// List inodes under the target directory 151 | fn readdir(&self, path: &str) -> Option>; 152 | } 153 | -------------------------------------------------------------------------------- /easy-fs/src/lib.rs: -------------------------------------------------------------------------------- 1 | //!An easy file system isolated from the kernel 2 | #![no_std] 3 | // #![deny(missing_docs)] 4 | extern crate alloc; 5 | mod bitmap; 6 | mod block_cache; 7 | mod block_dev; 8 | mod efs; 9 | mod file; 10 | mod layout; 11 | mod vfs; 12 | /// Use a block size of 512 bytes 13 | pub const BLOCK_SZ: usize = 512; 14 | use bitmap::Bitmap; 15 | use block_cache::{block_cache_sync_all, get_block_cache}; 16 | pub use block_dev::BlockDevice; 17 | pub use efs::EasyFileSystem; 18 | pub use file::*; 19 | use layout::*; 20 | pub use vfs::Inode; 21 | -------------------------------------------------------------------------------- /kernel-alloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-alloc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | log = "0.4" 11 | customizable-buddy = "0.0.3" 12 | page-table = "0.0.6" 13 | -------------------------------------------------------------------------------- /kernel-alloc/README.md: -------------------------------------------------------------------------------- 1 | # 内核内存管理 2 | 3 | 这个模块提供 `#[global_allocator]`。 4 | 5 | 内核不必区分虚存分配和物理页分配的条件是**虚地址空间覆盖物理地址空间**,换句话说,内核能直接访问到所有物理内存而无需执行修改页表之类其他操作。 6 | -------------------------------------------------------------------------------- /kernel-alloc/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 内存分配。 2 | 3 | #![no_std] 4 | #![deny(warnings, missing_docs)] 5 | 6 | extern crate alloc; 7 | 8 | use alloc::alloc::handle_alloc_error; 9 | use core::{ 10 | alloc::{GlobalAlloc, Layout}, 11 | ptr::NonNull, 12 | }; 13 | use customizable_buddy::{BuddyAllocator, LinkedListBuddy, UsizeBuddy}; 14 | 15 | /// 初始化内存分配。 16 | /// 17 | /// 参数 `base_address` 表示动态内存区域的起始位置。 18 | #[inline] 19 | pub fn init(base_address: usize) { 20 | unsafe { 21 | HEAP.init( 22 | core::mem::size_of::().trailing_zeros() as _, 23 | NonNull::new(base_address as *mut u8).unwrap(), 24 | ) 25 | }; 26 | } 27 | 28 | /// 将一个内存块托管到内存分配器。 29 | /// 30 | /// # Safety 31 | /// 32 | /// `region` 内存块的所有权将转移到分配器,因此需要调用者确保这个内存块与已经转移到分配器的内存块都不重叠,且未被其他对象引用。 33 | /// 并且这个内存块必须位于初始化时传入的起始位置之后。 34 | #[inline] 35 | pub unsafe fn transfer(region: &'static mut [u8]) { 36 | let ptr = NonNull::new(region.as_mut_ptr()).unwrap(); 37 | HEAP.transfer(ptr, region.len()); 38 | } 39 | 40 | /// 堆分配器。 41 | /// 42 | /// 最大容量:6 + 21 + 3 = 30 -> 1 GiB。 43 | /// 不考虑并发使用,因此没有加锁。 44 | static mut HEAP: BuddyAllocator<21, UsizeBuddy, LinkedListBuddy> = BuddyAllocator::new(); 45 | 46 | struct Global; 47 | 48 | #[global_allocator] 49 | static GLOBAL: Global = Global; 50 | 51 | unsafe impl GlobalAlloc for Global { 52 | #[inline] 53 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 54 | if let Ok((ptr, _)) = HEAP.allocate_layout::(layout) { 55 | ptr.as_ptr() 56 | } else { 57 | handle_alloc_error(layout) 58 | } 59 | } 60 | 61 | #[inline] 62 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 63 | HEAP.deallocate_layout(NonNull::new(ptr).unwrap(), layout) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kernel-context/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-context" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [features] 10 | foreign = ["spin"] 11 | 12 | [dependencies] 13 | spin = { version = "0.9.4", optional = true } 14 | -------------------------------------------------------------------------------- /kernel-context/src/foreign/multislot_portal.rs: -------------------------------------------------------------------------------- 1 | use super::{MonoForeignPortal, PortalCache, PORTAL_TEXT}; 2 | 3 | /// 包含多个插槽的异界传送门。 4 | #[repr(C)] 5 | pub struct MultislotPortal { 6 | slot_count: usize, 7 | text_size: usize, 8 | } 9 | 10 | macro_rules! sizeof { 11 | ($ty:ty) => { 12 | core::mem::size_of::<$ty>() 13 | }; 14 | } 15 | 16 | impl MultislotPortal { 17 | /// 计算包括 `slots` 个插槽的传送门总长度。 18 | #[inline] 19 | pub fn calculate_size(slots: usize) -> usize { 20 | sizeof!(Self) + PORTAL_TEXT.aligned_size() + slots * sizeof!(PortalCache) 21 | } 22 | 23 | /// 初始化公共空间上的传送门。 24 | /// 25 | /// # Safety 26 | /// 27 | /// `transit` 必须是一个正确映射到公共地址空间上的地址。 28 | pub unsafe fn init_transit(transit: usize, slots: usize) -> &'static mut Self { 29 | // 判断 transit 满足对齐要求 30 | debug_assert!(transit.trailing_zeros() > sizeof!(usize).trailing_zeros()); 31 | // 拷贝代码 32 | PORTAL_TEXT.copy_to(transit + sizeof!(Self)); 33 | // 填写元数据 34 | let mut ans = &mut *(transit as *mut Self); 35 | ans.slot_count = slots; 36 | ans.text_size = PORTAL_TEXT.aligned_size(); 37 | ans 38 | } 39 | } 40 | 41 | impl MonoForeignPortal for MultislotPortal { 42 | #[inline] 43 | fn total_size(&self) -> usize { 44 | self.cache_offset(self.slot_count) 45 | } 46 | 47 | #[inline] 48 | fn transit_address(&self) -> usize { 49 | self as *const _ as usize 50 | } 51 | 52 | #[inline] 53 | fn text_offset(&self) -> usize { 54 | sizeof!(Self) 55 | } 56 | 57 | #[inline] 58 | fn cache_offset(&self, key: usize) -> usize { 59 | sizeof!(Self) + self.text_size + key * sizeof!(PortalCache) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kernel-vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kernel-vm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | spin = "0.9" 11 | page-table = "0.0.6" 12 | -------------------------------------------------------------------------------- /kernel-vm/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 内核虚存管理。 2 | 3 | #![no_std] 4 | #![deny(warnings, missing_docs)] 5 | 6 | mod space; 7 | 8 | pub extern crate page_table; 9 | pub use space::AddressSpace; 10 | 11 | use core::ptr::NonNull; 12 | use page_table::{Pte, VmFlags, VmMeta, PPN}; 13 | 14 | /// 物理页管理。 15 | pub trait PageManager { 16 | /// 新建根页表页。 17 | fn new_root() -> Self; 18 | 19 | /// 获取根页表。 20 | fn root_ptr(&self) -> NonNull>; 21 | 22 | /// 获取根页表的物理页号。 23 | #[inline] 24 | fn root_ppn(&self) -> PPN { 25 | self.v_to_p(self.root_ptr()) 26 | } 27 | 28 | /// 计算当前地址空间上指向物理页的指针。 29 | fn p_to_v(&self, ppn: PPN) -> NonNull; 30 | 31 | /// 计算当前地址空间上的指针指向的物理页。 32 | fn v_to_p(&self, ptr: NonNull) -> PPN; 33 | 34 | /// 检查是否拥有一个页的所有权。 35 | fn check_owned(&self, pte: Pte) -> bool; 36 | 37 | /// 为地址空间分配 `len` 个物理页。 38 | fn allocate(&mut self, len: usize, flags: &mut VmFlags) -> NonNull; 39 | 40 | /// 从地址空间释放 `pte` 指示的 `len` 个物理页。 41 | fn deallocate(&mut self, pte: Pte, len: usize) -> usize; 42 | 43 | /// 释放根页表。 44 | fn drop_root(&mut self); 45 | } 46 | -------------------------------------------------------------------------------- /kernel-vm/src/space/mapper.rs: -------------------------------------------------------------------------------- 1 | use crate::{AddressSpace, PageManager}; 2 | use core::{ops::Range, ptr::NonNull}; 3 | use page_table::{Decorator, Pos, Pte, Update, VmFlags, VmMeta, PPN}; 4 | 5 | pub(super) struct Mapper<'a, Meta: VmMeta, M: PageManager> { 6 | space: &'a mut AddressSpace, 7 | range: Range>, 8 | flags: VmFlags, 9 | done: bool, 10 | } 11 | 12 | impl<'a, Meta: VmMeta, M: PageManager> Mapper<'a, Meta, M> { 13 | #[inline] 14 | pub fn new( 15 | space: &'a mut AddressSpace, 16 | range: Range>, 17 | flags: VmFlags, 18 | ) -> Self { 19 | Self { 20 | space, 21 | range, 22 | flags, 23 | done: false, 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn ans(self) -> bool { 29 | self.done 30 | } 31 | } 32 | 33 | impl> Decorator for Mapper<'_, Meta, M> { 34 | #[inline] 35 | fn arrive(&mut self, pte: &mut Pte, target_hint: Pos) -> Pos { 36 | assert!(!pte.is_valid()); 37 | *pte = self.flags.build_pte(self.range.start); 38 | self.range.start += 1; 39 | if self.range.start == self.range.end { 40 | self.done = true; 41 | Pos::stop() 42 | } else { 43 | target_hint.next() 44 | } 45 | } 46 | 47 | #[inline] 48 | fn meet( 49 | &mut self, 50 | _level: usize, 51 | pte: Pte, 52 | _target_hint: Pos, 53 | ) -> Option>> { 54 | if self.space.page_manager.check_owned(pte) { 55 | Some(self.space.page_manager.p_to_v(pte.ppn())) 56 | } else { 57 | None 58 | } 59 | } 60 | 61 | #[inline] 62 | fn block(&mut self, _level: usize, pte: Pte, _target_hint: Pos) -> Update { 63 | assert!(!pte.is_valid()); 64 | let mut flags = VmFlags::VALID; 65 | let page = self.space.page_manager.allocate(1, &mut flags); 66 | let ppn = self.space.page_manager.v_to_p(page); 67 | Update::Pte(flags.build_pte(ppn), page.cast()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kernel-vm/src/space/mod.rs: -------------------------------------------------------------------------------- 1 | mod mapper; 2 | mod visitor; 3 | 4 | extern crate alloc; 5 | 6 | use crate::PageManager; 7 | use alloc::vec::Vec; 8 | use core::{fmt, ops::Range, ptr::NonNull}; 9 | use mapper::Mapper; 10 | use page_table::{PageTable, PageTableFormatter, Pos, VAddr, VmFlags, VmMeta, PPN, VPN}; 11 | use visitor::Visitor; 12 | 13 | /// 地址空间。 14 | pub struct AddressSpace> { 15 | /// 虚拟地址块 16 | pub areas: Vec>>, 17 | page_manager: M, 18 | } 19 | 20 | impl> AddressSpace { 21 | /// 创建新地址空间。 22 | #[inline] 23 | pub fn new() -> Self { 24 | Self { 25 | areas: Vec::new(), 26 | page_manager: M::new_root(), 27 | } 28 | } 29 | 30 | /// 地址空间根页表的物理页号。 31 | #[inline] 32 | pub fn root_ppn(&self) -> PPN { 33 | self.page_manager.root_ppn() 34 | } 35 | 36 | /// 地址空间根页表 37 | #[inline] 38 | pub fn root(&self) -> PageTable { 39 | unsafe { PageTable::from_root(self.page_manager.root_ptr()) } 40 | } 41 | 42 | /// 向地址空间增加映射关系。 43 | pub fn map_extern(&mut self, range: Range>, pbase: PPN, flags: VmFlags) { 44 | self.areas.push(range.start..range.end); 45 | let count = range.end.val() - range.start.val(); 46 | let mut root = self.root(); 47 | let mut mapper = Mapper::new(self, pbase..pbase + count, flags); 48 | root.walk_mut(Pos::new(range.start, 0), &mut mapper); 49 | if !mapper.ans() { 50 | // 映射失败,需要回滚吗? 51 | todo!() 52 | } 53 | } 54 | 55 | /// 分配新的物理页,拷贝数据并建立映射。 56 | pub fn map( 57 | &mut self, 58 | range: Range>, 59 | data: &[u8], 60 | offset: usize, 61 | mut flags: VmFlags, 62 | ) { 63 | let count = range.end.val() - range.start.val(); 64 | let size = count << Meta::PAGE_BITS; 65 | assert!(size >= data.len() + offset); 66 | let page = self.page_manager.allocate(count, &mut flags); 67 | unsafe { 68 | use core::slice::from_raw_parts_mut as slice; 69 | let mut ptr = page.as_ptr(); 70 | slice(ptr, offset).fill(0); 71 | ptr = ptr.add(offset); 72 | slice(ptr, data.len()).copy_from_slice(data); 73 | ptr = ptr.add(data.len()); 74 | slice(ptr, page.as_ptr().add(size).offset_from(ptr) as _).fill(0); 75 | } 76 | self.map_extern(range, self.page_manager.v_to_p(page), flags) 77 | } 78 | 79 | /// 检查 `flags` 的属性要求,然后将地址空间中的一个虚地址翻译成当前地址空间中的指针。 80 | pub fn translate(&self, addr: VAddr, flags: VmFlags) -> Option> { 81 | let mut visitor = Visitor::new(self); 82 | self.root().walk(Pos::new(addr.floor(), 0), &mut visitor); 83 | visitor 84 | .ans() 85 | .filter(|pte| pte.flags().contains(flags)) 86 | .map(|pte| unsafe { 87 | NonNull::new_unchecked( 88 | self.page_manager 89 | .p_to_v::(pte.ppn()) 90 | .as_ptr() 91 | .add(addr.offset()) 92 | .cast(), 93 | ) 94 | }) 95 | } 96 | 97 | /// 遍历地址空间,将其中的地址映射添加进自己的地址空间中,重新分配物理页并拷贝所有数据及代码 98 | pub fn cloneself(&self, new_addrspace: &mut AddressSpace) { 99 | let root = self.root(); 100 | let areas = &self.areas; 101 | for (_, range) in areas.iter().enumerate() { 102 | let mut visitor = Visitor::new(self); 103 | // 虚拟地址块的首地址的 vpn 104 | let vpn = range.start; 105 | // 利用 visitor 访问页表,并获取这个虚拟地址块的页属性 106 | root.walk(Pos::new(vpn, 0), &mut visitor); 107 | // 利用 visitor 获取这个虚拟地址块的页属性,以及起始地址 108 | let (mut flags, mut data_ptr) = visitor 109 | .ans() 110 | .filter(|pte| pte.is_valid()) 111 | .map(|pte| { 112 | (pte.flags(), unsafe { 113 | NonNull::new_unchecked(self.page_manager.p_to_v::(pte.ppn()).as_ptr()) 114 | }) 115 | }) 116 | .unwrap(); 117 | let vpn_range = range.start..range.end; 118 | // 虚拟地址块中页数量 119 | let count = range.end.val() - range.start.val(); 120 | let size = count << Meta::PAGE_BITS; 121 | // 分配 count 个 flags 属性的物理页面 122 | let paddr = new_addrspace.page_manager.allocate(count, &mut flags); 123 | let ppn = new_addrspace.page_manager.v_to_p(paddr); 124 | unsafe { 125 | use core::slice::from_raw_parts_mut as slice; 126 | let data = slice(data_ptr.as_mut(), size); 127 | let ptr = paddr.as_ptr(); 128 | slice(ptr, size).copy_from_slice(data); 129 | } 130 | new_addrspace.map_extern(vpn_range, ppn, flags); 131 | } 132 | } 133 | } 134 | 135 | impl> fmt::Debug for AddressSpace { 136 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 137 | writeln!(f, "root: {:#x}", self.root_ppn().val())?; 138 | write!( 139 | f, 140 | "{:?}", 141 | PageTableFormatter { 142 | pt: self.root(), 143 | f: |ppn| self.page_manager.p_to_v(ppn) 144 | } 145 | ) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /kernel-vm/src/space/visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::{AddressSpace, PageManager}; 2 | use core::ptr::NonNull; 3 | use page_table::{Pos, Pte, VmMeta}; 4 | 5 | pub(super) struct Visitor<'a, Meta: VmMeta, M: PageManager> { 6 | space: &'a AddressSpace, 7 | ans: Option>, 8 | } 9 | 10 | impl<'a, Meta: VmMeta, M: PageManager> Visitor<'a, Meta, M> { 11 | #[inline] 12 | pub const fn new(space: &'a AddressSpace) -> Self { 13 | Self { space, ans: None } 14 | } 15 | 16 | #[inline] 17 | pub const fn ans(self) -> Option> { 18 | self.ans 19 | } 20 | } 21 | 22 | impl<'a, Meta: VmMeta, M: PageManager> page_table::Visitor for Visitor<'a, Meta, M> { 23 | #[inline] 24 | fn arrive(&mut self, pte: Pte, _target_hint: Pos) -> Pos { 25 | if pte.is_valid() { 26 | self.ans = Some(pte); 27 | } 28 | Pos::stop() 29 | } 30 | 31 | #[inline] 32 | fn meet( 33 | &mut self, 34 | _level: usize, 35 | pte: Pte, 36 | _target_hint: Pos, 37 | ) -> Option>> { 38 | Some(self.space.page_manager.p_to_v(pte.ppn())) 39 | } 40 | 41 | #[inline] 42 | fn block(&mut self, _level: usize, _pte: Pte, _target: Pos) -> Pos { 43 | Pos::stop() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /linker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linker" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /linker/README.md: -------------------------------------------------------------------------------- 1 | # 链接脚本 2 | 3 | 内核链接脚本的结构将完全由这个板块控制。所有链接脚本上定义的符号都不会泄露出这个板块,内核二进制模块可以基于标准纯 rust 语法来使用模块,而不用再手写链接脚本或记住莫名其妙的 extern "C"。 4 | -------------------------------------------------------------------------------- /linker/src/app.rs: -------------------------------------------------------------------------------- 1 | /// 应用程序元数据。 2 | #[repr(C)] 3 | pub struct AppMeta { 4 | base: u64, 5 | step: u64, 6 | count: u64, 7 | first: u64, 8 | } 9 | 10 | impl AppMeta { 11 | /// 定位应用程序。 12 | #[inline] 13 | pub fn locate() -> &'static Self { 14 | extern "C" { 15 | static apps: AppMeta; 16 | } 17 | unsafe { &apps } 18 | } 19 | 20 | /// 遍历链接进来的应用程序。 21 | #[inline] 22 | pub fn iter(&'static self) -> AppIterator { 23 | AppIterator { meta: self, i: 0 } 24 | } 25 | } 26 | 27 | /// 应用程序迭代器。 28 | pub struct AppIterator { 29 | meta: &'static AppMeta, 30 | i: u64, 31 | } 32 | 33 | impl Iterator for AppIterator { 34 | type Item = &'static [u8]; 35 | 36 | fn next(&mut self) -> Option { 37 | if self.i >= self.meta.count { 38 | None 39 | } else { 40 | let i = self.i as usize; 41 | self.i += 1; 42 | unsafe { 43 | let slice = core::slice::from_raw_parts( 44 | &self.meta.first as *const _ as *const usize, 45 | (self.meta.count + 1) as _, 46 | ); 47 | let pos = slice[i]; 48 | let size = slice[i + 1] - pos; 49 | let base = self.meta.base as usize + i * self.meta.step as usize; 50 | if base != 0 { 51 | core::ptr::copy_nonoverlapping::(pos as _, base as _, size); 52 | core::slice::from_raw_parts_mut(base as *mut u8, 0x20_0000)[size..].fill(0); 53 | Some(core::slice::from_raw_parts(base as _, size)) 54 | } else { 55 | Some(core::slice::from_raw_parts(pos as _, size)) 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | channel = "nightly" 4 | components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] 5 | targets = ["riscv64gc-unknown-none-elf"] 6 | -------------------------------------------------------------------------------- /rustsbi-qemu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YdrMaster/rCore-Tutorial-in-single-workspace/0c76582ee6cba29bc1d77f043f53014cf89425af/rustsbi-qemu.bin -------------------------------------------------------------------------------- /signal-defs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-defs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["scPointer "] 6 | 7 | [dependencies] 8 | numeric-enum-macro = "0.2.0" 9 | -------------------------------------------------------------------------------- /signal-defs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy, Default)] 5 | /// 信号处理函数的定义 6 | pub struct SignalAction { 7 | pub handler: usize, 8 | pub mask: usize, 9 | } 10 | 11 | /// 最大的信号编号 12 | pub const MAX_SIG: usize = 31; 13 | 14 | // 信号标号的定义 15 | // 目前 rCore-Tutorial 没有用到 [32, 64) 部分的实时信号,但仍给出定义 16 | numeric_enum_macro::numeric_enum! { 17 | #[repr(u8)] 18 | #[allow(missing_docs)] 19 | #[derive(Eq, PartialEq, Debug, Copy, Clone)] 20 | /// 信号编号。 21 | /// 22 | /// 从 32 开始的部分为 SIGRT,其中 RT 表示 real time。 23 | /// 但目前实现时没有通过 ipi 等手段即时处理,而是像其他信号一样等到 trap 再处理 24 | pub enum SignalNo { 25 | ERR = 0, 26 | SIGHUP = 1, 27 | SIGINT = 2, 28 | SIGQUIT = 3, 29 | SIGILL = 4, 30 | SIGTRAP = 5, 31 | SIGABRT = 6, 32 | SIGBUS = 7, 33 | SIGFPE = 8, 34 | SIGKILL = 9, 35 | SIGUSR1 = 10, 36 | SIGSEGV = 11, 37 | SIGUSR2 = 12, 38 | SIGPIPE = 13, 39 | SIGALRM = 14, 40 | SIGTERM = 15, 41 | SIGSTKFLT = 16, 42 | SIGCHLD = 17, 43 | SIGCONT = 18, 44 | SIGSTOP = 19, 45 | SIGTSTP = 20, 46 | SIGTTIN = 21, 47 | SIGTTOU = 22, 48 | SIGURG = 23, 49 | SIGXCPU = 24, 50 | SIGXFSZ = 25, 51 | SIGVTALRM = 26, 52 | SIGPROF = 27, 53 | SIGWINCH = 28, 54 | SIGIO = 29, 55 | SIGPWR = 30, 56 | SIGSYS = 31, 57 | SIGRTMIN = 32, 58 | SIGRT1 = 33, 59 | SIGRT2 = 34, 60 | SIGRT3 = 35, 61 | SIGRT4 = 36, 62 | SIGRT5 = 37, 63 | SIGRT6 = 38, 64 | SIGRT7 = 39, 65 | SIGRT8 = 40, 66 | SIGRT9 = 41, 67 | SIGRT10 = 42, 68 | SIGRT11 = 43, 69 | SIGRT12 = 44, 70 | SIGRT13 = 45, 71 | SIGRT14 = 46, 72 | SIGRT15 = 47, 73 | SIGRT16 = 48, 74 | SIGRT17 = 49, 75 | SIGRT18 = 50, 76 | SIGRT19 = 51, 77 | SIGRT20 = 52, 78 | SIGRT21 = 53, 79 | SIGRT22 = 54, 80 | SIGRT23 = 55, 81 | SIGRT24 = 56, 82 | SIGRT25 = 57, 83 | SIGRT26 = 58, 84 | SIGRT27 = 59, 85 | SIGRT28 = 60, 86 | SIGRT29 = 61, 87 | SIGRT30 = 62, 88 | SIGRT31 = 63, 89 | } 90 | } 91 | 92 | impl From for SignalNo { 93 | fn from(num: usize) -> Self { 94 | Self::try_from(num as u8).unwrap_or(Self::ERR) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /signal-impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-impl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["scPointer "] 6 | 7 | [dependencies] 8 | kernel-context = { path = "../kernel-context" } 9 | signal = { path = "../signal" } -------------------------------------------------------------------------------- /signal-impl/src/default_action.rs: -------------------------------------------------------------------------------- 1 | use super::{SignalNo, SignalResult}; 2 | 3 | /// 没有处理函数时的默认行为。 4 | /// 参见 `https://venam.nixers.net/blog/unix/2016/10/21/unix-signals.html` 5 | pub enum DefaultAction { 6 | Terminate(i32), // 结束进程。其实更标准的实现应该细分为 terminate / terminate(core dump) / stop 7 | Ignore, // 忽略信号 8 | } 9 | 10 | impl From for DefaultAction { 11 | fn from(signal_no: SignalNo) -> Self { 12 | match signal_no { 13 | SignalNo::SIGCHLD | SignalNo::SIGURG => Self::Ignore, 14 | _ => Self::Terminate(-(signal_no as i32)), 15 | } 16 | } 17 | } 18 | 19 | impl Into for DefaultAction { 20 | fn into(self) -> SignalResult { 21 | match self { 22 | Self::Terminate(exit_code) => SignalResult::ProcessKilled(exit_code), 23 | Self::Ignore => SignalResult::Ignored, 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /signal-impl/src/signal_set.rs: -------------------------------------------------------------------------------- 1 | //! 字符数组。可取并集和差集,也可对给定的 mask 取首位 2 | //! 3 | 4 | #[derive(Clone, Copy, Debug)] 5 | /// bit数组 6 | pub struct SignalSet(pub usize); 7 | 8 | impl SignalSet { 9 | /// 新建一个空的数组 10 | pub fn empty() -> Self { 11 | Self(0) 12 | } 13 | /// 新建一个数组,长为 usize = 8Byte 14 | pub fn new(v: usize) -> Self { 15 | Self(v) 16 | } 17 | /// 直接暴力写入 SignalSet 18 | pub fn reset(&mut self, v: usize) { 19 | self.0 = v; 20 | } 21 | /// 清空 SignalSet 22 | pub fn clear(&mut self) { 23 | self.0 = 0; 24 | } 25 | /// 是否包含第 k 个 bit 26 | pub fn contain_bit(&self, kth: usize) -> bool { 27 | ((self.0 >> kth) & 1) > 0 28 | } 29 | /// 新增一个 bit 30 | pub fn add_bit(&mut self, kth: usize) { 31 | self.0 |= 1 << kth; 32 | } 33 | /// 删除一个 bit 34 | pub fn remove_bit(&mut self, kth: usize) { 35 | self.0 &= !(1 << kth); 36 | } 37 | /// 取交集 38 | pub fn get_union(&mut self, set: SignalSet) { 39 | self.0 |= set.0; 40 | } 41 | /// 取差集,即去掉 set 中的内容 42 | pub fn get_difference(&mut self, set: SignalSet) { 43 | self.0 &= !(set.0); 44 | } 45 | /// 直接设置为新值 46 | pub fn set_new(&mut self, set: SignalSet) -> usize { 47 | let old = self.0; 48 | self.0 = set.0; 49 | old 50 | } 51 | /// 获取后缀0个数,可以用来寻找最小的1 52 | pub fn get_trailing_zeros(&self) -> u32 { 53 | self.0.trailing_zeros() 54 | } 55 | /// 寻找不在mask中的最小的 1 的位置,如果有,返回其位置,如没有则返回 None。 56 | pub fn find_first_one(&self, mask: SignalSet) -> Option { 57 | let ans = (self.0 & !mask.0).trailing_zeros() as usize; 58 | if ans == 64 { 59 | None 60 | } else { 61 | Some(ans) 62 | } 63 | } 64 | } 65 | 66 | impl From for SignalSet { 67 | fn from(v: usize) -> Self { 68 | Self(v) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /signal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["scPointer "] 6 | 7 | [dependencies] 8 | kernel-context = { path = "../kernel-context" } 9 | signal-defs = { path = "../signal-defs" } 10 | -------------------------------------------------------------------------------- /signal/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 信号的管理和处理模块 2 | //! 3 | //! 信号模块的实际实现见 `signal_impl` 子模块 4 | //! 5 | //! 6 | 7 | #![no_std] 8 | 9 | extern crate alloc; 10 | use alloc::boxed::Box; 11 | use kernel_context::LocalContext; 12 | pub use signal_defs::{SignalAction, SignalNo, MAX_SIG}; 13 | 14 | mod signal_result; 15 | pub use signal_result::SignalResult; 16 | 17 | /// 一个信号模块需要对外暴露的接口 18 | pub trait Signal: Send + Sync { 19 | /// 当 fork 一个任务时(在通常的`linux syscall`中,fork是某种参数形式的sys_clone), 20 | /// 需要**继承原任务的信号处理函数和掩码**。 21 | /// 此时 `task` 模块会调用此函数,根据原任务的信号模块生成新任务的信号模块 22 | fn from_fork(&mut self) -> Box; 23 | 24 | /// `sys_exec`会使用。** `sys_exec` 不会继承信号处理函数和掩码** 25 | fn clear(&mut self); 26 | 27 | /// 添加一个信号 28 | fn add_signal(&mut self, signal: SignalNo); 29 | 30 | /// 是否当前正在处理信号 31 | fn is_handling_signal(&self) -> bool; 32 | 33 | /// 设置一个信号处理函数,返回设置是否成功。`sys_sigaction` 会使用。 34 | /// (**不成功说明设置是无效的,需要在 sig_action 中返回EINVAL**) 35 | fn set_action(&mut self, signum: SignalNo, action: &SignalAction) -> bool; 36 | 37 | /// 获取一个信号处理函数的值,返回设置是否成功。`sys_sigaction` 会使用 38 | ///(**不成功说明设置是无效的,需要在 sig_action 中返回EINVAL**) 39 | fn get_action_ref(&self, signum: SignalNo) -> Option; 40 | 41 | /// 设置信号掩码,并获取旧的信号掩码,`sys_procmask` 会使用 42 | fn update_mask(&mut self, mask: usize) -> usize; 43 | 44 | /// 进程执行结果,可能是直接返回用户程序或存栈或暂停或退出 45 | fn handle_signals(&mut self, current_context: &mut LocalContext) -> SignalResult; 46 | 47 | /// 从信号处理函数中退出,返回值表示是否成功。`sys_sigreturn` 会使用 48 | fn sig_return(&mut self, current_context: &mut LocalContext) -> bool; 49 | } 50 | -------------------------------------------------------------------------------- /signal/src/signal_result.rs: -------------------------------------------------------------------------------- 1 | /// 信号处理函数返回得到的结果 2 | pub enum SignalResult { 3 | /// 没有信号需要处理 4 | NoSignal, 5 | /// 目前正在处理信号,因而无法接受其他信号 6 | IsHandlingSignal, 7 | /// 已经处理了一个信号,接下来正常返回用户态即可 8 | Ignored, 9 | /// 已经处理了一个信号,并修改了用户上下文 10 | Handled, 11 | /// 需要结束当前进程,并给出退出时向父进程返回的 errno 12 | ProcessKilled(i32), 13 | /// 需要暂停当前进程,直到其他进程给出继续执行的信号 14 | ProcessSuspended, 15 | } 16 | -------------------------------------------------------------------------------- /sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sync" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["zflcs "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | riscv = "0.10.1" 11 | spin = "0.9" 12 | rcore-task-manage = { path = "../task-manage", features = ["thread"] } 13 | -------------------------------------------------------------------------------- /sync/src/condvar.rs: -------------------------------------------------------------------------------- 1 | use super::{Mutex, UPIntrFreeCell}; 2 | use alloc::{collections::VecDeque, sync::Arc}; 3 | use rcore_task_manage::ThreadId; 4 | 5 | /// Condvar 6 | pub struct Condvar { 7 | /// UPIntrFreeCell 8 | pub inner: UPIntrFreeCell, 9 | } 10 | 11 | /// CondvarInner 12 | pub struct CondvarInner { 13 | /// block queue 14 | pub wait_queue: VecDeque, 15 | } 16 | 17 | impl Condvar { 18 | /// new 19 | pub fn new() -> Self { 20 | Self { 21 | inner: unsafe { 22 | UPIntrFreeCell::new(CondvarInner { 23 | wait_queue: VecDeque::new(), 24 | }) 25 | }, 26 | } 27 | } 28 | /// 唤醒某个阻塞在当前条件变量上的线程 29 | pub fn signal(&self) -> Option { 30 | let mut inner = self.inner.exclusive_access(); 31 | inner.wait_queue.pop_front() 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, tid: ThreadId) -> bool { 44 | self.inner.exclusive_session(|inner| { 45 | inner.wait_queue.push_back(tid); 46 | }); 47 | false 48 | } 49 | /// 从 mutex 的锁中释放一个线程,并将其阻塞在条件变量的等待队列中,等待其他线程运行完毕,当前的线程再试图获取这个锁 50 | /// 51 | /// 注意:下面是简化版的实现,在 mutex 唤醒一个线程之后,当前线程就直接获取这个 mutex,不管能不能获取成功 52 | /// 这里是单纯为了过测例, 53 | pub fn wait_with_mutex( 54 | &self, 55 | tid: ThreadId, 56 | mutex: Arc, 57 | ) -> (bool, Option) { 58 | let waking_tid = mutex.unlock().unwrap(); 59 | (mutex.lock(tid), Some(waking_tid)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sync/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 同步互斥模块 2 | 3 | #![no_std] 4 | #![deny(warnings, missing_docs)] 5 | 6 | mod condvar; 7 | mod mutex; 8 | mod semaphore; 9 | mod up; 10 | 11 | extern crate alloc; 12 | 13 | pub use condvar::Condvar; 14 | pub use mutex::{Mutex, MutexBlocking}; 15 | pub use semaphore::Semaphore; 16 | pub use up::{UPIntrFreeCell, UPIntrRefMut}; 17 | -------------------------------------------------------------------------------- /sync/src/mutex.rs: -------------------------------------------------------------------------------- 1 | use super::UPIntrFreeCell; 2 | use alloc::collections::VecDeque; 3 | use rcore_task_manage::ThreadId; 4 | 5 | /// Mutex trait 6 | pub trait Mutex: Sync + Send { 7 | /// tid 表示的线程试图获取锁,并返回结果 8 | fn lock(&self, tid: ThreadId) -> bool; 9 | /// 当前线程释放锁,并唤醒某个阻塞在这个锁上的线程 10 | fn unlock(&self) -> Option; 11 | } 12 | 13 | /// MutexBlocking 14 | pub struct MutexBlocking { 15 | inner: UPIntrFreeCell, 16 | } 17 | 18 | /// MutexBlockingInner 19 | pub struct MutexBlockingInner { 20 | locked: bool, 21 | wait_queue: VecDeque, 22 | } 23 | 24 | impl MutexBlocking { 25 | /// new 26 | pub fn new() -> Self { 27 | Self { 28 | inner: unsafe { 29 | UPIntrFreeCell::new(MutexBlockingInner { 30 | locked: false, 31 | wait_queue: VecDeque::new(), 32 | }) 33 | }, 34 | } 35 | } 36 | } 37 | 38 | impl Mutex for MutexBlocking { 39 | // 获取锁,如果获取成功,返回 true,否则会返回 false,要求阻塞对应的线程 40 | fn lock(&self, tid: ThreadId) -> bool { 41 | let mut mutex_inner = self.inner.exclusive_access(); 42 | if mutex_inner.locked { 43 | mutex_inner.wait_queue.push_back(tid); 44 | drop(mutex_inner); 45 | false 46 | } else { 47 | mutex_inner.locked = true; 48 | true 49 | } 50 | } 51 | // 释放锁,释放之后会唤醒一个被阻塞的进程,要求重新进入调度队列 52 | fn unlock(&self) -> Option { 53 | let mut mutex_inner = self.inner.exclusive_access(); 54 | assert!(mutex_inner.locked); 55 | if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { 56 | Some(waking_task) 57 | } else { 58 | mutex_inner.locked = false; 59 | None 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sync/src/semaphore.rs: -------------------------------------------------------------------------------- 1 | use super::UPIntrFreeCell; 2 | use alloc::collections::VecDeque; 3 | use rcore_task_manage::ThreadId; 4 | 5 | /// Semaphore 6 | pub struct Semaphore { 7 | /// UPIntrFreeCell 8 | pub inner: UPIntrFreeCell, 9 | } 10 | 11 | /// SemaphoreInner 12 | pub struct SemaphoreInner { 13 | pub count: isize, 14 | pub wait_queue: VecDeque, 15 | } 16 | 17 | impl Semaphore { 18 | /// new 19 | pub fn new(res_count: usize) -> Self { 20 | Self { 21 | inner: unsafe { 22 | UPIntrFreeCell::new(SemaphoreInner { 23 | count: res_count as isize, 24 | wait_queue: VecDeque::new(), 25 | }) 26 | }, 27 | } 28 | } 29 | /// 当前线程释放信号量表示的一个资源,并唤醒一个阻塞的线程 30 | pub fn up(&self) -> Option { 31 | let mut inner = self.inner.exclusive_access(); 32 | inner.count += 1; 33 | inner.wait_queue.pop_front() 34 | } 35 | /// 当前线程试图获取信号量表示的资源,并返回结果 36 | pub fn down(&self, tid: ThreadId) -> bool { 37 | let mut inner = self.inner.exclusive_access(); 38 | inner.count -= 1; 39 | if inner.count < 0 { 40 | inner.wait_queue.push_back(tid); 41 | drop(inner); 42 | false 43 | } else { 44 | true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sync/src/up.rs: -------------------------------------------------------------------------------- 1 | use core::cell::{RefCell, RefMut, UnsafeCell}; 2 | use core::ops::{Deref, DerefMut}; 3 | use riscv::register::sstatus; 4 | use spin::Lazy; 5 | 6 | /* 7 | /// Wrap a static data structure inside it so that we are 8 | /// able to access it without any `unsafe`. 9 | /// 10 | /// We should only use it in uniprocessor. 11 | /// 12 | /// In order to get mutable reference of inner data, call 13 | /// `exclusive_access`. 14 | pub struct UPSafeCell { 15 | /// inner data 16 | inner: RefCell, 17 | } 18 | 19 | unsafe impl Sync for UPSafeCell {} 20 | 21 | impl UPSafeCell { 22 | /// User is responsible to guarantee that inner struct is only used in 23 | /// uniprocessor. 24 | pub unsafe fn new(value: T) -> Self { 25 | Self { 26 | inner: RefCell::new(value), 27 | } 28 | } 29 | /// Panic if the data has been borrowed. 30 | pub fn exclusive_access(&self) -> RefMut<'_, T> { 31 | self.inner.borrow_mut() 32 | } 33 | } 34 | */ 35 | 36 | /// interior mutability 37 | pub struct UPSafeCellRaw { 38 | inner: UnsafeCell, 39 | } 40 | 41 | unsafe impl Sync for UPSafeCellRaw {} 42 | 43 | impl UPSafeCellRaw { 44 | pub unsafe fn new(value: T) -> Self { 45 | Self { 46 | inner: UnsafeCell::new(value), 47 | } 48 | } 49 | pub fn get_mut(&self) -> &mut T { 50 | unsafe { &mut (*self.inner.get()) } 51 | } 52 | } 53 | 54 | /// 中断屏蔽信息 55 | pub struct IntrMaskingInfo { 56 | nested_level: usize, 57 | sie_before_masking: bool, 58 | } 59 | 60 | pub static INTR_MASKING_INFO: Lazy> = 61 | Lazy::new(|| unsafe { UPSafeCellRaw::new(IntrMaskingInfo::new()) }); 62 | 63 | impl IntrMaskingInfo { 64 | pub fn new() -> Self { 65 | Self { 66 | nested_level: 0, 67 | sie_before_masking: false, 68 | } 69 | } 70 | 71 | pub fn enter(&mut self) { 72 | let sie = sstatus::read().sie(); 73 | unsafe { 74 | sstatus::clear_sie(); 75 | } 76 | if self.nested_level == 0 { 77 | self.sie_before_masking = sie; 78 | } 79 | self.nested_level += 1; 80 | } 81 | 82 | pub fn exit(&mut self) { 83 | self.nested_level -= 1; 84 | if self.nested_level == 0 && self.sie_before_masking { 85 | unsafe { 86 | sstatus::set_sie(); 87 | } 88 | } 89 | } 90 | } 91 | 92 | /// A mutable memory location with dynamically checked borrow rules 93 | pub struct UPIntrFreeCell { 94 | /// inner data 95 | inner: RefCell, 96 | } 97 | 98 | unsafe impl Sync for UPIntrFreeCell {} 99 | 100 | /// A wrapper type for a mutably borrowed value from a RefCell 101 | pub struct UPIntrRefMut<'a, T>(Option>); 102 | 103 | impl UPIntrFreeCell { 104 | /// 105 | pub unsafe fn new(value: T) -> Self { 106 | Self { 107 | inner: RefCell::new(value), 108 | } 109 | } 110 | 111 | /// Panic if the data has been borrowed. 112 | pub fn exclusive_access(&self) -> UPIntrRefMut<'_, T> { 113 | INTR_MASKING_INFO.get_mut().enter(); 114 | UPIntrRefMut(Some(self.inner.borrow_mut())) 115 | } 116 | /// exclusive_session 117 | pub fn exclusive_session(&self, f: F) -> V 118 | where 119 | F: FnOnce(&mut T) -> V, 120 | { 121 | let mut inner = self.exclusive_access(); 122 | f(inner.deref_mut()) 123 | } 124 | } 125 | 126 | impl<'a, T> Drop for UPIntrRefMut<'a, T> { 127 | fn drop(&mut self) { 128 | self.0 = None; 129 | INTR_MASKING_INFO.get_mut().exit(); 130 | } 131 | } 132 | 133 | impl<'a, T> Deref for UPIntrRefMut<'a, T> { 134 | type Target = T; 135 | fn deref(&self) -> &Self::Target { 136 | self.0.as_ref().unwrap().deref() 137 | } 138 | } 139 | impl<'a, T> DerefMut for UPIntrRefMut<'a, T> { 140 | fn deref_mut(&mut self) -> &mut Self::Target { 141 | self.0.as_mut().unwrap().deref_mut() 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /syscall/.gitignore: -------------------------------------------------------------------------------- 1 | src/syscalls.rs 2 | -------------------------------------------------------------------------------- /syscall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syscall" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | spin = "0.9" 11 | bitflags = "1.2.1" 12 | signal-defs = { path = "../signal-defs" } 13 | 14 | [features] 15 | kernel = [] 16 | user = [] 17 | -------------------------------------------------------------------------------- /syscall/README.md: -------------------------------------------------------------------------------- 1 | # 系统调用 2 | 3 | 这个库封装了提供给操作系统和用户程序的系统调用。 4 | 5 | 系统调用号从 Musl Libc for RISC-V 源码生成,因为找不到标准文档。 6 | -------------------------------------------------------------------------------- /syscall/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{ 3 | fs::{self, File}, 4 | io::Write, 5 | }; 6 | 7 | const SYSCALL_H_IN: &str = "src/syscall.h.in"; 8 | println!("cargo:rerun-if-changed=build.rs"); 9 | println!("cargo:rerun-if-changed={SYSCALL_H_IN}"); 10 | 11 | let mut fout = File::create("src/syscalls.rs").unwrap(); 12 | writeln!( 13 | fout, 14 | "\ 15 | //! Generated by build.rs. DO NOT EDIT. 16 | 17 | impl crate::SyscallId {{" 18 | ) 19 | .unwrap(); 20 | fs::read_to_string(SYSCALL_H_IN) 21 | .unwrap() 22 | .lines() 23 | .filter_map(|line| line.strip_prefix("#define __NR_")) 24 | .filter_map(|line| line.split_once(' ')) 25 | .for_each(|(name, num)| { 26 | writeln!( 27 | fout, 28 | " pub const {name}: Self = Self({num});", 29 | name = name.to_uppercase() 30 | ) 31 | .unwrap(); 32 | }); 33 | writeln!(fout, "}}").unwrap(); 34 | } 35 | -------------------------------------------------------------------------------- /syscall/src/io.rs: -------------------------------------------------------------------------------- 1 | pub const STDIN: usize = 0; 2 | pub const STDOUT: usize = 1; 3 | pub const STDDEBUG: usize = 2; 4 | -------------------------------------------------------------------------------- /syscall/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![deny(warnings)] 3 | 4 | #[cfg(all(feature = "kernel", feature = "user"))] 5 | compile_error!("You can only use one of `supervisor` or `user` features at a time"); 6 | 7 | mod io; 8 | mod syscalls; 9 | mod time; 10 | 11 | pub use io::*; 12 | pub use signal_defs::{SignalAction, SignalNo, MAX_SIG}; 13 | pub use time::*; 14 | 15 | #[cfg(feature = "user")] 16 | mod user; 17 | 18 | #[cfg(feature = "user")] 19 | pub use user::*; 20 | 21 | #[cfg(feature = "kernel")] 22 | mod kernel; 23 | 24 | #[cfg(feature = "kernel")] 25 | pub use kernel::*; 26 | 27 | /// 系统调用号。 28 | /// 29 | /// 实现为包装类型,在不损失扩展性的情况下实现类型安全性。 30 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] 31 | #[repr(transparent)] 32 | pub struct SyscallId(pub usize); 33 | 34 | impl From for SyscallId { 35 | #[inline] 36 | fn from(val: usize) -> Self { 37 | Self(val) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /syscall/src/time.rs: -------------------------------------------------------------------------------- 1 | //! see . 2 | 3 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 4 | #[repr(transparent)] 5 | pub struct ClockId(pub usize); 6 | 7 | impl ClockId { 8 | pub const CLOCK_REALTIME: Self = Self(0); 9 | pub const CLOCK_MONOTONIC: Self = Self(1); 10 | pub const CLOCK_PROCESS_CPUTIME_ID: Self = Self(2); 11 | pub const CLOCK_THREAD_CPUTIME_ID: Self = Self(3); 12 | pub const CLOCK_MONOTONIC_RAW: Self = Self(4); 13 | pub const CLOCK_REALTIME_COARSE: Self = Self(5); 14 | pub const CLOCK_MONOTONIC_COARSE: Self = Self(6); 15 | pub const CLOCK_BOOTTIME: Self = Self(7); 16 | pub const CLOCK_REALTIME_ALARM: Self = Self(8); 17 | pub const CLOCK_BOOTTIME_ALARM: Self = Self(9); 18 | pub const CLOCK_SGI_CYCLE: Self = Self(10); 19 | pub const CLOCK_TAI: Self = Self(11); 20 | } 21 | 22 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)] 23 | #[repr(C)] 24 | pub struct TimeSpec { 25 | // seconds 26 | pub tv_sec: usize, 27 | // nanoseconds 28 | pub tv_nsec: usize, 29 | } 30 | 31 | impl TimeSpec { 32 | pub const ZERO: Self = Self { 33 | tv_sec: 0, 34 | tv_nsec: 0, 35 | }; 36 | pub const SECOND: Self = Self { 37 | tv_sec: 1, 38 | tv_nsec: 0, 39 | }; 40 | pub const MILLSECOND: Self = Self { 41 | tv_sec: 0, 42 | tv_nsec: 1_000_000, 43 | }; 44 | pub const MICROSECOND: Self = Self { 45 | tv_sec: 0, 46 | tv_nsec: 1_000, 47 | }; 48 | pub const NANOSECOND: Self = Self { 49 | tv_sec: 0, 50 | tv_nsec: 1, 51 | }; 52 | pub fn from_millsecond(millsecond: usize) -> Self { 53 | Self { 54 | tv_sec: millsecond / 1_000, 55 | tv_nsec: millsecond % 1_000 * 1_000_000, 56 | } 57 | } 58 | } 59 | 60 | impl core::ops::Add for TimeSpec { 61 | type Output = Self; 62 | fn add(self, rhs: Self) -> Self::Output { 63 | let mut ans = Self { 64 | tv_sec: self.tv_sec + rhs.tv_sec, 65 | tv_nsec: self.tv_nsec + rhs.tv_nsec, 66 | }; 67 | if ans.tv_nsec > 1_000_000_000 { 68 | ans.tv_sec += 1; 69 | ans.tv_nsec -= 1_000_000_000; 70 | } 71 | ans 72 | } 73 | } 74 | 75 | impl core::fmt::Display for TimeSpec { 76 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 77 | write!(f, "TimeSpec({}.{:09})", self.tv_sec, self.tv_nsec) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /task-manage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rcore-task-manage" 3 | description = "Manages tasks and maintain relationships between them" 4 | version = "0.0.0" 5 | edition = "2021" 6 | authors = ["zflcs "] 7 | repository = "https://github.com/YdrMaster/rCore-Tutorial-in-single-workspace/task-manage" 8 | documentation = "https://docs.rs/rcore-task-manage" 9 | license = "WTFPL" 10 | readme = "README.md" 11 | keywords = ["rcore", "task-manage"] 12 | categories = ["no-std"] 13 | 14 | 15 | [features] 16 | proc = [] 17 | thread = [] 18 | 19 | -------------------------------------------------------------------------------- /task-manage/README.md: -------------------------------------------------------------------------------- 1 | # 任务管理 2 | 3 | [![Latest version](https://img.shields.io/crates/v/rcore-task-manage.svg)](https://crates.io/crates/rcore-task-manage) 4 | [![Documentation](https://docs.rs/rcore-task-manage/badge.svg)](https://docs.rs/rcore-task-manage) 5 | ![license](https://img.shields.io/github/license/YdrMaster/rCore-Tutorial-in-single-workspace) 6 | 7 | #### 事先申明:对于 `feature` 的使用不太熟悉,所以代码不是很优雅 8 | 9 | #### 任务 id 类型,自增不回收,任务对象之间的关系通过 id 类型来实现 10 | * `ProcId` 11 | * `ThreadId` 12 | * `CoroId` 13 | #### 任务对象管理 `manage trait`,对标数据库增删改查操作 14 | * `insert` 15 | * `delete` 16 | * `get_mut` 17 | #### 任务调度 `schedule trait`,队列中保存需要调度的任务 `Id` 18 | * `add`:任务进入调度队列 19 | * `fetch`:从调度队列中取出一个任务 20 | #### 封装任务之间的关系,使得 `PCB`、`TCB` 内部更加简洁 21 | * `ProcRel`:进程与其子进程之间的关系 22 | * `ProcThreadRel`:进程、子进程以及它地址空间内的线程之间的关系 23 | 24 | 25 | -------------------------------------------------------------------------------- /task-manage/src/id.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | /// 进程 Id 4 | #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Ord, PartialOrd)] 5 | pub struct ProcId(usize); 6 | 7 | impl ProcId { 8 | /// 9 | pub fn new() -> Self { 10 | // 任务编号计数器,任务编号自增 11 | static PID_COUNTER: AtomicUsize = AtomicUsize::new(0); 12 | let id = PID_COUNTER.fetch_add(1, Ordering::Relaxed); 13 | Self(id) 14 | } 15 | /// 16 | pub fn from_usize(v: usize) -> Self { 17 | Self(v) 18 | } 19 | /// 20 | pub fn get_usize(&self) -> usize { 21 | self.0 22 | } 23 | } 24 | 25 | /// 线程 Id 26 | #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Ord, PartialOrd)] 27 | pub struct ThreadId(usize); 28 | 29 | impl ThreadId { 30 | /// 31 | pub fn new() -> Self { 32 | // 任务编号计数器,任务编号自增 33 | static TID_COUNTER: AtomicUsize = AtomicUsize::new(0); 34 | let id = TID_COUNTER.fetch_add(1, Ordering::Relaxed); 35 | Self(id) 36 | } 37 | /// 38 | pub fn from_usize(v: usize) -> Self { 39 | Self(v) 40 | } 41 | /// 42 | pub fn get_usize(&self) -> usize { 43 | self.0 44 | } 45 | } 46 | 47 | /// 协程 Id 48 | #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Ord, PartialOrd)] 49 | pub struct CoroId(usize); 50 | 51 | impl CoroId { 52 | /// 53 | pub fn new() -> Self { 54 | // 任务编号计数器,任务编号自增 55 | static CID_COUNTER: AtomicUsize = AtomicUsize::new(0); 56 | let id = CID_COUNTER.fetch_add(1, Ordering::Relaxed); 57 | Self(id) 58 | } 59 | /// 60 | pub fn from_usize(v: usize) -> Self { 61 | Self(v) 62 | } 63 | /// 64 | pub fn get_usize(&self) -> usize { 65 | self.0 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /task-manage/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 任务管理 lib 2 | 3 | #![no_std] 4 | #![feature(doc_cfg)] 5 | #![deny(warnings, missing_docs)] 6 | 7 | extern crate alloc; 8 | 9 | mod id; 10 | mod manager; 11 | mod scheduler; 12 | 13 | pub use id::*; 14 | pub use manager::Manage; 15 | pub use scheduler::Schedule; 16 | 17 | #[cfg(feature = "proc")] 18 | mod proc_manage; 19 | #[cfg(feature = "proc")] 20 | mod proc_rel; 21 | #[cfg(feature = "proc")] 22 | pub use proc_manage::PManager; 23 | #[cfg(feature = "proc")] 24 | pub use proc_rel::ProcRel; 25 | 26 | #[cfg(feature = "thread")] 27 | mod proc_thread_rel; 28 | #[cfg(feature = "thread")] 29 | mod thread_manager; 30 | #[cfg(feature = "thread")] 31 | pub use proc_thread_rel::ProcThreadRel; 32 | #[cfg(feature = "thread")] 33 | pub use thread_manager::PThreadManager; 34 | -------------------------------------------------------------------------------- /task-manage/src/manager.rs: -------------------------------------------------------------------------------- 1 | /// Manager trait 2 | pub trait Manage { 3 | /// 插入 item 4 | fn insert(&mut self, id: I, item: T); 5 | /// 删除 item 6 | fn delete(&mut self, id: I); 7 | /// 获取 mut item 8 | fn get_mut(&mut self, id: I) -> Option<&mut T>; 9 | } 10 | -------------------------------------------------------------------------------- /task-manage/src/proc_manage.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::BTreeMap; 2 | 3 | use super::id::ProcId; 4 | use super::manager::Manage; 5 | use super::scheduler::Schedule; 6 | use super::ProcRel; 7 | use core::marker::PhantomData; 8 | 9 | /// ProcManager 数据结构,只管理进程以及进程之间的父子关系 10 | /// P 表示进程 11 | #[cfg(feature = "proc")] 12 | #[doc(cfg(feature = "proc"))] 13 | pub struct PManager + Schedule> { 14 | // 进程之间父子关系 15 | rel_map: BTreeMap, 16 | // 进程对象管理和调度 17 | manager: Option, 18 | // 当前正在运行的进程 ID 19 | current: Option, 20 | phantom_data: PhantomData

, 21 | } 22 | 23 | impl + Schedule> PManager { 24 | /// 新建 PManager 25 | pub const fn new() -> Self { 26 | Self { 27 | rel_map: BTreeMap::new(), 28 | manager: None, 29 | current: None, 30 | phantom_data: PhantomData::

, 31 | } 32 | } 33 | /// 找到下一个进程 34 | pub fn find_next(&mut self) -> Option<&mut P> { 35 | if let Some(id) = self.manager.as_mut().unwrap().fetch() { 36 | if let Some(task) = self.manager.as_mut().unwrap().get_mut(id) { 37 | self.current = Some(id); 38 | Some(task) 39 | } else { 40 | None 41 | } 42 | } else { 43 | None 44 | } 45 | } 46 | /// 设置 manager 47 | pub fn set_manager(&mut self, manager: MP) { 48 | self.manager = Some(manager); 49 | } 50 | /// 阻塞当前进程 51 | pub fn make_current_suspend(&mut self) { 52 | let id = self.current.unwrap(); 53 | self.manager.as_mut().unwrap().add(id); 54 | self.current = None; 55 | } 56 | /// 结束当前进程,只会删除进程的内容,以及与当前进程相关的关系 57 | pub fn make_current_exited(&mut self, exit_code: isize) { 58 | let id = self.current.unwrap(); 59 | self.manager.as_mut().unwrap().delete(id); 60 | let current_rel = self.rel_map.remove(&id).unwrap(); 61 | let parent_pid = current_rel.parent; 62 | let children = current_rel.children; 63 | // 从父进程中删除当前进程 64 | if let Some(parent_rel) = self.rel_map.get_mut(&parent_pid) { 65 | parent_rel.del_child(id, exit_code); 66 | } 67 | // 把当前进程的所有子进程转移到 0 号进程 68 | for i in children { 69 | self.rel_map.get_mut(&i).unwrap().parent = ProcId::from_usize(0); 70 | self.rel_map 71 | .get_mut(&ProcId::from_usize(0)) 72 | .unwrap() 73 | .add_child(i); 74 | } 75 | self.current = None; 76 | } 77 | /// 添加进程,需要指明创建的进程的父进程 Id 78 | pub fn add(&mut self, id: ProcId, task: P, parent: ProcId) { 79 | self.manager.as_mut().unwrap().insert(id, task); 80 | self.manager.as_mut().unwrap().add(id); 81 | if let Some(parent_relation) = self.rel_map.get_mut(&parent) { 82 | parent_relation.add_child(id); 83 | } 84 | self.rel_map.insert(id, ProcRel::new(parent)); 85 | } 86 | /// 当前进程 87 | pub fn current(&mut self) -> Option<&mut P> { 88 | let id = self.current.unwrap(); 89 | self.manager.as_mut().unwrap().get_mut(id) 90 | } 91 | /// 获取某个进程 92 | #[inline] 93 | pub fn get_task(&mut self, id: ProcId) -> Option<&mut P> { 94 | self.manager.as_mut().unwrap().get_mut(id) 95 | } 96 | /// wait 系统调用,返回结束的子进程 id 和 exit_code,正在运行的子进程不返回 None,返回 (-2, -1) 97 | pub fn wait(&mut self, child_pid: ProcId) -> Option<(ProcId, isize)> { 98 | let id = self.current.unwrap(); 99 | let current_rel = self.rel_map.get_mut(&id).unwrap(); 100 | if child_pid.get_usize() == usize::MAX { 101 | current_rel.wait_any_child() 102 | } else { 103 | current_rel.wait_child(child_pid) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /task-manage/src/proc_rel.rs: -------------------------------------------------------------------------------- 1 | use super::id::ProcId; 2 | use alloc::vec::Vec; 3 | 4 | /// 进程之间的关系,通过进程的 Id 来查询这个关系 5 | #[cfg(feature = "proc")] 6 | #[doc(cfg(feature = "proc"))] 7 | pub struct ProcRel { 8 | /// 父进程 Id 9 | pub parent: ProcId, 10 | /// 子进程列表 11 | pub children: Vec, 12 | /// 已经结束的进程 13 | pub dead_children: Vec<(ProcId, isize)>, 14 | } 15 | 16 | impl ProcRel { 17 | /// new/fork 创建进程时使用 18 | pub fn new(parent_pid: ProcId) -> Self { 19 | Self { 20 | parent: parent_pid, 21 | children: Vec::new(), 22 | dead_children: Vec::new(), 23 | } 24 | } 25 | /// 添加子进程 Id 26 | pub fn add_child(&mut self, child_pid: ProcId) { 27 | self.children.push(child_pid); 28 | } 29 | /// 子进程结束,子进程 Id 被移入到 dead_children 队列中,等待 wait 系统调用来处理 30 | pub fn del_child(&mut self, child_pid: ProcId, exit_code: isize) { 31 | let pair = self 32 | .children 33 | .iter() 34 | .enumerate() 35 | .find(|(_, &id)| id == child_pid); 36 | if let Some((idx, _)) = pair { 37 | let dead_child = self.children.remove(idx); 38 | self.dead_children.push((dead_child, exit_code)); 39 | } 40 | } 41 | /// 等待任意一个结束的子进程,直接弹出 dead_children 队首,如果队列为空,则返回 -2 42 | pub fn wait_any_child(&mut self) -> Option<(ProcId, isize)> { 43 | if self.dead_children.is_empty() { 44 | if self.children.is_empty() { 45 | None 46 | } else { 47 | Some((ProcId::from_usize(-2 as _), -1)) 48 | } 49 | } else { 50 | self.dead_children.pop() 51 | } 52 | } 53 | /// 等待特定的子进程 54 | pub fn wait_child(&mut self, child_pid: ProcId) -> Option<(ProcId, isize)> { 55 | let pair = self 56 | .dead_children 57 | .iter() 58 | .enumerate() 59 | .find(|(_, &(id, _))| id == child_pid); 60 | if let Some((idx, _)) = pair { 61 | // 等待的子进程确已结束 62 | Some(self.dead_children.remove(idx)) 63 | } else { 64 | let pair = self 65 | .children 66 | .iter() 67 | .enumerate() 68 | .find(|(_, &id)| id == child_pid); 69 | if let Some(_) = pair { 70 | // 等待的子进程正在运行 71 | Some((ProcId::from_usize(-2 as _), -1)) 72 | } else { 73 | // 等待的子进程不存在 74 | None 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /task-manage/src/proc_thread_rel.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use super::id::{ProcId, ThreadId}; 4 | 5 | /// 线程、进程之间的关系,通过进程的 Id 来查询这个关系 6 | #[cfg(feature = "thread")] 7 | #[doc(cfg(feature = "thread"))] 8 | pub struct ProcThreadRel { 9 | /// 父进程 Id 10 | pub parent: ProcId, 11 | /// 子进程列表 12 | pub children: Vec, 13 | /// 已经结束的子进程 14 | pub dead_children: Vec<(ProcId, isize)>, 15 | /// 线程 16 | pub threads: Vec, 17 | /// 已经结束的线程 18 | pub dead_threads: Vec<(ThreadId, isize)>, 19 | } 20 | 21 | impl ProcThreadRel { 22 | /// new/fork 创建进程时使用 23 | pub fn new(parent_pid: ProcId) -> Self { 24 | Self { 25 | parent: parent_pid, 26 | children: Vec::new(), 27 | dead_children: Vec::new(), 28 | threads: Vec::new(), 29 | dead_threads: Vec::new(), 30 | } 31 | } 32 | /// 添加子进程 Id 33 | pub fn add_child(&mut self, child_pid: ProcId) { 34 | self.children.push(child_pid); 35 | } 36 | /// 子进程结束,子进程 Id 被移入到 dead_children 队列中,等待 wait 系统调用来处理 37 | pub fn del_child(&mut self, child_pid: ProcId, exit_code: isize) { 38 | let pair = self 39 | .children 40 | .iter() 41 | .enumerate() 42 | .find(|(_, &id)| id == child_pid); 43 | if let Some((idx, _)) = pair { 44 | let dead_child = self.children.remove(idx); 45 | self.dead_children.push((dead_child, exit_code)); 46 | } 47 | } 48 | /// 等待任意一个结束的子进程,直接弹出 dead_children 队首,如果队列为空,则返回 -2 49 | pub fn wait_any_child(&mut self) -> Option<(ProcId, isize)> { 50 | if self.dead_children.is_empty() { 51 | if self.children.is_empty() { 52 | None 53 | } else { 54 | Some((ProcId::from_usize(-2 as _), -1)) 55 | } 56 | } else { 57 | self.dead_children.pop() 58 | } 59 | } 60 | /// 等待特定的子进程 61 | pub fn wait_child(&mut self, child_pid: ProcId) -> Option<(ProcId, isize)> { 62 | let pair = self 63 | .dead_children 64 | .iter() 65 | .enumerate() 66 | .find(|(_, &(id, _))| id == child_pid); 67 | if let Some((idx, _)) = pair { 68 | // 等待的子进程确已结束 69 | Some(self.dead_children.remove(idx)) 70 | } else { 71 | let pair = self 72 | .children 73 | .iter() 74 | .enumerate() 75 | .find(|(_, &id)| id == child_pid); 76 | if let Some(_) = pair { 77 | // 等待的子进程正在运行 78 | Some((ProcId::from_usize(-2 as _), -1)) 79 | } else { 80 | // 等待的子进程不存在 81 | None 82 | } 83 | } 84 | } 85 | /// 添加线程 86 | pub fn add_thread(&mut self, tid: ThreadId) { 87 | self.threads.push(tid); 88 | } 89 | /// 删除线程 90 | pub fn del_thread(&mut self, tid: ThreadId, exit_code: isize) { 91 | let pair = self.threads.iter().enumerate().find(|(_, &id)| id == tid); 92 | if let Some((idx, _)) = pair { 93 | let dead_thread = self.threads.remove(idx); 94 | self.dead_threads.push((dead_thread, exit_code)); 95 | } 96 | } 97 | /// 等待特定的线程结束 98 | pub fn wait_thread(&mut self, thread_tid: ThreadId) -> Option { 99 | let pair = self 100 | .dead_threads 101 | .iter() 102 | .enumerate() 103 | .find(|(_, &(id, _))| id == thread_tid); 104 | if let Some((idx, _)) = pair { 105 | // 等待的子进程确已结束 106 | Some(self.dead_threads.remove(idx).1) 107 | } else { 108 | let pair = self 109 | .threads 110 | .iter() 111 | .enumerate() 112 | .find(|(_, &id)| id == thread_tid); 113 | if let Some(_) = pair { 114 | // 等待的子进程正在运行 115 | Some(-2) 116 | } else { 117 | // 等待的子进程不存在 118 | None 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /task-manage/src/scheduler.rs: -------------------------------------------------------------------------------- 1 | /// Scheduler 2 | pub trait Schedule { 3 | /// 入队 4 | fn add(&mut self, id: I); 5 | /// 出队 6 | fn fetch(&mut self) -> Option; 7 | } 8 | -------------------------------------------------------------------------------- /user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user_lib" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | [dependencies] 8 | rcore-console = { path = "../console" } 9 | syscall = { path = "../syscall", features = ["user"] } 10 | customizable-buddy = "0.0.2" 11 | -------------------------------------------------------------------------------- /user/README.md: -------------------------------------------------------------------------------- 1 | # 用户程序 2 | 3 | 编译出可在操作系统中加载和运行的应用程序。 4 | 5 | - ch1:不支持应用程序 6 | - ch2-ch3:应用程序二进制静态链接到内核二进制,且需要定制链接脚本以指定应用程序位置 7 | - ch4-ch5:应用程序二进制静态链接到内核二进制,但不需要定制链接脚本 8 | - ch6-ch7:通过文件系统加载应用程序 9 | -------------------------------------------------------------------------------- /user/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | println!("cargo:rerun-if-changed=build.rs"); 5 | println!("cargo:rerun-if-env-changed=LOG"); 6 | println!("cargo:rerun-if-env-changed=BASE_ADDRESS"); 7 | 8 | if let Some(base) = env::var("BASE_ADDRESS") 9 | .ok() 10 | .and_then(|s| s.parse::().ok()) 11 | { 12 | let text = format!( 13 | "\ 14 | OUTPUT_ARCH(riscv) 15 | ENTRY(_start) 16 | SECTIONS {{ 17 | . = {base}; 18 | .text : {{ 19 | *(.text.entry) 20 | *(.text .text.*) 21 | }} 22 | .rodata : {{ 23 | *(.rodata .rodata.*) 24 | *(.srodata .srodata.*) 25 | }} 26 | .data : {{ 27 | *(.data .data.*) 28 | *(.sdata .sdata.*) 29 | }} 30 | .bss : {{ 31 | *(.bss .bss.*) 32 | *(.sbss .sbss.*) 33 | }} 34 | }}" 35 | ); 36 | let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 37 | fs::write(&ld, text).unwrap(); 38 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /user/cases.toml: -------------------------------------------------------------------------------- 1 | [ch2] 2 | base = 0x8040_0000 3 | step = 0 4 | cases = [ 5 | "00hello_world", 6 | "01store_fault", 7 | "02power", 8 | "03priv_inst", 9 | "04priv_csr", 10 | ] 11 | 12 | [ch3] 13 | base = 0x8040_0000 14 | step = 0x0020_0000 15 | cases = [ 16 | "00hello_world", 17 | "01store_fault", 18 | "02power", 19 | "03priv_inst", 20 | "04priv_csr", 21 | "05write_a", 22 | "06write_b", 23 | "07write_c", 24 | "08power_3", 25 | "09power_5", 26 | "10power_7", 27 | "11sleep", 28 | ] 29 | 30 | [ch4] 31 | cases = [ 32 | "00hello_world", 33 | "01store_fault", 34 | "02power", 35 | "03priv_inst", 36 | "04priv_csr", 37 | "05write_a", 38 | "06write_b", 39 | "07write_c", 40 | "08power_3", 41 | "09power_5", 42 | "10power_7", 43 | "11sleep", 44 | ] 45 | 46 | [ch5] 47 | cases = [ 48 | "00hello_world", 49 | "01store_fault", 50 | "02power", 51 | "03priv_inst", 52 | "04priv_csr", 53 | "12forktest", 54 | "13forktree", 55 | "14forktest2", 56 | "15matrix", 57 | "user_shell", 58 | "initproc", 59 | ] 60 | 61 | [ch6] 62 | cases = [ 63 | "00hello_world", 64 | "01store_fault", 65 | "02power", 66 | "03priv_inst", 67 | "04priv_csr", 68 | "12forktest", 69 | "13forktree", 70 | "14forktest2", 71 | "15matrix", 72 | "user_shell", 73 | "initproc", 74 | "filetest_simple", 75 | "cat_filea", 76 | ] 77 | 78 | [ch7] 79 | cases = [ 80 | "00hello_world", 81 | "01store_fault", 82 | "02power", 83 | "03priv_inst", 84 | "04priv_csr", 85 | "12forktest", 86 | "13forktree", 87 | "14forktest2", 88 | "15matrix", 89 | "user_shell", 90 | "initproc", 91 | "filetest_simple", 92 | "cat_filea", 93 | "sig_simple", 94 | "sig_simple2", 95 | "sig_ctrlc", 96 | "sig_tests", 97 | ] 98 | 99 | [ch8] 100 | cases = [ 101 | "00hello_world", 102 | "01store_fault", 103 | "02power", 104 | "03priv_inst", 105 | "04priv_csr", 106 | "12forktest", 107 | "13forktree", 108 | "14forktest2", 109 | "15matrix", 110 | "user_shell", 111 | "initproc", 112 | "filetest_simple", 113 | "cat_filea", 114 | "sig_simple", 115 | "sig_simple2", 116 | "sig_ctrlc", 117 | "sig_tests", 118 | "threads", 119 | "threads_arg", 120 | "mpsc_sem", 121 | "sync_sem", 122 | "race_adder_mutex_blocking", 123 | "test_condvar", 124 | ] -------------------------------------------------------------------------------- /user/src/bin/00hello_world.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[no_mangle] 8 | fn main() -> i32 { 9 | println!("Hello, world!"); 10 | 0 11 | } 12 | -------------------------------------------------------------------------------- /user/src/bin/01store_fault.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[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 { core::ptr::null_mut::().write_volatile(0) }; 12 | 0 13 | } 14 | -------------------------------------------------------------------------------- /user/src/bin/02power.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | const SIZE: usize = 10; 8 | const P: u32 = 3; 9 | const STEP: usize = 100000; 10 | const MOD: u32 = 10007; 11 | 12 | #[no_mangle] 13 | fn main() -> i32 { 14 | let mut pow = [0u32; SIZE]; 15 | let mut index: usize = 0; 16 | pow[index] = 1; 17 | for i in 1..=STEP { 18 | let last = pow[index]; 19 | index = (index + 1) % SIZE; 20 | pow[index] = last * P % MOD; 21 | if i % 10000 == 0 { 22 | println!("{}^{}={}(MOD {})", P, i, pow[index], MOD); 23 | } 24 | } 25 | println!("Test power OK!"); 26 | 0 27 | } 28 | -------------------------------------------------------------------------------- /user/src/bin/03priv_inst.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[no_mangle] 8 | fn main() -> i32 { 9 | println!("Try to execute privileged instruction in U Mode"); 10 | println!("Kernel should kill this application!"); 11 | unsafe { core::arch::asm!("sret") }; 12 | 0 13 | } 14 | -------------------------------------------------------------------------------- /user/src/bin/04priv_csr.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | #[no_mangle] 8 | fn main() -> i32 { 9 | println!("Try to access privileged CSR in U Mode"); 10 | println!("Kernel should kill this application!"); 11 | unsafe { core::arch::asm!("csrw stvec, zero") }; 12 | 0 13 | } 14 | -------------------------------------------------------------------------------- /user/src/bin/05write_a.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::sched_yield; 8 | 9 | const WIDTH: usize = 10; 10 | const HEIGHT: usize = 5; 11 | 12 | #[no_mangle] 13 | fn main() -> i32 { 14 | for i in 0..HEIGHT { 15 | for _ in 0..WIDTH { 16 | print!("A"); 17 | } 18 | println!(" [{}/{}]", i + 1, HEIGHT); 19 | sched_yield(); 20 | } 21 | println!("Test write_a OK!"); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/bin/06write_b.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::sched_yield; 8 | 9 | const WIDTH: usize = 10; 10 | const HEIGHT: usize = 2; 11 | 12 | #[no_mangle] 13 | fn main() -> i32 { 14 | for i in 0..HEIGHT { 15 | for _ in 0..WIDTH { 16 | print!("B"); 17 | } 18 | println!(" [{}/{}]", i + 1, HEIGHT); 19 | sched_yield(); 20 | } 21 | println!("Test write_b OK!"); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/bin/07write_c.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::sched_yield; 8 | 9 | const WIDTH: usize = 10; 10 | const HEIGHT: usize = 3; 11 | 12 | #[no_mangle] 13 | fn main() -> i32 { 14 | for i in 0..HEIGHT { 15 | for _ in 0..WIDTH { 16 | print!("C"); 17 | } 18 | println!(" [{}/{}]", i + 1, HEIGHT); 19 | sched_yield(); 20 | } 21 | println!("Test write_c OK!"); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/bin/08power_3.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | const LEN: usize = 100; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | let p = 3u64; 12 | let m = 998244353u64; 13 | let iter: usize = 200000; 14 | let mut s = [0u64; LEN]; 15 | let mut cur = 0usize; 16 | s[cur] = 1; 17 | for i in 1..=iter { 18 | let next = if cur + 1 == LEN { 0 } else { cur + 1 }; 19 | s[next] = s[cur] * p % m; 20 | cur = next; 21 | if i % 10000 == 0 { 22 | println!("power_3 [{}/{}]", i, iter); 23 | } 24 | } 25 | println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); 26 | println!("Test power_3 OK!"); 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /user/src/bin/09power_5.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | const LEN: usize = 100; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | let p = 5u64; 12 | let m = 998244353u64; 13 | let iter: usize = 140000; 14 | let mut s = [0u64; LEN]; 15 | let mut cur = 0usize; 16 | s[cur] = 1; 17 | for i in 1..=iter { 18 | let next = if cur + 1 == LEN { 0 } else { cur + 1 }; 19 | s[next] = s[cur] * p % m; 20 | cur = next; 21 | if i % 10000 == 0 { 22 | println!("power_5 [{}/{}]", i, iter); 23 | } 24 | } 25 | println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); 26 | println!("Test power_5 OK!"); 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /user/src/bin/10power_7.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | const LEN: usize = 100; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | let p = 7u64; 12 | let m = 998244353u64; 13 | let iter: usize = 160000; 14 | let mut s = [0u64; LEN]; 15 | let mut cur = 0usize; 16 | s[cur] = 1; 17 | for i in 1..=iter { 18 | let next = if cur + 1 == LEN { 0 } else { cur + 1 }; 19 | s[next] = s[cur] * p % m; 20 | cur = next; 21 | if i % 10000 == 0 { 22 | println!("power_7 [{}/{}]", i, iter); 23 | } 24 | } 25 | println!("{}^{} = {}(MOD {})", p, iter, s[cur], m); 26 | println!("Test power_7 OK!"); 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /user/src/bin/11sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{clock_gettime, sched_yield, ClockId, TimeSpec}; 8 | 9 | #[no_mangle] 10 | fn main() -> i32 { 11 | let mut time: TimeSpec = TimeSpec::ZERO; 12 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _); 13 | let time = time + TimeSpec::SECOND; 14 | loop { 15 | let mut now: TimeSpec = TimeSpec::ZERO; 16 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _); 17 | if now > time { 18 | break; 19 | } 20 | sched_yield(); 21 | } 22 | println!("Test sleep OK!"); 23 | 0 24 | } 25 | -------------------------------------------------------------------------------- /user/src/bin/12forktest.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 | #[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/13forktree.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, getpid, sched_yield, sleep, wait}; 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 | sched_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 | #[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/14forktest2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{clock_gettime, exit, fork, getpid, sleep, wait, ClockId, TimeSpec}; 8 | 9 | static NUM: usize = 30; 10 | 11 | #[no_mangle] 12 | pub fn main() -> i32 { 13 | for _ in 0..NUM { 14 | let pid = fork(); 15 | if pid == 0 { 16 | let mut time: TimeSpec = TimeSpec::ZERO; 17 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _); 18 | let current_time = (time.tv_sec * 1000) + (time.tv_nsec / 1000000); 19 | let sleep_length = 20 | (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000; 21 | println!("pid {} sleep for {} ms", getpid(), sleep_length); 22 | sleep(sleep_length as usize); 23 | println!("pid {} OK!", getpid()); 24 | exit(0); 25 | } 26 | } 27 | 28 | let mut exit_code: i32 = 0; 29 | for _ in 0..NUM { 30 | // println!("child {}", wait(&mut exit_code)); 31 | assert!(wait(&mut exit_code) > 0); 32 | assert_eq!(exit_code, 0); 33 | } 34 | assert!(wait(&mut exit_code) < 0); 35 | println!("forktest2 test passed!"); 36 | 0 37 | } 38 | -------------------------------------------------------------------------------- /user/src/bin/15matrix.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::{clock_gettime, exit, fork, getpid, sched_yield, wait, ClockId, TimeSpec}; 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 | sched_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 | #[no_mangle] 48 | pub fn main() -> i32 { 49 | for _ in 0..NUM { 50 | let pid = fork(); 51 | if pid == 0 { 52 | let mut time: TimeSpec = TimeSpec::ZERO; 53 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _); 54 | let current_time = (time.tv_sec * 1000) + (time.tv_nsec / 1000000); 55 | let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000; 56 | work(times * 10); 57 | } 58 | } 59 | 60 | println!("fork ok."); 61 | 62 | let mut exit_code: i32 = 0; 63 | for _ in 0..NUM { 64 | if wait(&mut exit_code) < 0 { 65 | panic!("wait failed."); 66 | } 67 | } 68 | assert!(wait(&mut exit_code) < 0); 69 | println!("matrix passed."); 70 | 0 71 | } 72 | -------------------------------------------------------------------------------- /user/src/bin/cat_filea.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::{close, open, read, OpenFlags}; 9 | 10 | #[no_mangle] 11 | pub fn main() -> i32 { 12 | let fd = open("filea\0", OpenFlags::RDONLY); 13 | if fd == -1 { 14 | panic!("Error occured when opening file"); 15 | } 16 | let fd = fd as usize; 17 | let mut buf = [0u8; 256]; 18 | loop { 19 | let size = read(fd, &mut buf) as usize; 20 | if size == 0 { 21 | break; 22 | } 23 | println!("{}", core::str::from_utf8(&buf[..size]).unwrap()); 24 | } 25 | close(fd); 26 | 0 27 | } 28 | -------------------------------------------------------------------------------- /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::{close, open, read, write, OpenFlags}; 8 | 9 | #[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 | -------------------------------------------------------------------------------- /user/src/bin/initproc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::{exec, fork, sched_yield, wait}; 7 | 8 | #[no_mangle] 9 | fn main() -> i32 { 10 | if fork() == 0 { 11 | // exec("user_shell\0", &[core::ptr::null::()]); 12 | exec("user_shell"); 13 | } else { 14 | loop { 15 | let mut exit_code: i32 = 0; 16 | let pid = wait(&mut exit_code); 17 | if pid == -1 { 18 | sched_yield(); 19 | continue; 20 | } 21 | 22 | // println!( 23 | // "[initproc] Released a zombie process, pid={}, exit_code={}", 24 | // pid, 25 | // exit_code, 26 | // ); 27 | } 28 | } 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /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_EXISTED: 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 | unsafe fn producer(id: *const usize) -> isize { 26 | let id = *id; 27 | for _ in 0..NUMBER_PER_PRODUCER { 28 | semaphore_down(SEM_EMPTY); 29 | semaphore_down(SEM_MUTEX); 30 | BUFFER[FRONT] = id; 31 | FRONT = (FRONT + 1) % BUFFER_SIZE; 32 | semaphore_up(SEM_MUTEX); 33 | semaphore_up(SEM_EXISTED); 34 | } 35 | exit(0) 36 | } 37 | 38 | unsafe fn consumer() -> isize { 39 | for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { 40 | semaphore_down(SEM_EXISTED); 41 | semaphore_down(SEM_MUTEX); 42 | print!("{} ", BUFFER[TAIL]); 43 | TAIL = (TAIL + 1) % BUFFER_SIZE; 44 | semaphore_up(SEM_MUTEX); 45 | semaphore_up(SEM_EMPTY); 46 | } 47 | println!(""); 48 | exit(0) 49 | } 50 | 51 | #[no_mangle] 52 | pub fn main() -> i32 { 53 | // create semaphores 54 | assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); 55 | assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); 56 | assert_eq!(semaphore_create(0) as usize, SEM_EXISTED); 57 | // create threads 58 | let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); 59 | let mut threads = Vec::new(); 60 | for i in 0..PRODUCER_COUNT { 61 | threads.push(thread_create( 62 | producer as usize, 63 | &ids.as_slice()[i] as *const _ as usize, 64 | )); 65 | } 66 | threads.push(thread_create(consumer as usize, 0)); 67 | // wait for all threads to complete 68 | for thread in threads.iter() { 69 | waittid(*thread as usize); 70 | } 71 | println!("mpsc_sem passed!"); 72 | 0 73 | } 74 | -------------------------------------------------------------------------------- /user/src/bin/race_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 user_lib::{clock_gettime, exit, thread_create, waittid, ClockId, TimeSpec}; 10 | use user_lib::{mutex_create, mutex_lock, mutex_unlock}; 11 | 12 | static mut A: usize = 0; 13 | const PER_THREAD: usize = 1000; 14 | const THREAD_COUNT: usize = 16; 15 | 16 | unsafe fn f() -> isize { 17 | let mut t = 2usize; 18 | for _ in 0..PER_THREAD { 19 | mutex_lock(0); 20 | let a = &mut A as *mut usize; 21 | let cur = a.read_volatile(); 22 | for _ in 0..500 { 23 | t = t * t % 10007; 24 | } 25 | a.write_volatile(cur + 1); 26 | mutex_unlock(0); 27 | } 28 | exit(t as i32) 29 | } 30 | 31 | #[no_mangle] 32 | pub fn main() -> i32 { 33 | let mut start_time: TimeSpec = TimeSpec::ZERO; 34 | let mut end_time: TimeSpec = TimeSpec::ZERO; 35 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut start_time as *mut _ as _); 36 | assert_eq!(mutex_create(true), 0); 37 | let mut v = Vec::new(); 38 | for _ in 0..THREAD_COUNT { 39 | v.push(thread_create(f as usize, 0) as usize); 40 | } 41 | let mut time_cost = Vec::new(); 42 | for tid in v.iter() { 43 | time_cost.push(waittid(*tid)); 44 | } 45 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut end_time as *mut _ as _); 46 | let total_time = end_time.tv_sec * 1000 + end_time.tv_nsec / 1_000_000 47 | - start_time.tv_sec * 1000 48 | - start_time.tv_nsec / 1_000_000; 49 | println!("time cost is {}ms", total_time); 50 | assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); 51 | 0 52 | } 53 | -------------------------------------------------------------------------------- /user/src/bin/sig_ctrlc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | extern crate user_lib; 7 | use user_lib::*; 8 | 9 | const LF: u8 = 0x0au8; 10 | const CR: u8 = 0x0du8; 11 | 12 | fn func() { 13 | println!("signal_handler: caught signal SIGINT, and exit(1)"); 14 | exit(1); 15 | } 16 | 17 | #[no_mangle] 18 | pub fn main() -> i32 { 19 | println!("sig_ctrlc starting.... Press 'ctrl-c' or 'ENTER' will quit."); 20 | 21 | let mut new = SignalAction::default(); 22 | let old = SignalAction::default(); 23 | new.handler = func as usize; 24 | 25 | println!("sig_ctrlc: sigaction"); 26 | if sigaction(SignalNo::SIGINT, &new, &old) < 0 { 27 | panic!("Sigaction failed!"); 28 | } 29 | println!("sig_ctrlc: getchar...."); 30 | loop { 31 | let c = getchar(); 32 | 33 | println!("Got Char {}", c); 34 | if c == LF || c == CR { 35 | break; 36 | } 37 | } 38 | println!("sig_ctrlc: Done"); 39 | 0 40 | } 41 | -------------------------------------------------------------------------------- /user/src/bin/sig_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::*; 7 | 8 | fn func() { 9 | println!("user_sig_test succsess"); 10 | sigreturn(); 11 | } 12 | 13 | #[no_mangle] 14 | pub fn main() -> i32 { 15 | let mut new = SignalAction::default(); 16 | let old = SignalAction::default(); 17 | new.handler = func as usize; 18 | println!("pid = {}", getpid()); 19 | println!("signal_simple: sigaction"); 20 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 21 | panic!("Sigaction failed!"); 22 | } 23 | println!("signal_simple: kill"); 24 | if kill(getpid(), SignalNo::SIGUSR1) < 0 { 25 | println!("Kill failed!"); 26 | exit(1); 27 | } 28 | println!("signal_simple: Done"); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/sig_simple2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::*; 7 | 8 | fn func() { 9 | println!("user_sig_test succsess"); 10 | sigreturn(); 11 | } 12 | 13 | #[no_mangle] 14 | pub fn main() -> i32 { 15 | let pid = fork(); 16 | if pid == 0 { 17 | let mut new = SignalAction::default(); 18 | let old = SignalAction::default(); 19 | new.handler = func as usize; 20 | 21 | println!("signal_simple2: child sigaction"); 22 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 23 | panic!("Sigaction failed!"); 24 | } 25 | sleep(1000); 26 | println!("signal_simple2: child done"); 27 | exit(0); 28 | } else if pid > 0 { 29 | println!("signal_simple2: parent kill child"); 30 | sleep(500); 31 | if kill(pid, SignalNo::SIGUSR1) < 0 { 32 | println!("Kill failed!"); 33 | exit(1); 34 | } 35 | println!("signal_simple2: parent wait child"); 36 | let mut exit_code = 0; 37 | waitpid(pid, &mut exit_code); 38 | println!("signal_simple2: parent Done"); 39 | exit(0); 40 | } 41 | 42 | 0 43 | } 44 | -------------------------------------------------------------------------------- /user/src/bin/sig_tests.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::*; 7 | 8 | fn func() { 9 | println!("user_sig_test succsess"); 10 | sigreturn(); 11 | } 12 | 13 | fn func2() { 14 | loop { 15 | print!(""); 16 | } 17 | } 18 | 19 | fn func3() { 20 | println!("interrupt"); 21 | sigreturn(); 22 | } 23 | 24 | fn user_sig_test_failsignum() { 25 | let mut new = SignalAction::default(); 26 | let old = SignalAction::default(); 27 | new.handler = func as usize; 28 | if sigaction(50.into(), &new, &old) >= 0 { 29 | panic!("Wrong sigaction but success!"); 30 | } 31 | } 32 | 33 | fn user_sig_test_kill() { 34 | let mut new = SignalAction::default(); 35 | let old = SignalAction::default(); 36 | new.handler = func as usize; 37 | 38 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 39 | panic!("Sigaction failed!"); 40 | } 41 | if kill(getpid(), SignalNo::SIGUSR1) < 0 { 42 | println!("Kill failed!"); 43 | exit(1); 44 | } 45 | } 46 | 47 | fn user_sig_test_multiprocsignals() { 48 | let pid = fork(); 49 | if pid == 0 { 50 | let mut new = SignalAction::default(); 51 | let old = SignalAction::default(); 52 | new.handler = func as usize; 53 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 54 | panic!("Sigaction failed!"); 55 | } 56 | } else { 57 | if kill(pid, SignalNo::SIGUSR1) < 0 { 58 | println!("Kill failed!"); 59 | exit(1); 60 | } 61 | let mut exit_code = 0; 62 | wait(&mut exit_code); 63 | } 64 | } 65 | 66 | fn user_sig_test_restore() { 67 | let mut new = SignalAction::default(); 68 | let old = SignalAction::default(); 69 | let old2 = SignalAction::default(); 70 | new.handler = func as usize; 71 | 72 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 73 | panic!("Sigaction failed!"); 74 | } 75 | 76 | if sigaction(SignalNo::SIGUSR1, &old, &old2) < 0 { 77 | panic!("Sigaction failed!"); 78 | } 79 | 80 | if old2.handler != new.handler { 81 | println!("Restore failed!"); 82 | exit(-1); 83 | } 84 | } 85 | 86 | fn kernel_sig_test_ignore() { 87 | sigprocmask(1 << SignalNo::SIGSTOP as usize); 88 | if kill(getpid(), SignalNo::SIGSTOP) < 0 { 89 | println!("kill faild\n"); 90 | exit(-1); 91 | } 92 | } 93 | 94 | fn kernel_sig_test_stop_cont() { 95 | let pid = fork(); 96 | if pid == 0 { 97 | kill(getpid(), SignalNo::SIGSTOP); 98 | sleep(1000); 99 | exit(-1); 100 | } else { 101 | sleep(5000); 102 | kill(pid, SignalNo::SIGCONT); 103 | let mut exit_code = 0; 104 | wait(&mut exit_code); 105 | } 106 | } 107 | 108 | fn kernel_sig_test_failignorekill() { 109 | let mut new = SignalAction::default(); 110 | let old = SignalAction::default(); 111 | new.handler = func as usize; 112 | 113 | if sigaction(SignalNo::SIGKILL, &new, &old) >= 0 { 114 | panic!("Should not set sigaction to kill!"); 115 | } 116 | 117 | if sigaction(SignalNo::SIGKILL, &new, 0 as *const SignalAction) >= 0 { 118 | panic!("Should not set sigaction to kill!"); 119 | } 120 | 121 | if sigaction(SignalNo::SIGKILL, 0 as *const SignalAction, &old) >= 0 { 122 | panic!("Should not set sigaction to kill!"); 123 | } 124 | } 125 | 126 | fn final_sig_test() { 127 | let mut new = SignalAction::default(); 128 | let old = SignalAction::default(); 129 | new.handler = func2 as usize; 130 | 131 | let mut new2 = SignalAction::default(); 132 | let old2 = SignalAction::default(); 133 | new2.handler = func3 as usize; 134 | 135 | let pid = fork(); 136 | if pid == 0 { 137 | if sigaction(SignalNo::SIGUSR1, &new, &old) < 0 { 138 | panic!("Sigaction failed!"); 139 | } 140 | if sigaction(14.into(), &new2, &old2) < 0 { 141 | panic!("Sigaction failed!"); 142 | } 143 | if kill(getpid(), SignalNo::SIGUSR1) < 0 { 144 | println!("Kill failed!"); 145 | exit(-1); 146 | } 147 | } else { 148 | sleep(1000); 149 | if kill(pid, 14.into()) < 0 { 150 | println!("Kill failed!"); 151 | exit(-1); 152 | } 153 | sleep(1000); 154 | kill(pid, SignalNo::SIGKILL); 155 | } 156 | } 157 | 158 | fn run(f: fn()) -> bool { 159 | let pid = fork(); 160 | if pid == 0 { 161 | f(); 162 | exit(0); 163 | unreachable!() 164 | } else { 165 | let mut exit_code: i32 = 0; 166 | wait(&mut exit_code); 167 | if exit_code != 0 { 168 | println!("FAILED!"); 169 | } else { 170 | println!("OK!"); 171 | } 172 | exit_code == 0 173 | } 174 | } 175 | 176 | #[no_mangle] 177 | pub fn main() -> i32 { 178 | let tests: [(fn(), &str); 8] = [ 179 | (user_sig_test_failsignum, "user_sig_test_failsignum"), 180 | (user_sig_test_kill, "user_sig_test_kill"), 181 | ( 182 | user_sig_test_multiprocsignals, 183 | "user_sig_test_multiprocsignals", 184 | ), 185 | (user_sig_test_restore, "user_sig_test_restore"), 186 | (kernel_sig_test_ignore, "kernel_sig_test_ignore"), 187 | (kernel_sig_test_stop_cont, "kernel_sig_test_stop_cont"), 188 | ( 189 | kernel_sig_test_failignorekill, 190 | "kernel_sig_test_failignorekill", 191 | ), 192 | (final_sig_test, "final_sig_test"), 193 | ]; 194 | let mut fail_num = 0; 195 | for test in tests { 196 | println!("Testing {}", test.1); 197 | if !run(test.0) { 198 | fail_num += 1; 199 | } 200 | } 201 | if fail_num == 0 { 202 | println!("ALL TESTS PASSED"); 203 | 0 204 | } else { 205 | println!("SOME TESTS FAILED"); 206 | -1 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /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() -> isize { 17 | sleep(10); 18 | println!("First work and wakeup Second"); 19 | semaphore_up(SEM_SYNC); 20 | exit(0) 21 | } 22 | 23 | unsafe fn second() -> isize { 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 | #[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 | -------------------------------------------------------------------------------- /user/src/bin/test_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_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 | unsafe fn first() -> isize { 22 | sleep(10); 23 | println!("First work, Change A --> 1 and wakeup Second"); 24 | mutex_lock(MUTEX_ID); 25 | A = 1; 26 | condvar_signal(CONDVAR_ID); 27 | mutex_unlock(MUTEX_ID); 28 | exit(0) 29 | } 30 | 31 | unsafe fn second() -> isize { 32 | println!("Second want to continue,but need to wait A=1"); 33 | mutex_lock(MUTEX_ID); 34 | while A == 0 { 35 | println!("Second: A is {}", A); 36 | condvar_wait(CONDVAR_ID, MUTEX_ID); 37 | } 38 | mutex_unlock(MUTEX_ID); 39 | println!("A is {}, Second can work now", A); 40 | exit(0) 41 | } 42 | 43 | #[no_mangle] 44 | pub fn main() -> i32 { 45 | // create condvar & mutex 46 | assert_eq!(condvar_create() as usize, CONDVAR_ID); 47 | assert_eq!(mutex_create(true) as usize, MUTEX_ID); 48 | // create threads 49 | let threads = vec![ 50 | thread_create(first as usize, 0), 51 | thread_create(second as usize, 0), 52 | ]; 53 | // wait for all threads to complete 54 | for thread in threads.iter() { 55 | waittid(*thread as usize); 56 | } 57 | println!("test_condvar passed!"); 58 | 0 59 | } 60 | -------------------------------------------------------------------------------- /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() -> isize { 12 | for _ in 0..1000 { 13 | print!("a"); 14 | } 15 | exit(1) 16 | } 17 | 18 | pub fn thread_b() -> isize { 19 | for _ in 0..1000 { 20 | print!("b"); 21 | } 22 | exit(2) 23 | } 24 | 25 | pub fn thread_c() -> isize { 26 | for _ in 0..1000 { 27 | print!("c"); 28 | } 29 | exit(3) 30 | } 31 | 32 | #[no_mangle] 33 | pub fn main() -> i32 { 34 | println!("threads test========"); 35 | let mut v = vec![ 36 | thread_create(thread_a as usize, 0), 37 | thread_create(thread_b as usize, 0), 38 | thread_create(thread_c as usize, 0), 39 | ]; 40 | println!("v :{:?}", v); 41 | let max_len = 5; 42 | for i in 0..max_len { 43 | println!("create tid: {}", i + 4); 44 | let tid = thread_create(thread_b as usize, 0); 45 | println!("create tid: {} end", i + 4); 46 | v.push(tid); 47 | } 48 | println!("create end"); 49 | for tid in v.iter() { 50 | let exit_code = waittid(*tid as usize); 51 | println!("thread#{} exited with code {}", tid, exit_code); 52 | } 53 | println!("main thread exited."); 54 | println!("==========================v size: {:?}", v); 55 | 0 56 | } 57 | -------------------------------------------------------------------------------- /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) -> isize { 17 | let arg = unsafe { &*arg }; 18 | for _ in 0..1000 { 19 | print!("{}", arg.ch); 20 | } 21 | exit(arg.rc) 22 | } 23 | 24 | #[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/user_shell.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![allow(clippy::println_empty_string)] 4 | 5 | extern crate alloc; 6 | 7 | #[macro_use] 8 | extern crate user_lib; 9 | 10 | const LF: u8 = 0x0au8; 11 | const CR: u8 = 0x0du8; 12 | const DL: u8 = 0x7fu8; 13 | const BS: u8 = 0x08u8; 14 | 15 | use alloc::string::String; 16 | use user_lib::{exec, fork, getchar, waitpid}; 17 | 18 | #[no_mangle] 19 | pub fn main() -> i32 { 20 | println!("Rust user shell"); 21 | let mut line: String = String::new(); // 记录着当前输入的命令 22 | print!(">> "); 23 | loop { 24 | let c = getchar(); 25 | match c { 26 | LF | CR => { 27 | // 换行 28 | println!(); 29 | if !line.is_empty() { 30 | let pid = fork(); 31 | if pid == 0 { 32 | // child process 33 | if exec(line.as_str()) == -1 { 34 | println!("Error when executing!"); 35 | return -4; 36 | } 37 | unreachable!(); 38 | } else { 39 | let mut exit_code: i32 = 0; 40 | let exit_pid = waitpid(pid as isize, &mut exit_code); 41 | assert_eq!(pid, exit_pid); 42 | println!("Shell: Process {} exited with code {}", pid, exit_code); 43 | } 44 | line.clear(); 45 | } 46 | print!(">> "); 47 | } 48 | BS | DL => { 49 | // backspace 50 | if !line.is_empty() { 51 | print!("{}", BS as char); 52 | print!(" "); 53 | print!("{}", BS as char); 54 | line.pop(); 55 | } 56 | } 57 | _ => { 58 | print!("{}", c as char); 59 | line.push(c as char); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /user/src/heap.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::handle_alloc_error; 2 | use core::{ 3 | alloc::{GlobalAlloc, Layout}, 4 | ptr::NonNull, 5 | }; 6 | use customizable_buddy::{BuddyAllocator, LinkedListBuddy, UsizeBuddy}; 7 | 8 | /// 初始化全局分配器和内核堆分配器。 9 | pub fn init() { 10 | // 托管空间 16 KiB 11 | const MEMORY_SIZE: usize = 16 << 10; 12 | static mut MEMORY: [u8; MEMORY_SIZE] = [0u8; MEMORY_SIZE]; 13 | unsafe { 14 | HEAP.init( 15 | core::mem::size_of::().trailing_zeros() as _, 16 | NonNull::new(MEMORY.as_mut_ptr()).unwrap(), 17 | ); 18 | HEAP.transfer(NonNull::new_unchecked(MEMORY.as_mut_ptr()), MEMORY.len()); 19 | } 20 | } 21 | 22 | type MutAllocator = BuddyAllocator; 23 | static mut HEAP: MutAllocator<32> = MutAllocator::new(); 24 | 25 | struct Global; 26 | 27 | #[global_allocator] 28 | static GLOBAL: Global = Global; 29 | 30 | unsafe impl GlobalAlloc for Global { 31 | #[inline] 32 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 33 | if let Ok((ptr, _)) = HEAP.allocate_layout::(layout) { 34 | ptr.as_ptr() 35 | } else { 36 | handle_alloc_error(layout) 37 | } 38 | } 39 | 40 | #[inline] 41 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 42 | HEAP.deallocate_layout(NonNull::new(ptr).unwrap(), layout) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /user/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(linkage)] 3 | #![feature(panic_info_message)] 4 | #![feature(alloc_error_handler)] 5 | 6 | mod heap; 7 | 8 | extern crate alloc; 9 | 10 | use core::alloc::Layout; 11 | use rcore_console::log; 12 | 13 | pub use rcore_console::{print, println}; 14 | pub use syscall::*; 15 | 16 | #[no_mangle] 17 | #[link_section = ".text.entry"] 18 | pub extern "C" fn _start() -> ! { 19 | rcore_console::init_console(&Console); 20 | rcore_console::set_log_level(option_env!("LOG")); 21 | heap::init(); 22 | exit(main()); 23 | unreachable!() 24 | } 25 | 26 | #[linkage = "weak"] 27 | #[no_mangle] 28 | fn main() -> i32 { 29 | panic!("Cannot find main!"); 30 | } 31 | 32 | #[panic_handler] 33 | fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { 34 | let err = panic_info.message().unwrap(); 35 | if let Some(location) = panic_info.location() { 36 | log::error!("Panicked at {}:{}, {err}", location.file(), location.line()); 37 | } else { 38 | log::error!("Panicked: {err}"); 39 | } 40 | exit(1); 41 | unreachable!() 42 | } 43 | 44 | #[alloc_error_handler] 45 | fn alloc_error_handler(layout: Layout) -> ! { 46 | panic!("Failed to alloc {layout:?}") 47 | } 48 | 49 | pub fn getchar() -> u8 { 50 | let mut c = [0u8; 1]; 51 | read(STDIN, &mut c); 52 | c[0] 53 | } 54 | 55 | struct Console; 56 | 57 | impl rcore_console::Console for Console { 58 | #[inline] 59 | fn put_char(&self, c: u8) { 60 | syscall::write(STDOUT, &[c]); 61 | } 62 | 63 | #[inline] 64 | fn put_str(&self, s: &str) { 65 | syscall::write(STDOUT, s.as_bytes()); 66 | } 67 | } 68 | 69 | pub fn sleep(period_ms: usize) { 70 | let mut time: TimeSpec = TimeSpec::ZERO; 71 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _); 72 | let time = time + TimeSpec::from_millsecond(period_ms); 73 | loop { 74 | let mut now: TimeSpec = TimeSpec::ZERO; 75 | clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _); 76 | if now > time { 77 | break; 78 | } 79 | sched_yield(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { version = "4.1", features = ["derive"] } 11 | os-xtask-utils = "0.0.0" 12 | once_cell = "1.17" 13 | 14 | serde = "1.0" 15 | serde_derive = "1.0" 16 | toml = "0.7" 17 | 18 | easy-fs = { path = "../easy-fs" } 19 | -------------------------------------------------------------------------------- /xtask/src/fs_pack.rs: -------------------------------------------------------------------------------- 1 | use easy_fs::{BlockDevice, EasyFileSystem}; 2 | use std::fs::{File, OpenOptions}; 3 | use std::io::{Read, Seek, SeekFrom, Write}; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | const BLOCK_SZ: usize = 512; 7 | 8 | struct BlockFile(Mutex); 9 | 10 | impl BlockDevice for BlockFile { 11 | fn read_block(&self, block_id: usize, buf: &mut [u8]) { 12 | let mut file = self.0.lock().unwrap(); 13 | file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) 14 | .expect("Error when seeking!"); 15 | assert_eq!(file.read(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); 16 | } 17 | 18 | fn write_block(&self, block_id: usize, buf: &[u8]) { 19 | let mut file = self.0.lock().unwrap(); 20 | file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) 21 | .expect("Error when seeking!"); 22 | assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); 23 | } 24 | } 25 | 26 | pub fn easy_fs_pack(cases: &Vec, target: &str) -> std::io::Result<()> { 27 | let block_file = Arc::new(BlockFile(Mutex::new({ 28 | let f = OpenOptions::new() 29 | .read(true) 30 | .write(true) 31 | .create(true) 32 | .open(format!("{}/{}", target, "fs.img"))?; 33 | f.set_len(64 * 2048 * 512).unwrap(); 34 | f 35 | }))); 36 | println!("Packing Testcases..."); 37 | let efs = EasyFileSystem::create(block_file, 64 * 2048, 1); 38 | println!("Packing Testcases..."); 39 | let root_inode = Arc::new(EasyFileSystem::root_inode(&efs)); 40 | println!("Packing Testcases..."); 41 | for case in cases { 42 | println!("{}", format!("{}/{}", target, case)); 43 | // load app data from host file system 44 | let mut host_file = File::open(format!("{}/{}", target, case)).unwrap(); 45 | let mut all_data: Vec = Vec::new(); 46 | host_file.read_to_end(&mut all_data).unwrap(); 47 | // create a file in easy-fs 48 | let inode = root_inode.create(case.as_str()).unwrap(); 49 | // write data to easy-fs 50 | inode.write_at(0, all_data.as_slice()); 51 | // println!("{}", all_data.len()); 52 | } 53 | println!("List Testcases in EFS: "); 54 | // list app 55 | for case in root_inode.readdir() { 56 | println!("{}", case); 57 | } 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /xtask/src/user.rs: -------------------------------------------------------------------------------- 1 | use crate::{fs_pack::easy_fs_pack, objcopy, PROJECT, TARGET, TARGET_ARCH}; 2 | use os_xtask_utils::{Cargo, CommandExt}; 3 | use serde_derive::Deserialize; 4 | use std::{collections::HashMap, ffi::OsStr, fs::File, io::Write, path::PathBuf}; 5 | 6 | #[derive(Deserialize, Default)] 7 | struct Cases { 8 | base: Option, 9 | step: Option, 10 | pub cases: Option>, 11 | } 12 | 13 | pub struct CasesInfo { 14 | base: u64, 15 | step: u64, 16 | bins: Vec, 17 | } 18 | 19 | impl Cases { 20 | fn build(&mut self, release: bool) -> CasesInfo { 21 | if let Some(names) = &self.cases { 22 | let base = self.base.unwrap_or(0); 23 | let step = self.step.filter(|_| self.base.is_some()).unwrap_or(0); 24 | let cases = names 25 | .into_iter() 26 | .enumerate() 27 | .map(|(i, name)| build_one(name, release, base + i as u64 * step)) 28 | .collect(); 29 | CasesInfo { 30 | base, 31 | step, 32 | bins: cases, 33 | } 34 | } else { 35 | CasesInfo { 36 | base: 0, 37 | step: 0, 38 | bins: vec![], 39 | } 40 | } 41 | } 42 | } 43 | 44 | fn build_one(name: impl AsRef, release: bool, base_address: u64) -> PathBuf { 45 | let name = name.as_ref(); 46 | let binary = base_address != 0; 47 | if binary { 48 | println!("build {name:?} at {base_address:#x}"); 49 | } 50 | Cargo::build() 51 | .package("user_lib") 52 | .target(TARGET_ARCH) 53 | .arg("--bin") 54 | .arg(name) 55 | .conditional(release, |cargo| { 56 | cargo.release(); 57 | }) 58 | .conditional(binary, |cargo| { 59 | cargo.env("BASE_ADDRESS", base_address.to_string()); 60 | }) 61 | .invoke(); 62 | let elf = TARGET 63 | .join(if release { "release" } else { "debug" }) 64 | .join(name); 65 | if binary { 66 | objcopy(elf, binary) 67 | } else { 68 | elf 69 | } 70 | } 71 | 72 | pub fn build_for(ch: u8, release: bool) { 73 | let cfg = std::fs::read_to_string(PROJECT.join("user/cases.toml")).unwrap(); 74 | let mut cases = toml::from_str::>(&cfg) 75 | .unwrap() 76 | .remove(&format!("ch{ch}")) 77 | .unwrap_or_default(); 78 | let CasesInfo { base, step, bins } = cases.build(release); 79 | if bins.is_empty() { 80 | return; 81 | } 82 | let asm = TARGET 83 | .join(if release { "release" } else { "debug" }) 84 | .join("app.asm"); 85 | let mut ld = File::create(asm).unwrap(); 86 | writeln!( 87 | ld, 88 | "\ 89 | .global apps 90 | .section .data 91 | .align 3 92 | apps: 93 | .quad {base:#x} 94 | .quad {step:#x} 95 | .quad {}", 96 | bins.len(), 97 | ) 98 | .unwrap(); 99 | 100 | (0..bins.len()).for_each(|i| writeln!(ld, " .quad app_{i}_start").unwrap()); 101 | 102 | writeln!(ld, " .quad app_{}_end", bins.len() - 1).unwrap(); 103 | 104 | bins.iter().enumerate().for_each(|(i, path)| { 105 | writeln!( 106 | ld, 107 | " 108 | app_{i}_start: 109 | .incbin {path:?} 110 | app_{i}_end:", 111 | ) 112 | .unwrap(); 113 | }); 114 | 115 | if ch == 5 { 116 | writeln!( 117 | ld, 118 | " 119 | .align 3 120 | .section .data 121 | .global app_names 122 | app_names:" 123 | ) 124 | .unwrap(); 125 | bins.iter().enumerate().for_each(|(_, path)| { 126 | writeln!(ld, " .string {:?}", path.file_name().unwrap()).unwrap(); 127 | }); 128 | } else if ch >= 6 { 129 | easy_fs_pack( 130 | &cases.cases.unwrap(), 131 | TARGET 132 | .join(if release { "release" } else { "debug" }) 133 | .into_os_string() 134 | .into_string() 135 | .unwrap() 136 | .as_str(), 137 | ) 138 | .unwrap(); 139 | } 140 | } 141 | --------------------------------------------------------------------------------