Compare commits

...
Sign in to create a new pull request.

80 commits
main ... ch3

Author SHA1 Message Date
Tateisi
8b2f8bf2a3 lab 03
Some checks failed
Build Rust Doc And Run tests / build-doc (push) Has been cancelled
Build Rust Doc And Run tests / run-tests (push) Has been cancelled
2025-08-03 11:54:14 +08:00
Yifan Wu
5ab1ca507c buildtools: feat #135: Check the version of QEMU ahead 2024-07-14 20:57:30 +08:00
Yifan Wu
7545f393a2 toolchain: Bump Rust to 1.80.0-nightly 2024-05-02 11:58:44 +08:00
Yifan Wu
8afce1c1a1 bugfix: Putting fence.i after loading apps 2024-02-19 22:35:40 +08:00
Yifan Wu
d27f835f3e Merge pull request #139 from jklincn/main 2024-01-26 22:14:22 +08:00
Yifan Wu
5a1eb8d01e Merge pull request #134 from cndoit18/add-devcontainer
feat: support devcontainer
2024-01-21 01:25:02 +08:00
Yifan Wu
8595e87779 Bump rust to version 1.77.0-nightly 2024-01-20 22:23:28 +08:00
Yifan Wu
48b1fcc643 Update doc-and-test.yml 2023-10-15 15:26:32 +08:00
Yifan Wu
1c7033dafa Bump rust to version 1.75.0-nightly 2023-10-10 21:51:02 +08:00
Yifan Wu
4dec6a89c6 Update os/Makefile: Added QEMU_ARGS 2023-04-30 01:07:19 +08:00
Yifan Wu
2f9711a3f1 fix ch3 2023-03-29 20:24:30 +08:00
Yifan Wu
78d0a70221 Use error! instead of println! when panicking 2023-03-29 20:13:47 +08:00
Yifan Wu
b56fe855c3 Using sbi-rt instead of asm && update rustsbi-qemu to latest
rustsbi-qemu version: a4f0bbe44d9f2f1069a9e5becd09f291e542852c
2023-03-29 20:13:41 +08:00
Yifan Wu
fd990bed1b Remove unnecessary output. 2023-02-01 21:10:31 +08:00
Yu Chen
fdd8c38b09 fix for rust-analyzer warning 2022-12-31 11:09:59 +08:00
Yifan Wu
744d329388 user add fp support 2022-12-21 21:21:10 +08:00
Yifan Wu
d0c8246901 Workflow: Remove k210 support. 2022-12-14 00:30:51 +08:00
Yifan Wu
19964a0ced Remove K210 support. 2022-12-14 00:04:55 +08:00
Yifan Wu
82aff1d510 Remove K210 support. 2022-12-13 22:44:22 +08:00
Yifan Wu
a279615610 in entry.asm: boot_stack->boot_stack_lower_bound 2022-11-29 09:29:01 +08:00
YdrMaster
14dbb45993 build: update toolchain
Signed-off-by: YdrMaster <ydrml@hotmail.com>
2022-10-20 11:52:30 +08:00
Yifan Wu
93f87596f7 Bump rustsbi-qemu to 701e891 2022-10-09 05:05:53 +08:00
Yifan Wu
8da34d24db Update Docker 2022-10-01 20:22:19 +08:00
Yu Chen
a0255fd6e8 udpate rust-toolchain: nightly-2022-07-20, cargo-utils 0.36 2022-07-25 11:50:50 +08:00
Yu Chen
faf00a71b5 update Dockerfile: ubuntu 18.04-->20.04, QEMU-5.0-->7.0 2022-07-18 11:25:45 +08:00
Yu Chen
d740d3cbfd support rust-analyzer for board_qemu features 2022-07-14 10:10:36 +08:00
Yu Chen
5b2ff98722 add CI autotest, update README 2022-06-23 23:45:54 +08:00
Yu Chen
0fb28f1b28 update rustsbi commit-id 74c103bc0f0c1931074c6edcd65a83fdff3cec33 in git@github.com:YdrMaster/rustsbi-qemu.git 2022-06-22 00:44:27 +08:00
Yu Chen
442f0c5653 sbi spec uses a6, a7 as sbicall id, so we need to set a6 to 0 2022-06-22 00:44:22 +08:00
Yu Chen
2c2ea7b218 Merge branch 'ch3' of github.com:rcore-os/rCore-Tutorial-v3 into ch3 2022-05-20 08:56:16 +08:00
Yu Chen
2f610554f7 add cargo fmt in Makefile, and exec make fmt 2022-05-20 08:55:49 +08:00
Yifan Wu
343e9657ed Rollback rustsbi && debugging in release mode 2022-04-16 15:33:48 -07:00
Yifan Wu
9decf93cff Bump to Rust nightly-2022-04-11 rustsbi-qemu=5992db7 rustsbi-k210=1937341 default-mode=debug 2022-04-15 03:40:35 -07:00
Yu Chen
676fdd86b7 update comments 2022-03-25 16:45:39 +08:00
Yu Chen
5b9dfeb6ad fix typo comments 2022-03-25 14:48:17 +08:00
Yu Chen
745ea760d0 add #![deny(missing_docs)] AND #![deny(warnings)] in main.rs, and add more comments 2022-03-25 11:17:40 +08:00
Yu Chen
2b53281dd8 update README 2022-03-20 23:52:36 +08:00
Yu Chen
f65400f88f add CI for build-doc 2022-03-20 21:27:57 +08:00
chyyuu
8f350ff5fa Merge pull request #1 from dramforever/ch3_doc
Merge docs ch3_doc -> ch3
2022-03-20 20:34:00 +08:00
田凯夫
e60ef5a67a Update docs 2022-03-14 20:35:50 +08:00
Yifan Wu
493fba58fe Add boards/ && cargo clippy&fmt 2022-01-24 15:43:57 -08:00
Yifan Wu
ce80bc2bfd cargo clippy & fmt 2022-01-21 14:21:32 -08:00
Yifan Wu
5e4e0c4fa6 Bump Rust to nightly-2022-01-19 2022-01-20 16:11:58 -08:00
Yifan Wu
4b2330ab08 Update .gitignore 2022-01-19 05:06:56 -08:00
Yifan Wu
e4a768f220 Kernel cannot dump now. 2022-01-03 19:36:52 -08:00
Yifan Wu
cace5043d6 Bump to rust nightly-2022-01-01, feature global_asm,asm->stable 2022-01-01 01:42:53 -08:00
Yifan Wu
f7870ff392 Bump to Rust nightly 2021-12-15 2021-12-22 02:29:48 -08:00
Yifan Wu
1e715d3c75 Update os/Makefile, rm ... -f -> rm -f ... 2021-11-27 01:54:06 -08:00
Yu Chen
7d3276e4a2 update .gitignore, README.md, dev-env-info.md 2021-11-20 16:24:32 +08:00
Yifan Wu
92d9170fca rust->nightly-2021-10-15,cargo-binutils->0.3.3 2021-10-20 13:27:05 -07:00
Yifan Wu
4e22f9d2fc Update README 2021-08-26 19:59:51 +08:00
Yifan Wu
5a24228565 Update rustc && rustsbi; llvm_asm -> asm in user 2021-08-26 19:51:08 +08:00
Yu Chen
859afce7e3 update to rustc 1.56.0-nightly (08095fc1f 2021-07-26) 2021-07-29 16:08:57 +08:00
Yifan Wu
93ea7f9d4d Exclusive UPSafeCell: A RefCell wrapper 2021-07-19 23:56:32 +08:00
Yifan Wu
fd00e8de3a Replace llvm_asm! with asm 2021-07-18 18:59:54 +08:00
Yifan Wu
7b815ac2f5 rustc 1.55.0-nightly (2f391da2e 2021-07-14) 2021-07-18 00:07:26 +08:00
Yifan Wu
8731d981ec Bump rustsbi to qemu[d4968dd2] k210[b689314e]. 2021-07-17 00:34:04 +08:00
Yifan Wu
8dfbb05fc2 Remove a warning. 2021-07-12 22:11:34 +08:00
Yifan Wu
92b949a01f Move TaskContext into TCB instead of kstack. 2021-07-10 12:35:17 +08:00
Yifan Wu
f1aabb5e0e RefCell->UPSafeCell 2021-07-09 21:18:51 +08:00
Yifan Wu
5a40784918 Downgrade cargo-binutils to 0.2.0 2021-04-05 16:23:52 +08:00
Yifan Wu
80b03c6740 Bump rustsbi to 0.2.0-alpha.1[81d53d8] 2021-03-09 16:08:36 +08:00
Yifan Wu
cd9c1708a6 Link small sections in linker 2021-03-07 19:54:09 +08:00
Yifan Wu
8406e8d8fe Do not fetch tools when running on qemu. 2021-03-06 03:21:29 +08:00
Yifan Wu
db66972228 Add Ubuntu18.04 docker 2021-02-28 06:32:04 +08:00
Yifan Wu
9944ac8701 Fix alignment in os/build.rs 2021-02-14 01:00:46 +08:00
Yifan Wu
6666ca5344 Move kflash.py out of proj. 2021-02-08 11:27:20 +08:00
Yifan Wu
e0646c2d53 Bump rustsbi to 0.1.1 && make config of qemu/k210 different 2021-02-07 18:04:38 +08:00
Yifan Wu
a8388036f1 Update os/Makefile && Update rust to 2021-01-30 2021-02-02 16:30:29 +08:00
Yifan Wu
fd8f673dbe Declare _unused explicitly when running first task. 2021-01-30 01:19:30 +08:00
Yifan Wu
e4eee06cb2 Fix os/Makefile: Support macOS 2021-01-21 00:14:56 +08:00
Yu Chen
2beee556e8 rust-toochain --> nightly 2021-01-16 19:27:58 +08:00
Yifan Wu
15ea8d934a Wrap syscalls in user_lib && change xstate to exit_code 2021-01-06 00:09:52 +08:00
Yifan Wu
923abbbc96 Move some variable name to task_cx to task_cx_ptr2 2021-01-04 16:03:07 +08:00
Yifan Wu
1ea79921bd Remove meaningless sstatus::set_sie() when initializing. 2021-01-03 15:34:44 +08:00
Yifan Wu
1d4041a418 Update format of link_user.S 2021-01-02 10:17:52 +08:00
Yifan Wu
b4ade2c68e Add env 2021-01-02 10:02:36 +08:00
Yifan Wu
45fb4113d1 Update sys_time unit to ms && Update clock freq of K210. 2020-12-29 21:49:51 +08:00
Yifan Wu
5985986249 Update rustsbi-qemu.bin. 2020-12-29 16:46:58 +08:00
Yifan Wu
abdfaeae89 Release mode app worked on ch3. 2020-11-30 10:29:17 +08:00
57 changed files with 1240 additions and 1916 deletions

