Compare commits
275 Commits
Author | SHA1 | Date |
---|---|---|
|
21bae8b034 | 3 years ago |
|
a773a977e7 | 3 years ago |
|
d52256baa9 | 3 years ago |
|
7c83dd49e7 | 3 years ago |
|
9a95c7dcb4 | 3 years ago |
|
cc10cd10eb | 3 years ago |
|
b732da242d | 3 years ago |
|
ca997784ab | 3 years ago |
|
a85cb98743 | 3 years ago |
|
31a2873a61 | 3 years ago |
|
1371aab236 | 3 years ago |
|
e615ae4483 | 3 years ago |
|
18d4a89ab7 | 3 years ago |
|
c234d1417b | 3 years ago |
|
cf00c3ae5d | 3 years ago |
|
87cd106b67 | 3 years ago |
|
3d5e9ed9e6 | 3 years ago |
|
2bd092dd64 | 3 years ago |
|
07675fcd00 | 3 years ago |
|
c9a288c110 | 3 years ago |
|
8e73480c99 | 3 years ago |
|
8657b656e5 | 3 years ago |
|
1afa429e93 | 3 years ago |
|
964445e59c | 3 years ago |
|
df36cbe657 | 3 years ago |
|
9d2690f8d9 | 3 years ago |
|
a8e4c13e99 | 3 years ago |
|
a70a983497 | 3 years ago |
|
ca1d7a07b8 | 3 years ago |
|
3b7c4b1cdc | 3 years ago |
|
91c29d0b70 | 3 years ago |
|
a61d34e2b1 | 3 years ago |
|
858334bc02 | 3 years ago |
|
b6978bf6ec | 3 years ago |
|
aec0a6ebe5 | 3 years ago |
|
34e1a3def8 | 3 years ago |
|
d6a3de2510 | 3 years ago |
|
3a44decf58 | 3 years ago |
|
923638023d | 3 years ago |
|
c009012d85 | 3 years ago |
|
21d57c1396 | 3 years ago |
|
334d868a5c | 3 years ago |
|
fbe8e39b38 | 3 years ago |
|
53034d7c33 | 3 years ago |
|
d3bd19867c | 4 years ago |
|
6ef566faac | 4 years ago |
|
f0f7f6fcaa | 4 years ago |
|
89b9d7c161 | 4 years ago |
|
e6b6251979 | 4 years ago |
|
61152471b7 | 4 years ago |
|
be2ed8fa37 | 4 years ago |
|
6f09af2c0f | 4 years ago |
|
48bdefe6b8 | 4 years ago |
|
29d6d26644 | 4 years ago |
|
1c55663478 | 4 years ago |
|
740730e7f7 | 4 years ago |
|
f65451bc72 | 4 years ago |
|
e8686526bb | 4 years ago |
|
514c110a28 | 4 years ago |
|
40f37501af | 4 years ago |
|
24e530935f | 4 years ago |
|
c358424fae | 4 years ago |
|
2ac621972c | 4 years ago |
|
aa104ecd54 | 4 years ago |
|
d81560a492 | 4 years ago |
|
cba8d9e6d8 | 4 years ago |
|
72d6b38fca | 4 years ago |
|
cf6d905ac4 | 4 years ago |
|
abd9d361e4 | 4 years ago |
|
045c47e4ef | 4 years ago |
|
97fdd4f2a2 | 4 years ago |
|
22db3123d8 | 4 years ago |
|
f6b210adbe | 4 years ago |
|
760615095e | 4 years ago |
|
06243fac76 | 4 years ago |
|
713e78ea91 | 4 years ago |
|
1c0bbf4404 | 4 years ago |
|
90796450fe | 4 years ago |
|
4ad64e83f6 | 4 years ago |
|
3dc6d9c97c | 4 years ago |
|
b77b108a28 | 4 years ago |
|
1baf177f9e | 4 years ago |
|
a3f9b5fea9 | 4 years ago |
|
11a389592a | 4 years ago |
|
7caf43bbbf | 4 years ago |
|
bb5dca2158 | 4 years ago |
|
3d2909e990 | 4 years ago |
|
2041a7c0d4 | 4 years ago |
|
cd6754a7df | 4 years ago |
|
5753a09366 | 4 years ago |
|
aedd7f5612 | 4 years ago |
|
b851f8d743 | 4 years ago |
|
8adfc90db9 | 4 years ago |
|
7225254d8a | 4 years ago |
|
b0fad5aca3 | 4 years ago |
|
43c6b7cf01 | 4 years ago |
|
136e26ae6c | 4 years ago |
|
24b3c82b8a | 4 years ago |
|
1493ec9459 | 4 years ago |
|
c599a31dd0 | 4 years ago |
|
6693de9611 | 4 years ago |
|
26bbec6320 | 4 years ago |
|
91a758d657 | 4 years ago |
|
03ea339e58 | 4 years ago |
|
638eb8666a | 4 years ago |
|
bf69560f9b | 4 years ago |
|
940073e9f3 | 4 years ago |
|
03151ac124 | 4 years ago |
|
46900cc9af | 4 years ago |
|
e68f261ed6 | 4 years ago |
|
cf9218113f | 4 years ago |
|
315e61da1a | 4 years ago |
|
1590923666 | 4 years ago |
|
aaed6006aa | 4 years ago |
|
95431917ed | 4 years ago |
|
b5f7fb6c45 | 4 years ago |
|
f04110e6e3 | 4 years ago |
|
a7b981b14d | 4 years ago |
|
3a6e4e38f2 | 4 years ago |
|
81559f70b9 | 4 years ago |
|
0a8bd2c3fd | 4 years ago |
|
01098eb113 | 4 years ago |
|
e7f120bab3 | 4 years ago |
|
7a36cdb77f | 4 years ago |
|
f7ed29756c | 4 years ago |
|
bfa6a80732 | 4 years ago |
|
ff69985d79 | 4 years ago |
|
a57d470edc | 4 years ago |
|
2e76499676 | 4 years ago |
|
91d4d6d40c | 4 years ago |
|
a09429b32c | 4 years ago |
|
60477da9be | 4 years ago |
|
a1cda4aa59 | 4 years ago |
|
d302a0d616 | 4 years ago |
|
3554e20dc6 | 4 years ago |
|
d57a160b32 | 4 years ago |
|
818363f2ca | 4 years ago |
|
195816ce2c | 4 years ago |
|
613f77c5a4 | 4 years ago |
|
230e4442d0 | 4 years ago |
|
6298f57a87 | 4 years ago |
|
a589179adc | 4 years ago |
|
1e2e83e886 | 4 years ago |
|
3e47a0dbee | 4 years ago |
|
a43dbc4e34 | 4 years ago |
|
dd2be93ef0 | 4 years ago |
|
e643af7937 | 4 years ago |
|
e55c5200c5 | 4 years ago |
|
67372ac84d | 4 years ago |
|
cfa3819bee | 4 years ago |
|
33395156f9 | 4 years ago |
|
3e1c12b6a1 | 4 years ago |
|
a7346c96b4 | 4 years ago |
|
8a1c96d963 | 4 years ago |
|
01280fc833 | 4 years ago |
|
90d351bfe8 | 4 years ago |
|
4668911483 | 4 years ago |
|
9196963e44 | 4 years ago |
|
3f5308f46c | 4 years ago |
|
e0a3933b1c | 4 years ago |
|
35cc3d6e2f | 4 years ago |
|
b8f1db4aa3 | 4 years ago |
|
1346fb1a1f | 4 years ago |
|
12c6c53af5 | 4 years ago |
|
e8a0682cf8 | 4 years ago |
|
40508d68ab | 4 years ago |
|
920d077a66 | 4 years ago |
|
8b27976d23 | 4 years ago |
|
84a55c17c4 | 4 years ago |
|
c6a262d215 | 4 years ago |
|
69933e2985 | 4 years ago |
|
00084f5165 | 5 years ago |
|
87743bac4d | 5 years ago |
|
04114ad949 | 5 years ago |
|
a5c4f3228e | 5 years ago |
|
1b6f2c4c1e | 5 years ago |
|
9b65abcfa8 | 5 years ago |
|
caac1beb0a | 5 years ago |
|
fd75ac027a | 5 years ago |
|
d97b0a20ab | 5 years ago |
|
e04394af56 | 5 years ago |
|
ea4222bed0 | 5 years ago |
|
b28d94b226 | 5 years ago |
|
b6626d534b | 5 years ago |
|
0d9024b5bd | 5 years ago |
|
b659f10d22 | 5 years ago |
|
b8240fac5a | 5 years ago |
|
29d02e7442 | 5 years ago |
|
4b6bd7deaa | 5 years ago |
|
1cc75ded25 | 5 years ago |
|
dddd04b683 | 5 years ago |
|
c01b3289c6 | 5 years ago |
|
eb5ef8e956 | 5 years ago |
|
33373aa20d | 5 years ago |
|
5afed009c0 | 5 years ago |
|
07467287da | 5 years ago |
|
a6c7b52283 | 5 years ago |
|
654f6eb959 | 5 years ago |
|
18da8a3879 | 5 years ago |
|
4f7db8b92e | 5 years ago |
|
6267c5d922 | 5 years ago |
|
9d7882a73d | 5 years ago |
|
9772373743 | 5 years ago |
|
e588d40d70 | 5 years ago |
|
c14392ec60 | 5 years ago |
|
c763a3be96 | 5 years ago |
|
c65ce846ce | 5 years ago |
|
e9453d7834 | 5 years ago |
|
3b8b42ec4c | 5 years ago |
|
05d34106ce | 5 years ago |
|
1a7261d86d | 5 years ago |
|
dbe56c1362 | 5 years ago |
|
12747d71b4 | 5 years ago |
|
fd638d5388 | 5 years ago |
|
208b827b5c | 5 years ago |
|
b674fd5a77 | 5 years ago |
|
fdab87d2ed | 5 years ago |
|
7c0d66c58f | 5 years ago |
|
606abbe6a1 | 5 years ago |
|
1bafe9615f | 5 years ago |
|
ae9eecf97b | 5 years ago |
|
ea515323d3 | 5 years ago |
|
ecccea65a0 | 5 years ago |
|
d13380603b | 5 years ago |
|
dbaa7c8c6c | 5 years ago |
|
5ea07840ce | 5 years ago |
|
c351635e2f | 5 years ago |
|
459efec595 | 5 years ago |
|
af02e68d19 | 5 years ago |
|
84900e8b94 | 5 years ago |
|
63bccc4e8f | 5 years ago |
|
914f042617 | 5 years ago |
|
f754326d0a | 5 years ago |
|
8e178c0080 | 5 years ago |
|
2e6734027d | 5 years ago |
|
f5c029d3db | 5 years ago |
|
84b10893d4 | 5 years ago |
|
244c0ee84d | 5 years ago |
|
9165e64d1b | 5 years ago |
|
7a1bc49eb3 | 5 years ago |
|
3a2f89fc67 | 5 years ago |
|
3642f9c56d | 5 years ago |
|
81bef97f09 | 5 years ago |
|
eddbc8954c | 5 years ago |
|
1bc53c0b5f | 5 years ago |
|
0c12d43b61 | 5 years ago |
|
949f2095bb | 5 years ago |
|
8ee3671269 | 5 years ago |
|
f54573ae15 | 5 years ago |
|
064f1cb5cb | 5 years ago |
|
3625b7578d | 5 years ago |
|
0011fe9477 | 5 years ago |
|
1256085d36 | 5 years ago |
|
2fd70b0ff4 | 5 years ago |
|
4b01ff7cc4 | 5 years ago |
|
63aaa9d0a1 | 5 years ago |
|
4590f233b5 | 5 years ago |
|
91043b08cd | 5 years ago |
|
e552f3cfca | 5 years ago |
|
f53f5e4b98 | 5 years ago |
|
5e5ed05399 | 5 years ago |
|
3b920ac8f5 | 5 years ago |
|
e9949e5bd4 | 5 years ago |
|
0d0c7255b6 | 5 years ago |
|
f613fa122c | 5 years ago |
|
d1fcf2fc9f | 5 years ago |
|
7577c4f637 | 5 years ago |
|
db8fbdc512 | 5 years ago |
|
db0c174f52 | 5 years ago |
|
c701cd00ce | 5 years ago |
|
49050aca3f | 5 years ago |
|
406acaae2c | 5 years ago |
|
721d0e5ecf | 5 years ago |
|
5370f725be | 5 years ago |
|
29f4683ad8 | 5 years ago |
@ -0,0 +1 @@
|
||||
*/*
|
@ -0,0 +1,66 @@
|
||||
name: Build Rust Doc And Run tests
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build-doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-04-11
|
||||
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: nightly-2022-04-11
|
||||
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
|
@ -1 +1,13 @@
|
||||
.idea/*
|
||||
.*/*
|
||||
!.github/*
|
||||
!.vscode/settings.json
|
||||
|
||||
**/target/
|
||||
**/Cargo.lock
|
||||
|
||||
os/src/link_app.S
|
||||
os/src/linker.ld
|
||||
os/last-*
|
||||
os/.gdb_history
|
||||
tools/
|
||||
pushall.sh
|
||||
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
// 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
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
FROM ubuntu:18.04
|
||||
LABEL maintainer="dinghao188" \
|
||||
version="1.1" \
|
||||
description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3"
|
||||
|
||||
#install some deps
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y curl wget autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
|
||||
gawk build-essential bison flex texinfo gperf libtool patchutils bc xz-utils \
|
||||
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3
|
||||
|
||||
#install rust and qemu
|
||||
RUN set -x; \
|
||||
RUSTUP='/root/rustup.sh' \
|
||||
&& cd $HOME \
|
||||
#install rust
|
||||
&& curl https://sh.rustup.rs -sSf > $RUSTUP && chmod +x $RUSTUP \
|
||||
&& $RUSTUP -y --default-toolchain nightly --profile minimal \
|
||||
|
||||
#compile qemu
|
||||
&& wget https://ftp.osuosl.org/pub/blfs/conglomeration/qemu/qemu-5.0.0.tar.xz \
|
||||
&& tar xvJf qemu-5.0.0.tar.xz \
|
||||
&& cd qemu-5.0.0 \
|
||||
&& ./configure --target-list=riscv64-softmmu,riscv64-linux-user \
|
||||
&& make -j$(nproc) install \
|
||||
&& cd $HOME && rm -rf qemu-5.0.0 qemu-5.0.0.tar.xz
|
||||
|
||||
#for chinese network
|
||||
RUN set -x; \
|
||||
APT_CONF='/etc/apt/sources.list'; \
|
||||
CARGO_CONF='/root/.cargo/config'; \
|
||||
BASHRC='/root/.bashrc' \
|
||||
&& echo 'export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static' >> $BASHRC \
|
||||
&& echo 'export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup' >> $BASHRC \
|
||||
&& touch $CARGO_CONF \
|
||||
&& echo '[source.crates-io]' > $CARGO_CONF \
|
||||
&& echo "replace-with = 'ustc'" >> $CARGO_CONF \
|
||||
&& echo '[source.ustc]' >> $CARGO_CONF \
|
||||
&& echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_CONF
|
@ -0,0 +1,10 @@
|
||||
DOCKER_NAME ?= dinghao188/rcore-tutorial
|
||||
.PHONY: docker build_docker
|
||||
|
||||
docker:
|
||||
docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME}
|
||||
|
||||
build_docker:
|
||||
docker build -t ${DOCKER_NAME} .
|
||||
fmt:
|
||||
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..
|
@ -1,2 +1,300 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.
|
||||
rCore-Tutorial version 3.5. 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
|
||||
- 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.
|
||||
|
||||
## 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 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.
|
||||
|
||||
## 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.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.
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
# Tutorial 第一章测试用例
|
||||
|
||||
第一章只需要在 RV64 裸机平台运行一个嵌入式应用,因此只要能够在 SBI 的帮助下在屏幕上输出一行字符串就算成功。
|
@ -1,2 +0,0 @@
|
||||
target/*
|
||||
Cargo.lock
|
@ -1,21 +0,0 @@
|
||||
TARGET := riscv64gc-unknown-none-elf
|
||||
MODE := release
|
||||
APP_DIR := src/bin
|
||||
TARGET_DIR := target/$(TARGET)/$(MODE)
|
||||
APPS := $(wildcard $(APP_DIR)/*.rs)
|
||||
ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
|
||||
BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
|
||||
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
|
||||
elf:
|
||||
@cargo build --release
|
||||
@echo $(APPS)
|
||||
@echo $(ELFS)
|
||||
@echo $(BINS)
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
@ -1,20 +0,0 @@
|
||||
# Tutorial 第二章测试用例
|
||||
|
||||
第二章我们需要实现一个批处理系统。在 `src/bin` 目录中,我们可以找到三个应用程序:
|
||||
|
||||
* `00hello_world`
|
||||
* `01store_fault`
|
||||
* `02power`
|
||||
|
||||
我们需要按照编号从小到大的顺序去加载并运行它们。
|
||||
|
||||
应用被设计为运行在用户模式,批处理系统应运行在监督模式,它们都直接访问物理内存。
|
||||
|
||||
三个应用被需要被加载到同一个物理地址。
|
||||
|
||||
本章需要实现的系统调用:
|
||||
|
||||
* `sys_write` 用于向屏幕输出字符串;
|
||||
* `sys_exit` 用于告知批处理系统当前应用退出,应切换到下一个应用。
|
||||
|
||||
注意:应用 `01store_fault` 会访问非法的物理地址,批处理系统需要杀死它并能够正常运行序列中的下一个应用 `02power`。
|
@ -1,11 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
println!("Hello, world!");
|
||||
0
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const SIZE: usize = 10;
|
||||
const P: u32 = 3;
|
||||
const STEP: usize = 100000;
|
||||
const MOD: u32 = 10007;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let mut pow = [0u32; SIZE];
|
||||
let mut index: usize = 0;
|
||||
pow[index] = 1;
|
||||
for i in 1..=STEP {
|
||||
let last = pow[index];
|
||||
index = (index + 1) % SIZE;
|
||||
pow[index] = last * P % MOD;
|
||||
if i % 10000 == 0 {
|
||||
println!("{}^{}={}", P, i, pow[index]);
|
||||
}
|
||||
}
|
||||
println!("Test power OK!");
|
||||
0
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
use core::fmt::{self, Write};
|
||||
use crate::syscall::{STDOUT, sys_write};
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
sys_write(STDOUT, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
loop {}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
clear_bss();
|
||||
syscall::sys_exit(main());
|
||||
panic!("unreachable after sys_exit!");
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
fn clear_bss() {
|
||||
extern "C" {
|
||||
fn start_bss();
|
||||
fn end_bss();
|
||||
}
|
||||
(start_bss as usize..end_bss as usize).for_each(|addr| {
|
||||
unsafe { (addr as *mut u8).write_volatile(0); }
|
||||
});
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x80040000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BASE_ADDRESS;
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text .text.*)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
.bss : {
|
||||
start_bss = .;
|
||||
*(.bss .bss.*)
|
||||
end_bss = .;
|
||||
}
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
pub const STDOUT: usize = 1;
|
||||
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(xstate: i32) -> isize {
|
||||
syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
]
|
@ -1,9 +0,0 @@
|
||||
[package]
|
||||
name = "user_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["Yifan Wu <shinbokuow@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
@ -1,23 +0,0 @@
|
||||
TARGET := riscv64gc-unknown-none-elf
|
||||
MODE := release
|
||||
APP_DIR := src/bin
|
||||
TARGET_DIR := target/$(TARGET)/$(MODE)
|
||||
APPS := $(wildcard $(APP_DIR)/*.rs)
|
||||
ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
|
||||
BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
|
||||
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
|
||||
elf: $(APPS)
|
||||
@python3 build.py
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
|
||||
.PHONY: elf binary build clean
|
@ -1,25 +0,0 @@
|
||||
import os
|
||||
|
||||
base_address = 0x80100000
|
||||
step = 0x20000
|
||||
linker = 'src/linker.ld'
|
||||
|
||||
app_id = 0
|
||||
apps = os.listdir('src/bin')
|
||||
apps.sort()
|
||||
for app in apps:
|
||||
app = app[:app.find('.')]
|
||||
lines = []
|
||||
lines_before = []
|
||||
with open(linker, 'r') as f:
|
||||
for line in f.readlines():
|
||||
lines_before.append(line)
|
||||
line = line.replace(hex(base_address), hex(base_address+step*app_id))
|
||||
lines.append(line)
|
||||
with open(linker, 'w+') as f:
|
||||
f.writelines(lines)
|
||||
os.system('cargo build --bin %s --release' % app)
|
||||
print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id)))
|
||||
with open(linker, 'w+') as f:
|
||||
f.writelines(lines_before)
|
||||
app_id = app_id + 1
|
@ -1,21 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::sys_yield;
|
||||
|
||||
const WIDTH: usize = 10;
|
||||
const HEIGHT: usize = 5;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
for i in 0..HEIGHT {
|
||||
for _ in 0..WIDTH { print!("A"); }
|
||||
println!(" [{}/{}]", i + 1, HEIGHT);
|
||||
sys_yield();
|
||||
}
|
||||
println!("Test write_a OK!");
|
||||
0
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::sys_yield;
|
||||
|
||||
const WIDTH: usize = 10;
|
||||
const HEIGHT: usize = 2;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
for i in 0..HEIGHT {
|
||||
for _ in 0..WIDTH { print!("B"); }
|
||||
println!(" [{}/{}]", i + 1, HEIGHT);
|
||||
sys_yield();
|
||||
}
|
||||
println!("Test write_b OK!");
|
||||
0
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::sys_yield;
|
||||
|
||||
const WIDTH: usize = 10;
|
||||
const HEIGHT: usize = 3;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
for i in 0..HEIGHT {
|
||||
for _ in 0..WIDTH { print!("C"); }
|
||||
println!(" [{}/{}]", i + 1, HEIGHT);
|
||||
sys_yield();
|
||||
}
|
||||
println!("Test write_c OK!");
|
||||
0
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
loop {}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
clear_bss();
|
||||
syscall::sys_exit(main());
|
||||
panic!("unreachable after sys_exit!");
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
fn clear_bss() {
|
||||
extern "C" {
|
||||
fn start_bss();
|
||||
fn end_bss();
|
||||
}
|
||||
(start_bss as usize..end_bss as usize).for_each(|addr| {
|
||||
unsafe { (addr as *mut u8).write_volatile(0); }
|
||||
});
|
||||
}
|
||||
|
||||
pub use syscall::*;
|
@ -1,29 +0,0 @@
|
||||
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x80100000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BASE_ADDRESS;
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text .text.*)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
.bss : {
|
||||
start_bss = .;
|
||||
*(.bss .bss.*)
|
||||
end_bss = .;
|
||||
}
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
pub const STDOUT: usize = 1;
|
||||
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
const SYSCALL_YIELD: usize = 124;
|
||||
const SYSCALL_GET_TIME: usize = 169;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(xstate: i32) -> isize {
|
||||
syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_yield() -> isize {
|
||||
syscall(SYSCALL_YIELD, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_get_time() -> isize {
|
||||
syscall(SYSCALL_GET_TIME, [0, 0, 0])
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
]
|
@ -1 +0,0 @@
|
||||
target/*
|
@ -1,23 +0,0 @@
|
||||
TARGET := riscv64gc-unknown-none-elf
|
||||
MODE := release
|
||||
APP_DIR := src/bin
|
||||
TARGET_DIR := target/$(TARGET)/$(MODE)
|
||||
APPS := $(wildcard $(APP_DIR)/*.rs)
|
||||
ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
|
||||
BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
|
||||
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
|
||||
elf: $(APPS)
|
||||
@python3 build.py
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
|
||||
.PHONY: elf binary build clean
|
@ -1,19 +0,0 @@
|
||||
# Tutorial 第三章测试用例 part2
|
||||
|
||||
在第三章第二阶段中,需要在第一阶段的基础上实现一个抢占式调度的分时多任务系统。在 `src/bin` 目录下,我们可以找到四个应用程序:
|
||||
|
||||
* ``00power_3``
|
||||
* ``01power_5``
|
||||
* ``02power_7``
|
||||
* ``03sleep``
|
||||
|
||||
像第一阶段一样,它们也需要被加载到不同的物理地址处运行。
|
||||
|
||||
应用被设计为运行在用户模式,批处理系统应运行在监督模式,它们都直接访问物理内存。
|
||||
|
||||
这一阶段需要实现的系统调用:
|
||||
|
||||
* `sys_get_time`:获取系统当前的毫秒数,在应用 `03sleep` 中使用,可以简单估计所有应用运行的总时间。
|
||||
|
||||
阅读应用程序代码可知,三个 `power` 应用分别计算一个质数的幂次对另一个大质数取模的余数,由于次数很高在一个时间片之内无法完成,而它们又不会使用 `sys_yield` 主动交出 CPU 使用权,因此只能由内核进行强制切换。
|
||||
|
@ -1,25 +0,0 @@
|
||||
import os
|
||||
|
||||
base_address = 0x80100000
|
||||
step = 0x20000
|
||||
linker = 'src/linker.ld'
|
||||
|
||||
app_id = 0
|
||||
apps = os.listdir('src/bin')
|
||||
apps.sort()
|
||||
for app in apps:
|
||||
app = app[:app.find('.')]
|
||||
lines = []
|
||||
lines_before = []
|
||||
with open(linker, 'r') as f:
|
||||
for line in f.readlines():
|
||||
lines_before.append(line)
|
||||
line = line.replace(hex(base_address), hex(base_address+step*app_id))
|
||||
lines.append(line)
|
||||
with open(linker, 'w+') as f:
|
||||
f.writelines(lines)
|
||||
os.system('cargo build --bin %s --release' % app)
|
||||
print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id)))
|
||||
with open(linker, 'w+') as f:
|
||||
f.writelines(lines_before)
|
||||
app_id = app_id + 1
|
@ -1,28 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let p = 3u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 200000;
|
||||
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
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let p = 5u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 140000;
|
||||
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_5 [{}/{}]", i, iter);
|
||||
}
|
||||
}
|
||||
println!("{}^{} = {}", p, iter, s[cur]);
|
||||
println!("Test power_5 OK!");
|
||||
0
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let p = 7u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 160000;
|
||||
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_7 [{}/{}]", i, iter);
|
||||
}
|
||||
}
|
||||
println!("{}^{} = {}", p, iter, s[cur]);
|
||||
println!("Test power_7 OK!");
|
||||
0
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{sys_get_time, sys_yield};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let current_timer = sys_get_time();
|
||||
let wait_for = current_timer + 10000000;
|
||||
while sys_get_time() < wait_for {
|
||||
sys_yield();
|
||||
}
|
||||
println!("Test sleep OK!");
|
||||
0
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
use core::fmt::{self, Write};
|
||||
use crate::syscall::{STDOUT, sys_write};
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
sys_write(STDOUT, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
loop {}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
clear_bss();
|
||||
syscall::sys_exit(main());
|
||||
panic!("unreachable after sys_exit!");
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
fn clear_bss() {
|
||||
extern "C" {
|
||||
fn start_bss();
|
||||
fn end_bss();
|
||||
}
|
||||
(start_bss as usize..end_bss as usize).for_each(|addr| {
|
||||
unsafe { (addr as *mut u8).write_volatile(0); }
|
||||
});
|
||||
}
|
||||
|
||||
pub use syscall::*;
|
@ -1,29 +0,0 @@
|
||||
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x80100000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BASE_ADDRESS;
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text .text.*)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
.bss : {
|
||||
start_bss = .;
|
||||
*(.bss .bss.*)
|
||||
end_bss = .;
|
||||
}
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
pub const STDOUT: usize = 1;
|
||||
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
const SYSCALL_YIELD: usize = 124;
|
||||
const SYSCALL_GET_TIME: usize = 169;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(xstate: i32) -> isize {
|
||||
syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_yield() -> isize {
|
||||
syscall(SYSCALL_YIELD, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_get_time() -> isize {
|
||||
syscall(SYSCALL_GET_TIME, [0, 0, 0])
|
||||
}
|
@ -1 +0,0 @@
|
||||
target/*
|
@ -1,3 +0,0 @@
|
||||
# Tutorial 第四章测试用例
|
||||
|
||||
第四章的应用与第三章第二阶段的应用一样,只是它们均被链接到 0x0 开头的地址空间上。这就需要我们利用 CPU 提供的页表机制,为每个应用创造一个虚拟地址空间。
|
@ -1,29 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
static mut S: [u64; LEN] = [0u64; LEN];
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn main() -> i32 {
|
||||
let p = 3u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 300000;
|
||||
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!("{}^{} = {}(mod {})", p, iter, S[cur], m);
|
||||
println!("Test power_3 OK!");
|
||||
0
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
static mut S: [u64; LEN] = [0u64; LEN];
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn main() -> i32 {
|
||||
let p = 5u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 210000;
|
||||
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_5 [{}/{}]", i, iter);
|
||||
}
|
||||
}
|
||||
println!("{}^{} = {}(mod {})", p, iter, S[cur], m);
|
||||
println!("Test power_5 OK!");
|
||||
0
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LEN: usize = 100;
|
||||
|
||||
static mut S: [u64; LEN] = [0u64; LEN];
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn main() -> i32 {
|
||||
let p = 7u64;
|
||||
let m = 998244353u64;
|
||||
let iter: usize = 240000;
|
||||
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_7 [{}/{}]", i, iter);
|
||||
}
|
||||
}
|
||||
println!("{}^{} = {}(mod {})", p, iter, S[cur], m);
|
||||
println!("Test power_7 OK!");
|
||||
0
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{sys_get_time, sys_yield};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
let current_timer = sys_get_time();
|
||||
let wait_for = current_timer + 10000000;
|
||||
while sys_get_time() < wait_for {
|
||||
sys_yield();
|
||||
}
|
||||
println!("Test sleep OK!");
|
||||
0
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
use core::fmt::{self, Write};
|
||||
use crate::syscall::{STDOUT, sys_write};
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
sys_write(STDOUT, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
loop {}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
syscall::sys_exit(main());
|
||||
panic!("unreachable after sys_exit!");
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
|
||||
pub use syscall::*;
|
@ -1,29 +0,0 @@
|
||||
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x0;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BASE_ADDRESS;
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text .text.*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
}
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
pub const STDOUT: usize = 1;
|
||||
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
const SYSCALL_YIELD: usize = 124;
|
||||
const SYSCALL_GET_TIME: usize = 169;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(xstate: i32) -> isize {
|
||||
syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_yield() -> isize {
|
||||
syscall(SYSCALL_YIELD, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_get_time() -> isize {
|
||||
syscall(SYSCALL_GET_TIME, [0, 0, 0])
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
]
|
@ -1 +0,0 @@
|
||||
target/*
|
@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "user_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["Yifan Wu <shinbokuow@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
buddy_system_allocator = "0.6"
|
@ -1,23 +0,0 @@
|
||||
TARGET := riscv64gc-unknown-none-elf
|
||||
MODE := release
|
||||
APP_DIR := src/bin
|
||||
TARGET_DIR := target/$(TARGET)/$(MODE)
|
||||
APPS := $(wildcard $(APP_DIR)/*.rs)
|
||||
ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
|
||||
BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
|
||||
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
|
||||
elf: $(APPS)
|
||||
@cargo build --release
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
|
||||
.PHONY: elf binary build clean
|
@ -1,44 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
macro_rules! color_text {
|
||||
($text:expr, $color:expr) => {{
|
||||
format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
|
||||
}};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!(
|
||||
"{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
|
||||
color_text!("H", 31),
|
||||
color_text!("e", 32),
|
||||
color_text!("l", 33),
|
||||
color_text!("l", 34),
|
||||
color_text!("o", 35),
|
||||
color_text!("R", 36),
|
||||
color_text!("u", 37),
|
||||
color_text!("s", 90),
|
||||
color_text!("t", 91),
|
||||
color_text!("u", 92),
|
||||
color_text!("C", 93),
|
||||
color_text!("o", 94),
|
||||
color_text!("r", 95),
|
||||
color_text!("e", 96),
|
||||
color_text!("!", 97),
|
||||
);
|
||||
|
||||
let text =
|
||||
"reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
|
||||
println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
|
||||
for i in 31..38 {
|
||||
println!("{}", color_text!(text, i));
|
||||
}
|
||||
for i in 90..98 {
|
||||
println!("{}", color_text!(text, i));
|
||||
}
|
||||
0
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{fork, wait, exit};
|
||||
|
||||
const MAX_CHILD: usize = 40;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for i in 0..MAX_CHILD {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
println!("I am child {}", i);
|
||||
exit(0);
|
||||
} else {
|
||||
println!("forked child pid = {}", pid);
|
||||
}
|
||||
assert!(pid > 0);
|
||||
}
|
||||
let mut exit_code: i32 = 0;
|
||||
for _ in 0..MAX_CHILD {
|
||||
if wait(&mut exit_code) <= 0 {
|
||||
panic!("wait stopped early");
|
||||
}
|
||||
}
|
||||
if wait(&mut exit_code) > 0 {
|
||||
panic!("wait got too many");
|
||||
}
|
||||
println!("forktest pass.");
|
||||
0
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{fork, getpid, wait};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
assert_eq!(wait(&mut 0i32), -1);
|
||||
println!("sys_wait without child process test passed!");
|
||||
println!("parent start, pid = {}!", getpid());
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
// child process
|
||||
println!("hello child process!");
|
||||
100
|
||||
} else {
|
||||
// parent process
|
||||
let mut xstate: i32 = 0;
|
||||
println!("ready waiting on parent process!");
|
||||
assert_eq!(pid, wait(&mut xstate));
|
||||
assert_eq!(xstate, 100);
|
||||
println!("child process pid = {}, exit code = {}", pid, xstate);
|
||||
0
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{sleep, getpid, fork, exit, yield_};
|
||||
|
||||
const DEPTH: usize = 4;
|
||||
|
||||
fn fork_child(cur: &str, branch: char) {
|
||||
let mut next = [0u8; DEPTH + 1];
|
||||
let l = cur.len();
|
||||
if l >= DEPTH {
|
||||
return;
|
||||
}
|
||||
&mut next[..l].copy_from_slice(cur.as_bytes());
|
||||
next[l] = branch as u8;
|
||||
if fork() == 0 {
|
||||
fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap());
|
||||
yield_();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn fork_tree(cur: &str) {
|
||||
println!("pid{}: {}", getpid(), cur);
|
||||
fork_child(cur, '0');
|
||||
fork_child(cur, '1');
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
fork_tree("");
|
||||
sleep(3000);
|
||||
0
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{fork, wait, yield_, exit, getpid, get_time};
|
||||
|
||||
static NUM: usize = 35;
|
||||
const N: usize = 10;
|
||||
static P: i32 = 10007;
|
||||
type Arr = [[i32; N]; N];
|
||||
|
||||
fn work(times: isize) {
|
||||
let mut a: Arr = Default::default();
|
||||
let mut b: Arr = Default::default();
|
||||
let mut c: Arr = Default::default();
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
a[i][j] = 1;
|
||||
b[i][j] = 1;
|
||||
}
|
||||
}
|
||||
yield_();
|
||||
println!("pid {} is running ({} times)!.", getpid(), times);
|
||||
for _ in 0..times {
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
c[i][j] = 0;
|
||||
for k in 0..N {
|
||||
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P;
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
a[i][j] = c[i][j];
|
||||
b[i][j] = c[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("pid {} done!.", getpid());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for _ in 0..NUM {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
let current_time = get_time();
|
||||
let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000;
|
||||
work(times * 10);
|
||||
}
|
||||
}
|
||||
|
||||
println!("fork ok.");
|
||||
|
||||
let mut xstate: i32 = 0;
|
||||
for _ in 0..NUM {
|
||||
if wait(&mut xstate) < 0 {
|
||||
panic!("wait failed.");
|
||||
}
|
||||
}
|
||||
assert!(wait(&mut xstate) < 0);
|
||||
println!("matrix passed.");
|
||||
0
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{sleep, exit, get_time, fork, waitpid};
|
||||
|
||||
fn sleepy() {
|
||||
let time: usize = 100;
|
||||
for i in 0..5 {
|
||||
sleep(time);
|
||||
println!("sleep {} x {} msecs.", i + 1, time);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let current_time = get_time();
|
||||
let pid = fork();
|
||||
let mut xstate: i32 = 0;
|
||||
if pid == 0 {
|
||||
sleepy();
|
||||
}
|
||||
assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == 0);
|
||||
println!("use {} msecs.", get_time() - current_time);
|
||||
println!("sleep pass.");
|
||||
0
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
fn f(d: usize) {
|
||||
println!("d = {}",d);
|
||||
f(d + 1);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("It should trigger segmentation fault!");
|
||||
f(0);
|
||||
0
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
const DL: u8 = 0x7fu8;
|
||||
const BS: u8 = 0x08u8;
|
||||
|
||||
use alloc::string::String;
|
||||
use user_lib::{fork, exec, waitpid, yield_};
|
||||
use user_lib::console::getchar;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("Rust user shell");
|
||||
let mut line: String = String::new();
|
||||
print!(">> ");
|
||||
loop {
|
||||
let c = getchar();
|
||||
match c {
|
||||
LF | CR => {
|
||||
println!("");
|
||||
if !line.is_empty() {
|
||||
line.push('\0');
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
// child process
|
||||
if exec(line.as_str()) == -1 {
|
||||
println!("Error when executing!");
|
||||
return -4;
|
||||
}
|
||||
unreachable!();
|
||||
} else {
|
||||
let mut xstate: i32 = 0;
|
||||
let mut exit_pid: isize;
|
||||
loop {
|
||||
exit_pid = waitpid(pid as usize, &mut xstate);
|
||||
if exit_pid == -1 {
|
||||
yield_();
|
||||
} else {
|
||||
assert_eq!(pid, exit_pid);
|
||||
println!("Shell: Process {} exited with code {}", pid, xstate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
print!(">> ");
|
||||
}
|
||||
BS | DL => {
|
||||
if !line.is_empty() {
|
||||
print!("{}", BS as char);
|
||||
print!(" ");
|
||||
print!("{}", BS as char);
|
||||
line.pop();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
print!("{}", c as char);
|
||||
line.push(c as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
static TESTS: &[&str] = &[
|
||||
"exit\0",
|
||||
"fantastic_text\0",
|
||||
"forktest\0",
|
||||
"forktest2\0",
|
||||
"forktest_simple\0",
|
||||
"hello_world\0",
|
||||
"matrix\0",
|
||||
"sleep\0",
|
||||
"sleep_simple\0",
|
||||
"stack_overflow\0",
|
||||
"yield\0",
|
||||
];
|
||||
|
||||
use user_lib::{exec, fork, waitpid};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for test in TESTS {
|
||||
println!("Usertests: Running {}", test);
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
exec(*test);
|
||||
panic!("unreachable!");
|
||||
} else {
|
||||
let mut xstate: i32 = Default::default();
|
||||
let wait_pid = waitpid(pid as usize, &mut xstate);
|
||||
assert_eq!(pid, wait_pid);
|
||||
println!("\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", test, pid, xstate);
|
||||
}
|
||||
}
|
||||
println!("Usertests passed!");
|
||||
0
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use user_lib::{getpid, yield_};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("Hello, I am process {}.", getpid());
|
||||
for i in 0..5 {
|
||||
yield_();
|
||||
println!("Back in process {}, iteration {}.", getpid(), i);
|
||||
}
|
||||
println!("yield pass.");
|
||||
0
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
loop {}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
use syscall::*;
|
||||
use buddy_system_allocator::LockedHeap;
|
||||
|
||||
const USER_HEAP_SIZE: usize = 16384;
|
||||
|
||||
static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: LockedHeap = LockedHeap::empty();
|
||||
|
||||
#[alloc_error_handler]
|
||||
pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
|
||||
panic!("Heap allocation error, layout = {:?}", layout);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
unsafe {
|
||||
HEAP.lock()
|
||||
.init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE);
|
||||
}
|
||||
exit(main());
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) }
|
||||
pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) }
|
||||
pub fn exit(exit_code: i32) -> ! { sys_exit(exit_code); }
|
||||
pub fn yield_() -> isize { sys_yield() }
|
||||
pub fn get_time() -> isize { sys_get_time() }
|
||||
pub fn getpid() -> isize { sys_getpid() }
|
||||
pub fn fork() -> isize { sys_fork() }
|
||||
pub fn exec(path: &str) -> isize { sys_exec(path) }
|
||||
pub fn wait(exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(-1, exit_code as *mut _) {
|
||||
-2 => { yield_(); }
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(pid as isize, exit_code as *mut _) {
|
||||
-2 => { yield_(); }
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn sleep(period_ms: usize) {
|
||||
let start = sys_get_time();
|
||||
while sys_get_time() < start + period_ms as isize {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x0;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = BASE_ADDRESS;
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
*(.text .text.*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
}
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
const SYSCALL_READ: usize = 63;
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
const SYSCALL_YIELD: usize = 124;
|
||||
const SYSCALL_GET_TIME: usize = 169;
|
||||
const SYSCALL_GETPID: usize = 172;
|
||||
const SYSCALL_FORK: usize = 220;
|
||||
const SYSCALL_EXEC: usize = 221;
|
||||
const SYSCALL_WAITPID: usize = 260;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize {
|
||||
syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(exit_code: i32) -> ! {
|
||||
syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]);
|
||||
panic!("sys_exit never returns!");
|
||||
}
|
||||
|
||||
pub fn sys_yield() -> isize {
|
||||
syscall(SYSCALL_YIELD, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_get_time() -> isize {
|
||||
syscall(SYSCALL_GET_TIME, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_getpid() -> isize {
|
||||
syscall(SYSCALL_GETPID, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_fork() -> isize {
|
||||
syscall(SYSCALL_FORK, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_exec(path: &str) -> isize {
|
||||
syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize {
|
||||
syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0])
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[build]
|
||||
target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
]
|
@ -1 +0,0 @@
|
||||
target/*
|
@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "user_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["Yifan Wu <shinbokuow@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
buddy_system_allocator = "0.6"
|
@ -1,23 +0,0 @@
|
||||
TARGET := riscv64gc-unknown-none-elf
|
||||
MODE := release
|
||||
APP_DIR := src/bin
|
||||
TARGET_DIR := target/$(TARGET)/$(MODE)
|
||||
APPS := $(wildcard $(APP_DIR)/*.rs)
|
||||
ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
|
||||
BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
|
||||
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
|
||||
elf: $(APPS)
|
||||
@cargo build --release
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
||||
|
||||
clean:
|
||||
@cargo clean
|
||||
|
||||
.PHONY: elf binary build clean
|
@ -1,29 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use user_lib::{fork, yield_, waitpid, exit, wait};
|
||||
|
||||
const MAGIC: i32 = -0x10384;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("I am the parent. Forking the child...");
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
println!("I am the child.");
|
||||
for _ in 0..7 { yield_(); }
|
||||
exit(MAGIC);
|
||||
} else {
|
||||
println!("I am parent, fork a child pid {}", pid);
|
||||
}
|
||||
println!("I am the parent, waiting now..");
|
||||
let mut xstate: i32 = 0;
|
||||
assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC);
|
||||
assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0);
|
||||
println!("waitpid {} ok.", pid);
|
||||
println!("exit pass.");
|
||||
0
|
||||
}
|
||||
|
@ -1,33 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{fork, wait, getpid, exit, sleep, get_time};
|
||||
|
||||
static NUM: usize = 30;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for _ in 0..NUM {
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
let current_time = get_time();
|
||||
let sleep_length = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000;
|
||||
println!("pid {} sleep for {} ms", getpid(), sleep_length);
|
||||
sleep(sleep_length as usize);
|
||||
println!("pid {} OK!", getpid());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut xstate: i32 = 0;
|
||||
for _ in 0..NUM {
|
||||
assert!(wait(&mut xstate) > 0);
|
||||
assert_eq!(xstate, 0);
|
||||
}
|
||||
assert!(wait(&mut xstate) < 0);
|
||||
println!("forktest2 test passed!");
|
||||
0
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("Hello world from user mode program!");
|
||||
0
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{
|
||||
fork,
|
||||
wait,
|
||||
exec,
|
||||
yield_,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
if fork() == 0 {
|
||||
exec("user_shell\0");
|
||||
} else {
|
||||
loop {
|
||||
let mut exit_code: i32 = 0;
|
||||
let pid = wait(&mut exit_code);
|
||||
if pid == -1 {
|
||||
yield_();
|
||||
continue;
|
||||
}
|
||||
println!(
|
||||
"[initproc] Released a zombie process, pid={}, exit_code={}",
|
||||
pid,
|
||||
exit_code,
|
||||
);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{get_time, sleep};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("into sleep test!");
|
||||
let start = get_time();
|
||||
println!("current time_msec = {}", start);
|
||||
sleep(100);
|
||||
let end = get_time();
|
||||
println!("time_msec = {} after sleeping 100 ticks, delta = {}ms!", end, end - start);
|
||||
println!("r_sleep passed!");
|
||||
0
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
const DL: u8 = 0x7fu8;
|
||||
const BS: u8 = 0x08u8;
|
||||
|
||||
use alloc::string::String;
|
||||
use user_lib::{fork, exec, waitpid, yield_};
|
||||
use user_lib::console::getchar;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("Rust user shell");
|
||||
let mut line: String = String::new();
|
||||
print!(">> ");
|
||||
loop {
|
||||
let c = getchar();
|
||||
match c {
|
||||
LF | CR => {
|
||||
println!("");
|
||||
if !line.is_empty() {
|
||||
line.push('\0');
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
// child process
|
||||
if exec(line.as_str()) == -1 {
|
||||
println!("Error when executing!");
|
||||
return -4;
|
||||
}
|
||||
unreachable!();
|
||||
} else {
|
||||
let mut xstate: i32 = 0;
|
||||
let mut exit_pid: isize;
|
||||
loop {
|
||||
exit_pid = waitpid(pid as usize, &mut xstate);
|
||||
if exit_pid == -1 {
|
||||
yield_();
|
||||
} else {
|
||||
assert_eq!(pid, exit_pid);
|
||||
println!("Shell: Process {} exited with code {}", pid, xstate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
print!(">> ");
|
||||
}
|
||||
BS | DL => {
|
||||
if !line.is_empty() {
|
||||
print!("{}", BS as char);
|
||||
print!(" ");
|
||||
print!("{}", BS as char);
|
||||
line.pop();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
print!("{}", c as char);
|
||||
line.push(c as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
static TESTS: &[&str] = &[
|
||||
"exit\0",
|
||||
"fantastic_text\0",
|
||||
"forktest\0",
|
||||
"forktest2\0",
|
||||
"forktest_simple\0",
|
||||
"hello_world\0",
|
||||
"matrix\0",
|
||||
"sleep\0",
|
||||
"sleep_simple\0",
|
||||
"stack_overflow\0",
|
||||
"yield\0",
|
||||
];
|
||||
|
||||
use user_lib::{exec, fork, waitpid};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for test in TESTS {
|
||||
println!("Usertests: Running {}", test);
|
||||
let pid = fork();
|
||||
if pid == 0 {
|
||||
exec(*test);
|
||||
panic!("unreachable!");
|
||||
} else {
|
||||
let mut xstate: i32 = Default::default();
|
||||
let wait_pid = waitpid(pid as usize, &mut xstate);
|
||||
assert_eq!(pid, wait_pid);
|
||||
println!("\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", test, pid, xstate);
|
||||
}
|
||||
}
|
||||
println!("Usertests passed!");
|
||||
0
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
const STDIN: usize = 0;
|
||||
const STDOUT: usize = 1;
|
||||
|
||||
use super::{read, write};
|
||||
|
||||
struct Stdout;
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
write(STDOUT, s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
Stdout.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getchar() -> u8 {
|
||||
let mut c = [0u8; 1];
|
||||
read(STDIN, &mut c);
|
||||
c[0]
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
use super::exit;
|
||||
|
||||
#[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);
|
||||
} else {
|
||||
println!("Panicked: {}", err);
|
||||
}
|
||||
exit(-1);
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
#[macro_use]
|
||||
pub mod console;
|
||||
mod syscall;
|
||||
mod lang_items;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use syscall::*;
|
||||
use buddy_system_allocator::LockedHeap;
|
||||
|
||||
const USER_HEAP_SIZE: usize = 16384;
|
||||
|
||||
static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: LockedHeap = LockedHeap::empty();
|
||||
|
||||
#[alloc_error_handler]
|
||||
pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
|
||||
panic!("Heap allocation error, layout = {:?}", layout);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".text.entry"]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
unsafe {
|
||||
HEAP.lock()
|
||||
.init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE);
|
||||
}
|
||||
exit(main());
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[no_mangle]
|
||||
fn main() -> i32 {
|
||||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
pub fn close(fd: usize) -> isize { sys_close(fd) }
|
||||
pub fn pipe(pipe_fd: &mut [usize]) -> isize { sys_pipe(pipe_fd) }
|
||||
pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) }
|
||||
pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) }
|
||||
pub fn exit(exit_code: i32) -> ! { sys_exit(exit_code); }
|
||||
pub fn yield_() -> isize { sys_yield() }
|
||||
pub fn get_time() -> isize { sys_get_time() }
|
||||
pub fn getpid() -> isize { sys_getpid() }
|
||||
pub fn fork() -> isize { sys_fork() }
|
||||
pub fn exec(path: &str) -> isize { sys_exec(path) }
|
||||
pub fn wait(exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(-1, exit_code as *mut _) {
|
||||
-2 => { yield_(); }
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(pid as isize, exit_code as *mut _) {
|
||||
-2 => { yield_(); }
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn sleep(period_ms: usize) {
|
||||
let start = sys_get_time();
|
||||
while sys_get_time() < start + period_ms as isize {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
const SYSCALL_CLOSE: usize = 57;
|
||||
const SYSCALL_PIPE: usize = 59;
|
||||
const SYSCALL_READ: usize = 63;
|
||||
const SYSCALL_WRITE: usize = 64;
|
||||
const SYSCALL_EXIT: usize = 93;
|
||||
const SYSCALL_YIELD: usize = 124;
|
||||
const SYSCALL_GET_TIME: usize = 169;
|
||||
const SYSCALL_GETPID: usize = 172;
|
||||
const SYSCALL_FORK: usize = 220;
|
||||
const SYSCALL_EXEC: usize = 221;
|
||||
const SYSCALL_WAITPID: usize = 260;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn sys_close(fd: usize) -> isize {
|
||||
syscall(SYSCALL_CLOSE, [fd, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_pipe(pipe: &mut [usize]) -> isize {
|
||||
syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize {
|
||||
syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
|
||||
syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
|
||||
}
|
||||
|
||||
pub fn sys_exit(exit_code: i32) -> ! {
|
||||
syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]);
|
||||
panic!("sys_exit never returns!");
|
||||
}
|
||||
|
||||
pub fn sys_yield() -> isize {
|
||||
syscall(SYSCALL_YIELD, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_get_time() -> isize {
|
||||
syscall(SYSCALL_GET_TIME, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_getpid() -> isize {
|
||||
syscall(SYSCALL_GETPID, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_fork() -> isize {
|
||||
syscall(SYSCALL_FORK, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_exec(path: &str) -> isize {
|
||||
syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize {
|
||||
syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0])
|
||||
}
|
@ -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,153 @@
|
||||
use clap::{App, Arg};
|
||||
use easy_fs::{BlockDevice, EasyFileSystem};
|
||||
use std::fs::{read_dir, File, OpenOptions};
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
const BLOCK_SZ: usize = 512;
|
||||
|
||||
struct BlockFile(Mutex<File>);
|
||||
|
||||
impl BlockDevice for BlockFile {
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
|
||||
let mut file = self.0.lock().unwrap();
|
||||
file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
|
||||
.expect("Error when seeking!");
|
||||
assert_eq!(file.read(buf).unwrap(), BLOCK_SZ, "Not a complete block!");
|
||||
}
|
||||
|
||||
fn write_block(&self, block_id: usize, buf: &[u8]) {
|
||||
let mut file = self.0.lock().unwrap();
|
||||
file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
|
||||
.expect("Error when seeking!");
|
||||
assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!");
|
||||
}
|
||||
|
||||
fn handle_irq(&self) { unimplemented!(); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
easy_fs_pack().expect("Error when packing easy-fs!");
|
||||
}
|
||||
|
||||
fn easy_fs_pack() -> std::io::Result<()> {
|
||||
let matches = App::new("EasyFileSystem packer")
|
||||
.arg(
|
||||
Arg::with_name("source")
|
||||
.short("s")
|
||||
.long("source")
|
||||
.takes_value(true)
|
||||
.help("Executable source dir(with backslash)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("target")
|
||||
.short("t")
|
||||
.long("target")
|
||||
.takes_value(true)
|
||||
.help("Executable target dir(with backslash)"),
|
||||
)
|
||||
.get_matches();
|
||||
let src_path = matches.value_of("source").unwrap();
|
||||
let target_path = matches.value_of("target").unwrap();
|
||||
println!("src_path = {}\ntarget_path = {}", src_path, target_path);
|
||||
let block_file = Arc::new(BlockFile(Mutex::new({
|
||||
let f = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(format!("{}{}", target_path, "fs.img"))?;
|
||||
f.set_len(16 * 2048 * 512).unwrap();
|
||||
f
|
||||
})));
|
||||
// 16MiB, at most 4095 files
|
||||
let efs = EasyFileSystem::create(block_file, 16 * 2048, 1);
|
||||
let root_inode = Arc::new(EasyFileSystem::root_inode(&efs));
|
||||
let apps: Vec<_> = read_dir(src_path)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|dir_entry| {
|
||||
let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap();
|
||||
name_with_ext.drain(name_with_ext.find('.').unwrap()..name_with_ext.len());
|
||||
name_with_ext
|
||||
})
|
||||
.collect();
|
||||
for app in apps {
|
||||
// load app data from host file system
|
||||
let mut host_file = File::open(format!("{}{}", target_path, app)).unwrap();
|
||||
let mut all_data: Vec<u8> = Vec::new();
|
||||
host_file.read_to_end(&mut all_data).unwrap();
|
||||
// create a file in easy-fs
|
||||
let inode = root_inode.create(app.as_str()).unwrap();
|
||||
// write data to easy-fs
|
||||
inode.write_at(0, all_data.as_slice());
|
||||
}
|
||||
// list apps
|
||||
for app in root_inode.ls() {
|
||||
println!("{}", app);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn efs_test() -> std::io::Result<()> {
|
||||
let block_file = Arc::new(BlockFile(Mutex::new({
|
||||
let f = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open("target/fs.img")?;
|
||||
f.set_len(8192 * 512).unwrap();
|
||||
f
|
||||
})));
|
||||
EasyFileSystem::create(block_file.clone(), 4096, 1);
|
||||
let efs = EasyFileSystem::open(block_file.clone());
|
||||
let root_inode = EasyFileSystem::root_inode(&efs);
|
||||
root_inode.create("filea");
|
||||
root_inode.create("fileb");
|
||||
for name in root_inode.ls() {
|
||||
println!("{}", name);
|
||||
}
|
||||
let filea = root_inode.find("filea").unwrap();
|
||||
let greet_str = "Hello, world!";
|
||||
filea.write_at(0, greet_str.as_bytes());
|
||||
//let mut buffer = [0u8; 512];
|
||||
let mut buffer = [0u8; 233];
|
||||
let len = filea.read_at(0, &mut buffer);
|
||||
assert_eq!(greet_str, core::str::from_utf8(&buffer[..len]).unwrap(),);
|
||||
|
||||
let mut random_str_test = |len: usize| {
|
||||
filea.clear();
|
||||
assert_eq!(filea.read_at(0, &mut buffer), 0,);
|
||||
let mut str = String::new();
|
||||
use rand;
|
||||
// random digit
|
||||
for _ in 0..len {
|
||||
str.push(char::from('0' as u8 + rand::random::<u8>() % 10));
|
||||
}
|
||||
filea.write_at(0, str.as_bytes());
|
||||
let mut read_buffer = [0u8; 127];
|
||||
let mut offset = 0usize;
|
||||
let mut read_str = String::new();
|
||||
loop {
|
||||
let len = filea.read_at(offset, &mut read_buffer);
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
offset += len;
|
||||
read_str.push_str(core::str::from_utf8(&read_buffer[..len]).unwrap());
|
||||
}
|
||||
assert_eq!(str, read_str);
|
||||
};
|
||||
|
||||
random_str_test(4 * BLOCK_SZ);
|
||||
random_str_test(8 * BLOCK_SZ + BLOCK_SZ / 2);
|
||||
random_str_test(100 * BLOCK_SZ);
|
||||
random_str_test(70 * BLOCK_SZ + BLOCK_SZ / 7);
|
||||
random_str_test((12 + 128) * BLOCK_SZ);
|
||||
random_str_test(400 * BLOCK_SZ);
|
||||
random_str_test(1000 * BLOCK_SZ);
|
||||
random_str_test(2000 * BLOCK_SZ);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
target/
|
||||
Cargo.lock
|
@ -0,0 +1,69 @@
|
||||
use super::{get_block_cache, BlockDevice, BLOCK_SZ};
|
||||
use alloc::sync::Arc;
|
||||
|
||||
type BitmapBlock = [u64; 64];
|
||||
|
||||
const BLOCK_BITS: usize = BLOCK_SZ * 8;
|
||||
|
||||
pub struct Bitmap {
|
||||
start_block_id: usize,
|
||||
blocks: usize,
|
||||
}
|
||||
|
||||
/// Return (block_pos, bits64_pos, inner_pos)
|
||||
fn decomposition(mut bit: usize) -> (usize, usize, usize) {
|
||||
let block_pos = bit / BLOCK_BITS;
|
||||
bit %= BLOCK_BITS;
|
||||
(block_pos, bit / 64, bit % 64)
|
||||
}
|
||||
|
||||
impl Bitmap {
|
||||
pub fn new(start_block_id: usize, blocks: usize) -> Self {
|
||||
Self {
|
||||
start_block_id,
|
||||
blocks,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc(&self, block_device: &Arc<dyn BlockDevice>) -> Option<usize> {
|
||||
for block_id in 0..self.blocks {
|
||||
let pos = get_block_cache(
|
||||
block_id + self.start_block_id as usize,
|
||||
Arc::clone(block_device),
|
||||
)
|
||||
.lock()
|
||||
.modify(0, |bitmap_block: &mut BitmapBlock| {
|
||||
if let Some((bits64_pos, inner_pos)) = bitmap_block
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, bits64)| **bits64 != u64::MAX)
|
||||
.map(|(bits64_pos, bits64)| (bits64_pos, bits64.trailing_ones() as usize))
|
||||
{
|
||||
// modify cache
|
||||
bitmap_block[bits64_pos] |= 1u64 << inner_pos;
|
||||
Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if pos.is_some() {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn dealloc(&self, block_device: &Arc<dyn BlockDevice>, bit: usize) {
|
||||
let (block_pos, bits64_pos, inner_pos) = decomposition(bit);
|
||||
get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |bitmap_block: &mut BitmapBlock| {
|
||||
assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0);
|
||||
bitmap_block[bits64_pos] -= 1u64 << inner_pos;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn maximum(&self) -> usize {
|
||||
self.blocks * BLOCK_BITS
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
use super::{BlockDevice, BLOCK_SZ};
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
use spin::Mutex;
|
||||
|
||||
pub struct BlockCache {
|
||||
cache: [u8; BLOCK_SZ],
|
||||
block_id: usize,
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
impl BlockCache {
|
||||
/// Load a new BlockCache from disk.
|
||||
pub fn new(block_id: usize, block_device: Arc<dyn BlockDevice>) -> Self {
|
||||
let mut cache = [0u8; BLOCK_SZ];
|
||||
block_device.read_block(block_id, &mut cache);
|
||||
Self {
|
||||
cache,
|
||||
block_id,
|
||||
block_device,
|
||||
modified: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn addr_of_offset(&self, offset: usize) -> usize {
|
||||
&self.cache[offset] as *const _ as usize
|
||||
}
|
||||
|
||||
pub fn get_ref<T>(&self, offset: usize) -> &T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
let type_size = core::mem::size_of::<T>();
|
||||
assert!(offset + type_size <= BLOCK_SZ);
|
||||
let addr = self.addr_of_offset(offset);
|
||||
unsafe { &*(addr as *const T) }
|
||||
}
|
||||
|
||||
pub fn get_mut<T>(&mut self, offset: usize) -> &mut T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
let type_size = core::mem::size_of::<T>();
|
||||
assert!(offset + type_size <= BLOCK_SZ);
|
||||
self.modified = true;
|
||||
let addr = self.addr_of_offset(offset);
|
||||
unsafe { &mut *(addr as *mut T) }
|
||||
}
|
||||
|
||||
pub fn read<T, V>(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V {
|
||||
f(self.get_ref(offset))
|
||||
}
|
||||
|
||||
pub fn modify<T, V>(&mut self, offset: usize, f: impl FnOnce(&mut T) -> V) -> V {
|
||||
f(self.get_mut(offset))
|
||||
}
|
||||
|
||||
pub fn sync(&mut self) {
|
||||
if self.modified {
|
||||
self.modified = false;
|
||||
self.block_device.write_block(self.block_id, &self.cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BlockCache {
|
||||
fn drop(&mut self) {
|
||||
self.sync()
|
||||
}
|
||||
}
|
||||
|
||||
const BLOCK_CACHE_SIZE: usize = 16;
|
||||
|
||||
pub struct BlockCacheManager {
|
||||
queue: VecDeque<(usize, Arc<Mutex<BlockCache>>)>,
|
||||
}
|
||||
|
||||
impl BlockCacheManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_block_cache(
|
||||
&mut self,
|
||||
block_id: usize,
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
) -> Arc<Mutex<BlockCache>> {
|
||||
if let Some(pair) = self.queue.iter().find(|pair| pair.0 == block_id) {
|
||||
Arc::clone(&pair.1)
|
||||
} else {
|
||||
// substitute
|
||||
if self.queue.len() == BLOCK_CACHE_SIZE {
|
||||
// from front to tail
|
||||
if let Some((idx, _)) = self
|
||||
.queue
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, pair)| Arc::strong_count(&pair.1) == 1)
|
||||
{
|
||||
self.queue.drain(idx..=idx);
|
||||
} else {
|
||||
panic!("Run out of BlockCache!");
|
||||
}
|
||||
}
|
||||
// load block into mem and push back
|
||||
let block_cache = Arc::new(Mutex::new(BlockCache::new(
|
||||
block_id,
|
||||
Arc::clone(&block_device),
|
||||
)));
|
||||
self.queue.push_back((block_id, Arc::clone(&block_cache)));
|
||||
block_cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref BLOCK_CACHE_MANAGER: Mutex<BlockCacheManager> =
|
||||
Mutex::new(BlockCacheManager::new());
|
||||
}
|
||||
|
||||
pub fn get_block_cache(
|
||||
block_id: usize,
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
) -> Arc<Mutex<BlockCache>> {
|
||||
BLOCK_CACHE_MANAGER
|
||||
.lock()
|
||||
.get_block_cache(block_id, block_device)
|
||||
}
|
||||
|
||||
pub fn block_cache_sync_all() {
|
||||
let manager = BLOCK_CACHE_MANAGER.lock();
|
||||
for (_, cache) in manager.queue.iter() {
|
||||
cache.lock().sync();
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
use core::any::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]);
|
||||
fn handle_irq(&self);
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
use super::{
|
||||
block_cache_sync_all, get_block_cache, Bitmap, BlockDevice, DiskInode, DiskInodeType, Inode,
|
||||
SuperBlock,
|
||||
};
|
||||
use crate::BLOCK_SZ;
|
||||
use alloc::sync::Arc;
|
||||
use spin::Mutex;
|
||||
|
||||
pub struct EasyFileSystem {
|
||||
pub block_device: Arc<dyn BlockDevice>,
|
||||
pub inode_bitmap: Bitmap,
|
||||
pub data_bitmap: Bitmap,
|
||||
inode_area_start_block: u32,
|
||||
data_area_start_block: u32,
|
||||
}
|
||||
|
||||
type DataBlock = [u8; BLOCK_SZ];
|
||||
|
||||
impl EasyFileSystem {
|
||||
pub fn create(
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
total_blocks: u32,
|
||||
inode_bitmap_blocks: u32,
|
||||
) -> Arc<Mutex<Self>> {
|
||||
// calculate block size of areas & create bitmaps
|
||||
let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize);
|
||||
let inode_num = inode_bitmap.maximum();
|
||||
let inode_area_blocks =
|
||||
((inode_num * core::mem::size_of::<DiskInode>() + BLOCK_SZ - 1) / BLOCK_SZ) as u32;
|
||||
let inode_total_blocks = inode_bitmap_blocks + inode_area_blocks;
|
||||
let data_total_blocks = total_blocks - 1 - inode_total_blocks;
|
||||
let data_bitmap_blocks = (data_total_blocks + 4096) / 4097;
|
||||
let data_area_blocks = data_total_blocks - data_bitmap_blocks;
|
||||
let data_bitmap = Bitmap::new(
|
||||
(1 + inode_bitmap_blocks + inode_area_blocks) as usize,
|
||||
data_bitmap_blocks as usize,
|
||||
);
|
||||
let mut efs = Self {
|
||||
block_device: Arc::clone(&block_device),
|
||||
inode_bitmap,
|
||||
data_bitmap,
|
||||
inode_area_start_block: 1 + inode_bitmap_blocks,
|
||||
data_area_start_block: 1 + inode_total_blocks + data_bitmap_blocks,
|
||||
};
|
||||
// clear all blocks
|
||||
for i in 0..total_blocks {
|
||||
get_block_cache(i as usize, Arc::clone(&block_device))
|
||||
.lock()
|
||||
.modify(0, |data_block: &mut DataBlock| {
|
||||
for byte in data_block.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
// initialize SuperBlock
|
||||
get_block_cache(0, Arc::clone(&block_device)).lock().modify(
|
||||
0,
|
||||
|super_block: &mut SuperBlock| {
|
||||
super_block.initialize(
|
||||
total_blocks,
|
||||
inode_bitmap_blocks,
|
||||
inode_area_blocks,
|
||||
data_bitmap_blocks,
|
||||
data_area_blocks,
|
||||
);
|
||||
},
|
||||
);
|
||||
// write back immediately
|
||||
// create a inode for root node "/"
|
||||
assert_eq!(efs.alloc_inode(), 0);
|
||||
let (root_inode_block_id, root_inode_offset) = efs.get_disk_inode_pos(0);
|
||||
get_block_cache(root_inode_block_id as usize, Arc::clone(&block_device))
|
||||
.lock()
|
||||
.modify(root_inode_offset, |disk_inode: &mut DiskInode| {
|
||||
disk_inode.initialize(DiskInodeType::Directory);
|
||||
});
|
||||
block_cache_sync_all();
|
||||
Arc::new(Mutex::new(efs))
|
||||
}
|
||||
|
||||
pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
|
||||
// read SuperBlock
|
||||
get_block_cache(0, Arc::clone(&block_device))
|
||||
.lock()
|
||||
.read(0, |super_block: &SuperBlock| {
|
||||
assert!(super_block.is_valid(), "Error loading EFS!");
|
||||
let inode_total_blocks =
|
||||
super_block.inode_bitmap_blocks + super_block.inode_area_blocks;
|
||||
let efs = Self {
|
||||
block_device,
|
||||
inode_bitmap: Bitmap::new(1, super_block.inode_bitmap_blocks as usize),
|
||||
data_bitmap: Bitmap::new(
|
||||
(1 + inode_total_blocks) as usize,
|
||||
super_block.data_bitmap_blocks as usize,
|
||||
),
|
||||
inode_area_start_block: 1 + super_block.inode_bitmap_blocks,
|
||||
data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,
|
||||
};
|
||||
Arc::new(Mutex::new(efs))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn root_inode(efs: &Arc<Mutex<Self>>) -> Inode {
|
||||
let block_device = Arc::clone(&efs.lock().block_device);
|
||||
// acquire efs lock temporarily
|
||||
let (block_id, block_offset) = efs.lock().get_disk_inode_pos(0);
|
||||
// release efs lock
|
||||
Inode::new(block_id, block_offset, Arc::clone(efs), block_device)
|
||||
}
|
||||
|
||||
pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) {
|
||||
let inode_size = core::mem::size_of::<DiskInode>();
|
||||
let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
|
||||
let block_id = self.inode_area_start_block + inode_id / inodes_per_block;
|
||||
(
|
||||
block_id,
|
||||
(inode_id % inodes_per_block) as usize * inode_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_data_block_id(&self, data_block_id: u32) -> u32 {
|
||||
self.data_area_start_block + data_block_id
|
||||
}
|
||||
|
||||
pub fn alloc_inode(&mut self) -> u32 {
|
||||
self.inode_bitmap.alloc(&self.block_device).unwrap() as u32
|
||||
}
|
||||
|
||||
/// Return a block ID not ID in the data area.
|
||||
pub fn alloc_data(&mut self) -> u32 {
|
||||
self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block
|
||||
}
|
||||
|
||||
pub fn dealloc_data(&mut self, block_id: u32) {
|
||||
get_block_cache(block_id as usize, Arc::clone(&self.block_device))
|
||||
.lock()
|
||||
.modify(0, |data_block: &mut DataBlock| {
|
||||
data_block.iter_mut().for_each(|p| {
|
||||
*p = 0;
|
||||
})
|
||||
});
|
||||
self.data_bitmap.dealloc(
|
||||
&self.block_device,
|
||||
(block_id - self.data_area_start_block) as usize,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,409 @@
|
||||
use super::{get_block_cache, BlockDevice, BLOCK_SZ};
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::{Debug, Formatter, Result};
|
||||
|
||||
const EFS_MAGIC: u32 = 0x3b800001;
|
||||
const INODE_DIRECT_COUNT: usize = 28;
|
||||
const NAME_LENGTH_LIMIT: usize = 27;
|
||||
const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4;
|
||||
const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT;
|
||||
const DIRECT_BOUND: usize = INODE_DIRECT_COUNT;
|
||||
const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT;
|
||||
#[allow(unused)]
|
||||
const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SuperBlock {
|
||||
magic: u32,
|
||||
pub total_blocks: u32,
|
||||
pub inode_bitmap_blocks: u32,
|
||||
pub inode_area_blocks: u32,
|
||||
pub data_bitmap_blocks: u32,
|
||||
pub data_area_blocks: u32,
|
||||
}
|
||||
|
||||
impl Debug for SuperBlock {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
f.debug_struct("SuperBlock")
|
||||
.field("total_blocks", &self.total_blocks)
|
||||
.field("inode_bitmap_blocks", &self.inode_bitmap_blocks)
|
||||
.field("inode_area_blocks", &self.inode_area_blocks)
|
||||
.field("data_bitmap_blocks", &self.data_bitmap_blocks)
|
||||
.field("data_area_blocks", &self.data_area_blocks)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SuperBlock {
|
||||
pub fn initialize(
|
||||
&mut self,
|
||||
total_blocks: u32,
|
||||
inode_bitmap_blocks: u32,
|
||||
inode_area_blocks: u32,
|
||||
data_bitmap_blocks: u32,
|
||||
data_area_blocks: u32,
|
||||
) {
|
||||
*self = Self {
|
||||
magic: EFS_MAGIC,
|
||||
total_blocks,
|
||||
inode_bitmap_blocks,
|
||||
inode_area_blocks,
|
||||
data_bitmap_blocks,
|
||||
data_area_blocks,
|
||||
}
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.magic == EFS_MAGIC
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum DiskInodeType {
|
||||
File,
|
||||
Directory,
|
||||
}
|
||||
|
||||
type IndirectBlock = [u32; BLOCK_SZ / 4];
|
||||
type DataBlock = [u8; BLOCK_SZ];
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DiskInode {
|
||||
pub size: u32,
|
||||
pub direct: [u32; INODE_DIRECT_COUNT],
|
||||
pub indirect1: u32,
|
||||
pub indirect2: u32,
|
||||
type_: DiskInodeType,
|
||||
}
|
||||
|
||||
impl DiskInode {
|
||||
/// indirect1 and indirect2 block are allocated only when they are needed.
|
||||
pub fn initialize(&mut self, type_: DiskInodeType) {
|
||||
self.size = 0;
|
||||
self.direct.iter_mut().for_each(|v| *v = 0);
|
||||
self.indirect1 = 0;
|
||||
self.indirect2 = 0;
|
||||
self.type_ = type_;
|
||||
}
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.type_ == DiskInodeType::Directory
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.type_ == DiskInodeType::File
|
||||
}
|
||||
/// Return block number correspond to size.
|
||||
pub fn data_blocks(&self) -> u32 {
|
||||
Self::_data_blocks(self.size)
|
||||
}
|
||||
fn _data_blocks(size: u32) -> u32 {
|
||||
(size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32
|
||||
}
|
||||
/// Return number of blocks needed include indirect1/2.
|
||||
pub fn total_blocks(size: u32) -> u32 {
|
||||
let data_blocks = Self::_data_blocks(size) as usize;
|
||||
let mut total = data_blocks as usize;
|
||||
// indirect1
|
||||
if data_blocks > INODE_DIRECT_COUNT {
|
||||
total += 1;
|
||||
}
|
||||
// indirect2
|
||||
if data_blocks > INDIRECT1_BOUND {
|
||||
total += 1;
|
||||
// sub indirect1
|
||||
total +=
|
||||
(data_blocks - INDIRECT1_BOUND + INODE_INDIRECT1_COUNT - 1) / INODE_INDIRECT1_COUNT;
|
||||
}
|
||||
total as u32
|
||||
}
|
||||
pub fn blocks_num_needed(&self, new_size: u32) -> u32 {
|
||||
assert!(new_size >= self.size);
|
||||
Self::total_blocks(new_size) - Self::total_blocks(self.size)
|
||||
}
|
||||
pub fn get_block_id(&self, inner_id: u32, block_device: &Arc<dyn BlockDevice>) -> u32 {
|
||||
let inner_id = inner_id as usize;
|
||||
if inner_id < INODE_DIRECT_COUNT {
|
||||
self.direct[inner_id]
|
||||
} else if inner_id < INDIRECT1_BOUND {
|
||||
get_block_cache(self.indirect1 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.read(0, |indirect_block: &IndirectBlock| {
|
||||
indirect_block[inner_id - INODE_DIRECT_COUNT]
|
||||
})
|
||||
} else {
|
||||
let last = inner_id - INDIRECT1_BOUND;
|
||||
let indirect1 = get_block_cache(self.indirect2 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.read(0, |indirect2: &IndirectBlock| {
|
||||
indirect2[last / INODE_INDIRECT1_COUNT]
|
||||
});
|
||||
get_block_cache(indirect1 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.read(0, |indirect1: &IndirectBlock| {
|
||||
indirect1[last % INODE_INDIRECT1_COUNT]
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn increase_size(
|
||||
&mut self,
|
||||
new_size: u32,
|
||||
new_blocks: Vec<u32>,
|
||||
block_device: &Arc<dyn BlockDevice>,
|
||||
) {
|
||||
let mut current_blocks = self.data_blocks();
|
||||
self.size = new_size;
|
||||
let mut total_blocks = self.data_blocks();
|
||||
let mut new_blocks = new_blocks.into_iter();
|
||||
// fill direct
|
||||
while current_blocks < total_blocks.min(INODE_DIRECT_COUNT as u32) {
|
||||
self.direct[current_blocks as usize] = new_blocks.next().unwrap();
|
||||
current_blocks += 1;
|
||||
}
|
||||
// alloc indirect1
|
||||
if total_blocks > INODE_DIRECT_COUNT as u32 {
|
||||
if current_blocks == INODE_DIRECT_COUNT as u32 {
|
||||
self.indirect1 = new_blocks.next().unwrap();
|
||||
}
|
||||
current_blocks -= INODE_DIRECT_COUNT as u32;
|
||||
total_blocks -= INODE_DIRECT_COUNT as u32;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// fill indirect1
|
||||
get_block_cache(self.indirect1 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect1: &mut IndirectBlock| {
|
||||
while current_blocks < total_blocks.min(INODE_INDIRECT1_COUNT as u32) {
|
||||
indirect1[current_blocks as usize] = new_blocks.next().unwrap();
|
||||
current_blocks += 1;
|
||||
}
|
||||
});
|
||||
// alloc indirect2
|
||||
if total_blocks > INODE_INDIRECT1_COUNT as u32 {
|
||||
if current_blocks == INODE_INDIRECT1_COUNT as u32 {
|
||||
self.indirect2 = new_blocks.next().unwrap();
|
||||
}
|
||||
current_blocks -= INODE_INDIRECT1_COUNT as u32;
|
||||
total_blocks -= INODE_INDIRECT1_COUNT as u32;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// fill indirect2 from (a0, b0) -> (a1, b1)
|
||||
let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT;
|
||||
let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT;
|
||||
let a1 = total_blocks as usize / INODE_INDIRECT1_COUNT;
|
||||
let b1 = total_blocks as usize % INODE_INDIRECT1_COUNT;
|
||||
// alloc low-level indirect1
|
||||
get_block_cache(self.indirect2 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect2: &mut IndirectBlock| {
|
||||
while (a0 < a1) || (a0 == a1 && b0 < b1) {
|
||||
if b0 == 0 {
|
||||
indirect2[a0] = new_blocks.next().unwrap();
|
||||
}
|
||||
// fill current
|
||||
get_block_cache(indirect2[a0] as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect1: &mut IndirectBlock| {
|
||||
indirect1[b0] = new_blocks.next().unwrap();
|
||||
});
|
||||
// move to next
|
||||
b0 += 1;
|
||||
if b0 == INODE_INDIRECT1_COUNT {
|
||||
b0 = 0;
|
||||
a0 += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Clear size to zero and return blocks that should be deallocated.
|
||||
///
|
||||
/// We will clear the block contents to zero later.
|
||||
pub fn clear_size(&mut self, block_device: &Arc<dyn BlockDevice>) -> Vec<u32> {
|
||||
let mut v: Vec<u32> = Vec::new();
|
||||
let mut data_blocks = self.data_blocks() as usize;
|
||||
self.size = 0;
|
||||
let mut current_blocks = 0usize;
|
||||
// direct
|
||||
while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) {
|
||||
v.push(self.direct[current_blocks]);
|
||||
self.direct[current_blocks] = 0;
|
||||
current_blocks += 1;
|
||||
}
|
||||
// indirect1 block
|
||||
if data_blocks > INODE_DIRECT_COUNT {
|
||||
v.push(self.indirect1);
|
||||
data_blocks -= INODE_DIRECT_COUNT;
|
||||
current_blocks = 0;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
// indirect1
|
||||
get_block_cache(self.indirect1 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect1: &mut IndirectBlock| {
|
||||
while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) {
|
||||
v.push(indirect1[current_blocks]);
|
||||
//indirect1[current_blocks] = 0;
|
||||
current_blocks += 1;
|
||||
}
|
||||
});
|
||||
self.indirect1 = 0;
|
||||
// indirect2 block
|
||||
if data_blocks > INODE_INDIRECT1_COUNT {
|
||||
v.push(self.indirect2);
|
||||
data_blocks -= INODE_INDIRECT1_COUNT;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
// indirect2
|
||||
assert!(data_blocks <= INODE_INDIRECT2_COUNT);
|
||||
let a1 = data_blocks / INODE_INDIRECT1_COUNT;
|
||||
let b1 = data_blocks % INODE_INDIRECT1_COUNT;
|
||||
get_block_cache(self.indirect2 as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect2: &mut IndirectBlock| {
|
||||
// full indirect1 blocks
|
||||
for entry in indirect2.iter_mut().take(a1) {
|
||||
v.push(*entry);
|
||||
get_block_cache(*entry as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect1: &mut IndirectBlock| {
|
||||
for entry in indirect1.iter() {
|
||||
v.push(*entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
// last indirect1 block
|
||||
if b1 > 0 {
|
||||
v.push(indirect2[a1]);
|
||||
get_block_cache(indirect2[a1] as usize, Arc::clone(block_device))
|
||||
.lock()
|
||||
.modify(0, |indirect1: &mut IndirectBlock| {
|
||||
for entry in indirect1.iter().take(b1) {
|
||||
v.push(*entry);
|
||||
}
|
||||
});
|
||||
//indirect2[a1] = 0;
|
||||
}
|
||||
});
|
||||
self.indirect2 = 0;
|
||||
v
|
||||
}
|
||||
pub fn read_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
buf: &mut [u8],
|
||||
block_device: &Arc<dyn BlockDevice>,
|
||||
) -> usize {
|
||||
let mut start = offset;
|
||||
let end = (offset + buf.len()).min(self.size as usize);
|
||||
if start >= end {
|
||||
return 0;
|
||||
}
|
||||
let mut start_block = start / BLOCK_SZ;
|
||||
let mut read_size = 0usize;
|
||||
loop {
|
||||
// calculate end of current block
|
||||
let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
|
||||
end_current_block = end_current_block.min(end);
|
||||
// read and update read size
|
||||
let block_read_size = end_current_block - start;
|
||||
let dst = &mut buf[read_size..read_size + block_read_size];
|
||||
get_block_cache(
|
||||
self.get_block_id(start_block as u32, block_device) as usize,
|
||||
Arc::clone(block_device),
|
||||
)
|
||||
.lock()
|
||||
.read(0, |data_block: &DataBlock| {
|
||||
let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size];
|
||||
dst.copy_from_slice(src);
|
||||
});
|
||||
read_size += block_read_size;
|
||||
// move to next block
|
||||
if end_current_block == end {
|
||||
break;
|
||||
}
|
||||
start_block += 1;
|
||||
start = end_current_block;
|
||||
}
|
||||
read_size
|
||||
}
|
||||
/// File size must be adjusted before.
|
||||
pub fn write_at(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
buf: &[u8],
|
||||
block_device: &Arc<dyn BlockDevice>,
|
||||
) -> usize {
|
||||
let mut start = offset;
|
||||
let end = (offset + buf.len()).min(self.size as usize);
|
||||
assert!(start <= end);
|
||||
let mut start_block = start / BLOCK_SZ;
|
||||
let mut write_size = 0usize;
|
||||
loop {
|
||||
// calculate end of current block
|
||||
let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
|
||||
end_current_block = end_current_block.min(end);
|
||||
// write and update write size
|
||||
let block_write_size = end_current_block - start;
|
||||
get_block_cache(
|
||||
self.get_block_id(start_block as u32, block_device) as usize,
|
||||
Arc::clone(block_device),
|
||||
)
|
||||
.lock()
|
||||
.modify(0, |data_block: &mut DataBlock| {
|
||||
let src = &buf[write_size..write_size + block_write_size];
|
||||
let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size];
|
||||
dst.copy_from_slice(src);
|
||||
});
|
||||
write_size += block_write_size;
|
||||
// move to next block
|
||||
if end_current_block == end {
|
||||
break;
|
||||
}
|
||||
start_block += 1;
|
||||
start = end_current_block;
|
||||
}
|
||||
write_size
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DirEntry {
|
||||
name: [u8; NAME_LENGTH_LIMIT + 1],
|
||||
inode_number: u32,
|
||||
}
|
||||
|
||||
pub const DIRENT_SZ: usize = 32;
|
||||
|
||||
impl DirEntry {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
name: [0u8; NAME_LENGTH_LIMIT + 1],
|
||||
inode_number: 0,
|
||||
}
|
||||
}
|
||||
pub fn new(name: &str, inode_number: u32) -> Self {
|
||||
let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1];
|
||||
bytes[..name.len()].copy_from_slice(name.as_bytes());
|
||||
Self {
|
||||
name: bytes,
|
||||
inode_number,
|
||||
}
|
||||
}
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { core::slice::from_raw_parts(self as *const _ as usize as *const u8, DIRENT_SZ) }
|
||||
}
|
||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { core::slice::from_raw_parts_mut(self as *mut _ as usize as *mut u8, DIRENT_SZ) }
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
let len = (0usize..).find(|i| self.name[*i] == 0).unwrap();
|
||||
core::str::from_utf8(&self.name[..len]).unwrap()
|
||||
}
|
||||
pub fn inode_number(&self) -> u32 {
|
||||
self.inode_number
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue