Compare commits
45 Commits
Author | SHA1 | Date |
---|---|---|
|
6906508d6c | 3 years ago |
|
8b8e48fd6d | 3 years ago |
|
855dc0d393 | 3 years ago |
|
aa91ba8a9e | 3 years ago |
|
ce7e6bc668 | 3 years ago |
|
2ba356050d | 3 years ago |
|
32098d61d3 | 3 years ago |
|
40c84071c0 | 3 years ago |
|
6a6dd5e948 | 3 years ago |
|
ad28f5f627 | 3 years ago |
|
a6b278fe10 | 3 years ago |
|
ae1a50673d | 3 years ago |
|
7a97faee6c | 3 years ago |
|
1d7b8141f0 | 3 years ago |
|
fae8641f36 | 3 years ago |
|
598d8d4538 | 3 years ago |
|
c8542d6107 | 3 years ago |
|
639643bc5f | 3 years ago |
|
3edce0932f | 3 years ago |
|
50db31e463 | 3 years ago |
|
10fe9b8ffe | 3 years ago |
|
7408b1a7b2 | 3 years ago |
|
91710ba1a5 | 3 years ago |
|
9df03206f9 | 3 years ago |
|
d1233cbb69 | 3 years ago |
|
1f55fbe4a2 | 3 years ago |
|
4a169450f8 | 3 years ago |
|
0d189e9ad7 | 3 years ago |
|
2f5cff7e21 | 3 years ago |
|
ad85266da1 | 3 years ago |
|
87e61ef7e9 | 3 years ago |
|
2ec8a4d28b | 3 years ago |
|
675fe88fea | 3 years ago |
|
53855b9997 | 3 years ago |
|
d01b99d3f9 | 4 years ago |
|
4822f6253a | 4 years ago |
|
a97e29fdb1 | 4 years ago |
|
39c9c80d35 | 4 years ago |
|
11cdc5f2e6 | 4 years ago |
|
b001d3c98e | 4 years ago |
|
c0d41dccf6 | 4 years ago |
|
913ea57a94 | 4 years ago |
|
940e88a002 | 4 years ago |
|
bb98f7f88c | 4 years ago |
|
7eda37a407 | 4 years ago |
@ -0,0 +1,25 @@
|
||||
name: Build Rust Doc
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build-doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build doc
|
||||
run: |
|
||||
rustup target add riscv64gc-unknown-none-elf
|
||||
rustup component add llvm-tools-preview
|
||||
rustup component add rust-src
|
||||
cd os
|
||||
cargo doc --no-deps --verbose
|
||||
- name: Deploy to Github Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc
|
||||
destination_dir: ${{ github.ref_name }}
|
@ -1,16 +1,260 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.x
|
||||
rCore-Tutorial version 3.5. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/).
|
||||
|
||||
## Dependency
|
||||
rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS)
|
||||
|
||||
### Binaries
|
||||
Official QQ group number: 735045051
|
||||
|
||||
* rustc 1.56.0-nightly (08095fc1f 2021-07-26)
|
||||
## news
|
||||
- 25/01/2022: Version 3.6.0 is on the way! Now we directly update the code on chX branches, please periodically check if there are any updates.
|
||||
|
||||
* qemu: 5.0.0
|
||||
## Overview
|
||||
|
||||
* rustsbi-lib: 0.2.0-alpha.4
|
||||
This project aims to show how to write an **Unix-like OS** running on **RISC-V** platforms **from scratch** in **[Rust](https://www.rust-lang.org/)** for **beginners** without any background knowledge about **computer architectures, assembly languages or operating systems**.
|
||||
|
||||
rustsbi-qemu: d4968dd2
|
||||
## Features
|
||||
|
||||
rustsbi-k210: b689314e
|
||||
* Platform supported: `qemu-system-riscv64` simulator or dev boards based on [Kendryte K210 SoC](https://canaan.io/product/kendryteai) such as [Maix Dock](https://www.seeedstudio.com/Sipeed-MAIX-Dock-p-4815.html)
|
||||
* OS
|
||||
* concurrency of multiple processes each of which contains mutiple native threads
|
||||
* preemptive scheduling(Round-Robin algorithm)
|
||||
* dynamic memory management in kernel
|
||||
* virtual memory
|
||||
* a simple file system with a block cache
|
||||
* an interactive shell in the userspace
|
||||
* **only 4K+ LoC**
|
||||
* [A detailed documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/) in spite of the lack of comments in the code(English version is not available at present)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Install Rust
|
||||
|
||||
See [official guide](https://www.rust-lang.org/tools/install).
|
||||
|
||||
Install some tools:
|
||||
|
||||
```sh
|
||||
$ rustup target add riscv64gc-unknown-none-elf
|
||||
$ cargo install cargo-binutils --vers =0.3.3
|
||||
$ rustup component add llvm-tools-preview
|
||||
$ rustup component add rust-src
|
||||
```
|
||||
|
||||
### Install Qemu
|
||||
|
||||
Here we manually compile and install Qemu 5.0.0. For example, on Ubuntu 18.04:
|
||||
|
||||
```sh
|
||||
# install dependency packages
|
||||
$ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
|
||||
gawk build-essential bison flex texinfo gperf libtool patchutils bc \
|
||||
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 python3-pip
|
||||
# download Qemu source code
|
||||
$ wget https://download.qemu.org/qemu-5.0.0.tar.xz
|
||||
# extract to qemu-5.0.0/
|
||||
$ tar xvJf qemu-5.0.0.tar.xz
|
||||
$ cd qemu-5.0.0
|
||||
# build
|
||||
$ ./configure --target-list=riscv64-softmmu,riscv64-linux-user
|
||||
$ make -j$(nproc)
|
||||
```
|
||||
|
||||
Then, add following contents to `~/.bashrc`(please adjust these paths according to your environment):
|
||||
|
||||
```
|
||||
export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0
|
||||
export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0/riscv64-softmmu
|
||||
export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0/riscv64-linux-user
|
||||
```
|
||||
|
||||
Finally, update the current shell:
|
||||
|
||||
```sh
|
||||
$ source ~/.bashrc
|
||||
```
|
||||
|
||||
Now we can check the version of Qemu:
|
||||
|
||||
```sh
|
||||
$ qemu-system-riscv64 --version
|
||||
QEMU emulator version 5.0.0
|
||||
Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers
|
||||
```
|
||||
|
||||
### Install RISC-V GNU Embedded Toolchain(including GDB)
|
||||
|
||||
Download the compressed file according to your platform From [Sifive website](https://www.sifive.com/software)(Ctrl+F 'toolchain').
|
||||
|
||||
Extract it and append the location of the 'bin' directory under its root directory to `$PATH`.
|
||||
|
||||
For example, we can check the version of GDB:
|
||||
|
||||
```sh
|
||||
$ riscv64-unknown-elf-gdb --version
|
||||
GNU gdb (SiFive GDB-Metal 10.1.0-2020.12.7) 10.1
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
```
|
||||
|
||||
### Install serial tools(Optional, if you want to run on K210)
|
||||
|
||||
```sh
|
||||
$ pip3 install pyserial
|
||||
$ sudo apt install python3-serial
|
||||
```
|
||||
|
||||
## Run our project
|
||||
|
||||
### Qemu
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
|
||||
$ cd rCore-Tutorial-v3/os
|
||||
$ make run
|
||||
```
|
||||
|
||||
After outputing some debug messages, the kernel lists all the applications available and enter the user shell:
|
||||
|
||||
```
|
||||
/**** APPS ****
|
||||
mpsc_sem
|
||||
usertests
|
||||
pipetest
|
||||
forktest2
|
||||
cat
|
||||
initproc
|
||||
race_adder_loop
|
||||
threads_arg
|
||||
race_adder_mutex_spin
|
||||
race_adder_mutex_blocking
|
||||
forktree
|
||||
user_shell
|
||||
huge_write
|
||||
race_adder
|
||||
race_adder_atomic
|
||||
threads
|
||||
stack_overflow
|
||||
filetest_simple
|
||||
forktest_simple
|
||||
cmdline_args
|
||||
run_pipe_test
|
||||
forktest
|
||||
matrix
|
||||
exit
|
||||
fantastic_text
|
||||
sleep_simple
|
||||
yield
|
||||
hello_world
|
||||
pipe_large_test
|
||||
sleep
|
||||
phil_din_mutex
|
||||
**************/
|
||||
Rust user shell
|
||||
>>
|
||||
```
|
||||
|
||||
You can run any application except for `initproc` and `user_shell` itself. To run an application, just input its filename and hit enter. `usertests` can run a bunch of applications, thus it is recommended.
|
||||
|
||||
Type `Ctrl+a` then `x` to exit Qemu.
|
||||
|
||||
### K210
|
||||
|
||||
Before chapter 6, you do not need a SD card:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
|
||||
$ cd rCore-Tutorial-v3/os
|
||||
$ make run BOARD=k210
|
||||
```
|
||||
|
||||
From chapter 6, before running the kernel, we should insert a SD card into PC and manually write the filesystem image to it:
|
||||
|
||||
```sh
|
||||
$ cd rCore-Tutorial-v3/os
|
||||
$ make sdcard
|
||||
```
|
||||
|
||||
By default it will overwrite the device `/dev/sdb` which is the SD card, but you can provide another location. For example, `make sdcard SDCARD=/dev/sdc`.
|
||||
|
||||
After that, remove the SD card from PC and insert it to the slot of K210. Connect the K210 to PC and then:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
|
||||
$ cd rCore-Tutorial-v3/os
|
||||
$ make run BOARD=k210
|
||||
```
|
||||
|
||||
Type `Ctrl+]` to disconnect from K210.
|
||||
|
||||
## Rustdoc
|
||||
|
||||
Currently it can only help you view the code since only a tiny part of the code has been documented.
|
||||
|
||||
You can open a doc html of `os` using `cargo doc --no-deps --open` under `os` directory.
|
||||
|
||||
### OS-API-DOCS
|
||||
The API Docs for Ten OS
|
||||
1. [Lib-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch1/os/index.html)
|
||||
1. [Batch-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch2/os/index.html)
|
||||
1. [MultiProg-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch3-coop/os/index.html)
|
||||
1. [TimeSharing-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch3/os/index.html)
|
||||
1. [AddrSpace-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch4/os/index.html)
|
||||
1. [Process-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch5/os/index.html)
|
||||
1. [FileSystem-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch6/os/index.html)
|
||||
1. [IPC-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch7/os/index.html)
|
||||
1. [SyncMutex-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch8/os/index.html)
|
||||
1. [IODevice-OS API doc](https://learningos.github.io/rCore-Tutorial-v3/ch9/os/index.html)
|
||||
|
||||
## Working in progress
|
||||
|
||||
Our first release 3.5.0 (chapter 1-7) has been published.
|
||||
|
||||
There will be 9 chapters in our next release 3.6.0, where 2 new chapters will be added:
|
||||
* chapter 8: synchronization on a uniprocessor
|
||||
* chapter 9: I/O devices
|
||||
|
||||
Current version is 3.6.0-alpha.1 and we are still working on it.
|
||||
|
||||
Here are the updates since 3.5.0:
|
||||
|
||||
### Completed
|
||||
|
||||
* [x] automatically clean up and rebuild before running our project on a different platform
|
||||
* [x] fix `power` series application in early chapters, now you can find modulus in the output
|
||||
* [x] use `UPSafeCell` instead of `RefCell` or `spin::Mutex` in order to access static data structures and adjust its API so that it cannot be borrowed twice at a time(mention `& .exclusive_access().task[0]` in `run_first_task`)
|
||||
* [x] move `TaskContext` into `TaskControlBlock` instead of restoring it in place on kernel stack(since ch3), eliminating annoying `task_cx_ptr2`
|
||||
* [x] replace `llvm_asm!` with `asm!`
|
||||
* [x] expand the fs image size generated by `rcore-fs-fuse` to 128MiB
|
||||
* [x] add a new test named `huge_write` which evaluates the fs performance(qemu\~500KiB/s k210\~50KiB/s)
|
||||
* [x] flush all block cache to disk after a fs transaction which involves write operation
|
||||
* [x] replace `spin::Mutex` with `UPSafeCell` before SMP chapter
|
||||
* [x] add codes for a new chapter about synchronization & mutual exclusion(uniprocessor only)
|
||||
* [x] bug fix: we should call `find_pte` rather than `find_pte_create` in `PageTable::unmap`
|
||||
* [x] clarify: "check validity of level-3 pte in `find_pte` instead of checking it outside this function" should not be a bug
|
||||
* [x] code of chapter 8: synchronization on a uniprocessor
|
||||
* [x] switch the code of chapter 6 and chapter 7
|
||||
* [x] support signal mechanism in chapter 7/8(only works for apps with a single thread)
|
||||
* [x] Add boards/ directory and support rustdoc, for example you can use `cargo doc --no-deps --open` to view the documentation of a crate
|
||||
|
||||
### Todo(High priority)
|
||||
|
||||
* [ ] review documentation, current progress: 5/9
|
||||
* [ ] support user-level sync primitives in chapter 8
|
||||
* [ ] code of chapter 9: device drivers based on interrupts, including UART and block devices
|
||||
* [ ] use old fs image optionally, do not always rebuild the image
|
||||
* [ ] add new system calls: getdents64/fstat
|
||||
* [ ] shell functionality improvement(to be continued...)
|
||||
* [ ] give every non-zero process exit code an unique and clear error type
|
||||
* [ ] effective error handling of mm module
|
||||
|
||||
### Todo(Low priority)
|
||||
|
||||
* [ ] rewrite practice doc and remove some inproper questions
|
||||
* [ ] provide smooth debug experience at a Rust source code level
|
||||
* [ ] format the code using official tools
|
||||
|
||||
### Crates
|
||||
|
||||
We will add them later.
|
||||
|
@ -0,0 +1,18 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.x
|
||||
|
||||
## Dependency
|
||||
|
||||
### Binaries
|
||||
|
||||
* rustc: 1.57.0-nightly (e1e9319d9 2021-10-14)
|
||||
|
||||
* cargo-binutils: 0.3.3
|
||||
|
||||
* qemu: 5.0.0
|
||||
|
||||
* rustsbi-lib: 0.2.0-alpha.4
|
||||
|
||||
rustsbi-qemu: d4968dd2
|
||||
|
||||
rustsbi-k210: b689314e
|
@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
target/
|
||||
Cargo.lock
|
@ -1,6 +1,6 @@
|
||||
use core::any::Any;
|
||||
|
||||
pub trait BlockDevice : Send + Sync + Any {
|
||||
pub trait BlockDevice: Send + Sync + Any {
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]);
|
||||
fn write_block(&self, block_id: usize, buf: &[u8]);
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
pub const CLOCK_FREQ: usize = 403000000 / 62;
|
||||
|
||||
pub const MMIO: &[(usize, usize)] = &[
|
||||
// we don't need clint in S priv when running
|
||||
// we only need claim/complete for target0 after initializing
|
||||
(0x0C00_0000, 0x3000), /* PLIC */
|
||||
(0x0C20_0000, 0x1000), /* PLIC */
|
||||
(0x3800_0000, 0x1000), /* UARTHS */
|
||||
(0x3800_1000, 0x1000), /* GPIOHS */
|
||||
(0x5020_0000, 0x1000), /* GPIO */
|
||||
(0x5024_0000, 0x1000), /* SPI_SLAVE */
|
||||
(0x502B_0000, 0x1000), /* FPIOA */
|
||||
(0x502D_0000, 0x1000), /* TIMER0 */
|
||||
(0x502E_0000, 0x1000), /* TIMER1 */
|
||||
(0x502F_0000, 0x1000), /* TIMER2 */
|
||||
(0x5044_0000, 0x1000), /* SYSCTL */
|
||||
(0x5200_0000, 0x1000), /* SPI0 */
|
||||
(0x5300_0000, 0x1000), /* SPI1 */
|
||||
(0x5400_0000, 0x1000), /* SPI2 */
|
||||
];
|
||||
|
||||
pub type BlockDeviceImpl = crate::drivers::block::SDCardWrapper;
|
@ -0,0 +1,5 @@
|
||||
pub const CLOCK_FREQ: usize = 12500000;
|
||||
|
||||
pub const MMIO: &[(usize, usize)] = &[(0x10001000, 0x1000)];
|
||||
|
||||
pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock;
|
@ -1,3 +1,3 @@
|
||||
mod block;
|
||||
pub mod block;
|
||||
|
||||
pub use block::BLOCK_DEVICE;
|
||||
pub use block::BLOCK_DEVICE;
|
||||
|
@ -1,16 +1,16 @@
|
||||
mod inode;
|
||||
mod pipe;
|
||||
mod stdio;
|
||||
mod inode;
|
||||
|
||||
use crate::mm::UserBuffer;
|
||||
|
||||
pub trait File : Send + Sync {
|
||||
pub trait File: Send + Sync {
|
||||
fn readable(&self) -> bool;
|
||||
fn writable(&self) -> bool;
|
||||
fn read(&self, buf: UserBuffer) -> usize;
|
||||
fn write(&self, buf: UserBuffer) -> usize;
|
||||
}
|
||||
|
||||
pub use pipe::{Pipe, make_pipe};
|
||||
pub use inode::{list_apps, open_file, OSInode, OpenFlags};
|
||||
pub use pipe::{make_pipe, Pipe};
|
||||
pub use stdio::{Stdin, Stdout};
|
||||
pub use inode::{OSInode, open_file, OpenFlags, list_apps};
|
@ -1,62 +0,0 @@
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::*;
|
||||
|
||||
pub fn get_num_app() -> usize {
|
||||
extern "C" { fn _num_app(); }
|
||||
unsafe { (_num_app as usize as *const usize).read_volatile() }
|
||||
}
|
||||
|
||||
pub fn get_app_data(app_id: usize) -> &'static [u8] {
|
||||
extern "C" { fn _num_app(); }
|
||||
let num_app_ptr = _num_app as usize as *const usize;
|
||||
let num_app = get_num_app();
|
||||
let app_start = unsafe {
|
||||
core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1)
|
||||
};
|
||||
assert!(app_id < num_app);
|
||||
unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
app_start[app_id] as *const u8,
|
||||
app_start[app_id + 1] - app_start[app_id]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref APP_NAMES: Vec<&'static str> = {
|
||||
let num_app = get_num_app();
|
||||
extern "C" { fn _app_names(); }
|
||||
let mut start = _app_names as usize as *const u8;
|
||||
let mut v = Vec::new();
|
||||
unsafe {
|
||||
for _ in 0..num_app {
|
||||
let mut end = start;
|
||||
while end.read_volatile() != '\0' as u8 {
|
||||
end = end.add(1);
|
||||
}
|
||||
let slice = core::slice::from_raw_parts(start, end as usize - start as usize);
|
||||
let str = core::str::from_utf8(slice).unwrap();
|
||||
v.push(str);
|
||||
start = end.add(1);
|
||||
}
|
||||
}
|
||||
v
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_app_data_by_name(name: &str) -> Option<&'static [u8]> {
|
||||
let num_app = get_num_app();
|
||||
(0..num_app)
|
||||
.find(|&i| APP_NAMES[i] == name)
|
||||
.map(|i| get_app_data(i))
|
||||
}
|
||||
|
||||
pub fn list_apps() {
|
||||
println!("/**** APPS ****");
|
||||
for app in APP_NAMES.iter() {
|
||||
println!("{}", app);
|
||||
}
|
||||
println!("**************/");
|
||||
}
|
@ -1,28 +1,22 @@
|
||||
mod heap_allocator;
|
||||
mod address;
|
||||
mod frame_allocator;
|
||||
mod page_table;
|
||||
mod heap_allocator;
|
||||
mod memory_set;
|
||||
mod page_table;
|
||||
|
||||
use page_table::PTEFlags;
|
||||
use address::VPNRange;
|
||||
pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum, StepByOne};
|
||||
pub use frame_allocator::{FrameTracker, frame_alloc, frame_dealloc,};
|
||||
pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum};
|
||||
pub use frame_allocator::{frame_alloc, frame_dealloc, FrameTracker};
|
||||
pub use memory_set::remap_test;
|
||||
pub use memory_set::{kernel_token, MapPermission, MemorySet, KERNEL_SPACE};
|
||||
use page_table::PTEFlags;
|
||||
pub use page_table::{
|
||||
PageTable,
|
||||
PageTableEntry,
|
||||
translated_byte_buffer,
|
||||
translated_str,
|
||||
translated_ref,
|
||||
translated_refmut,
|
||||
UserBuffer,
|
||||
UserBufferIterator,
|
||||
translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable,
|
||||
PageTableEntry, UserBuffer, UserBufferIterator,
|
||||
};
|
||||
pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission, kernel_token};
|
||||
pub use memory_set::remap_test;
|
||||
|
||||
pub fn init() {
|
||||
heap_allocator::init_heap();
|
||||
frame_allocator::init_frame_allocator();
|
||||
KERNEL_SPACE.exclusive_access().activate();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
mod up;
|
||||
|
||||
pub use up::UPSafeCell;
|
||||
pub use up::UPSafeCell;
|
||||
|
@ -0,0 +1,31 @@
|
||||
use crate::task::{SignalFlags, MAX_SIG};
|
||||
|
||||
/// Action for a signal
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SignalAction {
|
||||
pub handler: usize,
|
||||
pub mask: SignalFlags,
|
||||
}
|
||||
|
||||
impl Default for SignalAction {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handler: 0,
|
||||
mask: SignalFlags::from_bits(40).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SignalActions {
|
||||
pub table: [SignalAction; MAX_SIG + 1],
|
||||
}
|
||||
|
||||
impl Default for SignalActions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
table: [SignalAction::default(); MAX_SIG + 1],
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
use bitflags::*;
|
||||
|
||||
pub const MAX_SIG: usize = 31;
|
||||
|
||||
bitflags! {
|
||||
pub struct SignalFlags: u32 {
|
||||
const SIGDEF = 1; // Default signal handling
|
||||
const SIGHUP = 1 << 1;
|
||||
const SIGINT = 1 << 2;
|
||||
const SIGQUIT = 1 << 3;
|
||||
const SIGILL = 1 << 4;
|
||||
const SIGTRAP = 1 << 5;
|
||||
const SIGABRT = 1 << 6;
|
||||
const SIGBUS = 1 << 7;
|
||||
const SIGFPE = 1 << 8;
|
||||
const SIGKILL = 1 << 9;
|
||||
const SIGUSR1 = 1 << 10;
|
||||
const SIGSEGV = 1 << 11;
|
||||
const SIGUSR2 = 1 << 12;
|
||||
const SIGPIPE = 1 << 13;
|
||||
const SIGALRM = 1 << 14;
|
||||
const SIGTERM = 1 << 15;
|
||||
const SIGSTKFLT = 1 << 16;
|
||||
const SIGCHLD = 1 << 17;
|
||||
const SIGCONT = 1 << 18;
|
||||
const SIGSTOP = 1 << 19;
|
||||
const SIGTSTP = 1 << 20;
|
||||
const SIGTTIN = 1 << 21;
|
||||
const SIGTTOU = 1 << 22;
|
||||
const SIGURG = 1 << 23;
|
||||
const SIGXCPU = 1 << 24;
|
||||
const SIGXFSZ = 1 << 25;
|
||||
const SIGVTALRM = 1 << 26;
|
||||
const SIGPROF = 1 << 27;
|
||||
const SIGWINCH = 1 << 28;
|
||||
const SIGIO = 1 << 29;
|
||||
const SIGPWR = 1 << 30;
|
||||
const SIGSYS = 1 << 31;
|
||||
}
|
||||
}
|
||||
|
||||
impl SignalFlags {
|
||||
pub fn check_error(&self) -> Option<(i32, &'static str)> {
|
||||
if self.contains(Self::SIGINT) {
|
||||
Some((-2, "Killed, SIGINT=2"))
|
||||
} else if self.contains(Self::SIGILL) {
|
||||
Some((-4, "Illegal Instruction, SIGILL=4"))
|
||||
} else if self.contains(Self::SIGABRT) {
|
||||
Some((-6, "Aborted, SIGABRT=6"))
|
||||
} else if self.contains(Self::SIGFPE) {
|
||||
Some((-8, "Erroneous Arithmetic Operation, SIGFPE=8"))
|
||||
} else if self.contains(Self::SIGKILL) {
|
||||
Some((-9, "Killed, SIGKILL=9"))
|
||||
} else if self.contains(Self::SIGSEGV) {
|
||||
Some((-11, "Segmentation Fault, SIGSEGV=11"))
|
||||
} else {
|
||||
//println!("[K] signalflags check_error {:?}", self);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
global_asm!(include_str!("switch.S"));
|
||||
|
||||
use super::TaskContext;
|
||||
use core::arch::global_asm;
|
||||
|
||||
global_asm!(include_str!("switch.S"));
|
||||
|
||||
extern "C" {
|
||||
pub fn __switch(
|
||||
current_task_cx_ptr: *mut TaskContext,
|
||||
next_task_cx_ptr: *const TaskContext
|
||||
);
|
||||
pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext);
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
nightly-2021-08-25
|
||||
nightly-2022-04-11
|
||||
|
@ -0,0 +1,30 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::read;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(_argc: usize, _argv: &[&str]) -> i32 {
|
||||
let mut buf = [0u8; 256];
|
||||
let mut lines = 0usize;
|
||||
let mut total_size = 0usize;
|
||||
loop {
|
||||
let len = read(0, &mut buf) as usize;
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
total_size += len;
|
||||
let string = core::str::from_utf8(&buf[..len]).unwrap();
|
||||
lines += string
|
||||
.chars()
|
||||
.fold(0, |acc, c| acc + if c == '\n' { 1 } else { 0 });
|
||||
}
|
||||
if total_size > 0 {
|
||||
lines += 1;
|
||||
}
|
||||
println!("{}", lines);
|
||||
0
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use user_lib::console::getchar;
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("getchar starting.... Press 'ENTER' will quit.");
|
||||
|
||||
loop {
|
||||
let c = getchar();
|
||||
|
||||
println!("Got Char {}", c);
|
||||
if c == LF || c == CR {
|
||||
println!("exit(0)");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![allow(clippy::empty_loop)]
|
||||
|
||||
extern crate user_lib;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(_argc: usize, _argv: &[&str]) -> ! {
|
||||
loop {}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use riscv::register::sstatus::{self, SPP};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
println!("Try to access privileged CSR in U Mode");
|
||||
println!("Kernel should kill this application!");
|
||||
unsafe {
|
||||
sstatus::set_spp(SPP::User);
|
||||
}
|
||||
0
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
println!("Try to execute privileged instruction in U Mode");
|
||||
println!("Kernel should kill this application!");
|
||||
unsafe {
|
||||
asm!("sret");
|
||||
}
|
||||
0
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use user_lib::console::getchar;
|
||||
use user_lib::*;
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
|
||||
fn func() {
|
||||
println!("signal_handler: caught signal SIGINT, and exit(1)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("sig_ctrlc starting.... Press 'ctrl-c' or 'ENTER' will quit.");
|
||||
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
println!("sig_ctrlc: sigaction");
|
||||
if sigaction(SIGINT, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
println!("sig_ctrlc: getchar....");
|
||||
loop {
|
||||
let c = getchar();
|
||||
|
||||
println!("Got Char {}", c);
|
||||
if c == LF || c == CR {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
println!("sig_ctrlc: Done");
|
||||
0
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
// use user_lib::{sigaction, sigprocmask, SignalAction, SignalFlags, fork, exit, wait, kill, getpid, sleep, sigreturn};
|
||||
use user_lib::*;
|
||||
|
||||
fn func() {
|
||||
println!("user_sig_test succsess");
|
||||
sigreturn();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
println!("signal_simple: sigaction");
|
||||
if sigaction(SIGUSR1, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
println!("signal_simple: kill");
|
||||
if kill(getpid() as usize, SIGUSR1) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(1);
|
||||
}
|
||||
println!("signal_simple: Done");
|
||||
0
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{
|
||||
exit, fork, getpid, kill, sigaction, sigprocmask, sigreturn, sleep, waitpid, SignalAction,
|
||||
SignalFlags,
|
||||
};
|
||||
|
||||
fn func() {
|
||||
println!("user_sig_test succsess");
|
||||
sigreturn();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
println!("signal_simple2: child sigaction");
|
||||
if sigaction(10, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
sleep(1000);
|
||||
println!("signal_simple2: child done");
|
||||
exit(0);
|
||||
} else if pid > 0 {
|
||||
println!("signal_simple2: parent kill child");
|
||||
sleep(500);
|
||||
if kill(pid as usize, 1 << 10) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(1);
|
||||
}
|
||||
println!("signal_simple2: parent wait child");
|
||||
let mut exit_code = 0;
|
||||
waitpid(pid as usize, &mut exit_code);
|
||||
println!("signal_simple2: parent Done");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
// use user_lib::{sigaction, sigprocmask, SignalAction, SignalFlags, fork, exit, wait, kill, getpid, sleep, sigreturn};
|
||||
use user_lib::*;
|
||||
|
||||
fn func() {
|
||||
println!("user_sig_test succsess");
|
||||
sigreturn();
|
||||
}
|
||||
|
||||
fn func2() {
|
||||
loop {
|
||||
print!("");
|
||||
}
|
||||
}
|
||||
|
||||
fn func3() {
|
||||
println!("interrupt");
|
||||
sigreturn();
|
||||
}
|
||||
|
||||
fn user_sig_test_failsignum() {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
if sigaction(50, &new, &old) >= 0 {
|
||||
panic!("Wrong sigaction but success!");
|
||||
}
|
||||
}
|
||||
|
||||
fn user_sig_test_kill() {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
if sigaction(SIGUSR1, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
if kill(getpid() as usize, SIGUSR1) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn user_sig_test_multiprocsignals() {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
if sigaction(SIGUSR1, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
} else {
|
||||
if kill(pid as usize, SIGUSR1) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(1);
|
||||
}
|
||||
let mut exit_code = 0;
|
||||
wait(&mut exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
fn user_sig_test_restore() {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
let old2 = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
if sigaction(SIGUSR1, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
|
||||
if sigaction(SIGUSR1, &old, &old2) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
|
||||
if old2.handler != new.handler {
|
||||
println!("Restore failed!");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
fn kernel_sig_test_ignore() {
|
||||
sigprocmask(SignalFlags::SIGSTOP.bits() as u32);
|
||||
if kill(getpid() as usize, SignalFlags::SIGSTOP.bits()) < 0 {
|
||||
println!("kill faild\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
fn kernel_sig_test_stop_cont() {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
kill(getpid() as usize, SignalFlags::SIGSTOP.bits());
|
||||
sleep(1000);
|
||||
exit(-1);
|
||||
} else {
|
||||
sleep(5000);
|
||||
kill(pid as usize, SignalFlags::SIGCONT.bits());
|
||||
let mut exit_code = 0;
|
||||
wait(&mut exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
fn kernel_sig_test_failignorekill() {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func as usize;
|
||||
|
||||
if sigaction(9, &new, &old) >= 0 {
|
||||
panic!("Should not set sigaction to kill!");
|
||||
}
|
||||
|
||||
if sigaction(9, &new, 0 as *const SignalAction) >= 0 {
|
||||
panic!("Should not set sigaction to kill!");
|
||||
}
|
||||
|
||||
if sigaction(9, 0 as *const SignalAction, &old) >= 0 {
|
||||
panic!("Should not set sigaction to kill!");
|
||||
}
|
||||
}
|
||||
|
||||
fn final_sig_test() {
|
||||
let mut new = SignalAction::default();
|
||||
let old = SignalAction::default();
|
||||
new.handler = func2 as usize;
|
||||
|
||||
let mut new2 = SignalAction::default();
|
||||
let old2 = SignalAction::default();
|
||||
new2.handler = func3 as usize;
|
||||
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
if sigaction(SIGUSR1, &new, &old) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
if sigaction(14, &new2, &old2) < 0 {
|
||||
panic!("Sigaction failed!");
|
||||
}
|
||||
if kill(getpid() as usize, SIGUSR1) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
sleep(1000);
|
||||
if kill(pid as usize, 1 << 14) < 0 {
|
||||
println!("Kill failed!");
|
||||
exit(-1);
|
||||
}
|
||||
sleep(1000);
|
||||
kill(pid as usize, SignalFlags::SIGKILL.bits());
|
||||
}
|
||||
}
|
||||
|
||||
fn run(f: fn()) -> bool {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
f();
|
||||
exit(0);
|
||||
} else {
|
||||
let mut exit_code: i32 = 0;
|
||||
wait(&mut exit_code);
|
||||
if exit_code != 0 {
|
||||
println!("FAILED!");
|
||||
} else {
|
||||
println!("OK!");
|
||||
}
|
||||
exit_code == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let tests: [(fn(), &str); 8] = [
|
||||
(user_sig_test_failsignum, "user_sig_test_failsignum"),
|
||||
(user_sig_test_kill, "user_sig_test_kill"),
|
||||
(
|
||||
user_sig_test_multiprocsignals,
|
||||
"user_sig_test_multiprocsignals",
|
||||
),
|
||||
(user_sig_test_restore, "user_sig_test_restore"),
|
||||
(kernel_sig_test_ignore, "kernel_sig_test_ignore"),
|
||||
(kernel_sig_test_stop_cont, "kernel_sig_test_stop_cont"),
|
||||
(
|
||||
kernel_sig_test_failignorekill,
|
||||
"kernel_sig_test_failignorekill",
|
||||
),
|
||||
(final_sig_test, "final_sig_test"),
|
||||
];
|
||||
let mut fail_num = 0;
|
||||
for test in tests {
|
||||
println!("Testing {}", test.1);
|
||||
if !run(test.0) {
|
||||
fail_num += 1;
|
||||
}
|
||||
}
|
||||
if fail_num == 0 {
|
||||
println!("ALL TESTS PASSED");
|
||||
0
|
||||
} else {
|
||||
println!("SOME TESTS FAILED");
|
||||
-1
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
println!("Into Test store_fault, we will insert an invalid store operation...");
|
||||
println!("Kernel should kill this application!");
|
||||
unsafe {
|
||||
core::ptr::null_mut::<u8>().write_volatile(0);
|
||||
}
|
||||
0
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{exec, fork, get_time, kill, waitpid, waitpid_nb, SignalFlags};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
assert_eq!(argc, 3, "argc must be 3!");
|
||||
let timeout_ms = argv[2]
|
||||
.parse::<isize>()
|
||||
.expect("Error when parsing timeout!");
|
||||
let pid = fork() as usize;
|
||||
if pid == 0 {
|
||||
if exec(argv[1], &[core::ptr::null::<u8>()]) != 0 {
|
||||
println!("Error when executing '{}'", argv[1]);
|
||||
return -4;
|
||||
}
|
||||
} else {
|
||||
let start_time = get_time();
|
||||
let mut child_exited = false;
|
||||
let mut exit_code: i32 = 0;
|
||||
loop {
|
||||
if get_time() - start_time > timeout_ms {
|
||||
break;
|
||||
}
|
||||
if waitpid_nb(pid, &mut exit_code) as usize == pid {
|
||||
child_exited = true;
|
||||
println!(
|
||||
"child exited in {}ms, exit_code = {}",
|
||||
get_time() - start_time,
|
||||
exit_code,
|
||||
);
|
||||
}
|
||||
}
|
||||
if !child_exited {
|
||||
println!("child has run for {}ms, kill it!", timeout_ms);
|
||||
kill(pid, SignalFlags::SIGINT.bits());
|
||||
assert_eq!(waitpid(pid, &mut exit_code) as usize, pid);
|
||||
println!("exit code of the child is {}", exit_code);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
@ -1,12 +1,18 @@
|
||||
use super::exit;
|
||||
use super::{getpid, kill, SignalFlags};
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
|
||||
let err = panic_info.message().unwrap();
|
||||
if let Some(location) = panic_info.location() {
|
||||
println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
|
||||
println!(
|
||||
"Panicked at {}:{}, {}",
|
||||
location.file(),
|
||||
location.line(),
|
||||
err
|
||||
);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
kill(getpid() as usize, SignalFlags::SIGABRT.bits());
|
||||
unreachable!()
|
||||
}
|
||||
|
Loading…
Reference in new issue