View file

@ -0,0 +1,21 @@
{
"name": "rcore-tutorial-v3",
"build": {
"dockerfile": "../Dockerfile",
"args": {
"QEMU_VERSION": "7.0.0",
"DEBIAN_FRONTEND": "noninteractive",
"GDB_VERSION": "14.1"
}
},
"postCreateCommand": "rustup show",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"ms-vscode.cpptools",
"tamasfe.even-better-toml"
]
}
}
}

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
*/*
!rust-toolchain.toml

68
.github/workflows/doc-and-test.yml vendored Normal file
View file

@ -0,0 +1,68 @@
name: Build Rust Doc And Run tests
on: [push]
env:
CARGO_TERM_COLOR: always
rust_toolchain: nightly-2024-01-18
jobs:
build-doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview
target: riscv64gc-unknown-none-elf
- name: Build doc
run: 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 }}
run-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview
target: riscv64gc-unknown-none-elf
- uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
use-tool-cache: true
- name: Cache QEMU
uses: actions/cache@v3
with:
path: qemu-7.0.0
key: qemu-7.0.0-x86_64-riscv64
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install ninja-build -y
if [ ! -d qemu-7.0.0 ]; then
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar -xf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=riscv64-softmmu
make -j
else
cd qemu-7.0.0
fi
sudo make install
qemu-system-riscv64 --version
- name: Run usertests
run: cd os && make run TEST=1
timeout-minutes: 10

27
.gitignore vendored
View file

@ -1,6 +1,25 @@
.idea/* .*/*
os/target/* !.github/*
os/.idea/* !.vscode/settings.json
!.devcontainer/devcontainer.json
.idea
**/Cargo.lock
**/target/
os/src/link_app.S os/src/link_app.S
os/src/linker.ld
os/last-*
os/Cargo.lock
os/.gdb_history
user/build
user/target/* user/target/*
user/.idea/* user/.idea/*
user/Cargo.lock
easy-fs/Cargo.lock
easy-fs/target/*
easy-fs-fuse/Cargo.lock
easy-fs-fuse/target/*
tools/
pushall.sh
*.bak

13
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
// Prevent "can't find crate for `test`" error on no_std
// Ref: https://github.com/rust-lang/vscode-rust/issues/729
// For vscode-rust plugin users:
"rust.target": "riscv64gc-unknown-none-elf",
"rust.all_targets": false,
// For Rust Analyzer plugin users:
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
"rust-analyzer.checkOnSave.allTargets": false,
// "rust-analyzer.cargo.features": [
// "board_qemu"
// ]
}

72
Dockerfile Normal file
View file

@ -0,0 +1,72 @@
# syntax=docker/dockerfile:1
# Stage 1 Build QEMU
# - https://www.qemu.org/download/
# - https://wiki.qemu.org/Hosts/Linux#Building_QEMU_for_Linux
# - https://wiki.qemu.org/Documentation/Platforms/RISCV
FROM ubuntu:20.04 as build_qemu
ARG QEMU_VERSION=7.0.0
RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y wget build-essential libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build
RUN wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz && \
tar xf qemu-${QEMU_VERSION}.tar.xz && \
cd qemu-${QEMU_VERSION} && \
./configure --target-list=riscv64-softmmu,riscv64-linux-user && \
make -j$(nproc) && \
make install
# Stage 2 Set Lab Environment
FROM ubuntu:20.04 as build
WORKDIR /tmp
# 2.0. Install general tools
RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y jq curl git python3 wget build-essential \
# qemu dependency
libglib2.0-0 libfdt1 libpixman-1-0 zlib1g \
# gdb
gdb-multiarch
# 2.1. Copy qemu
COPY --from=build_qemu /usr/local/bin/* /usr/local/bin
# 2.2. Install Rust
# - https://www.rust-lang.org/tools/install
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH \
RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static \
RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly
# 2.3. Build env for labs
# See os/Makefile `env:` for example.
# This avoids having to wait for these steps each time using a new container.
COPY rust-toolchain.toml rust-toolchain.toml
RUN rustup target add riscv64gc-unknown-none-elf && \
cargo install toml-cli cargo-binutils && \
RUST_VERSION=$(toml get -r rust-toolchain.toml toolchain.channel) && \
Components=$(toml get -r rust-toolchain.toml toolchain.components | jq -r 'join(" ")') && \
rustup install $RUST_VERSION && \
rustup component add --toolchain $RUST_VERSION $Components
# 2.4. Set GDB
RUN ln -s /usr/bin/gdb-multiarch /usr/bin/riscv64-unknown-elf-gdb
# Stage 3 Sanity checking
FROM build as test
RUN qemu-system-riscv64 --version && \
qemu-riscv64 --version && \
rustup --version && \
cargo --version && \
rustc --version && \
riscv64-unknown-elf-gdb --version

12
Makefile Normal file
View file

@ -0,0 +1,12 @@
DOCKER_TAG ?= rcore-tutorial-v3:latest
.PHONY: docker build_docker
docker:
docker run --rm -it -v ${PWD}:/mnt -w /mnt --name rcore-tutorial-v3 ${DOCKER_TAG} bash
build_docker:
docker build -t ${DOCKER_TAG} --target build .
fmt:
cd os ; cargo fmt; cd ..

296
README.md
View file

@ -1,2 +1,296 @@
# rCore-Tutorial-v3 # rCore-Tutorial-v3
rCore-Tutorial version 3. rCore-Tutorial version 3.6. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/).
rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS)
If you don't know Rust Language and try to learn it, please visit [Rust Learning Resources](https://github.com/rcore-os/rCore/wiki/study-resource-of-system-programming-in-RUST)
Official QQ group number: 735045051
## news
- 23/06/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.
## Overview
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**.
## Features
* 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 7.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-7.0.0.tar.xz
# extract to qemu-7.0.0/
$ tar xvJf qemu-7.0.0.tar.xz
$ cd qemu-7.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-7.0.0
export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0/riscv64-softmmu
export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.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 7.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.
## Show runtime debug info of OS kernel version
The branch of ch9-log contains a lot of debug info. You could try to run rcore tutorial
for understand the internal behavior of os kernel.
```sh
$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
$ cd rCore-Tutorial-v3/os
$ git checkout ch9-log
$ make run
......
[rustsbi] RustSBI version 0.2.0-alpha.10, adapting to RISC-V SBI v0.3
.______ __ __ _______.___________. _______..______ __
| _ \ | | | | / | | / || _ \ | |
| |_) | | | | | | (----`---| |----`| (----`| |_) || |
| / | | | | \ \ | | \ \ | _ < | |
| |\ \----.| `--' |.----) | | | .----) | | |_) || |
| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|
[rustsbi] Implementation: RustSBI-QEMU Version 0.0.2
[rustsbi-dtb] Hart count: cluster0 with 1 cores
[rustsbi] misa: RV64ACDFIMSU
[rustsbi] mideleg: ssoft, stimer, sext (0x222)
[rustsbi] medeleg: ima, ia, bkpt, la, sa, uecall, ipage, lpage, spage (0xb1ab)
[rustsbi] pmp0: 0x10000000 ..= 0x10001fff (rw-)
[rustsbi] pmp1: 0x2000000 ..= 0x200ffff (rw-)
[rustsbi] pmp2: 0xc000000 ..= 0xc3fffff (rw-)
[rustsbi] pmp3: 0x80000000 ..= 0x8fffffff (rwx)
[rustsbi] enter supervisor 0x80200000
[KERN] rust_main() begin
[KERN] clear_bss() begin
[KERN] clear_bss() end
[KERN] mm::init() begin
[KERN] mm::init_heap() begin
[KERN] mm::init_heap() end
[KERN] mm::init_frame_allocator() begin
[KERN] mm::frame_allocator::lazy_static!FRAME_ALLOCATOR begin
......
```
## 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.6.0 (chapter 1-9) has been published, and we are still working on it.
* chapter 9: need more descripts about different I/O devices
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
* [x] code of chapter 9: device drivers based on interrupts, including UART, block, keyboard, mouse, gpu devices
* [x] add CI autotest and doc in github
### Todo(High priority)
* [ ] review documentation, current progress: 8/9
* [ ] use old fs image optionally, do not always rebuild the image
* [ ] 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
* [ ] add more os functions for understanding os conecpts and principles
### 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.

Binary file not shown.

Binary file not shown.

18
dev-env-info.md Normal file
View file

@ -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

132
os/Cargo.lock generated
View file

@ -1,132 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version",
]
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin",
]
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "os"
version = "0.1.0"
dependencies = [
"lazy_static",
"riscv",
]
[[package]]
name = "regex"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]]
name = "riscv"
version = "0.6.0"
source = "git+https://github.com/rcore-os/riscv#21e32ee1dc786cc0d5006ceee0040ce4f8398575"
dependencies = [
"bare-metal",
"bit_field",
"bitflags",
"riscv-target",
]
[[package]]
name = "riscv-target"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]

View file

@ -2,14 +2,15 @@
name = "os" name = "os"
version = "0.1.0" version = "0.1.0"
authors = ["Yifan Wu <shinbokuow@163.com>"] authors = ["Yifan Wu <shinbokuow@163.com>"]
edition = "2018" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
log = "0.4"
sbi-rt = { version = "0.0.2", features = ["legacy"] }
[features] [profile.release]
board_qemu = [] debug = true
board_k210 = []

View file

@ -3,17 +3,20 @@ TARGET := riscv64gc-unknown-none-elf
MODE := release MODE := release
KERNEL_ELF := target/$(TARGET)/$(MODE)/os KERNEL_ELF := target/$(TARGET)/$(MODE)/os
KERNEL_BIN := $(KERNEL_ELF).bin KERNEL_BIN := $(KERNEL_ELF).bin
KERNEL_ENTRY_PA := 0x80020000
DISASM_TMP := target/$(TARGET)/$(MODE)/asm DISASM_TMP := target/$(TARGET)/$(MODE)/asm
# BOARD # BOARD
BOARD ?= qemu BOARD := qemu
SBI ?= rustsbi SBI ?= rustsbi
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
# Run K210 # Building mode argument
K210-SERIALPORT = /dev/ttyUSB0 ifeq ($(MODE), release)
K210-BURNER = ../tools/kflash.py MODE_ARG := --release
endif
# KERNEL ENTRY
KERNEL_ENTRY_PA := 0x80200000
# Binutils # Binutils
OBJDUMP := rust-objdump --arch-name=riscv64 OBJDUMP := rust-objdump --arch-name=riscv64
@ -22,14 +25,23 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64
# Disassembly # Disassembly
DISASM ?= -x DISASM ?= -x
build: $(KERNEL_BIN) build: env $(KERNEL_BIN)
env:
(rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET)
cargo install cargo-binutils
rustup component add rust-src
rustup component add llvm-tools-preview
$(KERNEL_BIN): kernel $(KERNEL_BIN): kernel
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
kernel: kernel:
@cd ../user && make build @cd ../user && make build
@cargo build --release --features "board_$(BOARD)" @echo Platform: $(BOARD)
@cp src/linker-$(BOARD).ld src/linker.ld
@cargo build $(MODE_ARG)
@rm src/linker.ld
clean: clean:
@cargo clean @cargo clean
@ -44,26 +56,28 @@ disasm-vim: kernel
run: run-inner run: run-inner
run-inner: build QEMU_ARGS := -machine virt \
ifeq ($(BOARD),qemu) -nographic \
@qemu-system-riscv64 \ -bios $(BOOTLOADER) \
-machine virt \ -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA)
-nographic \
-bios $(BOOTLOADER) \
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA)
else
@cp $(BOOTLOADER) $(BOOTLOADER).copy
@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=128K seek=1
@mv $(BOOTLOADER).copy $(KERNEL_BIN)
@sudo chmod 777 $(K210-SERIALPORT)
python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN)
miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200
endif
debug: build QEMU_NAME := qemu-system-riscv64
qemu-version-check:
@sh scripts/qemu-ver-check.sh $(QEMU_NAME)
run-inner: qemu-version-check build
@qemu-system-riscv64 $(QEMU_ARGS)
debug: qemu-version-check build
@tmux new-session -d \ @tmux new-session -d \
"qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -s -S" && \ "qemu-system-riscv64 $(QEMU_ARGS) -s -S" && \
tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \
tmux -2 attach-session -d tmux -2 attach-session -d
.PHONY: build kernel clean disasm disasm-vim run-inner gdbserver: qemu-version-check build
@qemu-system-riscv64 $(QEMU_ARGS) -s -S
gdbclient:
@riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'
.PHONY: build env kernel clean disasm disasm-vim run-inner gdbserver gdbclient qemu-version-check

View file

@ -1,12 +1,13 @@
use std::fs::{read_dir, File};
use std::io::{Result, Write}; use std::io::{Result, Write};
use std::fs::{File, read_dir};
fn main() { fn main() {
println!("cargo:rerun-if-changed=../user/src/bin/"); println!("cargo:rerun-if-changed=../user/src/");
println!("cargo:rerun-if-changed={}", TARGET_PATH);
insert_app_data().unwrap(); insert_app_data().unwrap();
} }
static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/debug/"; static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/";
fn insert_app_data() -> Result<()> { fn insert_app_data() -> Result<()> {
let mut f = File::create("src/link_app.S").unwrap(); let mut f = File::create("src/link_app.S").unwrap();
@ -21,33 +22,35 @@ fn insert_app_data() -> Result<()> {
.collect(); .collect();
apps.sort(); apps.sort();
writeln!(f, r#" writeln!(
.align 4 f,
r#"
.align 3
.section .data .section .data
.global _num_app .global _num_app
_num_app: _num_app:
.quad {} .quad {}"#,
"#, apps.len())?; apps.len()
)?;
for i in 0..apps.len() { for i in 0..apps.len() {
writeln!(f, r#" writeln!(f, r#" .quad app_{}_start"#, i)?;
.quad app_{}_start
"#, i)?;
} }
writeln!(f, r#" writeln!(f, r#" .quad app_{}_end"#, apps.len() - 1)?;
.quad app_{}_end
"#, apps.len() - 1)?;
for (idx, app) in apps.iter().enumerate() { for (idx, app) in apps.iter().enumerate() {
println!("app_{}: {}", idx, app); println!("app_{}: {}", idx, app);
writeln!(f, r#" writeln!(
f,
r#"
.section .data .section .data
.global app_{0}_start .global app_{0}_start
.global app_{0}_end .global app_{0}_end
app_{0}_start: app_{0}_start:
.incbin "{2}{1}.bin" .incbin "{2}{1}.bin"
app_{0}_end: app_{0}_end:"#,
"#, idx, app, TARGET_PATH)?; idx, app, TARGET_PATH
)?;
} }
Ok(()) Ok(())
} }

View file

@ -0,0 +1,26 @@
#!/bin/sh
# Argument1: The filename of qemu executable, e.g. qemu-system-riscv64
QEMU_PATH=$(which $1)
RET=$?
MINIMUM_MAJOR_VERSION=7
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
if [ $RET != 0 ]
then
echo "$1 not found"
exit 1
else
QEMU_VERSION=$($1 --version|head -n 1|awk '{print $4}')
MAJOR_VERSION=$(echo $QEMU_VERSION|cut -c1-1)
if [ $MAJOR_VERSION -lt $MINIMUM_MAJOR_VERSION ]
then
echo "${RED}Error: Required major version of QEMU is ${MINIMUM_MAJOR_VERSION}, " \
"but current is ${QEMU_VERSION}.${NC}"
exit 1
else
echo "${GREEN}QEMU version is ${QEMU_VERSION}(>=${MINIMUM_MAJOR_VERSION}), OK!${NC}"
exit 0
fi
fi

3
os/src/boards/qemu.rs Normal file
View file

@ -0,0 +1,3 @@
//! Constants used in rCore for qemu
pub const CLOCK_FREQ: usize = 12500000;

View file

@ -1,11 +1,17 @@
pub const USER_STACK_SIZE: usize = 4096 * 2; //! Constants used in rCore
pub const USER_STACK_SIZE: usize = 4096;
pub const KERNEL_STACK_SIZE: usize = 4096 * 2; pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
pub const MAX_APP_NUM: usize = 4; pub const MAX_APP_NUM: usize = 4;
pub const APP_BASE_ADDRESS: usize = 0x80100000; pub const APP_BASE_ADDRESS: usize = 0x80400000;
pub const APP_SIZE_LIMIT: usize = 0x20000; pub const APP_SIZE_LIMIT: usize = 0x20000;
pub const MAX_SYSCALL_NUM: usize = 500;
/*
#[cfg(feature = "board_k210")] #[cfg(feature = "board_k210")]
pub const CPU_FREQ: usize = 10000000; pub const CLOCK_FREQ: usize = 403000000 / 62;
#[cfg(feature = "board_qemu")] #[cfg(feature = "board_qemu")]
pub const CPU_FREQ: usize = 12500000; pub const CLOCK_FREQ: usize = 12500000;
*/
pub use crate::board::CLOCK_FREQ;

View file

@ -1,5 +1,7 @@
use core::fmt::{self, Write}; //! SBI console driver, for text output
use crate::sbi::console_putchar; use crate::sbi::console_putchar;
use core::fmt::{self, Write};
struct Stdout; struct Stdout;
@ -16,6 +18,7 @@ pub fn print(args: fmt::Arguments) {
Stdout.write_fmt(args).unwrap(); Stdout.write_fmt(args).unwrap();
} }
/// print string macro
#[macro_export] #[macro_export]
macro_rules! print { macro_rules! print {
($fmt: literal $(, $($arg: tt)+)?) => { ($fmt: literal $(, $($arg: tt)+)?) => {
@ -23,11 +26,10 @@ macro_rules! print {
} }
} }
/// println string macro
#[macro_export] #[macro_export]
macro_rules! println { macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => { ($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
} }
} }

View file

@ -5,8 +5,8 @@ _start:
call rust_main call rust_main
.section .bss.stack .section .bss.stack
.globl boot_stack .globl boot_stack_lower_bound
boot_stack: boot_stack_lower_bound:
.space 4096 * 16 .space 4096 * 16
.globl boot_stack_top .globl boot_stack_top
boot_stack_top: boot_stack_top:

View file

@ -1,12 +1,20 @@
use core::panic::PanicInfo; //! The panic handler
use crate::sbi::shutdown; use crate::sbi::shutdown;
use core::panic::PanicInfo;
use log::*;
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
if let Some(location) = info.location() { if let Some(location) = info.location() {
println!("[kernel] Panicked at {}:{} {}", location.file(), location.line(), info.message().unwrap()); error!(
"[kernel] Panicked at {}:{} {}",
location.file(),
location.line(),
info.message().unwrap()
);
} else { } else {
println!("[kernel] Panicked: {}", info.message().unwrap()); error!("[kernel] Panicked: {}", info.message().unwrap());
} }
shutdown() shutdown(true)
} }

View file

@ -1,6 +1,6 @@
OUTPUT_ARCH(riscv) OUTPUT_ARCH(riscv)
ENTRY(_start) ENTRY(_start)
BASE_ADDRESS = 0x80020000; BASE_ADDRESS = 0x80200000;
SECTIONS SECTIONS
{ {
@ -18,6 +18,7 @@ SECTIONS
srodata = .; srodata = .;
.rodata : { .rodata : {
*(.rodata .rodata.*) *(.rodata .rodata.*)
*(.srodata .srodata.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@ -25,6 +26,7 @@ SECTIONS
sdata = .; sdata = .;
.data : { .data : {
*(.data .data.*) *(.data .data.*)
*(.sdata .sdata.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@ -33,6 +35,7 @@ SECTIONS
*(.bss.stack) *(.bss.stack)
sbss = .; sbss = .;
*(.bss .bss.*) *(.bss .bss.*)
*(.sbss .sbss.*)
} }
. = ALIGN(4K); . = ALIGN(4K);

View file

@ -1,39 +1,44 @@
use crate::trap::TrapContext; //! Loading user applications into memory
use crate::task::TaskContext; //!
//! For chapter 3, user applications are simply part of the data included in the
//! kernel binary, so we only need to copy them to the space allocated for each
//! app to load them. We also allocate fixed spaces for each task's
//! [`KernelStack`] and [`UserStack`].
use crate::config::*; use crate::config::*;
use crate::trap::TrapContext;
use core::arch::asm;
#[repr(align(4096))] #[repr(align(4096))]
#[derive(Copy, Clone)]
struct KernelStack { struct KernelStack {
data: [u8; KERNEL_STACK_SIZE], data: [u8; KERNEL_STACK_SIZE],
} }
#[repr(align(4096))] #[repr(align(4096))]
#[derive(Copy, Clone)]
struct UserStack { struct UserStack {
data: [u8; USER_STACK_SIZE], data: [u8; USER_STACK_SIZE],
} }
static KERNEL_STACK: [KernelStack; MAX_APP_NUM] = [ static KERNEL_STACK: [KernelStack; MAX_APP_NUM] = [KernelStack {
KernelStack { data: [0; KERNEL_STACK_SIZE], }; data: [0; KERNEL_STACK_SIZE],
MAX_APP_NUM }; MAX_APP_NUM];
];
static USER_STACK: [UserStack; MAX_APP_NUM] = [ static USER_STACK: [UserStack; MAX_APP_NUM] = [UserStack {
UserStack { data: [0; USER_STACK_SIZE], }; data: [0; USER_STACK_SIZE],
MAX_APP_NUM }; MAX_APP_NUM];
];
impl KernelStack { impl KernelStack {
fn get_sp(&self) -> usize { fn get_sp(&self) -> usize {
self.data.as_ptr() as usize + KERNEL_STACK_SIZE self.data.as_ptr() as usize + KERNEL_STACK_SIZE
} }
pub fn push_context(&self, trap_cx: TrapContext, task_cx: TaskContext) -> &'static mut TaskContext { pub fn push_context(&self, trap_cx: TrapContext) -> usize {
let trap_cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
unsafe { unsafe {
let trap_cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
*trap_cx_ptr = trap_cx; *trap_cx_ptr = trap_cx;
let task_cx_ptr = (trap_cx_ptr as usize - core::mem::size_of::<TaskContext>()) as *mut TaskContext;
*task_cx_ptr = task_cx;
task_cx_ptr.as_mut().unwrap()
} }
trap_cx_ptr as usize
} }
} }
@ -43,45 +48,56 @@ impl UserStack {
} }
} }
/// Get base address of app i.
fn get_base_i(app_id: usize) -> usize { fn get_base_i(app_id: usize) -> usize {
APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT
} }
/// Get the total number of applications.
pub fn get_num_app() -> usize { pub fn get_num_app() -> usize {
extern "C" { fn _num_app(); } extern "C" {
fn _num_app();
}
unsafe { (_num_app as usize as *const usize).read_volatile() } unsafe { (_num_app as usize as *const usize).read_volatile() }
} }
/// Load nth user app at
/// [APP_BASE_ADDRESS + n * APP_SIZE_LIMIT, APP_BASE_ADDRESS + (n+1) * APP_SIZE_LIMIT).
pub fn load_apps() { pub fn load_apps() {
extern "C" { fn _num_app(); } extern "C" {
fn _num_app();
}
let num_app_ptr = _num_app as usize as *const usize; let num_app_ptr = _num_app as usize as *const usize;
let num_app = get_num_app(); let num_app = get_num_app();
let app_start = unsafe { let app_start = unsafe { core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) };
core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1)
};
// clear i-cache first
unsafe { llvm_asm!("fence.i" :::: "volatile"); }
// load apps // load apps
for i in 0..num_app { for i in 0..num_app {
let base_i = get_base_i(i); let base_i = get_base_i(i);
// clear region // clear region
(base_i..base_i + APP_SIZE_LIMIT).for_each(|addr| unsafe { (base_i..base_i + APP_SIZE_LIMIT)
(addr as *mut u8).write_volatile(0) .for_each(|addr| unsafe { (addr as *mut u8).write_volatile(0) });
});
// load app from data section to memory // load app from data section to memory
let src = unsafe { let src = unsafe {
core::slice::from_raw_parts(app_start[i] as *const u8, app_start[i + 1] - app_start[i]) core::slice::from_raw_parts(app_start[i] as *const u8, app_start[i + 1] - app_start[i])
}; };
let dst = unsafe { let dst = unsafe { core::slice::from_raw_parts_mut(base_i as *mut u8, src.len()) };
core::slice::from_raw_parts_mut(base_i as *mut u8, src.len())
};
dst.copy_from_slice(src); dst.copy_from_slice(src);
} }
// Memory fence about fetching the instruction memory
// It is guaranteed that a subsequent instruction fetch must
// observes all previous writes to the instruction memory.
// Therefore, fence.i must be executed after we have loaded
// the code of the next app into the instruction memory.
// See also: riscv non-priv spec chapter 3, 'Zifencei' extension.
unsafe {
asm!("fence.i");
}
} }
pub fn init_app_cx(app_id: usize) -> &'static TaskContext { /// get app info with entry and sp and save `TrapContext` in kernel stack
KERNEL_STACK[app_id].push_context( pub fn init_app_cx(app_id: usize) -> usize {
TrapContext::app_init_context(get_base_i(app_id), USER_STACK[app_id].get_sp()), KERNEL_STACK[app_id].push_context(TrapContext::app_init_context(
TaskContext::goto_restore(), get_base_i(app_id),
) USER_STACK[app_id].get_sp(),
))
} }

View file

@ -1,43 +1,67 @@
//! The main module and entrypoint
//!
//! Various facilities of the kernels are implemented as submodules. The most
//! important ones are:
//!
//! - [`trap`]: Handles all cases of switching from userspace to the kernel
//! - [`task`]: Task management
//! - [`syscall`]: System call handling and implementation
//!
//! The operating system also starts in this module. Kernel code starts
//! executing from `entry.asm`, after which [`rust_main()`] is called to
//! initialize various pieces of functionality. (See its source code for
//! details.)
//!
//! We then call [`task::run_first_task()`] and for the first time go to
//! userspace.
#![deny(missing_docs)]
#![deny(warnings)]
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(global_asm)]
#![feature(llvm_asm)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(const_in_array_repeat_expressions)]
use core::arch::global_asm;
#[path = "boards/qemu.rs"]
mod board;
#[macro_use] #[macro_use]
mod console; mod console;
mod lang_items;
mod sbi;
mod syscall;
mod trap;
mod loader;
mod config; mod config;
mod task; mod lang_items;
mod loader;
mod sbi;
mod sync;
pub mod syscall;
pub mod task;
mod timer; mod timer;
pub mod trap;
global_asm!(include_str!("entry.asm")); global_asm!(include_str!("entry.asm"));
global_asm!(include_str!("link_app.S")); global_asm!(include_str!("link_app.S"));
/// clear BSS segment
fn clear_bss() { fn clear_bss() {
extern "C" { extern "C" {
fn sbss(); fn sbss();
fn ebss(); fn ebss();
} }
(sbss as usize..ebss as usize).for_each(|a| { unsafe {
unsafe { (a as *mut u8).write_volatile(0) } core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize)
}); .fill(0);
}
} }
/// the rust entry-point of os
#[no_mangle] #[no_mangle]
pub fn rust_main() -> ! { pub fn rust_main() -> ! {
clear_bss(); clear_bss();
println!("[kernel] Hello, world!"); println!("[kernel] Hello, world!");
trap::init(); trap::init();
loader::load_apps(); loader::load_apps();
trap::enable_interrupt();
trap::enable_timer_interrupt(); trap::enable_timer_interrupt();
timer::set_next_trigger(); timer::set_next_trigger();
task::run_first_task(); task::run_first_task();
panic!("Unreachable in rust_main!"); panic!("Unreachable in rust_main!");
} }

View file

@ -1,43 +1,23 @@
#![allow(unused)] //! SBI call wrappers
const SBI_SET_TIMER: usize = 0;
const SBI_CONSOLE_PUTCHAR: usize = 1;
const SBI_CONSOLE_GETCHAR: usize = 2;
const SBI_CLEAR_IPI: usize = 3;
const SBI_SEND_IPI: usize = 4;
const SBI_REMOTE_FENCE_I: usize = 5;
const SBI_REMOTE_SFENCE_VMA: usize = 6;
const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
const SBI_SHUTDOWN: usize = 8;
#[inline(always)]
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let mut ret;
unsafe {
llvm_asm!("ecall"
: "={x10}" (ret)
: "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
: "memory"
: "volatile"
);
}
ret
}
pub fn set_timer(timer: usize) {
sbi_call(SBI_SET_TIMER, timer, 0, 0);
}
/// use sbi call to putchar in console (qemu uart handler)
pub fn console_putchar(c: usize) { pub fn console_putchar(c: usize) {
sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); #[allow(deprecated)]
sbi_rt::legacy::console_putchar(c);
} }
pub fn console_getchar() -> usize { /// use sbi call to set timer
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) pub fn set_timer(timer: usize) {
sbi_rt::set_timer(timer as _);
} }
pub fn shutdown() -> ! { /// use sbi call to shutdown the kernel
sbi_call(SBI_SHUTDOWN, 0, 0, 0); pub fn shutdown(failure: bool) -> ! {
panic!("It should shutdown!"); use sbi_rt::{system_reset, NoReason, Shutdown, SystemFailure};
if !failure {
system_reset(Shutdown, NoReason);
} else {
system_reset(Shutdown, SystemFailure);
}
unreachable!()
} }

5
os/src/sync/mod.rs Normal file
View file

@ -0,0 +1,5 @@
//! Synchronization and interior mutability primitives
mod up;
pub use up::UPSafeCell;

31
os/src/sync/up.rs Normal file
View file

@ -0,0 +1,31 @@
//! Uniprocessor interior mutability primitives
use core::cell::{RefCell, RefMut};
/// Wrap a static data structure inside it so that we are
/// able to access it without any `unsafe`.
///
/// We should only use it in uniprocessor.
///
/// In order to get mutable reference of inner data, call
/// `exclusive_access`.
pub struct UPSafeCell<T> {
/// inner data
inner: RefCell<T>,
}
unsafe impl<T> Sync for UPSafeCell<T> {}
impl<T> UPSafeCell<T> {
/// User is responsible to guarantee that inner struct is only used in
/// uniprocessor.
pub unsafe fn new(value: T) -> Self {
Self {
inner: RefCell::new(value),
}
}
/// Exclusive access inner data in UPSafeCell. Panic if the data has been borrowed.
pub fn exclusive_access(&self) -> RefMut<'_, T> {
self.inner.borrow_mut()
}
}

View file

@ -1,5 +1,8 @@
//! File and filesystem-related syscalls
const FD_STDOUT: usize = 1; const FD_STDOUT: usize = 1;
/// write buf of length `len` to a file with `fd`
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
match fd { match fd {
FD_STDOUT => { FD_STDOUT => {
@ -7,9 +10,9 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
let str = core::str::from_utf8(slice).unwrap(); let str = core::str::from_utf8(slice).unwrap();
print!("{}", str); print!("{}", str);
len as isize len as isize
}, }
_ => { _ => {
panic!("Unsupported fd in sys_write!"); panic!("Unsupported fd in sys_write!");
} }
} }
} }

View file

@ -1,7 +1,20 @@
//! Implementation of syscalls
//!
//! The single entry point to all system calls, [`syscall()`], is called
//! whenever userspace wishes to perform a system call using the `ecall`
//! instruction. In this case, the processor raises an 'Environment call from
//! U-mode' exception, which is handled as one of the cases in
//! [`crate::trap::trap_handler`].
//!
//! For clarity, each single syscall is implemented as its own function, named
//! `sys_` then the name of the syscall. You can find functions like this in
//! submodules, and you should also implement syscalls this way.
const SYSCALL_WRITE: usize = 64; const SYSCALL_WRITE: usize = 64;
const SYSCALL_EXIT: usize = 93; const SYSCALL_EXIT: usize = 93;
const SYSCALL_YIELD: usize = 124; const SYSCALL_YIELD: usize = 124;
const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GET_TIME: usize = 169;
const SYSCALL_TASK_INFO: usize = 410;
mod fs; mod fs;
mod process; mod process;
@ -9,13 +22,17 @@ mod process;
use fs::*; use fs::*;
use process::*; use process::*;
use crate::task::TASK_MANAGER;
/// handle syscall exception with `syscall_id` and other arguments
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
TASK_MANAGER.mark_syscall(syscall_id);
match syscall_id { match syscall_id {
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_EXIT => sys_exit(args[0] as i32),
SYSCALL_YIELD => sys_yield(), SYSCALL_YIELD => sys_yield(),
SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GET_TIME => sys_get_time(),
SYSCALL_TASK_INFO => sys_task_info(args[0] as *mut TaskInfo),
_ => panic!("Unsupported syscall_id: {}", syscall_id), _ => panic!("Unsupported syscall_id: {}", syscall_id),
} }
} }

View file

@ -1,20 +1,46 @@
use crate::task::{ //! Process management syscalls
suspend_current_and_run_next, use log::trace;
exit_current_and_run_next,
};
use crate::timer::get_time;
pub fn sys_exit(xstate: i32) -> ! { use crate::task::{exit_current_and_run_next, suspend_current_and_run_next, TaskStatus, TASK_MANAGER};
println!("[kernel] Application exited with code {}", xstate); use crate::timer::{get_time, get_time_ms};
use crate::config::MAX_SYSCALL_NUM;
/// Task information
#[allow(dead_code)]
pub struct TaskInfo {
/// Task status in it's life cycle
status: TaskStatus,
/// The numbers of syscall called by task
syscall_times: [u32; MAX_SYSCALL_NUM],
/// Total running time of task
time: usize,
}
/// task exits and submit an exit code
pub fn sys_exit(exit_code: i32) -> ! {
println!("[kernel] Application exited with code {}", exit_code);
exit_current_and_run_next(); exit_current_and_run_next();
panic!("Unreachable in sys_exit!"); panic!("Unreachable in sys_exit!");
} }
/// current task gives up resources for other tasks
pub fn sys_yield() -> isize { pub fn sys_yield() -> isize {
suspend_current_and_run_next(); suspend_current_and_run_next();
0 0
} }
/// get time in milliseconds
pub fn sys_get_time() -> isize { pub fn sys_get_time() -> isize {
get_time() as isize get_time_ms() as isize
} }
/// YOUR JOB: Finish sys_task_info to pass testcases
pub fn sys_task_info(ti: *mut TaskInfo) -> isize {
unsafe {
(*ti).status = TASK_MANAGER.get_current_status();
(*ti).time = get_time();
(*ti).syscall_times = TASK_MANAGER.get_current_syscall_times()
}
trace!("kernel: sys_task_info");
0
}

View file

@ -1,16 +1,36 @@
//! Implementation of [`TaskContext`]
/// Task Context
#[derive(Copy, Clone)]
#[repr(C)] #[repr(C)]
pub struct TaskContext { pub struct TaskContext {
/// return address ( e.g. __restore ) of __switch ASM function
ra: usize, ra: usize,
/// kernel stack pointer of app
sp: usize,
/// callee saved registers: s 0..11
s: [usize; 12], s: [usize; 12],
} }
impl TaskContext { impl TaskContext {
pub fn goto_restore() -> Self { /// init task context
extern "C" { fn __restore(); } pub fn zero_init() -> Self {
Self {
ra: 0,
sp: 0,
s: [0; 12],
}
}
/// set task context {__restore ASM funciton, kernel stack, s_0..12 }
pub fn goto_restore(kstack_ptr: usize) -> Self {
extern "C" {
fn __restore();
}
Self { Self {
ra: __restore as usize, ra: __restore as usize,
sp: kstack_ptr,
s: [0; 12], s: [0; 12],
} }
} }
} }

View file

@ -1,126 +1,204 @@
//! Task management implementation
//!
//! Everything about task management, like starting and switching tasks is
//! implemented here.
//!
//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
//! all the tasks in the operating system.
//!
//! Be careful when you see `__switch` ASM function in `switch.S`. Control flow around this function
//! might not be what you expect.
mod context; mod context;
mod switch; mod switch;
#[allow(clippy::module_inception)]
mod task; mod task;
use crate::config::MAX_APP_NUM; use crate::config::{MAX_APP_NUM, MAX_SYSCALL_NUM};
use crate::loader::{get_num_app, init_app_cx}; use crate::loader::{get_num_app, init_app_cx};
use core::cell::RefCell; use crate::sbi::shutdown;
use crate::sync::UPSafeCell;
use lazy_static::*; use lazy_static::*;
use switch::__switch; use switch::__switch;
use task::{TaskControlBlock, TaskStatus}; pub use task::{TaskControlBlock, TaskStatus};
pub use context::TaskContext; pub use context::TaskContext;
/// The task manager, where all the tasks are managed.
///
/// Functions implemented on `TaskManager` deals with all task state transitions
/// and task context switching. For convenience, you can find wrappers around it
/// in the module level.
///
/// Most of `TaskManager` are hidden behind the field `inner`, to defer
/// borrowing checks to runtime. You can see examples on how to use `inner` in
/// existing functions on `TaskManager`.
pub struct TaskManager { pub struct TaskManager {
/// total number of tasks
num_app: usize, num_app: usize,
inner: RefCell<TaskManagerInner>, /// use inner value to get mutable access
inner: UPSafeCell<TaskManagerInner>,
} }
struct TaskManagerInner { /// Inner of Task Manager
pub struct TaskManagerInner {
/// task list
tasks: [TaskControlBlock; MAX_APP_NUM], tasks: [TaskControlBlock; MAX_APP_NUM],
/// id of current `Running` task
current_task: usize, current_task: usize,
} }
unsafe impl Sync for TaskManager {}
lazy_static! { lazy_static! {
/// Global variable: TASK_MANAGER
pub static ref TASK_MANAGER: TaskManager = { pub static ref TASK_MANAGER: TaskManager = {
let num_app = get_num_app(); let num_app = get_num_app();
let mut tasks = [ let mut tasks = [TaskControlBlock {
TaskControlBlock { task_cx_ptr: 0, task_status: TaskStatus::UnInit }; task_cx: TaskContext::zero_init(),
MAX_APP_NUM task_status: TaskStatus::UnInit,
]; syscall_times: [0; MAX_SYSCALL_NUM]
for i in 0..num_app { }; MAX_APP_NUM];
tasks[i].task_cx_ptr = init_app_cx(i) as * const _ as usize; for (i, task) in tasks.iter_mut().enumerate() {
tasks[i].task_status = TaskStatus::Ready; task.task_cx = TaskContext::goto_restore(init_app_cx(i));
task.task_status = TaskStatus::Ready;
} }
TaskManager { TaskManager {
num_app, num_app,
inner: RefCell::new(TaskManagerInner { inner: unsafe {
tasks, UPSafeCell::new(TaskManagerInner {
current_task: 0, tasks,
}), current_task: 0,
})
},
} }
}; };
} }
impl TaskManager { impl TaskManager {
fn run_first_task(&self) { /// Run the first task in task list.
self.inner.borrow_mut().tasks[0].task_status = TaskStatus::Running; ///
let next_task_cx = self.inner.borrow().tasks[0].get_task_cx_ptr2(); /// Generally, the first task in task list is an idle task (we call it zero process later).
/// But in ch3, we load apps statically, so the first task is a real app.
fn run_first_task(&self) -> ! {
let mut inner = self.inner.exclusive_access();
let task0 = &mut inner.tasks[0];
task0.task_status = TaskStatus::Running;
let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
drop(inner);
let mut _unused = TaskContext::zero_init();
// before this, we should drop local variables that must be dropped manually
unsafe { unsafe {
__switch( __switch(&mut _unused as *mut TaskContext, next_task_cx_ptr);
&0usize as *const _,
next_task_cx,
);
} }
panic!("unreachable in run_first_task!");
} }
/// Change the status of current `Running` task into `Ready`.
fn mark_current_suspended(&self) { fn mark_current_suspended(&self) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.exclusive_access();
let current = inner.current_task; let current = inner.current_task;
inner.tasks[current].task_status = TaskStatus::Ready; inner.tasks[current].task_status = TaskStatus::Ready;
} }
/// Change the status of current `Running` task into `Exited`.
fn mark_current_exited(&self) { fn mark_current_exited(&self) {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.exclusive_access();
let current = inner.current_task; let current = inner.current_task;
inner.tasks[current].task_status = TaskStatus::Exited; inner.tasks[current].task_status = TaskStatus::Exited;
} }
/// Find next task to run and return task id.
///
/// In this case, we only return the first `Ready` task in task list.
fn find_next_task(&self) -> Option<usize> { fn find_next_task(&self) -> Option<usize> {
let inner = self.inner.borrow(); let inner = self.inner.exclusive_access();
let current = inner.current_task; let current = inner.current_task;
(current + 1..current + self.num_app + 1) (current + 1..current + self.num_app + 1)
.map(|id| id % self.num_app) .map(|id| id % self.num_app)
.find(|id| { .find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
inner.tasks[*id].task_status == TaskStatus::Ready
})
} }
/// Switch current `Running` task to the task we have found,
/// or there is no `Ready` task and we can exit with all applications completed
fn run_next_task(&self) { fn run_next_task(&self) {
if let Some(next) = self.find_next_task() { if let Some(next) = self.find_next_task() {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.exclusive_access();
let current = inner.current_task; let current = inner.current_task;
inner.tasks[next].task_status = TaskStatus::Running; inner.tasks[next].task_status = TaskStatus::Running;
inner.current_task = next; inner.current_task = next;
let current_task_cx = inner.tasks[current].get_task_cx_ptr2(); let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
let next_task_cx = inner.tasks[next].get_task_cx_ptr2(); let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
core::mem::drop(inner); drop(inner);
// before this, we should drop local variables that must be dropped manually
unsafe { unsafe {
__switch( __switch(current_task_cx_ptr, next_task_cx_ptr);
current_task_cx,
next_task_cx,
);
} }
// go back to user mode
} else { } else {
panic!("All applications completed!"); println!("All applications completed!");
shutdown(false);
} }
} }
/// get current task id
pub fn get_current_id(&self) -> usize {
let inner = self.inner.exclusive_access();
inner.current_task
}
/// get current task status
pub fn get_current_status(&self) -> TaskStatus {
let inner = self.inner.exclusive_access();
let current = inner.current_task;
inner.tasks[current].task_status
}
/// get current task syscall times
pub fn get_current_syscall_times(&self) -> [u32; MAX_SYSCALL_NUM] {
let inner = self.inner.exclusive_access();
let current = inner.current_task;
return inner.tasks[current].syscall_times.clone();
}
/// record syscall
pub fn mark_syscall(&self, syscall_id: usize) {
if syscall_id >= MAX_SYSCALL_NUM {
return;
}
let mut inner = self.inner.exclusive_access();
let current = inner.current_task;
inner.tasks[current].syscall_times[syscall_id] += 1;
}
} }
/// run first task
pub fn run_first_task() { pub fn run_first_task() {
TASK_MANAGER.run_first_task(); TASK_MANAGER.run_first_task();
} }
/// rust next task
fn run_next_task() { fn run_next_task() {
TASK_MANAGER.run_next_task(); TASK_MANAGER.run_next_task();
} }
/// suspend current task
fn mark_current_suspended() { fn mark_current_suspended() {
TASK_MANAGER.mark_current_suspended(); TASK_MANAGER.mark_current_suspended();
} }
/// exit current task
fn mark_current_exited() { fn mark_current_exited() {
TASK_MANAGER.mark_current_exited(); TASK_MANAGER.mark_current_exited();
} }
/// suspend current task, then run next task
pub fn suspend_current_and_run_next() { pub fn suspend_current_and_run_next() {
mark_current_suspended(); mark_current_suspended();
run_next_task(); run_next_task();
} }
/// exit current task, then run next task
pub fn exit_current_and_run_next() { pub fn exit_current_and_run_next() {
mark_current_exited(); mark_current_exited();
run_next_task(); run_next_task();
} }

View file

@ -1,34 +1,34 @@
.altmacro .altmacro
.macro SAVE_SN n .macro SAVE_SN n
sd s\n, (\n+1)*8(sp) sd s\n, (\n+2)*8(a0)
.endm .endm
.macro LOAD_SN n .macro LOAD_SN n
ld s\n, (\n+1)*8(sp) ld s\n, (\n+2)*8(a1)
.endm .endm
.section .text .section .text
.globl __switch .globl __switch
__switch: __switch:
# __switch(current_task_cx: &*const TaskContext, next_task_cx: &*const TaskContext) # __switch(
# push TaskContext to current sp and save its address to where a0 points to # current_task_cx_ptr: *mut TaskContext,
addi sp, sp, -13*8 # next_task_cx_ptr: *const TaskContext
sd sp, 0(a0) # )
# fill TaskContext with ra & s0-s11 # save kernel stack of current task
sd ra, 0(sp) sd sp, 8(a0)
# save ra & s0~s11 of current execution
sd ra, 0(a0)
.set n, 0 .set n, 0
.rept 12 .rept 12
SAVE_SN %n SAVE_SN %n
.set n, n + 1 .set n, n + 1
.endr .endr
# ready for loading TaskContext a1 points to # restore ra & s0~s11 of next execution
ld sp, 0(a1) ld ra, 0(a1)
# load registers in the TaskContext
ld ra, 0(sp)
.set n, 0 .set n, 0
.rept 12 .rept 12
LOAD_SN %n LOAD_SN %n
.set n, n + 1 .set n, n + 1
.endr .endr
# pop TaskContext # restore kernel stack of next task
addi sp, sp, 13*8 ld sp, 8(a1)
ret ret

View file

@ -1,5 +1,17 @@
//! Rust wrapper around `__switch`.
//!
//! Switching to a different task's context happens here. The actual
//! implementation must not be in Rust and (essentially) has to be in assembly
//! language (Do you know why?), so this module really is just a wrapper around
//! `switch.S`.
use super::TaskContext;
use core::arch::global_asm;
global_asm!(include_str!("switch.S")); global_asm!(include_str!("switch.S"));
extern "C" { extern "C" {
pub fn __switch(current_task_cx: *const usize, next_task_cx: *const usize); /// Switch to the context of `next_task_cx_ptr`, saving the current context
/// in `current_task_cx_ptr`.
pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext);
} }

View file

@ -1,18 +1,29 @@
//! Types related to task management
use crate::config::MAX_SYSCALL_NUM;
use super::TaskContext;
/// The task control block (TCB) of a task.
#[derive(Copy, Clone)]
pub struct TaskControlBlock { pub struct TaskControlBlock {
pub task_cx_ptr: usize, /// The task status in it's lifecycle
pub task_status: TaskStatus, pub task_status: TaskStatus,
/// The task context
pub task_cx: TaskContext,
/// The numbers of syscall called by task
pub syscall_times: [u32; MAX_SYSCALL_NUM],
} }
impl TaskControlBlock { /// The status of a task
pub fn get_task_cx_ptr2(&self) -> *const usize {
&self.task_cx_ptr as *const usize
}
}
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum TaskStatus { pub enum TaskStatus {
/// uninitialized
UnInit, UnInit,
/// ready to run
Ready, Ready,
/// running
Running, Running,
/// exited
Exited, Exited,
} }

View file

@ -1,13 +1,23 @@
use riscv::register::time; //! RISC-V timer-related functionality
use crate::config::CLOCK_FREQ;
use crate::sbi::set_timer; use crate::sbi::set_timer;
use crate::config::CPU_FREQ; use riscv::register::time;
const TICKS_PER_SEC: usize = 100; const TICKS_PER_SEC: usize = 100;
const MSEC_PER_SEC: usize = 1000;
/// read the `mtime` register
pub fn get_time() -> usize { pub fn get_time() -> usize {
time::read() time::read()
} }
/// get current time in milliseconds
pub fn get_time_ms() -> usize {
time::read() / (CLOCK_FREQ / MSEC_PER_SEC)
}
/// set the next timer interrupt
pub fn set_next_trigger() { pub fn set_next_trigger() {
set_timer(get_time() + CPU_FREQ / TICKS_PER_SEC); set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
} }

View file

@ -1,23 +1,30 @@
use riscv::register::sstatus::{Sstatus, self, SPP}; use riscv::register::sstatus::{self, Sstatus, SPP};
/// Trap Context
#[repr(C)] #[repr(C)]
pub struct TrapContext { pub struct TrapContext {
/// general regs[0..31]
pub x: [usize; 32], pub x: [usize; 32],
/// CSR sstatus
pub sstatus: Sstatus, pub sstatus: Sstatus,
/// CSR sepc
pub sepc: usize, pub sepc: usize,
} }
impl TrapContext { impl TrapContext {
pub fn set_sp(&mut self, sp: usize) { self.x[2] = sp; } /// set stack pointer to x_2 reg (sp)
pub fn set_sp(&mut self, sp: usize) {
self.x[2] = sp;
}
/// init app context
pub fn app_init_context(entry: usize, sp: usize) -> Self { pub fn app_init_context(entry: usize, sp: usize) -> Self {
let mut sstatus = sstatus::read(); let mut sstatus = sstatus::read(); // CSR sstatus
sstatus.set_spp(SPP::User); sstatus.set_spp(SPP::User); //previous privilege mode: user mode
let mut cx = Self { let mut cx = Self {
x: [0; 32], x: [0; 32],
sstatus, sstatus,
sepc: entry, sepc: entry, // entry point of app
}; };
cx.set_sp(sp); cx.set_sp(sp); // app's user stack pointer
cx cx // return initial Trap Context of app
} }
} }

View file

@ -1,58 +1,64 @@
//! Trap handling functionality
//!
//! For rCore, we have a single trap entry point, namely `__alltraps`. At
//! initialization in [`init()`], we set the `stvec` CSR to point to it.
//!
//! All traps go through `__alltraps`, which is defined in `trap.S`. The
//! assembly language code does just enough work restore the kernel space
//! context, ensuring that Rust code safely runs, and transfers control to
//! [`trap_handler()`].
//!
//! It then calls different functionality based on what exactly the exception
//! was. For example, timer interrupts trigger task preemption, and syscalls go
//! to [`syscall()`].
mod context; mod context;
use crate::syscall::syscall;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::set_next_trigger;
use core::arch::global_asm;
use riscv::register::{ use riscv::register::{
mtvec::TrapMode, mtvec::TrapMode,
stvec, scause::{self, Exception, Interrupt, Trap},
scause::{ sie, stval, stvec,
self,
Trap,
Exception,
Interrupt,
},
stval,
sstatus,
sie,
}; };
use crate::syscall::syscall;
use crate::task::{
exit_current_and_run_next,
suspend_current_and_run_next,
};
use crate::timer::set_next_trigger;
global_asm!(include_str!("trap.S")); global_asm!(include_str!("trap.S"));
/// initialize CSR `stvec` as the entry of `__alltraps`
pub fn init() { pub fn init() {
extern "C" { fn __alltraps(); } extern "C" {
fn __alltraps();
}
unsafe { unsafe {
stvec::write(__alltraps as usize, TrapMode::Direct); stvec::write(__alltraps as usize, TrapMode::Direct);
} }
} }
pub fn enable_interrupt() { /// timer interrupt enabled
unsafe { sstatus::set_sie(); }
}
pub fn enable_timer_interrupt() { pub fn enable_timer_interrupt() {
unsafe { sie::set_stimer(); } unsafe {
sie::set_stimer();
}
} }
#[no_mangle] #[no_mangle]
/// handle an interrupt, exception, or system call from user space
pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext { pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
let scause = scause::read(); let scause = scause::read(); // get trap cause
let stval = stval::read(); let stval = stval::read(); // get extra value
match scause.cause() { match scause.cause() {
Trap::Exception(Exception::UserEnvCall) => { Trap::Exception(Exception::UserEnvCall) => {
cx.sepc += 4; cx.sepc += 4;
cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize; cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
} }
Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => {
Trap::Exception(Exception::StorePageFault) => { println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", stval, cx.sepc);
println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.", stval, cx.sepc);
exit_current_and_run_next(); exit_current_and_run_next();
} }
Trap::Exception(Exception::IllegalInstruction) => { Trap::Exception(Exception::IllegalInstruction) => {
println!("[kernel] IllegalInstruction in application, core dumped."); println!("[kernel] IllegalInstruction in application, kernel killed it.");
exit_current_and_run_next(); exit_current_and_run_next();
} }
Trap::Interrupt(Interrupt::SupervisorTimer) => { Trap::Interrupt(Interrupt::SupervisorTimer) => {
@ -60,10 +66,14 @@ pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext {
suspend_current_and_run_next(); suspend_current_and_run_next();
} }
_ => { _ => {
panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval); panic!(
"Unsupported trap {:?}, stval = {:#x}!",
scause.cause(),
stval
);
} }
} }
cx cx
} }
pub use context::TrapContext; pub use context::TrapContext;

View file

@ -1 +0,0 @@
nightly-2020-11-01

6
rust-toolchain.toml Normal file
View file

@ -0,0 +1,6 @@
[toolchain]
profile = "minimal"
# use the nightly version of the last stable toolchain, see <https://forge.rust-lang.org/>
channel = "nightly-2024-05-01"
components = ["rust-src", "llvm-tools", "rustfmt", "clippy"]
targets = ["riscv64gc-unknown-none-elf"]

File diff suppressed because one or more lines are too long

View file

@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf"
[target.riscv64gc-unknown-none-elf] [target.riscv64gc-unknown-none-elf]
rustflags = [ rustflags = [
"-Clink-args=-Tsrc/linker.ld", "-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
] ]

5
user/Cargo.lock generated
View file

@ -1,5 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "user_lib"
version = "0.1.0"

View file

@ -7,3 +7,10 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
[profile.release]
debug = true
# [features]
# board_qemu = []
# board_k210 = []

View file

@ -1,5 +1,5 @@
TARGET := riscv64gc-unknown-none-elf TARGET := riscv64gc-unknown-none-elf
MODE := debug MODE := release
APP_DIR := src/bin APP_DIR := src/bin
TARGET_DIR := target/$(TARGET)/$(MODE) TARGET_DIR := target/$(TARGET)/$(MODE)
APPS := $(wildcard $(APP_DIR)/*.rs) APPS := $(wildcard $(APP_DIR)/*.rs)
@ -13,7 +13,7 @@ elf: $(APPS)
@python3 build.py @python3 build.py
binary: elf binary: elf
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) @$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
build: binary build: binary

View file

@ -1,6 +1,6 @@
import os import os
base_address = 0x80100000 base_address = 0x80400000
step = 0x20000 step = 0x20000
linker = 'src/linker.ld' linker = 'src/linker.ld'
@ -18,7 +18,7 @@ for app in apps:
lines.append(line) lines.append(line)
with open(linker, 'w+') as f: with open(linker, 'w+') as f:
f.writelines(lines) f.writelines(lines)
os.system('cargo build --bin %s' % app) os.system('cargo build --bin %s --release' % app)
print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id))) print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id)))
with open(linker, 'w+') as f: with open(linker, 'w+') as f:
f.writelines(lines_before) f.writelines(lines_before)

View file

@ -1,28 +1,16 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#[macro_use]
extern crate user_lib; extern crate user_lib;
const LEN: usize = 100; use user_lib::{
println, task_info, TaskInfo,
};
#[no_mangle] #[no_mangle]
fn main() -> i32 { pub fn main() -> usize {
let p = 3u64; let info = TaskInfo::new();
let m = 998244353u64; assert_eq!(0, task_info(&info));
let iter: usize = 100000; println!("task_info {:?}", info);
let mut s = [0u64; LEN];
let mut cur = 0usize;
s[cur] = 1;
for i in 1..=iter {
let next = if cur + 1 == LEN { 0 } else { cur + 1 };
s[next] = s[cur] * p % m;
cur = next;
if i % 10000 == 0 {
println!("power_3 [{}/{}]", i, iter);
}
}
println!("{}^{} = {}", p, iter, s[cur]);
println!("Test power_3 OK!");
0 0
} }

View file

@ -10,7 +10,7 @@ const LEN: usize = 100;
fn main() -> i32 { fn main() -> i32 {
let p = 5u64; let p = 5u64;
let m = 998244353u64; let m = 998244353u64;
let iter: usize = 70000; let iter: usize = 140000;
let mut s = [0u64; LEN]; let mut s = [0u64; LEN];
let mut cur = 0usize; let mut cur = 0usize;
s[cur] = 1; s[cur] = 1;
@ -22,7 +22,7 @@ fn main() -> i32 {
println!("power_5 [{}/{}]", i, iter); println!("power_5 [{}/{}]", i, iter);
} }
} }
println!("{}^{} = {}", p, iter, s[cur]); println!("{}^{} = {}(MOD {})", p, iter, s[cur], m);
println!("Test power_5 OK!"); println!("Test power_5 OK!");
0 0
} }

View file

@ -10,7 +10,7 @@ const LEN: usize = 100;
fn main() -> i32 { fn main() -> i32 {
let p = 7u64; let p = 7u64;
let m = 998244353u64; let m = 998244353u64;
let iter: usize = 80000; let iter: usize = 160000;
let mut s = [0u64; LEN]; let mut s = [0u64; LEN];
let mut cur = 0usize; let mut cur = 0usize;
s[cur] = 1; s[cur] = 1;
@ -22,7 +22,7 @@ fn main() -> i32 {
println!("power_7 [{}/{}]", i, iter); println!("power_7 [{}/{}]", i, iter);
} }
} }
println!("{}^{} = {}", p, iter, s[cur]); println!("{}^{} = {}(MOD {})", p, iter, s[cur], m);
println!("Test power_7 OK!"); println!("Test power_7 OK!");
0 0
} }

View file

@ -4,15 +4,15 @@
#[macro_use] #[macro_use]
extern crate user_lib; extern crate user_lib;
use user_lib::{sys_get_time, sys_yield}; use user_lib::{get_time, yield_};
#[no_mangle] #[no_mangle]
fn main() -> i32 { fn main() -> i32 {
let current_timer = sys_get_time(); let current_timer = get_time();
let wait_for = current_timer + 10000000; let wait_for = current_timer + 3000;
while sys_get_time() < wait_for { while get_time() < wait_for {
sys_yield(); yield_();
} }
println!("Test sleep OK!"); println!("Test sleep OK!");
0 0
} }

View file

@ -0,0 +1,16 @@
#![no_std]
#![no_main]
extern crate user_lib;
use user_lib::{
println, task_info, TaskInfo,
};
#[no_mangle]
pub fn main() -> usize {
let info = TaskInfo::new();
assert_eq!(0, task_info(&info));
println!("task_info {:?}", info);
0
}

View file

@ -1,11 +1,13 @@
use super::write;
use core::fmt::{self, Write}; use core::fmt::{self, Write};
use crate::syscall::{STDOUT, sys_write};
struct Stdout; struct Stdout;
const STDOUT: usize = 1;
impl Write for Stdout { impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result { fn write_str(&mut self, s: &str) -> fmt::Result {
sys_write(STDOUT, s.as_bytes()); write(STDOUT, s.as_bytes());
Ok(()) Ok(())
} }
} }
@ -26,4 +28,4 @@ macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => { ($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
} }
} }

View file

@ -2,9 +2,14 @@
fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
let err = panic_info.message().unwrap(); let err = panic_info.message().unwrap();
if let Some(location) = panic_info.location() { if let Some(location) = panic_info.location() {
println!("Panicked at {}:{}, {}", location.file(), location.line(), err); println!(
"Panicked at {}:{}, {}",
location.file(),
location.line(),
err
);
} else { } else {
println!("Panicked: {}", err); println!("Panicked: {}", err);
} }
loop {} loop {}
} }

View file

@ -1,18 +1,17 @@
#![no_std] #![no_std]
#![feature(llvm_asm)]
#![feature(linkage)] #![feature(linkage)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#[macro_use] #[macro_use]
pub mod console; pub mod console;
mod syscall;
mod lang_items; mod lang_items;
mod syscall;
#[no_mangle] #[no_mangle]
#[link_section = ".text.entry"] #[link_section = ".text.entry"]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
clear_bss(); clear_bss();
syscall::sys_exit(main()); exit(main());
panic!("unreachable after sys_exit!"); panic!("unreachable after sys_exit!");
} }
@ -27,9 +26,54 @@ fn clear_bss() {
fn start_bss(); fn start_bss();
fn end_bss(); fn end_bss();
} }
(start_bss as usize..end_bss as usize).for_each(|addr| { (start_bss as usize..end_bss as usize).for_each(|addr| unsafe {
unsafe { (addr as *mut u8).write_volatile(0); } (addr as *mut u8).write_volatile(0);
}); });
} }
pub use syscall::*; use syscall::*;
pub fn write(fd: usize, buf: &[u8]) -> isize {
sys_write(fd, buf)
}
pub fn exit(exit_code: i32) -> isize {
sys_exit(exit_code)
}
pub fn yield_() -> isize {
sys_yield()
}
pub fn get_time() -> isize {
sys_get_time()
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TaskStatus {
UnInit,
Ready,
Running,
Exited,
}
const MAX_SYSCALL_NUM: usize = 500;
#[derive(Debug)]
pub struct TaskInfo {
pub status: TaskStatus,
pub syscall_times: [u32; MAX_SYSCALL_NUM],
pub time: usize,
}
impl TaskInfo {
pub fn new() -> Self {
TaskInfo {
status: TaskStatus::UnInit,
syscall_times: [0; MAX_SYSCALL_NUM],
time: 0,
}
}
}
pub fn task_info(info: &TaskInfo) -> isize {
sys_task_info(info)
}

View file

@ -2,7 +2,7 @@
OUTPUT_ARCH(riscv) OUTPUT_ARCH(riscv)
ENTRY(_start) ENTRY(_start)
BASE_ADDRESS = 0x80100000; BASE_ADDRESS = 0x80400000;
SECTIONS SECTIONS
{ {
@ -13,13 +13,16 @@ SECTIONS
} }
.rodata : { .rodata : {
*(.rodata .rodata.*) *(.rodata .rodata.*)
*(.srodata .srodata.*)
} }
.data : { .data : {
*(.data .data.*) *(.data .data.*)
*(.sdata .sdata.*)
} }
.bss : { .bss : {
start_bss = .; start_bss = .;
*(.bss .bss.*) *(.bss .bss.*)
*(.sbss .sbss.*)
end_bss = .; end_bss = .;
} }
/DISCARD/ : { /DISCARD/ : {

View file

@ -1,18 +1,22 @@
pub const STDOUT: usize = 1; use core::arch::asm;
use crate::TaskInfo;
const SYSCALL_WRITE: usize = 64; const SYSCALL_WRITE: usize = 64;
const SYSCALL_EXIT: usize = 93; const SYSCALL_EXIT: usize = 93;
const SYSCALL_YIELD: usize = 124; const SYSCALL_YIELD: usize = 124;
const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GET_TIME: usize = 169;
pub const SYSCALL_TASK_INFO: usize = 410;
fn syscall(id: usize, args: [usize; 3]) -> isize { fn syscall(id: usize, args: [usize; 3]) -> isize {
let mut ret: isize; let mut ret: isize;
unsafe { unsafe {
llvm_asm!("ecall" asm!(
: "={x10}" (ret) "ecall",
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id) inlateout("x10") args[0] => ret,
: "memory" in("x11") args[1],
: "volatile" in("x12") args[2],
in("x17") id
); );
} }
ret ret
@ -22,8 +26,8 @@ pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
} }
pub fn sys_exit(xstate: i32) -> isize { pub fn sys_exit(exit_code: i32) -> isize {
syscall(SYSCALL_EXIT, [xstate as usize, 0, 0]) syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0])
} }
pub fn sys_yield() -> isize { pub fn sys_yield() -> isize {
@ -32,4 +36,8 @@ pub fn sys_yield() -> isize {
pub fn sys_get_time() -> isize { pub fn sys_get_time() -> isize {
syscall(SYSCALL_GET_TIME, [0, 0, 0]) syscall(SYSCALL_GET_TIME, [0, 0, 0])
}
pub fn sys_task_info(info: &TaskInfo) -> isize {
syscall(SYSCALL_TASK_INFO, [info as *const _ as usize, 0, 0])
} }