diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..2d7932f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "rcore-tutorial-v3", + "build": { + "dockerfile": "../Dockerfile", + "args": { + "QEMU_VERSION": "7.0.0", + "DEBIAN_FRONTEND": "noninteractive", + "GDB_VERSION": "14.1" + } + }, + "postCreateCommand": "rustup show", + "customizations": { + "vscode": { + "extensions": [ + "rust-lang.rust-analyzer", + "ms-vscode.cpptools", + "tamasfe.even-better-toml" + ] + } + } +} diff --git a/.dockerignore b/.dockerignore index df3359d..8971c06 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -*/* \ No newline at end of file +*/* +!rust-toolchain.toml \ No newline at end of file diff --git a/.github/workflows/doc-and-test.yml b/.github/workflows/doc-and-test.yml new file mode 100644 index 0000000..f4027c2 --- /dev/null +++ b/.github/workflows/doc-and-test.yml @@ -0,0 +1,68 @@ +name: Build Rust Doc And Run tests + +on: [push] + +env: + CARGO_TERM_COLOR: always + rust_toolchain: nightly-2024-01-18 + +jobs: + build-doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ env.rust_toolchain }} + components: rust-src, llvm-tools-preview + target: riscv64gc-unknown-none-elf + - name: Build doc + run: cd os && cargo doc --no-deps --verbose + - name: Deploy to Github Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc + destination_dir: ${{ github.ref_name }} + + run-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ env.rust_toolchain }} + components: rust-src, llvm-tools-preview + target: riscv64gc-unknown-none-elf + - uses: actions-rs/install@v0.1 + with: + crate: cargo-binutils + version: latest + use-tool-cache: true + - name: Cache QEMU + uses: actions/cache@v3 + with: + path: qemu-7.0.0 + key: qemu-7.0.0-x86_64-riscv64 + - name: Install QEMU + run: | + sudo apt-get update + sudo apt-get install ninja-build -y + if [ ! -d qemu-7.0.0 ]; then + wget https://download.qemu.org/qemu-7.0.0.tar.xz + tar -xf qemu-7.0.0.tar.xz + cd qemu-7.0.0 + ./configure --target-list=riscv64-softmmu + make -j + else + cd qemu-7.0.0 + fi + sudo make install + qemu-system-riscv64 --version + + - name: Run usertests + run: cd os && make run TEST=1 + timeout-minutes: 10 + diff --git a/.gitignore b/.gitignore index aa359a4..45fffb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,16 @@ -.idea -Cargo.lock -target +.*/* +!.github/* +!.vscode/settings.json +!.devcontainer/devcontainer.json + +**/target/ +**/Cargo.lock + os/src/link_app.S +os/src/linker.ld os/last-* -os/Cargo.lock -user/build -user/target/* -user/.idea/* -user/Cargo.lock -easy-fs/Cargo.lock -easy-fs/target/* -easy-fs-fuse/Cargo.lock -easy-fs-fuse/target/* +os/.gdb_history +os/virt.out tools/ -pushall.sh \ No newline at end of file +pushall.sh +.vscode/*.log diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6a40655 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + // Prevent "can't find crate for `test`" error on no_std + // Ref: https://github.com/rust-lang/vscode-rust/issues/729 + // For vscode-rust plugin users: + "rust.target": "riscv64gc-unknown-none-elf", + "rust.all_targets": false, + // For Rust Analyzer plugin users: + "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", + "rust-analyzer.checkOnSave.allTargets": false, + // "rust-analyzer.cargo.features": [ + // "board_qemu" + // ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ac784bc..30731a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,72 @@ -FROM ubuntu:18.04 -LABEL maintainer="dinghao188" \ - version="1.1" \ - description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3" +# syntax=docker/dockerfile:1 -#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 +# Stage 1 Build QEMU +# - https://www.qemu.org/download/ +# - https://wiki.qemu.org/Hosts/Linux#Building_QEMU_for_Linux +# - https://wiki.qemu.org/Documentation/Platforms/RISCV -#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 \ +FROM ubuntu:20.04 as build_qemu - #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 +ARG QEMU_VERSION=7.0.0 -#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 \ No newline at end of file +RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ + sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y wget build-essential libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build + +RUN wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz && \ + tar xf qemu-${QEMU_VERSION}.tar.xz && \ + cd qemu-${QEMU_VERSION} && \ + ./configure --target-list=riscv64-softmmu,riscv64-linux-user && \ + make -j$(nproc) && \ + make install + +# Stage 2 Set Lab Environment +FROM ubuntu:20.04 as build + +WORKDIR /tmp + +# 2.0. Install general tools +RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y jq curl git python3 wget build-essential \ + # qemu dependency + libglib2.0-0 libfdt1 libpixman-1-0 zlib1g \ + # gdb + gdb-multiarch + +# 2.1. Copy qemu +COPY --from=build_qemu /usr/local/bin/* /usr/local/bin + +# 2.2. Install Rust +# - https://www.rust-lang.org/tools/install +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static \ + RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly + +# 2.3. Build env for labs +# See os/Makefile `env:` for example. +# This avoids having to wait for these steps each time using a new container. +COPY rust-toolchain.toml rust-toolchain.toml +RUN rustup target add riscv64gc-unknown-none-elf && \ + cargo install toml-cli cargo-binutils && \ + RUST_VERSION=$(toml get -r rust-toolchain.toml toolchain.channel) && \ + Components=$(toml get -r rust-toolchain.toml toolchain.components | jq -r 'join(" ")') && \ + rustup install $RUST_VERSION && \ + rustup component add --toolchain $RUST_VERSION $Components + +# 2.4. Set GDB +RUN ln -s /usr/bin/gdb-multiarch /usr/bin/riscv64-unknown-elf-gdb + +# Stage 3 Sanity checking +FROM build as test +RUN qemu-system-riscv64 --version && \ + qemu-riscv64 --version && \ + rustup --version && \ + cargo --version && \ + rustc --version && \ + riscv64-unknown-elf-gdb --version \ No newline at end of file diff --git a/Makefile b/Makefile index 2e33976..ee4d7d1 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,12 @@ -DOCKER_NAME ?= dinghao188/rcore-tutorial +DOCKER_TAG ?= rcore-tutorial-v3:latest .PHONY: docker build_docker - + docker: - docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME} + docker run --rm -it -v ${PWD}:/mnt -w /mnt --name rcore-tutorial-v3 ${DOCKER_TAG} bash build_docker: - docker build -t ${DOCKER_NAME} . + docker build -t ${DOCKER_TAG} --target build . + +fmt: + cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd .. + diff --git a/README.md b/README.md index e2dc4e3..2df01b0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # rCore-Tutorial-v3 -rCore-Tutorial version 3.5. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/). +rCore-Tutorial version 3.6. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/). + +rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS) + +If you don't know Rust Language and try to learn it, please visit [Rust Learning Resources](https://github.com/rcore-os/rCore/wiki/study-resource-of-system-programming-in-RUST) + +Official QQ group number: 735045051 ## news -- 2021.11.20: Now we are updating our labs. Please checkout chX-dev Branches for our current new labs. (Notice: please see the [Dependency] section in the end of this doc) +- 23/06/2022: Version 3.6.0 is on the way! Now we directly update the code on chX branches, please periodically check if there are any updates. ## Overview @@ -12,7 +18,7 @@ This project aims to show how to write an **Unix-like OS** running on **RISC-V** * 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 + * concurrency of multiple processes each of which contains mutiple native threads * preemptive scheduling(Round-Robin algorithm) * dynamic memory management in kernel * virtual memory @@ -21,15 +27,233 @@ This project aims to show how to write an **Unix-like OS** running on **RISC-V** * **only 4K+ LoC** * [A detailed documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/) in spite of the lack of comments in the code(English version is not available at present) +## Prerequisites + +### Install Rust + +See [official guide](https://www.rust-lang.org/tools/install). + +Install some tools: + +```sh +$ rustup target add riscv64gc-unknown-none-elf +$ cargo install cargo-binutils --vers =0.3.3 +$ rustup component add llvm-tools-preview +$ rustup component add rust-src +``` + +### Install Qemu + +Here we manually compile and install Qemu 7.0.0. For example, on Ubuntu 18.04: + +```sh +# install dependency packages +$ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ + gawk build-essential bison flex texinfo gperf libtool patchutils bc \ + zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 python3-pip ninja-build +# download Qemu source code +$ wget https://download.qemu.org/qemu-7.0.0.tar.xz +# extract to qemu-7.0.0/ +$ tar xvJf qemu-7.0.0.tar.xz +$ cd qemu-7.0.0 +# build +$ ./configure --target-list=riscv64-softmmu,riscv64-linux-user +$ make -j$(nproc) +``` + +Then, add following contents to `~/.bashrc`(please adjust these paths according to your environment): + +``` +export PATH=$PATH:/path/to/qemu-7.0.0/build +``` + +Finally, update the current shell: + +```sh +$ source ~/.bashrc +``` + +Now we can check the version of Qemu: + +```sh +$ qemu-system-riscv64 --version +QEMU emulator version 7.0.0 +Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers +``` + +### Install RISC-V GNU Embedded Toolchain(including GDB) + +Download the compressed file according to your platform From [Sifive website](https://www.sifive.com/software)(Ctrl+F 'toolchain'). + +Extract it and append the location of the 'bin' directory under its root directory to `$PATH`. + +For example, we can check the version of GDB: + +```sh +$ riscv64-unknown-elf-gdb --version +GNU gdb (SiFive GDB-Metal 10.1.0-2020.12.7) 10.1 +Copyright (C) 2020 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later +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 -TODO: +### 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 -Now we are still updating our project, you can find latest changes on branches `chX-dev` such as `ch1-dev`. We are intended to publish first release 3.5.0 after completing most of the tasks mentioned below. +Our first release 3.6.0 (chapter 1-9) has been published, and we are still working on it. -Overall progress: ch7 +* chapter 9: need more descripts about different I/O devices + +Here are the updates since 3.5.0: ### Completed @@ -43,25 +267,28 @@ Overall progress: ch7 * [x] flush all block cache to disk after a fs transaction which involves write operation * [x] replace `spin::Mutex` with `UPSafeCell` before SMP chapter * [x] add codes for a new chapter about synchronization & mutual exclusion(uniprocessor only) - +* [x] bug fix: we should call `find_pte` rather than `find_pte_create` in `PageTable::unmap` +* [x] clarify: "check validity of level-3 pte in `find_pte` instead of checking it outside this function" should not be a bug +* [x] code of chapter 8: synchronization on a uniprocessor +* [x] switch the code of chapter 6 and chapter 7 +* [x] support signal mechanism in chapter 7/8(only works for apps with a single thread) +* [x] Add boards/ directory and support rustdoc, for example you can use `cargo doc --no-deps --open` to view the documentation of a crate +* [x] code of chapter 9: device drivers based on interrupts, including UART, block, keyboard, mouse, gpu devices +* [x] add CI autotest and doc in github ### Todo(High priority) -* [ ] support Allwinner's RISC-V D1 chip -* [ ] bug fix: we should call `find_pte` rather than `find_pte_create` in `PageTable::unmap` -* [ ] bug fix: check validity of level-3 pte in `find_pte` instead of checking it outside this function +* [ ] review documentation, current progress: 8/9 * [ ] 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 - +* [ ] add more os functions for understanding os conecpts and principles ### Todo(Low priority) * [ ] rewrite practice doc and remove some inproper questions * [ ] provide smooth debug experience at a Rust source code level * [ ] format the code using official tools - ### Crates We will add them later. diff --git a/bootloader/rustsbi-k210.bin b/bootloader/rustsbi-k210.bin deleted file mode 100755 index c53ed1f..0000000 Binary files a/bootloader/rustsbi-k210.bin and /dev/null differ diff --git a/bootloader/rustsbi-qemu.bin b/bootloader/rustsbi-qemu.bin index ddbf336..f9015b8 100755 Binary files a/bootloader/rustsbi-qemu.bin and b/bootloader/rustsbi-qemu.bin differ diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml new file mode 100644 index 0000000..d8d4e7f --- /dev/null +++ b/easy-fs-fuse/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "easy-fs-fuse" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "2.33.3" +easy-fs = { path = "../easy-fs" } +rand = "0.8.0" + +# [features] +# board_qemu = [] +# board_k210 = [] diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs new file mode 100644 index 0000000..dceec15 --- /dev/null +++ b/easy-fs-fuse/src/main.rs @@ -0,0 +1,155 @@ +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); + +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(32 * 2048 * 512).unwrap(); + f + }))); + // 32MiB, at most 4095 files + let efs = EasyFileSystem::create(block_file, 32 * 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 = 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::() % 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(()) +} diff --git a/easy-fs/.gitignore b/easy-fs/.gitignore new file mode 100644 index 0000000..79f5db6 --- /dev/null +++ b/easy-fs/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +Cargo.lock diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml new file mode 100644 index 0000000..7a2f38e --- /dev/null +++ b/easy-fs/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "easy-fs" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +spin = "0.7.0" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } + +[profile.release] +debug = true + +[features] +board_qemu = [] +board_k210 = [] \ No newline at end of file diff --git a/easy-fs/src/bitmap.rs b/easy-fs/src/bitmap.rs new file mode 100644 index 0000000..2cea613 --- /dev/null +++ b/easy-fs/src/bitmap.rs @@ -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) -> Option { + 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, 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 + } +} diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs new file mode 100644 index 0000000..1b3b969 --- /dev/null +++ b/easy-fs/src/block_cache.rs @@ -0,0 +1,142 @@ +use super::{BlockDevice, BLOCK_SZ}; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::Vec; +use lazy_static::*; +use spin::Mutex; + +pub struct BlockCache { + cache: Vec, + block_id: usize, + block_device: Arc, + modified: bool, +} + +impl BlockCache { + /// Load a new BlockCache from disk. + pub fn new(block_id: usize, block_device: Arc) -> Self { + // for alignment and move effciency + let mut cache = vec![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(&self, offset: usize) -> &T + where + T: Sized, + { + let type_size = core::mem::size_of::(); + assert!(offset + type_size <= BLOCK_SZ); + let addr = self.addr_of_offset(offset); + unsafe { &*(addr as *const T) } + } + + pub fn get_mut(&mut self, offset: usize) -> &mut T + where + T: Sized, + { + let type_size = core::mem::size_of::(); + 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(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V { + f(self.get_ref(offset)) + } + + pub fn modify(&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>)>, +} + +impl BlockCacheManager { + pub fn new() -> Self { + Self { + queue: VecDeque::new(), + } + } + + pub fn get_block_cache( + &mut self, + block_id: usize, + block_device: Arc, + ) -> Arc> { + 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 = + Mutex::new(BlockCacheManager::new()); +} + +pub fn get_block_cache( + block_id: usize, + block_device: Arc, +) -> Arc> { + 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(); + } +} diff --git a/easy-fs/src/block_dev.rs b/easy-fs/src/block_dev.rs new file mode 100644 index 0000000..eb39fbd --- /dev/null +++ b/easy-fs/src/block_dev.rs @@ -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); +} diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs new file mode 100644 index 0000000..82e95ae --- /dev/null +++ b/easy-fs/src/efs.rs @@ -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, + 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, + total_blocks: u32, + inode_bitmap_blocks: u32, + ) -> Arc> { + // 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::() + 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) -> Arc> { + // 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>) -> 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::(); + 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, + ) + } +} diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs new file mode 100644 index 0000000..618484c --- /dev/null +++ b/easy-fs/src/layout.rs @@ -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) -> 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, + block_device: &Arc, + ) { + 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) -> Vec { + let mut v: Vec = 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, + ) -> 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, + ) -> 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 + } +} diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs new file mode 100644 index 0000000..fa36e6b --- /dev/null +++ b/easy-fs/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] + +extern crate alloc; + +mod bitmap; +mod block_cache; +mod block_dev; +mod efs; +mod layout; +mod vfs; + +pub const BLOCK_SZ: usize = 512; +use bitmap::Bitmap; +use block_cache::{block_cache_sync_all, get_block_cache}; +pub use block_dev::BlockDevice; +pub use efs::EasyFileSystem; +use layout::*; +pub use vfs::Inode; diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs new file mode 100644 index 0000000..7290fa4 --- /dev/null +++ b/easy-fs/src/vfs.rs @@ -0,0 +1,186 @@ +use super::{ + block_cache_sync_all, get_block_cache, BlockDevice, DirEntry, DiskInode, DiskInodeType, + EasyFileSystem, DIRENT_SZ, +}; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; +use spin::{Mutex, MutexGuard}; + +pub struct Inode { + block_id: usize, + block_offset: usize, + fs: Arc>, + block_device: Arc, +} + +impl Inode { + /// We should not acquire efs lock here. + pub fn new( + block_id: u32, + block_offset: usize, + fs: Arc>, + block_device: Arc, + ) -> Self { + Self { + block_id: block_id as usize, + block_offset, + fs, + block_device, + } + } + + fn read_disk_inode(&self, f: impl FnOnce(&DiskInode) -> V) -> V { + get_block_cache(self.block_id, Arc::clone(&self.block_device)) + .lock() + .read(self.block_offset, f) + } + + fn modify_disk_inode(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V { + get_block_cache(self.block_id, Arc::clone(&self.block_device)) + .lock() + .modify(self.block_offset, f) + } + + fn find_inode_id(&self, name: &str, disk_inode: &DiskInode) -> Option { + // assert it is a directory + assert!(disk_inode.is_dir()); + let file_count = (disk_inode.size as usize) / DIRENT_SZ; + let mut dirent = DirEntry::empty(); + for i in 0..file_count { + assert_eq!( + disk_inode.read_at(DIRENT_SZ * i, dirent.as_bytes_mut(), &self.block_device,), + DIRENT_SZ, + ); + if dirent.name() == name { + return Some(dirent.inode_number() as u32); + } + } + None + } + + pub fn find(&self, name: &str) -> Option> { + let fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| { + self.find_inode_id(name, disk_inode).map(|inode_id| { + let (block_id, block_offset) = fs.get_disk_inode_pos(inode_id); + Arc::new(Self::new( + block_id, + block_offset, + self.fs.clone(), + self.block_device.clone(), + )) + }) + }) + } + + fn increase_size( + &self, + new_size: u32, + disk_inode: &mut DiskInode, + fs: &mut MutexGuard, + ) { + if new_size < disk_inode.size { + return; + } + let blocks_needed = disk_inode.blocks_num_needed(new_size); + let mut v: Vec = Vec::new(); + for _ in 0..blocks_needed { + v.push(fs.alloc_data()); + } + disk_inode.increase_size(new_size, v, &self.block_device); + } + + pub fn create(&self, name: &str) -> Option> { + let mut fs = self.fs.lock(); + let op = |root_inode: &mut DiskInode| { + // assert it is a directory + assert!(root_inode.is_dir()); + // has the file been created? + self.find_inode_id(name, root_inode) + }; + if self.modify_disk_inode(op).is_some() { + return None; + } + // create a new file + // alloc a inode with an indirect block + let new_inode_id = fs.alloc_inode(); + // initialize inode + let (new_inode_block_id, new_inode_block_offset) = fs.get_disk_inode_pos(new_inode_id); + get_block_cache(new_inode_block_id as usize, Arc::clone(&self.block_device)) + .lock() + .modify(new_inode_block_offset, |new_inode: &mut DiskInode| { + new_inode.initialize(DiskInodeType::File); + }); + self.modify_disk_inode(|root_inode| { + // append file in the dirent + let file_count = (root_inode.size as usize) / DIRENT_SZ; + let new_size = (file_count + 1) * DIRENT_SZ; + // increase size + self.increase_size(new_size as u32, root_inode, &mut fs); + // write dirent + let dirent = DirEntry::new(name, new_inode_id); + root_inode.write_at( + file_count * DIRENT_SZ, + dirent.as_bytes(), + &self.block_device, + ); + }); + + let (block_id, block_offset) = fs.get_disk_inode_pos(new_inode_id); + block_cache_sync_all(); + // return inode + Some(Arc::new(Self::new( + block_id, + block_offset, + self.fs.clone(), + self.block_device.clone(), + ))) + // release efs lock automatically by compiler + } + + pub fn ls(&self) -> Vec { + let _fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| { + let file_count = (disk_inode.size as usize) / DIRENT_SZ; + let mut v: Vec = Vec::new(); + for i in 0..file_count { + let mut dirent = DirEntry::empty(); + assert_eq!( + disk_inode.read_at(i * DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device,), + DIRENT_SZ, + ); + v.push(String::from(dirent.name())); + } + v + }) + } + + pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize { + let _fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| disk_inode.read_at(offset, buf, &self.block_device)) + } + + pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize { + let mut fs = self.fs.lock(); + let size = self.modify_disk_inode(|disk_inode| { + self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs); + disk_inode.write_at(offset, buf, &self.block_device) + }); + block_cache_sync_all(); + size + } + + pub fn clear(&self) { + let mut fs = self.fs.lock(); + self.modify_disk_inode(|disk_inode| { + let size = disk_inode.size; + let data_blocks_dealloc = disk_inode.clear_size(&self.block_device); + assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize); + for data_block in data_blocks_dealloc.into_iter() { + fs.dealloc_data(data_block); + } + }); + block_cache_sync_all(); + } +} diff --git a/figures/logo.png b/figures/logo.png new file mode 100644 index 0000000..84af081 Binary files /dev/null and b/figures/logo.png differ diff --git a/os/.cargo/config b/os/.cargo/config.toml similarity index 100% rename from os/.cargo/config rename to os/.cargo/config.toml diff --git a/os/Cargo.toml b/os/Cargo.toml index 0d1bb34..fde4b6c 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -2,14 +2,24 @@ name = "os" version = "0.1.0" authors = ["Yifan Wu "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +buddy_system_allocator = "0.6" +bitflags = "1.2.1" +xmas-elf = "0.7.0" +volatile = "0.3" +virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } +lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "db42380" } +easy-fs = { path = "../easy-fs" } +embedded-graphics = "0.7.1" +tinybmp = "0.3.1" +log = "0.4" +sbi-rt = { version = "0.0.2", features = ["legacy"] } -[features] -board_qemu = [] -board_k210 = [] \ No newline at end of file +[profile.release] +debug = true diff --git a/os/Makefile b/os/Makefile index a9ed0b7..b0c3463 100644 --- a/os/Makefile +++ b/os/Makefile @@ -4,23 +4,27 @@ MODE := release KERNEL_ELF := target/$(TARGET)/$(MODE)/os KERNEL_BIN := $(KERNEL_ELF).bin DISASM_TMP := target/$(TARGET)/$(MODE)/asm +FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img +APPS := ../user/src/bin/* # BOARD -BOARD ?= qemu +BOARD := qemu SBI ?= rustsbi BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin -K210_BOOTLOADER_SIZE := 131072 -# KERNEL ENTRY -ifeq ($(BOARD), qemu) - KERNEL_ENTRY_PA := 0x80200000 -else ifeq ($(BOARD), k210) - KERNEL_ENTRY_PA := 0x80020000 +# GUI +GUI ?= off +ifeq ($(GUI), off) + GUI_OPTION := -display none endif -# Run K210 -K210-SERIALPORT = /dev/ttyUSB0 -K210-BURNER = ../tools/kflash.py +# Building mode argument +ifeq ($(MODE), release) + MODE_ARG := --release +endif + +# KERNEL ENTRY +KERNEL_ENTRY_PA := 0x80200000 # Binutils OBJDUMP := rust-objdump --arch-name=riscv64 @@ -29,31 +33,31 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64 # Disassembly DISASM ?= -x -TEST ?= 0 +# Run usertests or usershell +TEST ?= -build: env switch-check $(KERNEL_BIN) - -switch-check: -ifeq ($(BOARD), qemu) - (which last-qemu) || (rm last-k210 -f && touch last-qemu && make clean) -else ifeq ($(BOARD), k210) - (which last-k210) || (rm last-qemu -f && touch last-k210 && make clean) -endif +build: env $(KERNEL_BIN) fs-img env: (rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET) - cargo install cargo-binutils --vers =0.3.3 + cargo install cargo-binutils rustup component add rust-src rustup component add llvm-tools-preview $(KERNEL_BIN): kernel @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ -kernel: +fs-img: $(APPS) @cd ../user && make build TEST=$(TEST) + @rm -f $(FS_IMG) + @cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/ + +$(APPS): + +kernel: @echo Platform: $(BOARD) @cp src/linker-$(BOARD).ld src/linker.ld - @cargo build --release --features "board_$(BOARD)" + @cargo build --release @rm src/linker.ld clean: @@ -64,34 +68,45 @@ disasm: kernel disasm-vim: kernel @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP) - @vim $(DISASM_TMP) + @nvim $(DISASM_TMP) @rm $(DISASM_TMP) run: run-inner - +QEMU_ARGS := -machine virt \ + -bios $(BOOTLOADER) \ + -serial stdio \ + $(GUI_OPTION) \ + -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ + -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0 \ + -device virtio-gpu-device \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000,hostfwd=tcp::6201-:80 -run-inner: build -ifeq ($(BOARD),qemu) - @qemu-system-riscv64 \ - -machine virt \ - -nographic \ - -bios $(BOOTLOADER) \ - -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -else - (which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools) - @cp $(BOOTLOADER) $(BOOTLOADER).copy - @dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1 - @mv $(BOOTLOADER).copy $(KERNEL_BIN) - @sudo chmod 777 $(K210-SERIALPORT) - python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN) - python3 -m serial.tools.miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200 -endif +fdt: + @qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out + fdtdump virt.out -debug: build +QEMU_NAME := qemu-system-riscv64 +qemu-version-check: + @sh scripts/qemu-ver-check.sh $(QEMU_NAME) + +run-inner: qemu-version-check build + @qemu-system-riscv64 $(QEMU_ARGS) + +debug: qemu-version-check build @tmux new-session -d \ - "qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -s -S" && \ + "qemu-system-riscv64 $(QEMU_ARGS) -s -S" && \ tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ tmux -2 attach-session -d -.PHONY: build env kernel clean disasm disasm-vim run-inner switch-check +gdbserver: qemu-version-check build + @qemu-system-riscv64 $(QEMU_ARGS) -s -S + +gdbclient: + @riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234' + +.PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient fdt qemu-version-check diff --git a/os/build.rs b/os/build.rs index 4a44c56..5529b4f 100644 --- a/os/build.rs +++ b/os/build.rs @@ -1,56 +1,6 @@ -use std::fs::{read_dir, File}; -use std::io::{Result, Write}; +static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/"; fn main() { println!("cargo:rerun-if-changed=../user/src/"); println!("cargo:rerun-if-changed={}", TARGET_PATH); - insert_app_data().unwrap(); -} - -static TARGET_PATH: &str = "../user/build/bin/"; - -fn insert_app_data() -> Result<()> { - let mut f = File::create("src/link_app.S").unwrap(); - let mut apps: Vec<_> = read_dir("../user/build/bin/") - .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(); - apps.sort(); - - writeln!( - f, - r#" - .align 3 - .section .data - .global _num_app -_num_app: - .quad {}"#, - apps.len() - )?; - - for i in 0..apps.len() { - writeln!(f, r#" .quad app_{}_start"#, i)?; - } - writeln!(f, r#" .quad app_{}_end"#, apps.len() - 1)?; - - for (idx, app) in apps.iter().enumerate() { - println!("app_{}: {}", idx, app); - writeln!( - f, - r#" - .section .data - .global app_{0}_start - .global app_{0}_end -app_{0}_start: - .incbin "{2}{1}.bin" -app_{0}_end:"#, - idx, app, TARGET_PATH - )?; - } - Ok(()) } diff --git a/os/scripts/qemu-ver-check.sh b/os/scripts/qemu-ver-check.sh new file mode 100644 index 0000000..8c20456 --- /dev/null +++ b/os/scripts/qemu-ver-check.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Argument1: The filename of qemu executable, e.g. qemu-system-riscv64 +QEMU_PATH=$(which $1) +RET=$? +MINIMUM_MAJOR_VERSION=7 +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' +if [ $RET != 0 ] +then + echo "$1 not found" + exit 1 +else + QEMU_VERSION=$($1 --version|head -n 1|awk '{print $4}') + MAJOR_VERSION=$(echo $QEMU_VERSION|cut -c1-1) + if [ $MAJOR_VERSION -lt $MINIMUM_MAJOR_VERSION ] + then + echo "${RED}Error: Required major version of QEMU is ${MINIMUM_MAJOR_VERSION}, " \ + "but current is ${QEMU_VERSION}.${NC}" + exit 1 + else + echo "${GREEN}QEMU version is ${QEMU_VERSION}(>=${MINIMUM_MAJOR_VERSION}), OK!${NC}" + exit 0 + fi +fi diff --git a/os/src/assert/desktop.bmp b/os/src/assert/desktop.bmp new file mode 100644 index 0000000..a754f7a Binary files /dev/null and b/os/src/assert/desktop.bmp differ diff --git a/os/src/assert/file.bmp b/os/src/assert/file.bmp new file mode 100644 index 0000000..4a4e83b Binary files /dev/null and b/os/src/assert/file.bmp differ diff --git a/os/src/assert/folder.bmp b/os/src/assert/folder.bmp new file mode 100644 index 0000000..c8384cb Binary files /dev/null and b/os/src/assert/folder.bmp differ diff --git a/os/src/assert/mouse.bmp b/os/src/assert/mouse.bmp new file mode 100644 index 0000000..22a4613 Binary files /dev/null and b/os/src/assert/mouse.bmp differ diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs new file mode 100644 index 0000000..5daf541 --- /dev/null +++ b/os/src/boards/qemu.rs @@ -0,0 +1,55 @@ +pub const CLOCK_FREQ: usize = 12500000; +pub const MEMORY_END: usize = 0x8800_0000; + +pub const MMIO: &[(usize, usize)] = &[ + (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine + (0x2000000, 0x10000), // core local interrupter (CLINT) + (0xc000000, 0x210000), // VIRT_PLIC in virt machine + (0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine +]; + +pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock; +pub type CharDeviceImpl = crate::drivers::chardev::NS16550a; + +pub const VIRT_PLIC: usize = 0xC00_0000; +pub const VIRT_UART: usize = 0x1000_0000; +#[allow(unused)] +pub const VIRTGPU_XRES: u32 = 1280; +#[allow(unused)] +pub const VIRTGPU_YRES: u32 = 800; + +use crate::drivers::block::BLOCK_DEVICE; +use crate::drivers::chardev::{CharDevice, UART}; +use crate::drivers::plic::{IntrTargetPriority, PLIC}; +use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; + +pub fn device_init() { + use riscv::register::sie; + let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; + let hart_id: usize = 0; + let supervisor = IntrTargetPriority::Supervisor; + let machine = IntrTargetPriority::Machine; + plic.set_threshold(hart_id, supervisor, 0); + plic.set_threshold(hart_id, machine, 1); + //irq nums: 5 keyboard, 6 mouse, 8 block, 10 uart + for intr_src_id in [5usize, 6, 8, 10] { + plic.enable(hart_id, supervisor, intr_src_id); + plic.set_priority(intr_src_id, 1); + } + unsafe { + sie::set_sext(); + } +} + +pub fn irq_handler() { + let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; + let intr_src_id = plic.claim(0, IntrTargetPriority::Supervisor); + match intr_src_id { + 5 => KEYBOARD_DEVICE.handle_irq(), + 6 => MOUSE_DEVICE.handle_irq(), + 8 => BLOCK_DEVICE.handle_irq(), + 10 => UART.handle_irq(), + _ => panic!("unsupported IRQ {}", intr_src_id), + } + plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); +} diff --git a/os/src/config.rs b/os/src/config.rs index 3b994cd..d9b9b71 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -1,11 +1,12 @@ +#[allow(unused)] + pub const USER_STACK_SIZE: usize = 4096 * 2; pub const KERNEL_STACK_SIZE: usize = 4096 * 2; -pub const MAX_APP_NUM: usize = 8; -pub const APP_BASE_ADDRESS: usize = 0x80400000; -pub const APP_SIZE_LIMIT: usize = 0x20000; +pub const KERNEL_HEAP_SIZE: usize = 0x100_0000; +pub const PAGE_SIZE: usize = 0x1000; +pub const PAGE_SIZE_BITS: usize = 0xc; -#[cfg(feature = "board_k210")] -pub const CLOCK_FREQ: usize = 403000000 / 62; +pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; +pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE; -#[cfg(feature = "board_qemu")] -pub const CLOCK_FREQ: usize = 12500000; \ No newline at end of file +pub use crate::board::{CLOCK_FREQ, MEMORY_END, MMIO}; diff --git a/os/src/console.rs b/os/src/console.rs index 2bd5593..085637b 100644 --- a/os/src/console.rs +++ b/os/src/console.rs @@ -1,12 +1,13 @@ +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; use core::fmt::{self, Write}; -use crate::sbi::console_putchar; struct Stdout; impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.chars() { - console_putchar(c as usize); + UART.write(c as u8); } Ok(()) } @@ -19,15 +20,13 @@ pub fn print(args: fmt::Arguments) { #[macro_export] macro_rules! print { ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::console::print(format_args!($fmt $(, $($arg)+)?)); + $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)+)?)); + $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) } } - - diff --git a/os/src/drivers/block/mod.rs b/os/src/drivers/block/mod.rs new file mode 100644 index 0000000..add8da0 --- /dev/null +++ b/os/src/drivers/block/mod.rs @@ -0,0 +1,28 @@ +mod virtio_blk; + +pub use virtio_blk::VirtIOBlock; + +use crate::board::BlockDeviceImpl; +use alloc::sync::Arc; +use easy_fs::BlockDevice; +use lazy_static::*; + +lazy_static! { + pub static ref BLOCK_DEVICE: Arc = Arc::new(BlockDeviceImpl::new()); +} + +#[allow(unused)] +pub fn block_device_test() { + let block_device = BLOCK_DEVICE.clone(); + let mut write_buffer = [0u8; 512]; + let mut read_buffer = [0u8; 512]; + for i in 0..512 { + for byte in write_buffer.iter_mut() { + *byte = i as u8; + } + block_device.write_block(i as usize, &write_buffer); + block_device.read_block(i as usize, &mut read_buffer); + assert_eq!(write_buffer, read_buffer); + } + println!("block device test passed!"); +} diff --git a/os/src/drivers/block/virtio_blk.rs b/os/src/drivers/block/virtio_blk.rs new file mode 100644 index 0000000..fb89084 --- /dev/null +++ b/os/src/drivers/block/virtio_blk.rs @@ -0,0 +1,87 @@ +use super::BlockDevice; +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use crate::DEV_NON_BLOCKING_ACCESS; +use alloc::collections::BTreeMap; +use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader}; + +#[allow(unused)] +const VIRTIO0: usize = 0x10008000; + +pub struct VirtIOBlock { + virtio_blk: UPIntrFreeCell>, + condvars: BTreeMap, +} + +impl BlockDevice for VirtIOBlock { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); + if nb { + let mut resp = BlkResp::default(); + let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { + let token = unsafe { blk.read_block_nb(block_id, buf, &mut resp).unwrap() }; + self.condvars.get(&token).unwrap().wait_no_sched() + }); + schedule(task_cx_ptr); + assert_eq!( + resp.status(), + RespStatus::Ok, + "Error when reading VirtIOBlk" + ); + } else { + self.virtio_blk + .exclusive_access() + .read_block(block_id, buf) + .expect("Error when reading VirtIOBlk"); + } + } + fn write_block(&self, block_id: usize, buf: &[u8]) { + let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); + if nb { + let mut resp = BlkResp::default(); + let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { + let token = unsafe { blk.write_block_nb(block_id, buf, &mut resp).unwrap() }; + self.condvars.get(&token).unwrap().wait_no_sched() + }); + schedule(task_cx_ptr); + assert_eq!( + resp.status(), + RespStatus::Ok, + "Error when writing VirtIOBlk" + ); + } else { + self.virtio_blk + .exclusive_access() + .write_block(block_id, buf) + .expect("Error when writing VirtIOBlk"); + } + } + fn handle_irq(&self) { + self.virtio_blk.exclusive_session(|blk| { + while let Ok(token) = blk.pop_used() { + self.condvars.get(&token).unwrap().signal(); + } + }); + } +} + +impl VirtIOBlock { + pub fn new() -> Self { + let virtio_blk = unsafe { + UPIntrFreeCell::new( + VirtIOBlk::::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), + ) + }; + let mut condvars = BTreeMap::new(); + let channels = virtio_blk.exclusive_access().virt_queue_size(); + for i in 0..channels { + let condvar = Condvar::new(); + condvars.insert(i, condvar); + } + Self { + virtio_blk, + condvars, + } + } +} diff --git a/os/src/drivers/bus/mod.rs b/os/src/drivers/bus/mod.rs new file mode 100644 index 0000000..d43f304 --- /dev/null +++ b/os/src/drivers/bus/mod.rs @@ -0,0 +1 @@ +pub mod virtio; diff --git a/os/src/drivers/bus/virtio.rs b/os/src/drivers/bus/virtio.rs new file mode 100644 index 0000000..069d478 --- /dev/null +++ b/os/src/drivers/bus/virtio.rs @@ -0,0 +1,48 @@ +use crate::mm::{ + frame_alloc_more, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum, + StepByOne, VirtAddr, +}; +use crate::sync::UPIntrFreeCell; +use alloc::vec::Vec; +use lazy_static::*; +use virtio_drivers::Hal; + +lazy_static! { + static ref QUEUE_FRAMES: UPIntrFreeCell> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +pub struct VirtioHal; + +impl Hal for VirtioHal { + fn dma_alloc(pages: usize) -> usize { + let trakcers = frame_alloc_more(pages); + let ppn_base = trakcers.as_ref().unwrap().last().unwrap().ppn; + QUEUE_FRAMES + .exclusive_access() + .append(&mut trakcers.unwrap()); + let pa: PhysAddr = ppn_base.into(); + pa.0 + } + + fn dma_dealloc(pa: usize, pages: usize) -> i32 { + let pa = PhysAddr::from(pa); + let mut ppn_base: PhysPageNum = pa.into(); + for _ in 0..pages { + frame_dealloc(ppn_base); + ppn_base.step(); + } + 0 + } + + fn phys_to_virt(addr: usize) -> usize { + addr + } + + fn virt_to_phys(vaddr: usize) -> usize { + PageTable::from_token(kernel_token()) + .translate_va(VirtAddr::from(vaddr)) + .unwrap() + .0 + } +} diff --git a/os/src/drivers/chardev/mod.rs b/os/src/drivers/chardev/mod.rs new file mode 100644 index 0000000..64c168f --- /dev/null +++ b/os/src/drivers/chardev/mod.rs @@ -0,0 +1,17 @@ +mod ns16550a; + +use crate::board::CharDeviceImpl; +use alloc::sync::Arc; +use lazy_static::*; +pub use ns16550a::NS16550a; + +pub trait CharDevice { + fn init(&self); + fn read(&self) -> u8; + fn write(&self, ch: u8); + fn handle_irq(&self); +} + +lazy_static! { + pub static ref UART: Arc = Arc::new(CharDeviceImpl::new()); +} diff --git a/os/src/drivers/chardev/ns16550a.rs b/os/src/drivers/chardev/ns16550a.rs new file mode 100644 index 0000000..7122cec --- /dev/null +++ b/os/src/drivers/chardev/ns16550a.rs @@ -0,0 +1,186 @@ +///! Ref: https://www.lammertbies.nl/comm/info/serial-uart +///! Ref: ns16550a datasheet: https://datasheetspdf.com/pdf-file/605590/NationalSemiconductor/NS16550A/1 +///! Ref: ns16450 datasheet: https://datasheetspdf.com/pdf-file/1311818/NationalSemiconductor/NS16450/1 +use super::CharDevice; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use alloc::collections::VecDeque; +use bitflags::*; +use volatile::{ReadOnly, Volatile, WriteOnly}; + +bitflags! { + /// InterruptEnableRegister + pub struct IER: u8 { + const RX_AVAILABLE = 1 << 0; + const TX_EMPTY = 1 << 1; + } + + /// LineStatusRegister + pub struct LSR: u8 { + const DATA_AVAILABLE = 1 << 0; + const THR_EMPTY = 1 << 5; + } + + /// Model Control Register + pub struct MCR: u8 { + const DATA_TERMINAL_READY = 1 << 0; + const REQUEST_TO_SEND = 1 << 1; + const AUX_OUTPUT1 = 1 << 2; + const AUX_OUTPUT2 = 1 << 3; + } +} + +#[repr(C)] +#[allow(dead_code)] +struct ReadWithoutDLAB { + /// receiver buffer register + pub rbr: ReadOnly, + /// interrupt enable register + pub ier: Volatile, + /// interrupt identification register + pub iir: ReadOnly, + /// line control register + pub lcr: Volatile, + /// model control register + pub mcr: Volatile, + /// line status register + pub lsr: ReadOnly, + /// ignore MSR + _padding1: ReadOnly, + /// ignore SCR + _padding2: ReadOnly, +} + +#[repr(C)] +#[allow(dead_code)] +struct WriteWithoutDLAB { + /// transmitter holding register + pub thr: WriteOnly, + /// interrupt enable register + pub ier: Volatile, + /// ignore FCR + _padding0: ReadOnly, + /// line control register + pub lcr: Volatile, + /// modem control register + pub mcr: Volatile, + /// line status register + pub lsr: ReadOnly, + /// ignore other registers + _padding1: ReadOnly, +} + +pub struct NS16550aRaw { + base_addr: usize, +} + +impl NS16550aRaw { + fn read_end(&mut self) -> &mut ReadWithoutDLAB { + unsafe { &mut *(self.base_addr as *mut ReadWithoutDLAB) } + } + + fn write_end(&mut self) -> &mut WriteWithoutDLAB { + unsafe { &mut *(self.base_addr as *mut WriteWithoutDLAB) } + } + + pub fn new(base_addr: usize) -> Self { + Self { base_addr } + } + + pub fn init(&mut self) { + let read_end = self.read_end(); + let mut mcr = MCR::empty(); + mcr |= MCR::DATA_TERMINAL_READY; + mcr |= MCR::REQUEST_TO_SEND; + mcr |= MCR::AUX_OUTPUT2; + read_end.mcr.write(mcr); + let ier = IER::RX_AVAILABLE; + read_end.ier.write(ier); + } + + pub fn read(&mut self) -> Option { + let read_end = self.read_end(); + let lsr = read_end.lsr.read(); + if lsr.contains(LSR::DATA_AVAILABLE) { + Some(read_end.rbr.read()) + } else { + None + } + } + + pub fn write(&mut self, ch: u8) { + let write_end = self.write_end(); + loop { + if write_end.lsr.read().contains(LSR::THR_EMPTY) { + write_end.thr.write(ch); + break; + } + } + } +} + +struct NS16550aInner { + ns16550a: NS16550aRaw, + read_buffer: VecDeque, +} + +pub struct NS16550a { + inner: UPIntrFreeCell, + condvar: Condvar, +} + +impl NS16550a { + pub fn new() -> Self { + let inner = NS16550aInner { + ns16550a: NS16550aRaw::new(BASE_ADDR), + read_buffer: VecDeque::new(), + }; + //inner.ns16550a.init(); + Self { + inner: unsafe { UPIntrFreeCell::new(inner) }, + condvar: Condvar::new(), + } + } + + pub fn read_buffer_is_empty(&self) -> bool { + self.inner + .exclusive_session(|inner| inner.read_buffer.is_empty()) + } +} + +impl CharDevice for NS16550a { + fn init(&self) { + let mut inner = self.inner.exclusive_access(); + inner.ns16550a.init(); + drop(inner); + } + + fn read(&self) -> u8 { + loop { + let mut inner = self.inner.exclusive_access(); + if let Some(ch) = inner.read_buffer.pop_front() { + return ch; + } else { + let task_cx_ptr = self.condvar.wait_no_sched(); + drop(inner); + schedule(task_cx_ptr); + } + } + } + fn write(&self, ch: u8) { + let mut inner = self.inner.exclusive_access(); + inner.ns16550a.write(ch); + } + fn handle_irq(&self) { + let mut count = 0; + self.inner.exclusive_session(|inner| { + while let Some(ch) = inner.ns16550a.read() { + count += 1; + inner.read_buffer.push_back(ch); + } + }); + if count > 0 { + self.condvar.signal(); + } + } +} diff --git a/os/src/drivers/gpu/mod.rs b/os/src/drivers/gpu/mod.rs new file mode 100644 index 0000000..759633f --- /dev/null +++ b/os/src/drivers/gpu/mod.rs @@ -0,0 +1,68 @@ +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; +use alloc::{sync::Arc, vec::Vec}; +use core::any::Any; +use embedded_graphics::pixelcolor::Rgb888; +use tinybmp::Bmp; +use virtio_drivers::{VirtIOGpu, VirtIOHeader}; +const VIRTIO7: usize = 0x10007000; +pub trait GpuDevice: Send + Sync + Any { + fn update_cursor(&self); + fn get_framebuffer(&self) -> &mut [u8]; + fn flush(&self); +} + +lazy_static::lazy_static!( + pub static ref GPU_DEVICE: Arc = Arc::new(VirtIOGpuWrapper::new()); +); + +pub struct VirtIOGpuWrapper { + gpu: UPIntrFreeCell>, + fb: &'static [u8], +} +static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp"); +impl VirtIOGpuWrapper { + pub fn new() -> Self { + unsafe { + let mut virtio = + VirtIOGpu::::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap(); + + let fbuffer = virtio.setup_framebuffer().unwrap(); + let len = fbuffer.len(); + let ptr = fbuffer.as_mut_ptr(); + let fb = core::slice::from_raw_parts_mut(ptr, len); + + let bmp = Bmp::::from_slice(BMP_DATA).unwrap(); + let raw = bmp.as_raw(); + let mut b = Vec::new(); + for i in raw.image_data().chunks(3) { + let mut v = i.to_vec(); + b.append(&mut v); + if i == [255, 255, 255] { + b.push(0x0) + } else { + b.push(0xff) + } + } + virtio.setup_cursor(b.as_slice(), 50, 50, 50, 50).unwrap(); + + Self { + gpu: UPIntrFreeCell::new(virtio), + fb, + } + } + } +} + +impl GpuDevice for VirtIOGpuWrapper { + fn flush(&self) { + self.gpu.exclusive_access().flush().unwrap(); + } + fn get_framebuffer(&self) -> &mut [u8] { + unsafe { + let ptr = self.fb.as_ptr() as *const _ as *mut u8; + core::slice::from_raw_parts_mut(ptr, self.fb.len()) + } + } + fn update_cursor(&self) {} +} diff --git a/os/src/drivers/input/mod.rs b/os/src/drivers/input/mod.rs new file mode 100644 index 0000000..a9f5f0b --- /dev/null +++ b/os/src/drivers/input/mod.rs @@ -0,0 +1,83 @@ +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use core::any::Any; +use virtio_drivers::{VirtIOHeader, VirtIOInput}; + +const VIRTIO5: usize = 0x10005000; +const VIRTIO6: usize = 0x10006000; + +struct VirtIOInputInner { + virtio_input: VirtIOInput<'static, VirtioHal>, + events: VecDeque, +} + +struct VirtIOInputWrapper { + inner: UPIntrFreeCell, + condvar: Condvar, +} + +pub trait InputDevice: Send + Sync + Any { + fn read_event(&self) -> u64; + fn handle_irq(&self); + fn is_empty(&self) -> bool; +} + +lazy_static::lazy_static!( + pub static ref KEYBOARD_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO5)); + pub static ref MOUSE_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO6)); +); + +impl VirtIOInputWrapper { + pub fn new(addr: usize) -> Self { + let inner = VirtIOInputInner { + virtio_input: unsafe { + VirtIOInput::::new(&mut *(addr as *mut VirtIOHeader)).unwrap() + }, + events: VecDeque::new(), + }; + Self { + inner: unsafe { UPIntrFreeCell::new(inner) }, + condvar: Condvar::new(), + } + } +} + +impl InputDevice for VirtIOInputWrapper { + fn is_empty(&self) -> bool { + self.inner.exclusive_access().events.is_empty() + } + + fn read_event(&self) -> u64 { + loop { + let mut inner = self.inner.exclusive_access(); + if let Some(event) = inner.events.pop_front() { + return event; + } else { + let task_cx_ptr = self.condvar.wait_no_sched(); + drop(inner); + schedule(task_cx_ptr); + } + } + } + + fn handle_irq(&self) { + let mut count = 0; + let mut result = 0; + self.inner.exclusive_session(|inner| { + inner.virtio_input.ack_interrupt(); + while let Some(event) = inner.virtio_input.pop_pending_event() { + count += 1; + result = (event.event_type as u64) << 48 + | (event.code as u64) << 32 + | (event.value) as u64; + inner.events.push_back(result); + } + }); + if count > 0 { + self.condvar.signal(); + }; + } +} diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs new file mode 100644 index 0000000..605397d --- /dev/null +++ b/os/src/drivers/mod.rs @@ -0,0 +1,13 @@ +pub mod block; +pub mod bus; +pub mod chardev; +pub mod gpu; +pub mod input; +pub mod net; +pub mod plic; + +pub use block::BLOCK_DEVICE; +pub use bus::*; +pub use gpu::*; +pub use input::*; +pub use net::*; diff --git a/os/src/drivers/net/mod.rs b/os/src/drivers/net/mod.rs new file mode 100644 index 0000000..7a70587 --- /dev/null +++ b/os/src/drivers/net/mod.rs @@ -0,0 +1,46 @@ +use core::any::Any; + +use crate::drivers::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; +use alloc::sync::Arc; +use lazy_static::*; +use virtio_drivers::{VirtIOHeader, VirtIONet}; + +const VIRTIO8: usize = 0x10004000; + +lazy_static! { + pub static ref NET_DEVICE: Arc = Arc::new(VirtIONetWrapper::new()); +} + +pub trait NetDevice: Send + Sync + Any { + fn transmit(&self, data: &[u8]); + fn receive(&self, data: &mut [u8]) -> usize; +} + +pub struct VirtIONetWrapper(UPIntrFreeCell>); + +impl NetDevice for VirtIONetWrapper { + fn transmit(&self, data: &[u8]) { + self.0 + .exclusive_access() + .send(data) + .expect("can't send data") + } + + fn receive(&self, data: &mut [u8]) -> usize { + self.0 + .exclusive_access() + .recv(data) + .expect("can't receive data") + } +} + +impl VirtIONetWrapper { + pub fn new() -> Self { + unsafe { + let virtio = VirtIONet::::new(&mut *(VIRTIO8 as *mut VirtIOHeader)) + .expect("can't create net device by virtio"); + VirtIONetWrapper(UPIntrFreeCell::new(virtio)) + } + } +} diff --git a/os/src/drivers/plic.rs b/os/src/drivers/plic.rs new file mode 100644 index 0000000..77bad5f --- /dev/null +++ b/os/src/drivers/plic.rs @@ -0,0 +1,124 @@ +#[allow(clippy::upper_case_acronyms)] +pub struct PLIC { + base_addr: usize, +} + +#[derive(Copy, Clone)] +pub enum IntrTargetPriority { + Machine = 0, + Supervisor = 1, +} + +impl IntrTargetPriority { + pub fn supported_number() -> usize { + 2 + } +} + +impl PLIC { + fn priority_ptr(&self, intr_source_id: usize) -> *mut u32 { + assert!(intr_source_id > 0 && intr_source_id <= 132); + (self.base_addr + intr_source_id * 4) as *mut u32 + } + fn hart_id_with_priority(hart_id: usize, target_priority: IntrTargetPriority) -> usize { + let priority_num = IntrTargetPriority::supported_number(); + hart_id * priority_num + target_priority as usize + } + fn enable_ptr( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) -> (*mut u32, usize) { + let id = Self::hart_id_with_priority(hart_id, target_priority); + let (reg_id, reg_shift) = (intr_source_id / 32, intr_source_id % 32); + ( + (self.base_addr + 0x2000 + 0x80 * id + 0x4 * reg_id) as *mut u32, + reg_shift, + ) + } + fn threshold_ptr_of_hart_with_priority( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + ) -> *mut u32 { + let id = Self::hart_id_with_priority(hart_id, target_priority); + (self.base_addr + 0x20_0000 + 0x1000 * id) as *mut u32 + } + fn claim_comp_ptr_of_hart_with_priority( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + ) -> *mut u32 { + let id = Self::hart_id_with_priority(hart_id, target_priority); + (self.base_addr + 0x20_0004 + 0x1000 * id) as *mut u32 + } + pub unsafe fn new(base_addr: usize) -> Self { + Self { base_addr } + } + pub fn set_priority(&mut self, intr_source_id: usize, priority: u32) { + assert!(priority < 8); + unsafe { + self.priority_ptr(intr_source_id).write_volatile(priority); + } + } + #[allow(unused)] + pub fn get_priority(&mut self, intr_source_id: usize) -> u32 { + unsafe { self.priority_ptr(intr_source_id).read_volatile() & 7 } + } + pub fn enable( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) { + let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); + unsafe { + reg_ptr.write_volatile(reg_ptr.read_volatile() | 1 << shift); + } + } + #[allow(unused)] + pub fn disable( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) { + let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); + unsafe { + reg_ptr.write_volatile(reg_ptr.read_volatile() & (!(1u32 << shift))); + } + } + pub fn set_threshold( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + threshold: u32, + ) { + assert!(threshold < 8); + let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { + threshold_ptr.write_volatile(threshold); + } + } + #[allow(unused)] + pub fn get_threshold(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { + let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { threshold_ptr.read_volatile() & 7 } + } + pub fn claim(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { + let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { claim_comp_ptr.read_volatile() } + } + pub fn complete( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + completion: u32, + ) { + let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { + claim_comp_ptr.write_volatile(completion); + } + } +} diff --git a/os/src/entry.asm b/os/src/entry.asm index 9d2ff71..c32d68f 100644 --- a/os/src/entry.asm +++ b/os/src/entry.asm @@ -5,8 +5,8 @@ _start: call rust_main .section .bss.stack - .globl boot_stack -boot_stack: + .globl boot_stack_lower_bound +boot_stack_lower_bound: .space 4096 * 16 .globl boot_stack_top -boot_stack_top: \ No newline at end of file +boot_stack_top: diff --git a/os/src/fs/inode.rs b/os/src/fs/inode.rs new file mode 100644 index 0000000..321338d --- /dev/null +++ b/os/src/fs/inode.rs @@ -0,0 +1,139 @@ +use super::File; +use crate::drivers::BLOCK_DEVICE; +use crate::mm::UserBuffer; +use crate::sync::UPIntrFreeCell; +use alloc::sync::Arc; +use alloc::vec::Vec; +use bitflags::*; +use easy_fs::{EasyFileSystem, Inode}; +use lazy_static::*; + +pub struct OSInode { + readable: bool, + writable: bool, + inner: UPIntrFreeCell, +} + +pub struct OSInodeInner { + offset: usize, + inode: Arc, +} + +impl OSInode { + pub fn new(readable: bool, writable: bool, inode: Arc) -> Self { + Self { + readable, + writable, + inner: unsafe { UPIntrFreeCell::new(OSInodeInner { offset: 0, inode }) }, + } + } + pub fn read_all(&self) -> Vec { + let mut inner = self.inner.exclusive_access(); + let mut buffer = [0u8; 512]; + let mut v: Vec = Vec::new(); + loop { + let len = inner.inode.read_at(inner.offset, &mut buffer); + if len == 0 { + break; + } + inner.offset += len; + v.extend_from_slice(&buffer[..len]); + } + v + } +} + +lazy_static! { + pub static ref ROOT_INODE: Arc = { + let efs = EasyFileSystem::open(BLOCK_DEVICE.clone()); + Arc::new(EasyFileSystem::root_inode(&efs)) + }; +} + +pub fn list_apps() { + println!("/**** APPS ****"); + for app in ROOT_INODE.ls() { + println!("{}", app); + } + println!("**************/") +} + +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +impl OpenFlags { + /// Do not check validity for simplicity + /// Return (readable, writable) + pub fn read_write(&self) -> (bool, bool) { + if self.is_empty() { + (true, false) + } else if self.contains(Self::WRONLY) { + (false, true) + } else { + (true, true) + } + } +} + +pub fn open_file(name: &str, flags: OpenFlags) -> Option> { + let (readable, writable) = flags.read_write(); + if flags.contains(OpenFlags::CREATE) { + if let Some(inode) = ROOT_INODE.find(name) { + // clear size + inode.clear(); + Some(Arc::new(OSInode::new(readable, writable, inode))) + } else { + // create file + ROOT_INODE + .create(name) + .map(|inode| Arc::new(OSInode::new(readable, writable, inode))) + } + } else { + ROOT_INODE.find(name).map(|inode| { + if flags.contains(OpenFlags::TRUNC) { + inode.clear(); + } + Arc::new(OSInode::new(readable, writable, inode)) + }) + } +} + +impl File for OSInode { + fn readable(&self) -> bool { + self.readable + } + fn writable(&self) -> bool { + self.writable + } + fn read(&self, mut buf: UserBuffer) -> usize { + let mut inner = self.inner.exclusive_access(); + let mut total_read_size = 0usize; + for slice in buf.buffers.iter_mut() { + let read_size = inner.inode.read_at(inner.offset, *slice); + if read_size == 0 { + break; + } + inner.offset += read_size; + total_read_size += read_size; + } + total_read_size + } + fn write(&self, buf: UserBuffer) -> usize { + let mut inner = self.inner.exclusive_access(); + let mut total_write_size = 0usize; + for slice in buf.buffers.iter() { + let write_size = inner.inode.write_at(inner.offset, *slice); + assert_eq!(write_size, slice.len()); + inner.offset += write_size; + total_write_size += write_size; + } + total_write_size + } +} diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs new file mode 100644 index 0000000..cbde739 --- /dev/null +++ b/os/src/fs/mod.rs @@ -0,0 +1,16 @@ +mod inode; +mod pipe; +mod stdio; + +use crate::mm::UserBuffer; + +pub trait File: Send + Sync { + fn readable(&self) -> bool; + fn writable(&self) -> bool; + fn read(&self, buf: UserBuffer) -> usize; + fn write(&self, buf: UserBuffer) -> usize; +} + +pub use inode::{list_apps, open_file, OpenFlags}; +pub use pipe::make_pipe; +pub use stdio::{Stdin, Stdout}; diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs new file mode 100644 index 0000000..d10dc33 --- /dev/null +++ b/os/src/fs/pipe.rs @@ -0,0 +1,173 @@ +use super::File; +use crate::mm::UserBuffer; +use crate::sync::UPIntrFreeCell; +use alloc::sync::{Arc, Weak}; + +use crate::task::suspend_current_and_run_next; + +pub struct Pipe { + readable: bool, + writable: bool, + buffer: Arc>, +} + +impl Pipe { + pub fn read_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: true, + writable: false, + buffer, + } + } + pub fn write_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: false, + writable: true, + buffer, + } + } +} + +const RING_BUFFER_SIZE: usize = 32; + +#[derive(Copy, Clone, PartialEq)] +enum RingBufferStatus { + Full, + Empty, + Normal, +} + +pub struct PipeRingBuffer { + arr: [u8; RING_BUFFER_SIZE], + head: usize, + tail: usize, + status: RingBufferStatus, + write_end: Option>, +} + +impl PipeRingBuffer { + pub fn new() -> Self { + Self { + arr: [0; RING_BUFFER_SIZE], + head: 0, + tail: 0, + status: RingBufferStatus::Empty, + write_end: None, + } + } + pub fn set_write_end(&mut self, write_end: &Arc) { + self.write_end = Some(Arc::downgrade(write_end)); + } + pub fn write_byte(&mut self, byte: u8) { + self.status = RingBufferStatus::Normal; + self.arr[self.tail] = byte; + self.tail = (self.tail + 1) % RING_BUFFER_SIZE; + if self.tail == self.head { + self.status = RingBufferStatus::Full; + } + } + pub fn read_byte(&mut self) -> u8 { + self.status = RingBufferStatus::Normal; + let c = self.arr[self.head]; + self.head = (self.head + 1) % RING_BUFFER_SIZE; + if self.head == self.tail { + self.status = RingBufferStatus::Empty; + } + c + } + pub fn available_read(&self) -> usize { + if self.status == RingBufferStatus::Empty { + 0 + } else if self.tail > self.head { + self.tail - self.head + } else { + self.tail + RING_BUFFER_SIZE - self.head + } + } + pub fn available_write(&self) -> usize { + if self.status == RingBufferStatus::Full { + 0 + } else { + RING_BUFFER_SIZE - self.available_read() + } + } + pub fn all_write_ends_closed(&self) -> bool { + self.write_end.as_ref().unwrap().upgrade().is_none() + } +} + +/// Return (read_end, write_end) +pub fn make_pipe() -> (Arc, Arc) { + let buffer = Arc::new(unsafe { UPIntrFreeCell::new(PipeRingBuffer::new()) }); + let read_end = Arc::new(Pipe::read_end_with_buffer(buffer.clone())); + let write_end = Arc::new(Pipe::write_end_with_buffer(buffer.clone())); + buffer.exclusive_access().set_write_end(&write_end); + (read_end, write_end) +} + +impl File for Pipe { + fn readable(&self) -> bool { + self.readable + } + fn writable(&self) -> bool { + self.writable + } + fn read(&self, buf: UserBuffer) -> usize { + assert!(self.readable()); + let want_to_read = buf.len(); + let mut buf_iter = buf.into_iter(); + let mut already_read = 0usize; + loop { + let mut ring_buffer = self.buffer.exclusive_access(); + let loop_read = ring_buffer.available_read(); + if loop_read == 0 { + if ring_buffer.all_write_ends_closed() { + return already_read; + } + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + for _ in 0..loop_read { + if let Some(byte_ref) = buf_iter.next() { + unsafe { + *byte_ref = ring_buffer.read_byte(); + } + already_read += 1; + if already_read == want_to_read { + return want_to_read; + } + } else { + return already_read; + } + } + } + } + fn write(&self, buf: UserBuffer) -> usize { + assert!(self.writable()); + let want_to_write = buf.len(); + let mut buf_iter = buf.into_iter(); + let mut already_write = 0usize; + loop { + let mut ring_buffer = self.buffer.exclusive_access(); + let loop_write = ring_buffer.available_write(); + if loop_write == 0 { + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + // write at most loop_write bytes + for _ in 0..loop_write { + if let Some(byte_ref) = buf_iter.next() { + ring_buffer.write_byte(unsafe { *byte_ref }); + already_write += 1; + if already_write == want_to_write { + return want_to_write; + } + } else { + return already_write; + } + } + } + } +} diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs new file mode 100644 index 0000000..66f4c5a --- /dev/null +++ b/os/src/fs/stdio.rs @@ -0,0 +1,46 @@ +use super::File; +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; +use crate::mm::UserBuffer; + +pub struct Stdin; +pub struct Stdout; + +impl File for Stdin { + fn readable(&self) -> bool { + true + } + fn writable(&self) -> bool { + false + } + fn read(&self, mut user_buf: UserBuffer) -> usize { + assert_eq!(user_buf.len(), 1); + //println!("before UART.read() in Stdin::read()"); + let ch = UART.read(); + unsafe { + user_buf.buffers[0].as_mut_ptr().write_volatile(ch); + } + 1 + } + fn write(&self, _user_buf: UserBuffer) -> usize { + panic!("Cannot write to stdin!"); + } +} + +impl File for Stdout { + fn readable(&self) -> bool { + false + } + fn writable(&self) -> bool { + true + } + fn read(&self, _user_buf: UserBuffer) -> usize { + panic!("Cannot read from stdout!"); + } + fn write(&self, user_buf: UserBuffer) -> usize { + for buffer in user_buf.buffers.iter() { + print!("{}", core::str::from_utf8(*buffer).unwrap()); + } + user_buf.len() + } +} diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs index 3f5462a..021f16f 100644 --- a/os/src/lang_items.rs +++ b/os/src/lang_items.rs @@ -1,12 +1,38 @@ -use core::panic::PanicInfo; use crate::sbi::shutdown; +use crate::task::current_kstack_top; +use core::arch::asm; +use core::panic::PanicInfo; +use log::*; #[panic_handler] fn panic(info: &PanicInfo) -> ! { if let Some(location) = info.location() { - println!("[kernel] Panicked at {}:{} {}", location.file(), location.line(), info.message().unwrap()); + error!( + "[kernel] Panicked at {}:{} {}", + location.file(), + location.line(), + info.message().unwrap() + ); } else { - println!("[kernel] Panicked: {}", info.message().unwrap()); + error!("[kernel] Panicked: {}", info.message().unwrap()); } - shutdown() + unsafe { + backtrace(); + } + shutdown(true) +} + +unsafe fn backtrace() { + let mut fp: usize; + let stop = current_kstack_top(); + asm!("mv {}, s0", out(reg) fp); + println!("---START BACKTRACE---"); + for i in 0..10 { + if fp == stop { + break; + } + println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize)); + fp = *((fp - 16) as *const usize); + } + println!("---END BACKTRACE---"); } diff --git a/os/src/linker-k210.ld b/os/src/linker-k210.ld deleted file mode 100644 index 2ef74ca..0000000 --- a/os/src/linker-k210.ld +++ /dev/null @@ -1,48 +0,0 @@ -OUTPUT_ARCH(riscv) -ENTRY(_start) -BASE_ADDRESS = 0x80020000; - -SECTIONS -{ - . = BASE_ADDRESS; - skernel = .; - - stext = .; - .text : { - *(.text.entry) - *(.text .text.*) - } - - . = ALIGN(4K); - etext = .; - srodata = .; - .rodata : { - *(.rodata .rodata.*) - *(.srodata .srodata.*) - } - - . = ALIGN(4K); - erodata = .; - sdata = .; - .data : { - *(.data .data.*) - *(.sdata .sdata.*) - } - - . = ALIGN(4K); - edata = .; - .bss : { - *(.bss.stack) - sbss = .; - *(.bss .bss.*) - *(.sbss .sbss.*) - } - - . = ALIGN(4K); - ebss = .; - ekernel = .; - - /DISCARD/ : { - *(.eh_frame) - } -} \ No newline at end of file diff --git a/os/src/linker-qemu.ld b/os/src/linker-qemu.ld index 8bcce1a..5baafbd 100644 --- a/os/src/linker-qemu.ld +++ b/os/src/linker-qemu.ld @@ -10,6 +10,10 @@ SECTIONS stext = .; .text : { *(.text.entry) + . = ALIGN(4K); + strampoline = .; + *(.text.trampoline); + . = ALIGN(4K); *(.text .text.*) } @@ -31,6 +35,7 @@ SECTIONS . = ALIGN(4K); edata = .; + sbss_with_stack = .; .bss : { *(.bss.stack) sbss = .; diff --git a/os/src/loader.rs b/os/src/loader.rs deleted file mode 100644 index d4ae573..0000000 --- a/os/src/loader.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::trap::TrapContext; -use crate::config::*; - -#[repr(align(4096))] -#[derive(Copy, Clone)] -struct KernelStack { - data: [u8; KERNEL_STACK_SIZE], -} - -#[repr(align(4096))] -#[derive(Copy, Clone)] -struct UserStack { - data: [u8; USER_STACK_SIZE], -} - -static KERNEL_STACK: [KernelStack; MAX_APP_NUM] = [ - KernelStack { data: [0; KERNEL_STACK_SIZE], }; - MAX_APP_NUM -]; - -static USER_STACK: [UserStack; MAX_APP_NUM] = [ - UserStack { data: [0; USER_STACK_SIZE], }; - MAX_APP_NUM -]; - -impl KernelStack { - fn get_sp(&self) -> usize { - self.data.as_ptr() as usize + KERNEL_STACK_SIZE - } - pub fn push_context(&self, trap_cx: TrapContext) -> usize { - let trap_cx_ptr = (self.get_sp() - core::mem::size_of::()) as *mut TrapContext; - unsafe { *trap_cx_ptr = trap_cx; } - trap_cx_ptr as usize - } -} - -impl UserStack { - fn get_sp(&self) -> usize { - self.data.as_ptr() as usize + USER_STACK_SIZE - } -} - -fn get_base_i(app_id: usize) -> usize { - APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT -} - -pub fn get_num_app() -> usize { - extern "C" { fn _num_app(); } - unsafe { (_num_app as usize as *const usize).read_volatile() } -} - -pub fn load_apps() { - extern "C" { fn _num_app(); } - let num_app_ptr = _num_app as usize as *const usize; - let num_app = get_num_app(); - let app_start = unsafe { - core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) - }; - // clear i-cache first - unsafe { asm!("fence.i"); } - // load apps - for i in 0..num_app { - let base_i = get_base_i(i); - // clear region - (base_i..base_i + APP_SIZE_LIMIT).for_each(|addr| unsafe { - (addr as *mut u8).write_volatile(0) - }); - // load app from data section to memory - let src = unsafe { - core::slice::from_raw_parts(app_start[i] as *const u8, app_start[i + 1] - app_start[i]) - }; - let dst = unsafe { - core::slice::from_raw_parts_mut(base_i as *mut u8, src.len()) - }; - dst.copy_from_slice(src); - } -} - -pub fn init_app_cx(app_id: usize) -> usize { - KERNEL_STACK[app_id].push_context( - TrapContext::app_init_context(get_base_i(app_id), USER_STACK[app_id].get_sp()), - ) -} diff --git a/os/src/main.rs b/os/src/main.rs index 4689f03..276aaf5 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -1,23 +1,37 @@ #![no_std] #![no_main] -#![feature(global_asm)] -#![feature(asm)] #![feature(panic_info_message)] +#![feature(alloc_error_handler)] + +//use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE, INPUT_CONDVAR}; +use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; +extern crate alloc; + +#[macro_use] +extern crate bitflags; + +#[path = "boards/qemu.rs"] +mod board; #[macro_use] mod console; -mod lang_items; -mod sbi; -mod syscall; -mod trap; -mod loader; mod config; +mod drivers; +mod fs; +mod lang_items; +mod mm; +mod net; +mod sbi; +mod sync; +mod syscall; mod task; mod timer; -mod sync; +mod trap; -global_asm!(include_str!("entry.asm")); -global_asm!(include_str!("link_app.S")); +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; + +core::arch::global_asm!(include_str!("entry.asm")); fn clear_bss() { extern "C" { @@ -25,21 +39,38 @@ fn clear_bss() { fn ebss(); } unsafe { - core::slice::from_raw_parts_mut( - sbss as usize as *mut u8, - ebss as usize - sbss as usize, - ).fill(0); + core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize) + .fill(0); } } +use lazy_static::*; +use sync::UPIntrFreeCell; + +lazy_static! { + pub static ref DEV_NON_BLOCKING_ACCESS: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(false) }; +} + #[no_mangle] pub fn rust_main() -> ! { clear_bss(); - println!("[kernel] Hello, world!"); + mm::init(); + UART.init(); + println!("KERN: init gpu"); + let _gpu = GPU_DEVICE.clone(); + println!("KERN: init keyboard"); + let _keyboard = KEYBOARD_DEVICE.clone(); + println!("KERN: init mouse"); + let _mouse = MOUSE_DEVICE.clone(); + println!("KERN: init trap"); trap::init(); - loader::load_apps(); trap::enable_timer_interrupt(); timer::set_next_trigger(); - task::run_first_task(); + board::device_init(); + fs::list_apps(); + task::add_initproc(); + *DEV_NON_BLOCKING_ACCESS.exclusive_access() = true; + task::run_tasks(); panic!("Unreachable in rust_main!"); -} \ No newline at end of file +} diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs new file mode 100644 index 0000000..df3e130 --- /dev/null +++ b/os/src/mm/address.rs @@ -0,0 +1,270 @@ +use super::PageTableEntry; +use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS}; +use core::fmt::{self, Debug, Formatter}; + +const PA_WIDTH_SV39: usize = 56; +const VA_WIDTH_SV39: usize = 39; +const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; +const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; + +/// Definitions +#[repr(C)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysAddr(pub usize); + +#[repr(C)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtAddr(pub usize); + +#[repr(C)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PhysPageNum(pub usize); + +#[repr(C)] +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct VirtPageNum(pub usize); + +/// Debugging + +impl Debug for VirtAddr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("VA:{:#x}", self.0)) + } +} +impl Debug for VirtPageNum { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("VPN:{:#x}", self.0)) + } +} +impl Debug for PhysAddr { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("PA:{:#x}", self.0)) + } +} +impl Debug for PhysPageNum { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("PPN:{:#x}", self.0)) + } +} + +/// T: {PhysAddr, VirtAddr, PhysPageNum, VirtPageNum} +/// T -> usize: T.0 +/// usize -> T: usize.into() + +impl From for PhysAddr { + fn from(v: usize) -> Self { + Self(v & ((1 << PA_WIDTH_SV39) - 1)) + } +} +impl From for PhysPageNum { + fn from(v: usize) -> Self { + Self(v & ((1 << PPN_WIDTH_SV39) - 1)) + } +} +impl From for VirtAddr { + fn from(v: usize) -> Self { + Self(v & ((1 << VA_WIDTH_SV39) - 1)) + } +} +impl From for VirtPageNum { + fn from(v: usize) -> Self { + Self(v & ((1 << VPN_WIDTH_SV39) - 1)) + } +} +impl From for usize { + fn from(v: PhysAddr) -> Self { + v.0 + } +} +impl From for usize { + fn from(v: PhysPageNum) -> Self { + v.0 + } +} +impl From for usize { + fn from(v: VirtAddr) -> Self { + if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) { + v.0 | (!((1 << VA_WIDTH_SV39) - 1)) + } else { + v.0 + } + } +} +impl From for usize { + fn from(v: VirtPageNum) -> Self { + v.0 + } +} + +impl VirtAddr { + pub fn floor(&self) -> VirtPageNum { + VirtPageNum(self.0 / PAGE_SIZE) + } + pub fn ceil(&self) -> VirtPageNum { + if self.0 == 0 { + VirtPageNum(0) + } else { + VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) + } + } + pub fn page_offset(&self) -> usize { + self.0 & (PAGE_SIZE - 1) + } + pub fn aligned(&self) -> bool { + self.page_offset() == 0 + } +} +impl From for VirtPageNum { + fn from(v: VirtAddr) -> Self { + assert_eq!(v.page_offset(), 0); + v.floor() + } +} +impl From for VirtAddr { + fn from(v: VirtPageNum) -> Self { + Self(v.0 << PAGE_SIZE_BITS) + } +} +impl PhysAddr { + pub fn floor(&self) -> PhysPageNum { + PhysPageNum(self.0 / PAGE_SIZE) + } + pub fn ceil(&self) -> PhysPageNum { + if self.0 == 0 { + PhysPageNum(0) + } else { + PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) + } + } + pub fn page_offset(&self) -> usize { + self.0 & (PAGE_SIZE - 1) + } + pub fn aligned(&self) -> bool { + self.page_offset() == 0 + } +} +impl From for PhysPageNum { + fn from(v: PhysAddr) -> Self { + assert_eq!(v.page_offset(), 0); + v.floor() + } +} +impl From for PhysAddr { + fn from(v: PhysPageNum) -> Self { + Self(v.0 << PAGE_SIZE_BITS) + } +} + +impl VirtPageNum { + pub fn indexes(&self) -> [usize; 3] { + let mut vpn = self.0; + let mut idx = [0usize; 3]; + for i in (0..3).rev() { + idx[i] = vpn & 511; + vpn >>= 9; + } + idx + } +} + +impl PhysAddr { + pub fn get_ref(&self) -> &'static T { + unsafe { (self.0 as *const T).as_ref().unwrap() } + } + pub fn get_mut(&self) -> &'static mut T { + unsafe { (self.0 as *mut T).as_mut().unwrap() } + } +} +impl PhysPageNum { + pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { + let pa: PhysAddr = (*self).into(); + unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } + } + pub fn get_bytes_array(&self) -> &'static mut [u8] { + let pa: PhysAddr = (*self).into(); + unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) } + } + pub fn get_mut(&self) -> &'static mut T { + let pa: PhysAddr = (*self).into(); + pa.get_mut() + } +} + +pub trait StepByOne { + fn step(&mut self); +} +impl StepByOne for VirtPageNum { + fn step(&mut self) { + self.0 += 1; + } +} +impl StepByOne for PhysPageNum { + fn step(&mut self) { + self.0 += 1; + } +} + +#[derive(Copy, Clone)] +pub struct SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + l: T, + r: T, +} +impl SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + pub fn new(start: T, end: T) -> Self { + assert!(start <= end, "start {:?} > end {:?}!", start, end); + Self { l: start, r: end } + } + pub fn get_start(&self) -> T { + self.l + } + pub fn get_end(&self) -> T { + self.r + } +} +impl IntoIterator for SimpleRange +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + type Item = T; + type IntoIter = SimpleRangeIterator; + fn into_iter(self) -> Self::IntoIter { + SimpleRangeIterator::new(self.l, self.r) + } +} +pub struct SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + current: T, + end: T, +} +impl SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + pub fn new(l: T, r: T) -> Self { + Self { current: l, end: r } + } +} +impl Iterator for SimpleRangeIterator +where + T: StepByOne + Copy + PartialEq + PartialOrd + Debug, +{ + type Item = T; + fn next(&mut self) -> Option { + if self.current == self.end { + None + } else { + let t = self.current; + self.current.step(); + Some(t) + } + } +} +pub type VPNRange = SimpleRange; diff --git a/os/src/mm/frame_allocator.rs b/os/src/mm/frame_allocator.rs new file mode 100644 index 0000000..163bb57 --- /dev/null +++ b/os/src/mm/frame_allocator.rs @@ -0,0 +1,162 @@ +use super::{PhysAddr, PhysPageNum}; +use crate::config::MEMORY_END; +use crate::sync::UPIntrFreeCell; +use alloc::vec::Vec; +use core::fmt::{self, Debug, Formatter}; +use lazy_static::*; + +pub struct FrameTracker { + pub ppn: PhysPageNum, +} + +impl FrameTracker { + pub fn new(ppn: PhysPageNum) -> Self { + // page cleaning + let bytes_array = ppn.get_bytes_array(); + for i in bytes_array { + *i = 0; + } + Self { ppn } + } +} + +impl Debug for FrameTracker { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("FrameTracker:PPN={:#x}", self.ppn.0)) + } +} + +impl Drop for FrameTracker { + fn drop(&mut self) { + frame_dealloc(self.ppn); + } +} + +trait FrameAllocator { + fn new() -> Self; + fn alloc(&mut self) -> Option; + fn alloc_more(&mut self, pages: usize) -> Option>; + fn dealloc(&mut self, ppn: PhysPageNum); +} + +pub struct StackFrameAllocator { + current: usize, + end: usize, + recycled: Vec, +} + +impl StackFrameAllocator { + pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) { + self.current = l.0; + self.end = r.0; + // println!("last {} Physical Frames.", self.end - self.current); + } +} +impl FrameAllocator for StackFrameAllocator { + fn new() -> Self { + Self { + current: 0, + end: 0, + recycled: Vec::new(), + } + } + fn alloc(&mut self) -> Option { + if let Some(ppn) = self.recycled.pop() { + Some(ppn.into()) + } else if self.current == self.end { + None + } else { + self.current += 1; + Some((self.current - 1).into()) + } + } + fn alloc_more(&mut self, pages: usize) -> Option> { + if self.current + pages >= self.end { + None + } else { + self.current += pages; + let arr: Vec = (1..pages + 1).collect(); + let v = arr.iter().map(|x| (self.current - x).into()).collect(); + Some(v) + } + } + fn dealloc(&mut self, ppn: PhysPageNum) { + let ppn = ppn.0; + // validity check + if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) { + panic!("Frame ppn={:#x} has not been allocated!", ppn); + } + // recycle + self.recycled.push(ppn); + } +} + +type FrameAllocatorImpl = StackFrameAllocator; + +lazy_static! { + pub static ref FRAME_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(FrameAllocatorImpl::new()) }; +} + +pub fn init_frame_allocator() { + extern "C" { + fn ekernel(); + } + FRAME_ALLOCATOR.exclusive_access().init( + PhysAddr::from(ekernel as usize).ceil(), + PhysAddr::from(MEMORY_END).floor(), + ); +} + +pub fn frame_alloc() -> Option { + FRAME_ALLOCATOR + .exclusive_access() + .alloc() + .map(FrameTracker::new) +} + +pub fn frame_alloc_more(num: usize) -> Option> { + FRAME_ALLOCATOR + .exclusive_access() + .alloc_more(num) + .map(|x| x.iter().map(|&t| FrameTracker::new(t)).collect()) +} + +pub fn frame_dealloc(ppn: PhysPageNum) { + FRAME_ALLOCATOR.exclusive_access().dealloc(ppn); +} + +#[allow(unused)] +pub fn frame_allocator_test() { + let mut v: Vec = Vec::new(); + for i in 0..5 { + let frame = frame_alloc().unwrap(); + println!("{:?}", frame); + v.push(frame); + } + v.clear(); + for i in 0..5 { + let frame = frame_alloc().unwrap(); + println!("{:?}", frame); + v.push(frame); + } + drop(v); + println!("frame_allocator_test passed!"); +} + +#[allow(unused)] +pub fn frame_allocator_alloc_more_test() { + let mut v: Vec = Vec::new(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + v.extend(frames); + v.clear(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + drop(v); + println!("frame_allocator_test passed!"); +} diff --git a/os/src/mm/heap_allocator.rs b/os/src/mm/heap_allocator.rs new file mode 100644 index 0000000..42a6d76 --- /dev/null +++ b/os/src/mm/heap_allocator.rs @@ -0,0 +1,45 @@ +use crate::config::KERNEL_HEAP_SIZE; +use buddy_system_allocator::LockedHeap; + +#[global_allocator] +static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); + +#[alloc_error_handler] +pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { + panic!("Heap allocation error, layout = {:?}", layout); +} + +static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; + +pub fn init_heap() { + unsafe { + HEAP_ALLOCATOR + .lock() + .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE); + } +} + +#[allow(unused)] +pub fn heap_test() { + use alloc::boxed::Box; + use alloc::vec::Vec; + extern "C" { + fn sbss(); + fn ebss(); + } + let bss_range = sbss as usize..ebss as usize; + let a = Box::new(5); + assert_eq!(*a, 5); + assert!(bss_range.contains(&(a.as_ref() as *const _ as usize))); + drop(a); + let mut v: Vec = Vec::new(); + for i in 0..500 { + v.push(i); + } + for (i, val) in v.iter().take(500).enumerate() { + assert_eq!(*val, i); + } + assert!(bss_range.contains(&(v.as_ptr() as usize))); + drop(v); + println!("heap_test passed!"); +} diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs new file mode 100644 index 0000000..d607c88 --- /dev/null +++ b/os/src/mm/memory_set.rs @@ -0,0 +1,380 @@ +use super::{frame_alloc, FrameTracker}; +use super::{PTEFlags, PageTable, PageTableEntry}; +use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; +use super::{StepByOne, VPNRange}; +use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE}; +use crate::sync::UPIntrFreeCell; +use alloc::collections::BTreeMap; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::arch::asm; +use lazy_static::*; +use riscv::register::satp; + +extern "C" { + fn stext(); + fn etext(); + fn srodata(); + fn erodata(); + fn sdata(); + fn edata(); + fn sbss_with_stack(); + fn ebss(); + fn ekernel(); + fn strampoline(); +} + +lazy_static! { + pub static ref KERNEL_SPACE: Arc> = + Arc::new(unsafe { UPIntrFreeCell::new(MemorySet::new_kernel()) }); +} + +pub fn kernel_token() -> usize { + KERNEL_SPACE.exclusive_access().token() +} + +pub struct MemorySet { + page_table: PageTable, + areas: Vec, +} + +impl MemorySet { + pub fn new_bare() -> Self { + Self { + page_table: PageTable::new(), + areas: Vec::new(), + } + } + pub fn token(&self) -> usize { + self.page_table.token() + } + /// Assume that no conflicts. + pub fn insert_framed_area( + &mut self, + start_va: VirtAddr, + end_va: VirtAddr, + permission: MapPermission, + ) { + self.push( + MapArea::new(start_va, end_va, MapType::Framed, permission), + None, + ); + } + pub fn remove_area_with_start_vpn(&mut self, start_vpn: VirtPageNum) { + if let Some((idx, area)) = self + .areas + .iter_mut() + .enumerate() + .find(|(_, area)| area.vpn_range.get_start() == start_vpn) + { + area.unmap(&mut self.page_table); + self.areas.remove(idx); + } + } + /// Add a new MapArea into this MemorySet. + /// Assuming that there are no conflicts in the virtual address + /// space. + pub fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { + map_area.map(&mut self.page_table); + if let Some(data) = data { + map_area.copy_data(&self.page_table, data); + } + self.areas.push(map_area); + } + /// Mention that trampoline is not collected by areas. + fn map_trampoline(&mut self) { + self.page_table.map( + VirtAddr::from(TRAMPOLINE).into(), + PhysAddr::from(strampoline as usize).into(), + PTEFlags::R | PTEFlags::X, + ); + } + /// Without kernel stacks. + pub fn new_kernel() -> Self { + let mut memory_set = Self::new_bare(); + // map trampoline + memory_set.map_trampoline(); + // map kernel sections + // println!(".text [{:#x}, {:#x})", stext as usize, etext as usize); + // println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize); + // println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize); + // println!( + // ".bss [{:#x}, {:#x})", + // sbss_with_stack as usize, ebss as usize + // ); + // println!("mapping .text section"); + memory_set.push( + MapArea::new( + (stext as usize).into(), + (etext as usize).into(), + MapType::Identical, + MapPermission::R | MapPermission::X, + ), + None, + ); + // println!("mapping .rodata section"); + memory_set.push( + MapArea::new( + (srodata as usize).into(), + (erodata as usize).into(), + MapType::Identical, + MapPermission::R, + ), + None, + ); + // println!("mapping .data section"); + memory_set.push( + MapArea::new( + (sdata as usize).into(), + (edata as usize).into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), + None, + ); + // println!("mapping .bss section"); + memory_set.push( + MapArea::new( + (sbss_with_stack as usize).into(), + (ebss as usize).into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), + None, + ); + // println!("mapping physical memory"); + memory_set.push( + MapArea::new( + (ekernel as usize).into(), + MEMORY_END.into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), + None, + ); + //println!("mapping memory-mapped registers"); + for pair in MMIO { + memory_set.push( + MapArea::new( + (*pair).0.into(), + ((*pair).0 + (*pair).1).into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), + None, + ); + } + memory_set + } + /// Include sections in elf and trampoline, + /// also returns user_sp_base and entry point. + pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) { + let mut memory_set = Self::new_bare(); + // map trampoline + memory_set.map_trampoline(); + // map program headers of elf, with U flag + let elf = xmas_elf::ElfFile::new(elf_data).unwrap(); + let elf_header = elf.header; + let magic = elf_header.pt1.magic; + assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!"); + let ph_count = elf_header.pt2.ph_count(); + let mut max_end_vpn = VirtPageNum(0); + for i in 0..ph_count { + let ph = elf.program_header(i).unwrap(); + if ph.get_type().unwrap() == xmas_elf::program::Type::Load { + let start_va: VirtAddr = (ph.virtual_addr() as usize).into(); + let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into(); + let mut map_perm = MapPermission::U; + let ph_flags = ph.flags(); + if ph_flags.is_read() { + map_perm |= MapPermission::R; + } + if ph_flags.is_write() { + map_perm |= MapPermission::W; + } + if ph_flags.is_execute() { + map_perm |= MapPermission::X; + } + let map_area = MapArea::new(start_va, end_va, MapType::Framed, map_perm); + max_end_vpn = map_area.vpn_range.get_end(); + memory_set.push( + map_area, + Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize]), + ); + } + } + let max_end_va: VirtAddr = max_end_vpn.into(); + let mut user_stack_base: usize = max_end_va.into(); + user_stack_base += PAGE_SIZE; + ( + memory_set, + user_stack_base, + elf.header.pt2.entry_point() as usize, + ) + } + pub fn from_existed_user(user_space: &MemorySet) -> MemorySet { + let mut memory_set = Self::new_bare(); + // map trampoline + memory_set.map_trampoline(); + // copy data sections/trap_context/user_stack + for area in user_space.areas.iter() { + let new_area = MapArea::from_another(area); + memory_set.push(new_area, None); + // copy data from another space + for vpn in area.vpn_range { + let src_ppn = user_space.translate(vpn).unwrap().ppn(); + let dst_ppn = memory_set.translate(vpn).unwrap().ppn(); + dst_ppn + .get_bytes_array() + .copy_from_slice(src_ppn.get_bytes_array()); + } + } + memory_set + } + pub fn activate(&self) { + let satp = self.page_table.token(); + unsafe { + satp::write(satp); + asm!("sfence.vma"); + } + } + pub fn translate(&self, vpn: VirtPageNum) -> Option { + self.page_table.translate(vpn) + } + pub fn recycle_data_pages(&mut self) { + //*self = Self::new_bare(); + self.areas.clear(); + } +} + +pub struct MapArea { + vpn_range: VPNRange, + data_frames: BTreeMap, + map_type: MapType, + map_perm: MapPermission, +} + +impl MapArea { + pub fn new( + start_va: VirtAddr, + end_va: VirtAddr, + map_type: MapType, + map_perm: MapPermission, + ) -> Self { + let start_vpn: VirtPageNum = start_va.floor(); + let end_vpn: VirtPageNum = end_va.ceil(); + Self { + vpn_range: VPNRange::new(start_vpn, end_vpn), + data_frames: BTreeMap::new(), + map_type, + map_perm, + } + } + pub fn from_another(another: &MapArea) -> Self { + Self { + vpn_range: VPNRange::new(another.vpn_range.get_start(), another.vpn_range.get_end()), + data_frames: BTreeMap::new(), + map_type: another.map_type, + map_perm: another.map_perm, + } + } + pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { + let ppn: PhysPageNum; + match self.map_type { + MapType::Identical => { + ppn = PhysPageNum(vpn.0); + } + MapType::Framed => { + let frame = frame_alloc().unwrap(); + ppn = frame.ppn; + self.data_frames.insert(vpn, frame); + } + MapType::Linear(pn_offset) => { + // check for sv39 + assert!(vpn.0 < (1usize << 27)); + ppn = PhysPageNum((vpn.0 as isize + pn_offset) as usize); + } + } + let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); + page_table.map(vpn, ppn, pte_flags); + } + pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { + if self.map_type == MapType::Framed { + self.data_frames.remove(&vpn); + } + page_table.unmap(vpn); + } + pub fn map(&mut self, page_table: &mut PageTable) { + for vpn in self.vpn_range { + self.map_one(page_table, vpn); + } + } + pub fn unmap(&mut self, page_table: &mut PageTable) { + for vpn in self.vpn_range { + self.unmap_one(page_table, vpn); + } + } + /// data: start-aligned but maybe with shorter length + /// assume that all frames were cleared before + pub fn copy_data(&mut self, page_table: &PageTable, data: &[u8]) { + assert_eq!(self.map_type, MapType::Framed); + let mut start: usize = 0; + let mut current_vpn = self.vpn_range.get_start(); + let len = data.len(); + loop { + let src = &data[start..len.min(start + PAGE_SIZE)]; + let dst = &mut page_table + .translate(current_vpn) + .unwrap() + .ppn() + .get_bytes_array()[..src.len()]; + dst.copy_from_slice(src); + start += PAGE_SIZE; + if start >= len { + break; + } + current_vpn.step(); + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum MapType { + Identical, + Framed, + /// offset of page num + Linear(isize), +} + +bitflags! { + pub struct MapPermission: u8 { + const R = 1 << 1; + const W = 1 << 2; + const X = 1 << 3; + const U = 1 << 4; + } +} + +#[allow(unused)] +pub fn remap_test() { + let mut kernel_space = KERNEL_SPACE.exclusive_access(); + let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into(); + let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into(); + let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into(); + assert!(!kernel_space + .page_table + .translate(mid_text.floor()) + .unwrap() + .writable(),); + assert!(!kernel_space + .page_table + .translate(mid_rodata.floor()) + .unwrap() + .writable(),); + assert!(!kernel_space + .page_table + .translate(mid_data.floor()) + .unwrap() + .executable(),); + println!("remap_test passed!"); +} diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs new file mode 100644 index 0000000..642d0b7 --- /dev/null +++ b/os/src/mm/mod.rs @@ -0,0 +1,21 @@ +mod address; +mod frame_allocator; +mod heap_allocator; +mod memory_set; +mod page_table; + +pub use address::VPNRange; +pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; +pub use frame_allocator::{frame_alloc, frame_alloc_more, frame_dealloc, FrameTracker}; +pub use memory_set::{kernel_token, MapArea, MapPermission, MapType, MemorySet, KERNEL_SPACE}; +use page_table::PTEFlags; +pub use page_table::{ + translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable, + PageTableEntry, UserBuffer, +}; + +pub fn init() { + heap_allocator::init_heap(); + frame_allocator::init_frame_allocator(); + KERNEL_SPACE.exclusive_access().activate(); +} diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs new file mode 100644 index 0000000..dfaf4b6 --- /dev/null +++ b/os/src/mm/page_table.rs @@ -0,0 +1,249 @@ +use super::{frame_alloc, FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use bitflags::*; + +bitflags! { + pub struct PTEFlags: u8 { + const V = 1 << 0; + const R = 1 << 1; + const W = 1 << 2; + const X = 1 << 3; + const U = 1 << 4; + const G = 1 << 5; + const A = 1 << 6; + const D = 1 << 7; + } +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct PageTableEntry { + pub bits: usize, +} + +impl PageTableEntry { + pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self { + PageTableEntry { + bits: ppn.0 << 10 | flags.bits as usize, + } + } + pub fn empty() -> Self { + PageTableEntry { bits: 0 } + } + pub fn ppn(&self) -> PhysPageNum { + (self.bits >> 10 & ((1usize << 44) - 1)).into() + } + pub fn flags(&self) -> PTEFlags { + PTEFlags::from_bits(self.bits as u8).unwrap() + } + pub fn is_valid(&self) -> bool { + (self.flags() & PTEFlags::V) != PTEFlags::empty() + } + pub fn readable(&self) -> bool { + (self.flags() & PTEFlags::R) != PTEFlags::empty() + } + pub fn writable(&self) -> bool { + (self.flags() & PTEFlags::W) != PTEFlags::empty() + } + pub fn executable(&self) -> bool { + (self.flags() & PTEFlags::X) != PTEFlags::empty() + } +} + +pub struct PageTable { + root_ppn: PhysPageNum, + frames: Vec, +} + +/// Assume that it won't oom when creating/mapping. +impl PageTable { + pub fn new() -> Self { + let frame = frame_alloc().unwrap(); + PageTable { + root_ppn: frame.ppn, + frames: vec![frame], + } + } + /// Temporarily used to get arguments from user space. + pub fn from_token(satp: usize) -> Self { + Self { + root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)), + frames: Vec::new(), + } + } + fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { + let idxs = vpn.indexes(); + let mut ppn = self.root_ppn; + let mut result: Option<&mut PageTableEntry> = None; + for (i, idx) in idxs.iter().enumerate() { + let pte = &mut ppn.get_pte_array()[*idx]; + if i == 2 { + result = Some(pte); + break; + } + if !pte.is_valid() { + let frame = frame_alloc().unwrap(); + *pte = PageTableEntry::new(frame.ppn, PTEFlags::V); + self.frames.push(frame); + } + ppn = pte.ppn(); + } + result + } + fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { + let idxs = vpn.indexes(); + let mut ppn = self.root_ppn; + let mut result: Option<&mut PageTableEntry> = None; + for (i, idx) in idxs.iter().enumerate() { + let pte = &mut ppn.get_pte_array()[*idx]; + if i == 2 { + result = Some(pte); + break; + } + if !pte.is_valid() { + return None; + } + ppn = pte.ppn(); + } + result + } + #[allow(unused)] + pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { + let pte = self.find_pte_create(vpn).unwrap(); + assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn); + *pte = PageTableEntry::new(ppn, flags | PTEFlags::V); + } + #[allow(unused)] + pub fn unmap(&mut self, vpn: VirtPageNum) { + let pte = self.find_pte(vpn).unwrap(); + assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn); + *pte = PageTableEntry::empty(); + } + pub fn translate(&self, vpn: VirtPageNum) -> Option { + self.find_pte(vpn).map(|pte| *pte) + } + pub fn translate_va(&self, va: VirtAddr) -> Option { + self.find_pte(va.clone().floor()).map(|pte| { + let aligned_pa: PhysAddr = pte.ppn().into(); + let offset = va.page_offset(); + let aligned_pa_usize: usize = aligned_pa.into(); + (aligned_pa_usize + offset).into() + }) + } + pub fn token(&self) -> usize { + 8usize << 60 | self.root_ppn.0 + } +} + +pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { + let page_table = PageTable::from_token(token); + let mut start = ptr as usize; + let end = start + len; + let mut v = Vec::new(); + while start < end { + let start_va = VirtAddr::from(start); + let mut vpn = start_va.floor(); + let ppn = page_table.translate(vpn).unwrap().ppn(); + vpn.step(); + let mut end_va: VirtAddr = vpn.into(); + end_va = end_va.min(VirtAddr::from(end)); + if end_va.page_offset() == 0 { + v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]); + } else { + v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]); + } + start = end_va.into(); + } + v +} + +/// Load a string from other address spaces into kernel space without an end `\0`. +pub fn translated_str(token: usize, ptr: *const u8) -> String { + let page_table = PageTable::from_token(token); + let mut string = String::new(); + let mut va = ptr as usize; + loop { + let ch: u8 = *(page_table + .translate_va(VirtAddr::from(va)) + .unwrap() + .get_mut()); + if ch == 0 { + break; + } + string.push(ch as char); + va += 1; + } + string +} + +pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { + let page_table = PageTable::from_token(token); + page_table + .translate_va(VirtAddr::from(ptr as usize)) + .unwrap() + .get_ref() +} + +pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { + let page_table = PageTable::from_token(token); + let va = ptr as usize; + page_table + .translate_va(VirtAddr::from(va)) + .unwrap() + .get_mut() +} + +pub struct UserBuffer { + pub buffers: Vec<&'static mut [u8]>, +} + +impl UserBuffer { + pub fn new(buffers: Vec<&'static mut [u8]>) -> Self { + Self { buffers } + } + pub fn len(&self) -> usize { + let mut total: usize = 0; + for b in self.buffers.iter() { + total += b.len(); + } + total + } +} + +impl IntoIterator for UserBuffer { + type Item = *mut u8; + type IntoIter = UserBufferIterator; + fn into_iter(self) -> Self::IntoIter { + UserBufferIterator { + buffers: self.buffers, + current_buffer: 0, + current_idx: 0, + } + } +} + +pub struct UserBufferIterator { + buffers: Vec<&'static mut [u8]>, + current_buffer: usize, + current_idx: usize, +} + +impl Iterator for UserBufferIterator { + type Item = *mut u8; + fn next(&mut self) -> Option { + if self.current_buffer >= self.buffers.len() { + None + } else { + let r = &mut self.buffers[self.current_buffer][self.current_idx] as *mut _; + if self.current_idx + 1 == self.buffers[self.current_buffer].len() { + self.current_idx = 0; + self.current_buffer += 1; + } else { + self.current_idx += 1; + } + Some(r) + } + } +} diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs new file mode 100644 index 0000000..469a365 --- /dev/null +++ b/os/src/net/mod.rs @@ -0,0 +1,136 @@ +pub mod port_table; +pub mod socket; +pub mod tcp; +pub mod udp; + +pub use lose_net_stack::IPv4; + +use alloc::{sync::Arc, vec}; +use lose_net_stack::{results::Packet, LoseStack, MacAddress, TcpFlags}; + +use crate::{ + drivers::NET_DEVICE, + net::socket::{get_socket, push_data}, + sync::UPIntrFreeCell, +}; + +use self::{port_table::check_accept, socket::set_s_a_by_index}; + +pub struct NetStack(UPIntrFreeCell); + +impl NetStack { + pub fn new() -> Self { + unsafe { + NetStack(UPIntrFreeCell::new(LoseStack::new( + IPv4::new(10, 0, 2, 15), + MacAddress::new([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]), + ))) + } + } +} + +lazy_static::lazy_static! { + static ref LOSE_NET_STACK: Arc = Arc::new(NetStack::new()); +} + +pub fn net_interrupt_handler() { + let mut recv_buf = vec![0u8; 1024]; + + let len = NET_DEVICE.receive(&mut recv_buf); + + let packet = LOSE_NET_STACK + .0 + .exclusive_access() + .analysis(&recv_buf[..len]); + + // println!("[kernel] receive a packet"); + // hexdump(&recv_buf[..len]); + + match packet { + Packet::ARP(arp_packet) => { + let lose_stack = LOSE_NET_STACK.0.exclusive_access(); + let reply_packet = arp_packet + .reply_packet(lose_stack.ip, lose_stack.mac) + .expect("can't build reply"); + let reply_data = reply_packet.build_data(); + NET_DEVICE.transmit(&reply_data) + } + + Packet::UDP(udp_packet) => { + let target = udp_packet.source_ip; + let lport = udp_packet.dest_port; + let rport = udp_packet.source_port; + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, udp_packet.data.to_vec()); + } + } + + Packet::TCP(tcp_packet) => { + let target = tcp_packet.source_ip; + let lport = tcp_packet.dest_port; + let rport = tcp_packet.source_port; + let flags = tcp_packet.flags; + + if flags.contains(TcpFlags::S) { + // if it has a port to accept, then response the request + if check_accept(lport, &tcp_packet).is_some() { + let mut reply_packet = tcp_packet.ack(); + reply_packet.flags = TcpFlags::S | TcpFlags::A; + NET_DEVICE.transmit(&reply_packet.build_data()); + } + return; + } else if tcp_packet.flags.contains(TcpFlags::F) { + // tcp disconnected + let reply_packet = tcp_packet.ack(); + NET_DEVICE.transmit(&reply_packet.build_data()); + + let mut end_packet = reply_packet.ack(); + end_packet.flags |= TcpFlags::F; + NET_DEVICE.transmit(&end_packet.build_data()); + } else if tcp_packet.flags.contains(TcpFlags::A) && tcp_packet.data_len == 0 { + return; + } + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, tcp_packet.data.to_vec()); + set_s_a_by_index(socket_index, tcp_packet.seq, tcp_packet.ack); + } + } + _ => {} + } +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("[kernel] {:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + print!("[kernel] "); + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("[kernel] {:-^1$}", " hexdump end ", PRELAND_WIDTH); +} diff --git a/os/src/net/port_table.rs b/os/src/net/port_table.rs new file mode 100644 index 0000000..bcda046 --- /dev/null +++ b/os/src/net/port_table.rs @@ -0,0 +1,141 @@ +use alloc::{sync::Arc, vec::Vec}; +use lazy_static::lazy_static; +use lose_net_stack::packets::tcp::TCPPacket; + +use crate::fs::File; +use crate::sync::UPIntrFreeCell; +use crate::task::TaskControlBlock; + +use super::tcp::TCP; + +pub struct Port { + pub port: u16, + pub receivable: bool, + pub schedule: Option>, +} + +lazy_static! { + static ref LISTEN_TABLE: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +pub fn listen(port: u16) -> Option { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..listen_table.len() { + if listen_table[i].is_none() { + index = i; + break; + } + } + + let listen_port = Port { + port, + receivable: false, + schedule: None, + }; + + if index == usize::MAX { + listen_table.push(Some(listen_port)); + Some(listen_table.len() - 1) + } else { + listen_table[index] = Some(listen_port); + Some(index) + } +} + +// can accept request +pub fn accept(listen_index: usize, task: Arc) { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + assert!(listen_index < listen_table.len()); + let listen_port = listen_table[listen_index].as_mut(); + assert!(listen_port.is_some()); + let listen_port = listen_port.unwrap(); + listen_port.receivable = true; + listen_port.schedule = Some(task); +} + +pub fn port_acceptable(listen_index: usize) -> bool { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + assert!(listen_index < listen_table.len()); + + let listen_port = listen_table[listen_index].as_mut(); + listen_port.map_or(false, |x| x.receivable) +} + +// check whether it can accept request +pub fn check_accept(port: u16, tcp_packet: &TCPPacket) -> Option<()> { + LISTEN_TABLE.exclusive_session(|listen_table| { + let mut listen_ports: Vec<&mut Option> = listen_table + .iter_mut() + .filter(|x| match x { + Some(t) => t.port == port && t.receivable == true, + None => false, + }) + .collect(); + if listen_ports.len() == 0 { + None + } else { + let listen_port = listen_ports[0].as_mut().unwrap(); + let task = listen_port.schedule.clone().unwrap(); + // wakeup_task(Arc::clone(&listen_port.schedule.clone().unwrap())); + listen_port.schedule = None; + listen_port.receivable = false; + + accept_connection(port, tcp_packet, task); + Some(()) + } + }) +} + +pub fn accept_connection(_port: u16, tcp_packet: &TCPPacket, task: Arc) { + let process = task.process.upgrade().unwrap(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + + let tcp_socket = TCP::new( + tcp_packet.source_ip, + tcp_packet.dest_port, + tcp_packet.source_port, + tcp_packet.seq, + tcp_packet.ack, + ); + + inner.fd_table[fd] = Some(Arc::new(tcp_socket)); + + let cx = task.inner_exclusive_access().get_trap_cx(); + cx.x[10] = fd; +} + +// store in the fd_table, delete the listen table when close the application. +pub struct PortFd(usize); + +impl PortFd { + pub fn new(port_index: usize) -> Self { + PortFd(port_index) + } +} + +impl Drop for PortFd { + fn drop(&mut self) { + LISTEN_TABLE.exclusive_access()[self.0] = None + } +} + +impl File for PortFd { + fn readable(&self) -> bool { + false + } + + fn writable(&self) -> bool { + false + } + + fn read(&self, _buf: crate::mm::UserBuffer) -> usize { + 0 + } + + fn write(&self, _buf: crate::mm::UserBuffer) -> usize { + 0 + } +} diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs new file mode 100644 index 0000000..a37ba82 --- /dev/null +++ b/os/src/net/socket.rs @@ -0,0 +1,123 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use lose_net_stack::IPv4; + +use crate::sync::UPIntrFreeCell; + +// TODO: specify the protocol, TCP or UDP +pub struct Socket { + pub raddr: IPv4, // remote address + pub lport: u16, // local port + pub rport: u16, // rempote port + pub buffers: VecDeque>, // datas + pub seq: u32, + pub ack: u32, +} + +lazy_static! { + static ref SOCKET_TABLE: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +/// get the seq and ack by socket index +pub fn get_s_a_by_index(index: usize) -> Option<(u32, u32)> { + let socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(index < socket_table.len()); + + socket_table.get(index).map_or(None, |x| match x { + Some(x) => Some((x.seq, x.ack)), + None => None, + }) +} + +pub fn set_s_a_by_index(index: usize, seq: u32, ack: u32) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + let sock = socket_table[index].as_mut().unwrap(); + + sock.ack = ack; + sock.seq = seq; +} + +pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + let socket_table = SOCKET_TABLE.exclusive_access(); + for i in 0..socket_table.len() { + let sock = &socket_table[i]; + if sock.is_none() { + continue; + } + + let sock = sock.as_ref().unwrap(); + if sock.raddr == raddr && sock.lport == lport && sock.rport == rport { + return Some(i); + } + } + None +} + +pub fn add_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + if get_socket(raddr, lport, rport).is_some() { + return None; + } + + let mut socket_table = SOCKET_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..socket_table.len() { + if socket_table[i].is_none() { + index = i; + break; + } + } + + let socket = Socket { + raddr, + lport, + rport, + buffers: VecDeque::new(), + seq: 0, + ack: 0, + }; + + if index == usize::MAX { + socket_table.push(Some(socket)); + Some(socket_table.len() - 1) + } else { + socket_table[index] = Some(socket); + Some(index) + } +} + +pub fn remove_socket(index: usize) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + + socket_table[index] = None; +} + +pub fn push_data(index: usize, data: Vec) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index] + .as_mut() + .unwrap() + .buffers + .push_back(data); +} + +pub fn pop_data(index: usize) -> Option> { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.pop_front() +} diff --git a/os/src/net/tcp.rs b/os/src/net/tcp.rs new file mode 100644 index 0000000..d1f7bd2 --- /dev/null +++ b/os/src/net/tcp.rs @@ -0,0 +1,113 @@ +use alloc::vec; +use lose_net_stack::packets::tcp::TCPPacket; +use lose_net_stack::IPv4; +use lose_net_stack::MacAddress; +use lose_net_stack::TcpFlags; + +use crate::{drivers::NET_DEVICE, fs::File}; + +use super::socket::get_s_a_by_index; +use super::{ + net_interrupt_handler, + socket::{add_socket, pop_data, remove_socket}, + LOSE_NET_STACK, +}; + +// add tcp packet info to this structure +pub struct TCP { + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub seq: u32, + pub ack: u32, + pub socket_index: usize, +} + +impl TCP { + pub fn new(target: IPv4, sport: u16, dport: u16, seq: u32, ack: u32) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + seq, + ack, + socket_index: index, + } + } +} + +impl File for TCP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len] + .copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + // get sock and sequence + let (ack, seq) = get_s_a_by_index(self.socket_index).map_or((0, 0), |x| x); + + let tcp_packet = TCPPacket { + source_ip: lose_net_stack.ip, + source_mac: lose_net_stack.mac, + source_port: self.sport, + dest_ip: self.target, + dest_mac: MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + dest_port: self.dport, + data_len: len, + seq, + ack, + flags: TcpFlags::A, + win: 65535, + urg: 0, + data: data.as_ref(), + }; + NET_DEVICE.transmit(&tcp_packet.build_data()); + len + } +} + +impl Drop for TCP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} diff --git a/os/src/net/udp.rs b/os/src/net/udp.rs new file mode 100644 index 0000000..f343ed4 --- /dev/null +++ b/os/src/net/udp.rs @@ -0,0 +1,95 @@ +use super::net_interrupt_handler; +use super::socket::{add_socket, pop_data, remove_socket}; +use super::LOSE_NET_STACK; +use super::NET_DEVICE; +use crate::fs::File; +use alloc::vec; +use lose_net_stack::packets::udp::UDPPacket; +use lose_net_stack::IPv4; +use lose_net_stack::MacAddress; + +pub struct UDP { + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub socket_index: usize, +} + +impl UDP { + pub fn new(target: IPv4, sport: u16, dport: u16) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + socket_index: index, + } + } +} + +impl File for UDP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len] + .copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + let udp_packet = UDPPacket::new( + lose_net_stack.ip, + lose_net_stack.mac, + self.sport, + self.target, + MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + self.dport, + len, + data.as_ref(), + ); + NET_DEVICE.transmit(&udp_packet.build_data()); + len + } +} + +impl Drop for UDP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} diff --git a/os/src/sbi.rs b/os/src/sbi.rs index 276c199..2c0aaa5 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -1,44 +1,15 @@ -#![allow(unused)] - -const SBI_SET_TIMER: usize = 0; -const SBI_CONSOLE_PUTCHAR: usize = 1; -const SBI_CONSOLE_GETCHAR: usize = 2; -const SBI_CLEAR_IPI: usize = 3; -const SBI_SEND_IPI: usize = 4; -const SBI_REMOTE_FENCE_I: usize = 5; -const SBI_REMOTE_SFENCE_VMA: usize = 6; -const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; -const SBI_SHUTDOWN: usize = 8; - -#[inline(always)] -fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { - let mut ret; - unsafe { - asm!( - "ecall", - inlateout("x10") arg0 => ret, - in("x11") arg1, - in("x12") arg2, - in("x17") which, - ); - } - ret -} - +/// use sbi call to set timer pub fn set_timer(timer: usize) { - sbi_call(SBI_SET_TIMER, timer, 0, 0); + sbi_rt::set_timer(timer as _); } -pub fn console_putchar(c: usize) { - sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); +/// use sbi call to shutdown the kernel +pub fn shutdown(failure: bool) -> ! { + use sbi_rt::{system_reset, NoReason, Shutdown, SystemFailure}; + if !failure { + system_reset(Shutdown, NoReason); + } else { + system_reset(Shutdown, SystemFailure); + } + unreachable!() } - -pub fn console_getchar() -> usize { - sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) -} - -pub fn shutdown() -> ! { - sbi_call(SBI_SHUTDOWN, 0, 0, 0); - panic!("It should shutdown!"); -} - diff --git a/os/src/sync/condvar.rs b/os/src/sync/condvar.rs new file mode 100644 index 0000000..d13652f --- /dev/null +++ b/os/src/sync/condvar.rs @@ -0,0 +1,58 @@ +use crate::sync::{Mutex, UPIntrFreeCell}; +use crate::task::{ + block_current_and_run_next, block_current_task, current_task, wakeup_task, TaskContext, + TaskControlBlock, +}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub struct Condvar { + pub inner: UPIntrFreeCell, +} + +pub struct CondvarInner { + pub wait_queue: VecDeque>, +} + +impl Condvar { + pub fn new() -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(CondvarInner { + wait_queue: VecDeque::new(), + }) + }, + } + } + + pub fn signal(&self) { + let mut inner = self.inner.exclusive_access(); + if let Some(task) = inner.wait_queue.pop_front() { + wakeup_task(task); + } + } + + /* + pub fn wait(&self) { + let mut inner = self.inner.exclusive_access(); + inner.wait_queue.push_back(current_task().unwrap()); + drop(inner); + block_current_and_run_next(); + } + */ + + pub fn wait_no_sched(&self) -> *mut TaskContext { + self.inner.exclusive_session(|inner| { + inner.wait_queue.push_back(current_task().unwrap()); + }); + block_current_task() + } + + pub fn wait_with_mutex(&self, mutex: Arc) { + mutex.unlock(); + self.inner.exclusive_session(|inner| { + inner.wait_queue.push_back(current_task().unwrap()); + }); + block_current_and_run_next(); + mutex.lock(); + } +} diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index 7729524..d9a00b5 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -1,3 +1,9 @@ +mod condvar; +mod mutex; +mod semaphore; mod up; -pub use up::UPSafeCell; \ No newline at end of file +pub use condvar::Condvar; +pub use mutex::{Mutex, MutexBlocking, MutexSpin}; +pub use semaphore::Semaphore; +pub use up::{UPIntrFreeCell, UPIntrRefMut}; diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs new file mode 100644 index 0000000..aaf1571 --- /dev/null +++ b/os/src/sync/mutex.rs @@ -0,0 +1,88 @@ +use super::UPIntrFreeCell; +use crate::task::TaskControlBlock; +use crate::task::{block_current_and_run_next, suspend_current_and_run_next}; +use crate::task::{current_task, wakeup_task}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub trait Mutex: Sync + Send { + fn lock(&self); + fn unlock(&self); +} + +pub struct MutexSpin { + locked: UPIntrFreeCell, +} + +impl MutexSpin { + pub fn new() -> Self { + Self { + locked: unsafe { UPIntrFreeCell::new(false) }, + } + } +} + +impl Mutex for MutexSpin { + fn lock(&self) { + loop { + let mut locked = self.locked.exclusive_access(); + if *locked { + drop(locked); + suspend_current_and_run_next(); + continue; + } else { + *locked = true; + return; + } + } + } + + fn unlock(&self) { + let mut locked = self.locked.exclusive_access(); + *locked = false; + } +} + +pub struct MutexBlocking { + inner: UPIntrFreeCell, +} + +pub struct MutexBlockingInner { + locked: bool, + wait_queue: VecDeque>, +} + +impl MutexBlocking { + pub fn new() -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(MutexBlockingInner { + locked: false, + wait_queue: VecDeque::new(), + }) + }, + } + } +} + +impl Mutex for MutexBlocking { + fn lock(&self) { + let mut mutex_inner = self.inner.exclusive_access(); + if mutex_inner.locked { + mutex_inner.wait_queue.push_back(current_task().unwrap()); + drop(mutex_inner); + block_current_and_run_next(); + } else { + mutex_inner.locked = true; + } + } + + fn unlock(&self) { + let mut mutex_inner = self.inner.exclusive_access(); + assert!(mutex_inner.locked); + if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { + wakeup_task(waking_task); + } else { + mutex_inner.locked = false; + } + } +} diff --git a/os/src/sync/semaphore.rs b/os/src/sync/semaphore.rs new file mode 100644 index 0000000..bdad319 --- /dev/null +++ b/os/src/sync/semaphore.rs @@ -0,0 +1,45 @@ +use crate::sync::UPIntrFreeCell; +use crate::task::{block_current_and_run_next, current_task, wakeup_task, TaskControlBlock}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub struct Semaphore { + pub inner: UPIntrFreeCell, +} + +pub struct SemaphoreInner { + pub count: isize, + pub wait_queue: VecDeque>, +} + +impl Semaphore { + pub fn new(res_count: usize) -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(SemaphoreInner { + count: res_count as isize, + wait_queue: VecDeque::new(), + }) + }, + } + } + + pub fn up(&self) { + let mut inner = self.inner.exclusive_access(); + inner.count += 1; + if inner.count <= 0 { + if let Some(task) = inner.wait_queue.pop_front() { + wakeup_task(task); + } + } + } + + pub fn down(&self) { + let mut inner = self.inner.exclusive_access(); + inner.count -= 1; + if inner.count < 0 { + inner.wait_queue.push_back(current_task().unwrap()); + drop(inner); + block_current_and_run_next(); + } + } +} diff --git a/os/src/sync/up.rs b/os/src/sync/up.rs index 642668c..6c7a0f4 100644 --- a/os/src/sync/up.rs +++ b/os/src/sync/up.rs @@ -1,5 +1,9 @@ -use core::cell::{RefCell, RefMut}; +use core::cell::{RefCell, RefMut, UnsafeCell}; +use core::ops::{Deref, DerefMut}; +use lazy_static::*; +use riscv::register::sstatus; +/* /// Wrap a static data structure inside it so that we are /// able to access it without any `unsafe`. /// @@ -18,10 +22,119 @@ impl UPSafeCell { /// User is responsible to guarantee that inner struct is only used in /// uniprocessor. pub unsafe fn new(value: T) -> Self { - Self { inner: RefCell::new(value) } + Self { + inner: RefCell::new(value), + } } /// Panic if the data has been borrowed. pub fn exclusive_access(&self) -> RefMut<'_, T> { self.inner.borrow_mut() } -} \ No newline at end of file +} +*/ + +pub struct UPSafeCellRaw { + inner: UnsafeCell, +} + +unsafe impl Sync for UPSafeCellRaw {} + +impl UPSafeCellRaw { + pub unsafe fn new(value: T) -> Self { + Self { + inner: UnsafeCell::new(value), + } + } + pub fn get_mut(&self) -> &mut T { + unsafe { &mut (*self.inner.get()) } + } +} + +pub struct IntrMaskingInfo { + nested_level: usize, + sie_before_masking: bool, +} + +lazy_static! { + static ref INTR_MASKING_INFO: UPSafeCellRaw = + unsafe { UPSafeCellRaw::new(IntrMaskingInfo::new()) }; +} + +impl IntrMaskingInfo { + pub fn new() -> Self { + Self { + nested_level: 0, + sie_before_masking: false, + } + } + + pub fn enter(&mut self) { + let sie = sstatus::read().sie(); + unsafe { + sstatus::clear_sie(); + } + if self.nested_level == 0 { + self.sie_before_masking = sie; + } + self.nested_level += 1; + } + + pub fn exit(&mut self) { + self.nested_level -= 1; + if self.nested_level == 0 && self.sie_before_masking { + unsafe { + sstatus::set_sie(); + } + } + } +} + +pub struct UPIntrFreeCell { + /// inner data + inner: RefCell, +} + +unsafe impl Sync for UPIntrFreeCell {} + +pub struct UPIntrRefMut<'a, T>(Option>); + +impl UPIntrFreeCell { + pub unsafe fn new(value: T) -> Self { + Self { + inner: RefCell::new(value), + } + } + + /// Panic if the data has been borrowed. + pub fn exclusive_access(&self) -> UPIntrRefMut<'_, T> { + INTR_MASKING_INFO.get_mut().enter(); + UPIntrRefMut(Some(self.inner.borrow_mut())) + } + + pub fn exclusive_session(&self, f: F) -> V + where + F: FnOnce(&mut T) -> V, + { + let mut inner = self.exclusive_access(); + f(inner.deref_mut()) + } +} + +impl<'a, T> Drop for UPIntrRefMut<'a, T> { + fn drop(&mut self) { + self.0 = None; + INTR_MASKING_INFO.get_mut().exit(); + } +} + +impl<'a, T> Deref for UPIntrRefMut<'a, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + self.0.as_ref().unwrap().deref() + } +} +impl<'a, T> DerefMut for UPIntrRefMut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut().unwrap().deref_mut() + } +} diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index b8c4659..2758825 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,15 +1,99 @@ -const FD_STDOUT: usize = 1; +use crate::fs::{make_pipe, open_file, OpenFlags}; +use crate::mm::{translated_byte_buffer, translated_refmut, translated_str, UserBuffer}; +use crate::task::{current_process, current_user_token}; +use alloc::sync::Arc; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { - match fd { - FD_STDOUT => { - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; - let str = core::str::from_utf8(slice).unwrap(); - print!("{}", str); - len as isize - }, - _ => { - panic!("Unsupported fd in sys_write!"); - } + let token = current_user_token(); + let process = current_process(); + let inner = process.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; } -} \ No newline at end of file + if let Some(file) = &inner.fd_table[fd] { + if !file.writable() { + return -1; + } + let file = file.clone(); + // release current task TCB manually to avoid multi-borrow + drop(inner); + file.write(UserBuffer::new(translated_byte_buffer(token, buf, len))) as isize + } else { + -1 + } +} + +pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { + let token = current_user_token(); + let process = current_process(); + let inner = process.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; + } + if let Some(file) = &inner.fd_table[fd] { + let file = file.clone(); + if !file.readable() { + return -1; + } + // release current task TCB manually to avoid multi-borrow + drop(inner); + file.read(UserBuffer::new(translated_byte_buffer(token, buf, len))) as isize + } else { + -1 + } +} + +pub fn sys_open(path: *const u8, flags: u32) -> isize { + let process = current_process(); + let token = current_user_token(); + let path = translated_str(token, path); + if let Some(inode) = open_file(path.as_str(), OpenFlags::from_bits(flags).unwrap()) { + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + inner.fd_table[fd] = Some(inode); + fd as isize + } else { + -1 + } +} + +pub fn sys_close(fd: usize) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; + } + if inner.fd_table[fd].is_none() { + return -1; + } + inner.fd_table[fd].take(); + 0 +} + +pub fn sys_pipe(pipe: *mut usize) -> isize { + let process = current_process(); + let token = current_user_token(); + let mut inner = process.inner_exclusive_access(); + let (pipe_read, pipe_write) = make_pipe(); + let read_fd = inner.alloc_fd(); + inner.fd_table[read_fd] = Some(pipe_read); + let write_fd = inner.alloc_fd(); + inner.fd_table[write_fd] = Some(pipe_write); + *translated_refmut(token, pipe) = read_fd; + *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd; + 0 +} + +pub fn sys_dup(fd: usize) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; + } + if inner.fd_table[fd].is_none() { + return -1; + } + let new_fd = inner.alloc_fd(); + inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); + new_fd as isize +} diff --git a/os/src/syscall/gui.rs b/os/src/syscall/gui.rs new file mode 100644 index 0000000..c5ba8a4 --- /dev/null +++ b/os/src/syscall/gui.rs @@ -0,0 +1,34 @@ +use crate::drivers::GPU_DEVICE; +use crate::mm::{MapArea, MapPermission, MapType, PhysAddr, VirtAddr}; +use crate::task::current_process; + +const FB_VADDR: usize = 0x10000000; + +pub fn sys_framebuffer() -> isize { + let fb = GPU_DEVICE.get_framebuffer(); + let len = fb.len(); + // println!("[kernel] FrameBuffer: addr 0x{:X}, len {}", fb.as_ptr() as usize , len); + let fb_start_pa = PhysAddr::from(fb.as_ptr() as usize); + assert!(fb_start_pa.aligned()); + let fb_start_ppn = fb_start_pa.floor(); + let fb_start_vpn = VirtAddr::from(FB_VADDR).floor(); + let pn_offset = fb_start_ppn.0 as isize - fb_start_vpn.0 as isize; + + let current_process = current_process(); + let mut inner = current_process.inner_exclusive_access(); + inner.memory_set.push( + MapArea::new( + (FB_VADDR as usize).into(), + (FB_VADDR + len as usize).into(), + MapType::Linear(pn_offset), + MapPermission::R | MapPermission::W | MapPermission::U, + ), + None, + ); + FB_VADDR as isize +} + +pub fn sys_framebuffer_flush() -> isize { + GPU_DEVICE.flush(); + 0 +} diff --git a/os/src/syscall/input.rs b/os/src/syscall/input.rs new file mode 100644 index 0000000..ee86bd3 --- /dev/null +++ b/os/src/syscall/input.rs @@ -0,0 +1,28 @@ +//use crate::drivers::{KEYBOARD_DEVICE,MOUSE_DEVICE,INPUT_CONDVAR,read_input_event}; +use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; + +pub fn sys_event_get() -> isize { + let kb = KEYBOARD_DEVICE.clone(); + let mouse = MOUSE_DEVICE.clone(); + //let input=INPUT_CONDVAR.clone(); + //read_input_event() as isize + if !kb.is_empty() { + kb.read_event() as isize + } else if !mouse.is_empty() { + mouse.read_event() as isize + } else { + 0 + } +} + +use crate::drivers::chardev::UART; + +/// check UART's read-buffer is empty or not +pub fn sys_key_pressed() -> isize { + let res = !UART.read_buffer_is_empty(); + if res { + 1 + } else { + 0 + } +} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 8ca22fe..cd12de0 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,21 +1,90 @@ +const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; +const SYSCALL_LISTEN: usize = 30; +const SYSCALL_ACCEPT: usize = 31; +const SYSCALL_OPEN: usize = 56; +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_SLEEP: usize = 101; const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; 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; +const SYSCALL_THREAD_CREATE: usize = 1000; +const SYSCALL_GETTID: usize = 1001; +const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; +const SYSCALL_SEMAPHORE_CREATE: usize = 1020; +const SYSCALL_SEMAPHORE_UP: usize = 1021; +const SYSCALL_SEMAPHORE_DOWN: usize = 1022; +const SYSCALL_CONDVAR_CREATE: usize = 1030; +const SYSCALL_CONDVAR_SIGNAL: usize = 1031; +const SYSCALL_CONDVAR_WAIT: usize = 1032; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; mod fs; +mod gui; +mod input; +mod net; mod process; +mod sync; +mod thread; use fs::*; +use gui::*; +use input::*; +use net::*; use process::*; +use sync::*; +use thread::*; pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { + SYSCALL_DUP => sys_dup(args[0]), + SYSCALL_CONNECT => sys_connect(args[0] as _, args[1] as _, args[2] as _), + SYSCALL_LISTEN => sys_listen(args[0] as _), + SYSCALL_ACCEPT => sys_accept(args[0] as _), + SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), + SYSCALL_CLOSE => sys_close(args[0]), + SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), + SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]), SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_EXIT => sys_exit(args[0] as i32), + SYSCALL_SLEEP => sys_sleep(args[0]), SYSCALL_YIELD => sys_yield(), - SYSCALL_GET_TIME => sys_get_time(args[0] as *mut TimeVal, args[1]), + SYSCALL_KILL => sys_kill(args[0], args[1] as u32), + SYSCALL_GET_TIME => sys_get_time(), + SYSCALL_GETPID => sys_getpid(), + SYSCALL_FORK => sys_fork(), + SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), + SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), + SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]), + SYSCALL_GETTID => sys_gettid(), + SYSCALL_WAITTID => sys_waittid(args[0]) as isize, + SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1), + SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]), + SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]), + SYSCALL_SEMAPHORE_CREATE => sys_semaphore_create(args[0]), + SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]), + SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]), + SYSCALL_CONDVAR_CREATE => sys_condvar_create(), + SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]), + SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]), + SYSCALL_FRAMEBUFFER => sys_framebuffer(), + SYSCALL_FRAMEBUFFER_FLUSH => sys_framebuffer_flush(), + SYSCALL_EVENT_GET => sys_event_get(), + SYSCALL_KEY_PRESSED => sys_key_pressed(), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } - diff --git a/os/src/syscall/net.rs b/os/src/syscall/net.rs new file mode 100644 index 0000000..c16b9ce --- /dev/null +++ b/os/src/syscall/net.rs @@ -0,0 +1,53 @@ +use crate::net::port_table::{accept, listen, port_acceptable, PortFd}; +use crate::net::udp::UDP; +use crate::net::{net_interrupt_handler, IPv4}; +use crate::task::{current_process, current_task, current_trap_cx}; +use alloc::sync::Arc; + +// just support udp +pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); + inner.fd_table[fd] = Some(Arc::new(udp_node)); + fd as isize +} + +// listen a port +pub fn sys_listen(port: u16) -> isize { + match listen(port) { + Some(port_index) => { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let port_fd = PortFd::new(port_index); + inner.fd_table[fd] = Some(Arc::new(port_fd)); + + // NOTICE: this return the port index, not the fd + port_index as isize + } + None => -1, + } +} + +// accept a tcp connection +pub fn sys_accept(port_index: usize) -> isize { + println!("accepting port {}", port_index); + + let task = current_task().unwrap(); + accept(port_index, task); + // block_current_and_run_next(); + + // NOTICE: There does not have interrupt handler, just call it munually. + loop { + net_interrupt_handler(); + + if !port_acceptable(port_index) { + break; + } + } + + let cx = current_trap_cx(); + cx.x[10] as isize +} diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index d016849..7d5b67a 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,19 +1,16 @@ +use crate::fs::{open_file, OpenFlags}; +use crate::mm::{translated_ref, translated_refmut, translated_str}; use crate::task::{ - suspend_current_and_run_next, - exit_current_and_run_next, + current_process, current_task, current_user_token, exit_current_and_run_next, pid2process, + suspend_current_and_run_next, SignalFlags, }; -use crate::timer::get_time_us; - -#[repr(C)] -#[derive(Debug)] -pub struct TimeVal { - pub sec: usize, - pub usec: usize, -} +use crate::timer::get_time_ms; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; pub fn sys_exit(exit_code: i32) -> ! { - println!("[kernel] Application exited with code {}", exit_code); - exit_current_and_run_next(); + exit_current_and_run_next(exit_code); panic!("Unreachable in sys_exit!"); } @@ -22,17 +19,99 @@ pub fn sys_yield() -> isize { 0 } -// pub fn sys_get_time() -> isize { -// get_time_ms() as isize -// } +pub fn sys_get_time() -> isize { + get_time_ms() as isize +} -pub fn sys_get_time(ts: *mut TimeVal, _tz: usize) -> isize { - let us = get_time_us(); - unsafe { - *ts = TimeVal { - sec: us / 1_000_000, - usec: us % 1_000_000, - }; +pub fn sys_getpid() -> isize { + current_task().unwrap().process.upgrade().unwrap().getpid() as isize +} + +pub fn sys_fork() -> isize { + let current_process = current_process(); + let new_process = current_process.fork(); + let new_pid = new_process.getpid(); + // modify trap context of new_task, because it returns immediately after switching + let new_process_inner = new_process.inner_exclusive_access(); + let task = new_process_inner.tasks[0].as_ref().unwrap(); + let trap_cx = task.inner_exclusive_access().get_trap_cx(); + // we do not have to move to next instruction since we have done it before + // for child process, fork returns 0 + trap_cx.x[10] = 0; + new_pid as isize +} + +pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { + let token = current_user_token(); + let path = translated_str(token, path); + let mut args_vec: Vec = Vec::new(); + loop { + let arg_str_ptr = *translated_ref(token, args); + if arg_str_ptr == 0 { + break; + } + args_vec.push(translated_str(token, arg_str_ptr as *const u8)); + unsafe { + args = args.add(1); + } } - 0 -} \ No newline at end of file + if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) { + let all_data = app_inode.read_all(); + let process = current_process(); + let argc = args_vec.len(); + process.exec(all_data.as_slice(), args_vec); + // return argc because cx.x[10] will be covered with it later + argc as isize + } else { + -1 + } +} + +/// If there is not a child process whose pid is same as given, return -1. +/// Else if there is a child process but it is still running, return -2. +pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { + let process = current_process(); + // find a child process + + let mut inner = process.inner_exclusive_access(); + if !inner + .children + .iter() + .any(|p| pid == -1 || pid as usize == p.getpid()) + { + return -1; + // ---- release current PCB + } + let pair = inner.children.iter().enumerate().find(|(_, p)| { + // ++++ temporarily access child PCB exclusively + p.inner_exclusive_access().is_zombie && (pid == -1 || pid as usize == p.getpid()) + // ++++ release child PCB + }); + if let Some((idx, _)) = pair { + let child = inner.children.remove(idx); + // confirm that child will be deallocated after being removed from children list + assert_eq!(Arc::strong_count(&child), 1); + let found_pid = child.getpid(); + // ++++ temporarily access child PCB exclusively + let exit_code = child.inner_exclusive_access().exit_code; + // ++++ release child PCB + *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code; + found_pid as isize + } else { + -2 + } + // ---- release current PCB automatically +} + +pub fn sys_kill(pid: usize, signal: u32) -> isize { + if let Some(process) = pid2process(pid) { + if let Some(flag) = SignalFlags::from_bits(signal) { + process.inner_exclusive_access().signals |= flag; + 0 + } else { + -1 + } + } else { + -1 + } +} diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs new file mode 100644 index 0000000..1180669 --- /dev/null +++ b/os/src/syscall/sync.rs @@ -0,0 +1,134 @@ +use crate::sync::{Condvar, Mutex, MutexBlocking, MutexSpin, Semaphore}; +use crate::task::{block_current_and_run_next, current_process, current_task}; +use crate::timer::{add_timer, get_time_ms}; +use alloc::sync::Arc; + +pub fn sys_sleep(ms: usize) -> isize { + let expire_ms = get_time_ms() + ms; + let task = current_task().unwrap(); + add_timer(expire_ms, task); + block_current_and_run_next(); + 0 +} + +pub fn sys_mutex_create(blocking: bool) -> isize { + let process = current_process(); + let mutex: Option> = if !blocking { + Some(Arc::new(MutexSpin::new())) + } else { + Some(Arc::new(MutexBlocking::new())) + }; + let mut process_inner = process.inner_exclusive_access(); + if let Some(id) = process_inner + .mutex_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.mutex_list[id] = mutex; + id as isize + } else { + process_inner.mutex_list.push(mutex); + process_inner.mutex_list.len() as isize - 1 + } +} + +pub fn sys_mutex_lock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.lock(); + 0 +} + +pub fn sys_mutex_unlock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.unlock(); + 0 +} + +pub fn sys_semaphore_create(res_count: usize) -> isize { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + let id = if let Some(id) = process_inner + .semaphore_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.semaphore_list[id] = Some(Arc::new(Semaphore::new(res_count))); + id + } else { + process_inner + .semaphore_list + .push(Some(Arc::new(Semaphore::new(res_count)))); + process_inner.semaphore_list.len() - 1 + }; + id as isize +} + +pub fn sys_semaphore_up(sem_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); + drop(process_inner); + sem.up(); + 0 +} + +pub fn sys_semaphore_down(sem_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); + drop(process_inner); + sem.down(); + 0 +} + +pub fn sys_condvar_create() -> isize { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + let id = if let Some(id) = process_inner + .condvar_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.condvar_list[id] = Some(Arc::new(Condvar::new())); + id + } else { + process_inner + .condvar_list + .push(Some(Arc::new(Condvar::new()))); + process_inner.condvar_list.len() - 1 + }; + id as isize +} + +pub fn sys_condvar_signal(condvar_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); + drop(process_inner); + condvar.signal(); + 0 +} + +pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + condvar.wait_with_mutex(mutex); + 0 +} diff --git a/os/src/syscall/thread.rs b/os/src/syscall/thread.rs new file mode 100644 index 0000000..3955d9d --- /dev/null +++ b/os/src/syscall/thread.rs @@ -0,0 +1,85 @@ +use crate::{ + mm::kernel_token, + task::{add_task, current_task, TaskControlBlock}, + trap::{trap_handler, TrapContext}, +}; +use alloc::sync::Arc; + +pub fn sys_thread_create(entry: usize, arg: usize) -> isize { + let task = current_task().unwrap(); + let process = task.process.upgrade().unwrap(); + // create a new thread + let new_task = Arc::new(TaskControlBlock::new( + Arc::clone(&process), + task.inner_exclusive_access() + .res + .as_ref() + .unwrap() + .ustack_base, + true, + )); + // add new task to scheduler + add_task(Arc::clone(&new_task)); + let new_task_inner = new_task.inner_exclusive_access(); + let new_task_res = new_task_inner.res.as_ref().unwrap(); + let new_task_tid = new_task_res.tid; + let mut process_inner = process.inner_exclusive_access(); + // add new thread to current process + let tasks = &mut process_inner.tasks; + while tasks.len() < new_task_tid + 1 { + tasks.push(None); + } + tasks[new_task_tid] = Some(Arc::clone(&new_task)); + let new_task_trap_cx = new_task_inner.get_trap_cx(); + *new_task_trap_cx = TrapContext::app_init_context( + entry, + new_task_res.ustack_top(), + kernel_token(), + new_task.kstack.get_top(), + trap_handler as usize, + ); + (*new_task_trap_cx).x[10] = arg; + new_task_tid as isize +} + +pub fn sys_gettid() -> isize { + current_task() + .unwrap() + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .tid as isize +} + +/// thread does not exist, return -1 +/// thread has not exited yet, return -2 +/// otherwise, return thread's exit code +pub fn sys_waittid(tid: usize) -> i32 { + let task = current_task().unwrap(); + let process = task.process.upgrade().unwrap(); + let task_inner = task.inner_exclusive_access(); + let mut process_inner = process.inner_exclusive_access(); + // a thread cannot wait for itself + if task_inner.res.as_ref().unwrap().tid == tid { + return -1; + } + let mut exit_code: Option = None; + let waited_task = process_inner.tasks[tid].as_ref(); + if let Some(waited_task) = waited_task { + if let Some(waited_exit_code) = waited_task.inner_exclusive_access().exit_code { + exit_code = Some(waited_exit_code); + } + } else { + // waited thread does not exist + return -1; + } + if let Some(exit_code) = exit_code { + // dealloc the exited thread + process_inner.tasks[tid] = None; + exit_code + } else { + // waited thread has not exited + -2 + } +} diff --git a/os/src/task/context.rs b/os/src/task/context.rs index 35055d0..e4f59d8 100644 --- a/os/src/task/context.rs +++ b/os/src/task/context.rs @@ -1,4 +1,5 @@ -#[derive(Copy, Clone)] +use crate::trap::trap_return; + #[repr(C)] pub struct TaskContext { ra: usize, @@ -14,13 +15,11 @@ impl TaskContext { s: [0; 12], } } - pub fn goto_restore(kstack_ptr: usize) -> Self { - extern "C" { fn __restore(); } + pub fn goto_trap_return(kstack_ptr: usize) -> Self { Self { - ra: __restore as usize, + ra: trap_return as usize, sp: kstack_ptr, s: [0; 12], } } } - diff --git a/os/src/task/id.rs b/os/src/task/id.rs new file mode 100644 index 0000000..23ddf7b --- /dev/null +++ b/os/src/task/id.rs @@ -0,0 +1,226 @@ +use super::ProcessControlBlock; +use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT_BASE, USER_STACK_SIZE}; +use crate::mm::{MapPermission, PhysPageNum, VirtAddr, KERNEL_SPACE}; +use crate::sync::UPIntrFreeCell; +use alloc::{ + sync::{Arc, Weak}, + vec::Vec, +}; +use lazy_static::*; + +pub struct RecycleAllocator { + current: usize, + recycled: Vec, +} + +impl RecycleAllocator { + pub fn new() -> Self { + RecycleAllocator { + current: 0, + recycled: Vec::new(), + } + } + pub fn alloc(&mut self) -> usize { + if let Some(id) = self.recycled.pop() { + id + } else { + self.current += 1; + self.current - 1 + } + } + pub fn dealloc(&mut self, id: usize) { + assert!(id < self.current); + assert!( + !self.recycled.iter().any(|i| *i == id), + "id {} has been deallocated!", + id + ); + self.recycled.push(id); + } +} + +lazy_static! { + static ref PID_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) }; + static ref KSTACK_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) }; +} + +pub const IDLE_PID: usize = 0; + +pub struct PidHandle(pub usize); + +pub fn pid_alloc() -> PidHandle { + PidHandle(PID_ALLOCATOR.exclusive_access().alloc()) +} + +impl Drop for PidHandle { + fn drop(&mut self) { + PID_ALLOCATOR.exclusive_access().dealloc(self.0); + } +} + +/// Return (bottom, top) of a kernel stack in kernel space. +pub fn kernel_stack_position(kstack_id: usize) -> (usize, usize) { + let top = TRAMPOLINE - kstack_id * (KERNEL_STACK_SIZE + PAGE_SIZE); + let bottom = top - KERNEL_STACK_SIZE; + (bottom, top) +} + +pub struct KernelStack(pub usize); + +pub fn kstack_alloc() -> KernelStack { + let kstack_id = KSTACK_ALLOCATOR.exclusive_access().alloc(); + let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id); + KERNEL_SPACE.exclusive_access().insert_framed_area( + kstack_bottom.into(), + kstack_top.into(), + MapPermission::R | MapPermission::W, + ); + KernelStack(kstack_id) +} + +impl Drop for KernelStack { + fn drop(&mut self) { + let (kernel_stack_bottom, _) = kernel_stack_position(self.0); + let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into(); + KERNEL_SPACE + .exclusive_access() + .remove_area_with_start_vpn(kernel_stack_bottom_va.into()); + KSTACK_ALLOCATOR.exclusive_access().dealloc(self.0); + } +} + +impl KernelStack { + #[allow(unused)] + pub fn push_on_top(&self, value: T) -> *mut T + where + T: Sized, + { + let kernel_stack_top = self.get_top(); + let ptr_mut = (kernel_stack_top - core::mem::size_of::()) as *mut T; + unsafe { + *ptr_mut = value; + } + ptr_mut + } + pub fn get_top(&self) -> usize { + let (_, kernel_stack_top) = kernel_stack_position(self.0); + kernel_stack_top + } +} + +pub struct TaskUserRes { + pub tid: usize, + pub ustack_base: usize, + pub process: Weak, +} + +fn trap_cx_bottom_from_tid(tid: usize) -> usize { + TRAP_CONTEXT_BASE - tid * PAGE_SIZE +} + +fn ustack_bottom_from_tid(ustack_base: usize, tid: usize) -> usize { + ustack_base + tid * (PAGE_SIZE + USER_STACK_SIZE) +} + +impl TaskUserRes { + pub fn new( + process: Arc, + ustack_base: usize, + alloc_user_res: bool, + ) -> Self { + let tid = process.inner_exclusive_access().alloc_tid(); + let task_user_res = Self { + tid, + ustack_base, + process: Arc::downgrade(&process), + }; + if alloc_user_res { + task_user_res.alloc_user_res(); + } + task_user_res + } + + pub fn alloc_user_res(&self) { + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + // alloc user stack + let ustack_bottom = ustack_bottom_from_tid(self.ustack_base, self.tid); + let ustack_top = ustack_bottom + USER_STACK_SIZE; + process_inner.memory_set.insert_framed_area( + ustack_bottom.into(), + ustack_top.into(), + MapPermission::R | MapPermission::W | MapPermission::U, + ); + // alloc trap_cx + let trap_cx_bottom = trap_cx_bottom_from_tid(self.tid); + let trap_cx_top = trap_cx_bottom + PAGE_SIZE; + process_inner.memory_set.insert_framed_area( + trap_cx_bottom.into(), + trap_cx_top.into(), + MapPermission::R | MapPermission::W, + ); + } + + fn dealloc_user_res(&self) { + // dealloc tid + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + // dealloc ustack manually + let ustack_bottom_va: VirtAddr = ustack_bottom_from_tid(self.ustack_base, self.tid).into(); + process_inner + .memory_set + .remove_area_with_start_vpn(ustack_bottom_va.into()); + // dealloc trap_cx manually + let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into(); + process_inner + .memory_set + .remove_area_with_start_vpn(trap_cx_bottom_va.into()); + } + + #[allow(unused)] + pub fn alloc_tid(&mut self) { + self.tid = self + .process + .upgrade() + .unwrap() + .inner_exclusive_access() + .alloc_tid(); + } + + pub fn dealloc_tid(&self) { + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + process_inner.dealloc_tid(self.tid); + } + + pub fn trap_cx_user_va(&self) -> usize { + trap_cx_bottom_from_tid(self.tid) + } + + pub fn trap_cx_ppn(&self) -> PhysPageNum { + let process = self.process.upgrade().unwrap(); + let process_inner = process.inner_exclusive_access(); + let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into(); + process_inner + .memory_set + .translate(trap_cx_bottom_va.into()) + .unwrap() + .ppn() + } + + pub fn ustack_base(&self) -> usize { + self.ustack_base + } + pub fn ustack_top(&self) -> usize { + ustack_bottom_from_tid(self.ustack_base, self.tid) + USER_STACK_SIZE + } +} + +impl Drop for TaskUserRes { + fn drop(&mut self) { + self.dealloc_tid(); + self.dealloc_user_res(); + } +} diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs new file mode 100644 index 0000000..7672cf5 --- /dev/null +++ b/os/src/task/manager.rs @@ -0,0 +1,62 @@ +use super::{ProcessControlBlock, TaskControlBlock, TaskStatus}; +use crate::sync::UPIntrFreeCell; +use alloc::collections::{BTreeMap, VecDeque}; +use alloc::sync::Arc; +use lazy_static::*; + +pub struct TaskManager { + ready_queue: VecDeque>, +} + +/// A simple FIFO scheduler. +impl TaskManager { + pub fn new() -> Self { + Self { + ready_queue: VecDeque::new(), + } + } + pub fn add(&mut self, task: Arc) { + self.ready_queue.push_back(task); + } + pub fn fetch(&mut self) -> Option> { + self.ready_queue.pop_front() + } +} + +lazy_static! { + pub static ref TASK_MANAGER: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(TaskManager::new()) }; + pub static ref PID2PCB: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(BTreeMap::new()) }; +} + +pub fn add_task(task: Arc) { + TASK_MANAGER.exclusive_access().add(task); +} + +pub fn wakeup_task(task: Arc) { + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Ready; + drop(task_inner); + add_task(task); +} + +pub fn fetch_task() -> Option> { + TASK_MANAGER.exclusive_access().fetch() +} + +pub fn pid2process(pid: usize) -> Option> { + let map = PID2PCB.exclusive_access(); + map.get(&pid).map(Arc::clone) +} + +pub fn insert_into_pid2process(pid: usize, process: Arc) { + PID2PCB.exclusive_access().insert(pid, process); +} + +pub fn remove_from_pid2process(pid: usize) { + let mut map = PID2PCB.exclusive_access(); + if map.remove(&pid).is_none() { + panic!("cannot find pid {} in pid2task!", pid); + } +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 66c8a79..fe1343b 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -1,135 +1,166 @@ mod context; +mod id; +mod manager; +mod process; +mod processor; +mod signal; mod switch; +#[allow(clippy::module_inception)] mod task; -use crate::config::MAX_APP_NUM; -use crate::loader::{get_num_app, init_app_cx}; +use self::id::TaskUserRes; +use crate::fs::{open_file, OpenFlags}; +use crate::sbi::shutdown; +use alloc::{sync::Arc, vec::Vec}; use lazy_static::*; +use manager::fetch_task; +use process::ProcessControlBlock; use switch::__switch; -use task::{TaskControlBlock, TaskStatus}; -use crate::sync::UPSafeCell; pub use context::TaskContext; +pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle, IDLE_PID}; +pub use manager::{add_task, pid2process, remove_from_pid2process, wakeup_task}; +pub use processor::{ + current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va, + current_user_token, run_tasks, schedule, take_current_task, +}; +pub use signal::SignalFlags; +pub use task::{TaskControlBlock, TaskStatus}; -pub struct TaskManager { - num_app: usize, - inner: UPSafeCell, +pub fn suspend_current_and_run_next() { + // There must be an application running. + let task = take_current_task().unwrap(); + + // ---- access current TCB exclusively + let mut task_inner = task.inner_exclusive_access(); + let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext; + // Change status to Ready + task_inner.task_status = TaskStatus::Ready; + drop(task_inner); + // ---- release current TCB + + // push back to ready queue. + add_task(task); + // jump to scheduling cycle + schedule(task_cx_ptr); } -struct TaskManagerInner { - tasks: [TaskControlBlock; MAX_APP_NUM], - current_task: usize, +/// This function must be followed by a schedule +pub fn block_current_task() -> *mut TaskContext { + let task = take_current_task().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Blocked; + &mut task_inner.task_cx as *mut TaskContext +} + +pub fn block_current_and_run_next() { + let task_cx_ptr = block_current_task(); + schedule(task_cx_ptr); +} + +/// Exit the current 'Running' task and run the next task in task list. +pub fn exit_current_and_run_next(exit_code: i32) { + let task = take_current_task().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + let process = task.process.upgrade().unwrap(); + let tid = task_inner.res.as_ref().unwrap().tid; + // record exit code + task_inner.exit_code = Some(exit_code); + task_inner.res = None; + // here we do not remove the thread since we are still using the kstack + // it will be deallocated when sys_waittid is called + drop(task_inner); + drop(task); + // however, if this is the main thread of current process + // the process should terminate at once + if tid == 0 { + let pid = process.getpid(); + if pid == IDLE_PID { + println!( + "[kernel] Idle process exit with exit_code {} ...", + exit_code + ); + if exit_code != 0 { + //crate::sbi::shutdown(255); //255 == -1 for err hint + shutdown(true); + } else { + //crate::sbi::shutdown(0); //0 for success hint + shutdown(false); + } + } + remove_from_pid2process(pid); + let mut process_inner = process.inner_exclusive_access(); + // mark this process as a zombie process + process_inner.is_zombie = true; + // record exit code of main process + process_inner.exit_code = exit_code; + + { + // move all child processes under init process + let mut initproc_inner = INITPROC.inner_exclusive_access(); + for child in process_inner.children.iter() { + child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC)); + initproc_inner.children.push(child.clone()); + } + } + + // deallocate user res (including tid/trap_cx/ustack) of all threads + // it has to be done before we dealloc the whole memory_set + // otherwise they will be deallocated twice + let mut recycle_res = Vec::::new(); + for task in process_inner.tasks.iter().filter(|t| t.is_some()) { + let task = task.as_ref().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + if let Some(res) = task_inner.res.take() { + recycle_res.push(res); + } + } + // dealloc_tid and dealloc_user_res require access to PCB inner, so we + // need to collect those user res first, then release process_inner + // for now to avoid deadlock/double borrow problem. + drop(process_inner); + recycle_res.clear(); + + let mut process_inner = process.inner_exclusive_access(); + process_inner.children.clear(); + // deallocate other data in user space i.e. program code/data section + process_inner.memory_set.recycle_data_pages(); + // drop file descriptors + process_inner.fd_table.clear(); + // Remove all tasks except for the main thread itself. + // This is because we are still using the kstack under the TCB + // of the main thread. This TCB, including its kstack, will be + // deallocated when the process is reaped via waitpid. + while process_inner.tasks.len() > 1 { + process_inner.tasks.pop(); + } + } + drop(process); + // we do not have to save task context + let mut _unused = TaskContext::zero_init(); + schedule(&mut _unused as *mut _); } lazy_static! { - pub static ref TASK_MANAGER: TaskManager = { - let num_app = get_num_app(); - let mut tasks = [ - TaskControlBlock { - task_cx: TaskContext::zero_init(), - task_status: TaskStatus::UnInit - }; - MAX_APP_NUM - ]; - for i in 0..num_app { - tasks[i].task_cx = TaskContext::goto_restore(init_app_cx(i)); - tasks[i].task_status = TaskStatus::Ready; - } - TaskManager { - num_app, - inner: unsafe { UPSafeCell::new(TaskManagerInner { - tasks, - current_task: 0, - })}, - } + pub static ref INITPROC: Arc = { + let inode = open_file("initproc", OpenFlags::RDONLY).unwrap(); + let v = inode.read_all(); + ProcessControlBlock::new(v.as_slice()) }; } -impl TaskManager { - fn run_first_task(&self) -> ! { - let mut inner = self.inner.exclusive_access(); - let task0 = &mut inner.tasks[0]; - task0.task_status = TaskStatus::Running; - let next_task_cx_ptr = &task0.task_cx as *const TaskContext; - drop(inner); - let mut _unused = TaskContext::zero_init(); - // before this, we should drop local variables that must be dropped manually - unsafe { - __switch( - &mut _unused as *mut TaskContext, - next_task_cx_ptr, - ); - } - panic!("unreachable in run_first_task!"); - } - - fn mark_current_suspended(&self) { - let mut inner = self.inner.exclusive_access(); - let current = inner.current_task; - inner.tasks[current].task_status = TaskStatus::Ready; - } - - fn mark_current_exited(&self) { - let mut inner = self.inner.exclusive_access(); - let current = inner.current_task; - inner.tasks[current].task_status = TaskStatus::Exited; - } - - fn find_next_task(&self) -> Option { - let inner = self.inner.exclusive_access(); - let current = inner.current_task; - (current + 1..current + self.num_app + 1) - .map(|id| id % self.num_app) - .find(|id| { - inner.tasks[*id].task_status == TaskStatus::Ready - }) - } - - fn run_next_task(&self) { - if let Some(next) = self.find_next_task() { - let mut inner = self.inner.exclusive_access(); - let current = inner.current_task; - inner.tasks[next].task_status = TaskStatus::Running; - inner.current_task = next; - let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext; - let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext; - drop(inner); - // before this, we should drop local variables that must be dropped manually - unsafe { - __switch( - current_task_cx_ptr, - next_task_cx_ptr, - ); - } - // go back to user mode - } else { - panic!("All applications completed!"); - } - } +pub fn add_initproc() { + let _initproc = INITPROC.clone(); } -pub fn run_first_task() { - TASK_MANAGER.run_first_task(); +pub fn check_signals_of_current() -> Option<(i32, &'static str)> { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + process_inner.signals.check_error() } -fn run_next_task() { - TASK_MANAGER.run_next_task(); +pub fn current_add_signal(signal: SignalFlags) { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + process_inner.signals |= signal; } - -fn mark_current_suspended() { - TASK_MANAGER.mark_current_suspended(); -} - -fn mark_current_exited() { - TASK_MANAGER.mark_current_exited(); -} - -pub fn suspend_current_and_run_next() { - mark_current_suspended(); - run_next_task(); -} - -pub fn exit_current_and_run_next() { - mark_current_exited(); - run_next_task(); -} \ No newline at end of file diff --git a/os/src/task/process.rs b/os/src/task/process.rs new file mode 100644 index 0000000..b3976b7 --- /dev/null +++ b/os/src/task/process.rs @@ -0,0 +1,258 @@ +use super::id::RecycleAllocator; +use super::manager::insert_into_pid2process; +use super::TaskControlBlock; +use super::{add_task, SignalFlags}; +use super::{pid_alloc, PidHandle}; +use crate::fs::{File, Stdin, Stdout}; +use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE}; +use crate::sync::{Condvar, Mutex, Semaphore, UPIntrFreeCell, UPIntrRefMut}; +use crate::trap::{trap_handler, TrapContext}; +use alloc::string::String; +use alloc::sync::{Arc, Weak}; +use alloc::vec; +use alloc::vec::Vec; + +pub struct ProcessControlBlock { + // immutable + pub pid: PidHandle, + // mutable + inner: UPIntrFreeCell, +} + +pub struct ProcessControlBlockInner { + pub is_zombie: bool, + pub memory_set: MemorySet, + pub parent: Option>, + pub children: Vec>, + pub exit_code: i32, + pub fd_table: Vec>>, + pub signals: SignalFlags, + pub tasks: Vec>>, + pub task_res_allocator: RecycleAllocator, + pub mutex_list: Vec>>, + pub semaphore_list: Vec>>, + pub condvar_list: Vec>>, +} + +impl ProcessControlBlockInner { + #[allow(unused)] + pub fn get_user_token(&self) -> usize { + self.memory_set.token() + } + + pub fn alloc_fd(&mut self) -> usize { + if let Some(fd) = (0..self.fd_table.len()).find(|fd| self.fd_table[*fd].is_none()) { + fd + } else { + self.fd_table.push(None); + self.fd_table.len() - 1 + } + } + + pub fn alloc_tid(&mut self) -> usize { + self.task_res_allocator.alloc() + } + + pub fn dealloc_tid(&mut self, tid: usize) { + self.task_res_allocator.dealloc(tid) + } + + pub fn thread_count(&self) -> usize { + self.tasks.len() + } + + pub fn get_task(&self, tid: usize) -> Arc { + self.tasks[tid].as_ref().unwrap().clone() + } +} + +impl ProcessControlBlock { + pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, ProcessControlBlockInner> { + self.inner.exclusive_access() + } + + pub fn new(elf_data: &[u8]) -> Arc { + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); + // allocate a pid + let pid_handle = pid_alloc(); + let process = Arc::new(Self { + pid: pid_handle, + inner: unsafe { + UPIntrFreeCell::new(ProcessControlBlockInner { + is_zombie: false, + memory_set, + parent: None, + children: Vec::new(), + exit_code: 0, + fd_table: vec![ + // 0 -> stdin + Some(Arc::new(Stdin)), + // 1 -> stdout + Some(Arc::new(Stdout)), + // 2 -> stderr + Some(Arc::new(Stdout)), + ], + signals: SignalFlags::empty(), + tasks: Vec::new(), + task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), + semaphore_list: Vec::new(), + condvar_list: Vec::new(), + }) + }, + }); + // create a main thread, we should allocate ustack and trap_cx here + let task = Arc::new(TaskControlBlock::new( + Arc::clone(&process), + ustack_base, + true, + )); + // prepare trap_cx of main thread + let task_inner = task.inner_exclusive_access(); + let trap_cx = task_inner.get_trap_cx(); + let ustack_top = task_inner.res.as_ref().unwrap().ustack_top(); + let kstack_top = task.kstack.get_top(); + drop(task_inner); + *trap_cx = TrapContext::app_init_context( + entry_point, + ustack_top, + KERNEL_SPACE.exclusive_access().token(), + kstack_top, + trap_handler as usize, + ); + // add main thread to the process + let mut process_inner = process.inner_exclusive_access(); + process_inner.tasks.push(Some(Arc::clone(&task))); + drop(process_inner); + insert_into_pid2process(process.getpid(), Arc::clone(&process)); + // add main thread to scheduler + add_task(task); + process + } + + /// Only support processes with a single thread. + pub fn exec(self: &Arc, elf_data: &[u8], args: Vec) { + assert_eq!(self.inner_exclusive_access().thread_count(), 1); + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); + let new_token = memory_set.token(); + // substitute memory_set + self.inner_exclusive_access().memory_set = memory_set; + // then we alloc user resource for main thread again + // since memory_set has been changed + let task = self.inner_exclusive_access().get_task(0); + let mut task_inner = task.inner_exclusive_access(); + task_inner.res.as_mut().unwrap().ustack_base = ustack_base; + task_inner.res.as_mut().unwrap().alloc_user_res(); + task_inner.trap_cx_ppn = task_inner.res.as_mut().unwrap().trap_cx_ppn(); + // push arguments on user stack + let mut user_sp = task_inner.res.as_mut().unwrap().ustack_top(); + user_sp -= (args.len() + 1) * core::mem::size_of::(); + let argv_base = user_sp; + let mut argv: Vec<_> = (0..=args.len()) + .map(|arg| { + translated_refmut( + new_token, + (argv_base + arg * core::mem::size_of::()) as *mut usize, + ) + }) + .collect(); + *argv[args.len()] = 0; + for i in 0..args.len() { + user_sp -= args[i].len() + 1; + *argv[i] = user_sp; + let mut p = user_sp; + for c in args[i].as_bytes() { + *translated_refmut(new_token, p as *mut u8) = *c; + p += 1; + } + *translated_refmut(new_token, p as *mut u8) = 0; + } + // make the user_sp aligned to 8B for k210 platform + user_sp -= user_sp % core::mem::size_of::(); + // initialize trap_cx + let mut trap_cx = TrapContext::app_init_context( + entry_point, + user_sp, + KERNEL_SPACE.exclusive_access().token(), + task.kstack.get_top(), + trap_handler as usize, + ); + trap_cx.x[10] = args.len(); + trap_cx.x[11] = argv_base; + *task_inner.get_trap_cx() = trap_cx; + } + + /// Only support processes with a single thread. + pub fn fork(self: &Arc) -> Arc { + let mut parent = self.inner_exclusive_access(); + assert_eq!(parent.thread_count(), 1); + // clone parent's memory_set completely including trampoline/ustacks/trap_cxs + let memory_set = MemorySet::from_existed_user(&parent.memory_set); + // alloc a pid + let pid = pid_alloc(); + // copy fd table + let mut new_fd_table: Vec>> = Vec::new(); + for fd in parent.fd_table.iter() { + if let Some(file) = fd { + new_fd_table.push(Some(file.clone())); + } else { + new_fd_table.push(None); + } + } + // create child process pcb + let child = Arc::new(Self { + pid, + inner: unsafe { + UPIntrFreeCell::new(ProcessControlBlockInner { + is_zombie: false, + memory_set, + parent: Some(Arc::downgrade(self)), + children: Vec::new(), + exit_code: 0, + fd_table: new_fd_table, + signals: SignalFlags::empty(), + tasks: Vec::new(), + task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), + semaphore_list: Vec::new(), + condvar_list: Vec::new(), + }) + }, + }); + // add child + parent.children.push(Arc::clone(&child)); + // create main thread of child process + let task = Arc::new(TaskControlBlock::new( + Arc::clone(&child), + parent + .get_task(0) + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .ustack_base(), + // here we do not allocate trap_cx or ustack again + // but mention that we allocate a new kstack here + false, + )); + // attach task to child process + let mut child_inner = child.inner_exclusive_access(); + child_inner.tasks.push(Some(Arc::clone(&task))); + drop(child_inner); + // modify kstack_top in trap_cx of this thread + let task_inner = task.inner_exclusive_access(); + let trap_cx = task_inner.get_trap_cx(); + trap_cx.kernel_sp = task.kstack.get_top(); + drop(task_inner); + insert_into_pid2process(child.getpid(), Arc::clone(&child)); + // add this thread to scheduler + add_task(task); + child + } + + pub fn getpid(&self) -> usize { + self.pid.0 + } +} diff --git a/os/src/task/processor.rs b/os/src/task/processor.rs new file mode 100644 index 0000000..96361d7 --- /dev/null +++ b/os/src/task/processor.rs @@ -0,0 +1,111 @@ +use super::__switch; +use super::{fetch_task, TaskStatus}; +use super::{ProcessControlBlock, TaskContext, TaskControlBlock}; +use crate::sync::UPIntrFreeCell; +use crate::trap::TrapContext; +use alloc::sync::Arc; +use core::arch::asm; +use lazy_static::*; + +pub struct Processor { + current: Option>, + idle_task_cx: TaskContext, +} + +impl Processor { + pub fn new() -> Self { + Self { + current: None, + idle_task_cx: TaskContext::zero_init(), + } + } + fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext { + &mut self.idle_task_cx as *mut _ + } + pub fn take_current(&mut self) -> Option> { + self.current.take() + } + pub fn current(&self) -> Option> { + self.current.as_ref().map(Arc::clone) + } +} + +lazy_static! { + pub static ref PROCESSOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(Processor::new()) }; +} + +pub fn run_tasks() { + loop { + let mut processor = PROCESSOR.exclusive_access(); + if let Some(task) = fetch_task() { + let idle_task_cx_ptr = processor.get_idle_task_cx_ptr(); + // access coming task TCB exclusively + let next_task_cx_ptr = task.inner.exclusive_session(|task_inner| { + task_inner.task_status = TaskStatus::Running; + &task_inner.task_cx as *const TaskContext + }); + processor.current = Some(task); + // release processor manually + drop(processor); + unsafe { + __switch(idle_task_cx_ptr, next_task_cx_ptr); + } + } else { + println!("no tasks available in run_tasks"); + } + } +} + +pub fn take_current_task() -> Option> { + PROCESSOR.exclusive_access().take_current() +} + +pub fn current_task() -> Option> { + PROCESSOR.exclusive_access().current() +} + +pub fn current_process() -> Arc { + current_task().unwrap().process.upgrade().unwrap() +} + +pub fn current_user_token() -> usize { + let task = current_task().unwrap(); + task.get_user_token() +} + +pub fn current_trap_cx() -> &'static mut TrapContext { + current_task() + .unwrap() + .inner_exclusive_access() + .get_trap_cx() +} + +pub fn current_trap_cx_user_va() -> usize { + current_task() + .unwrap() + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .trap_cx_user_va() +} + +pub fn current_kstack_top() -> usize { + if let Some(task) = current_task() { + task.kstack.get_top() + } else { + let mut boot_stack_top; + unsafe { asm!("la {},boot_stack_top",out(reg) boot_stack_top) }; + boot_stack_top + } + // current_task().unwrap().kstack.get_top() +} + +pub fn schedule(switched_task_cx_ptr: *mut TaskContext) { + let idle_task_cx_ptr = + PROCESSOR.exclusive_session(|processor| processor.get_idle_task_cx_ptr()); + unsafe { + __switch(switched_task_cx_ptr, idle_task_cx_ptr); + } +} diff --git a/os/src/task/signal.rs b/os/src/task/signal.rs new file mode 100644 index 0000000..46f1ad9 --- /dev/null +++ b/os/src/task/signal.rs @@ -0,0 +1,29 @@ +use bitflags::*; + +bitflags! { + pub struct SignalFlags: u32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +impl SignalFlags { + pub fn check_error(&self) -> Option<(i32, &'static str)> { + if self.contains(Self::SIGINT) { + Some((-2, "Killed, SIGINT=2")) + } else if self.contains(Self::SIGILL) { + Some((-4, "Illegal Instruction, SIGILL=4")) + } else if self.contains(Self::SIGABRT) { + Some((-6, "Aborted, SIGABRT=6")) + } else if self.contains(Self::SIGFPE) { + Some((-8, "Erroneous Arithmetic Operation, SIGFPE=8")) + } else if self.contains(Self::SIGSEGV) { + Some((-11, "Segmentation Fault, SIGSEGV=11")) + } else { + None + } + } +} diff --git a/os/src/task/switch.rs b/os/src/task/switch.rs index fa75be1..59f8b1a 100644 --- a/os/src/task/switch.rs +++ b/os/src/task/switch.rs @@ -1,10 +1,8 @@ +use super::TaskContext; +use core::arch::global_asm; + global_asm!(include_str!("switch.S")); -use super::TaskContext; - extern "C" { - pub fn __switch( - current_task_cx_ptr: *mut TaskContext, - next_task_cx_ptr: *const TaskContext - ); + pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext); } diff --git a/os/src/task/task.rs b/os/src/task/task.rs index fd5f5f9..ffc5c0e 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,15 +1,80 @@ -use super::TaskContext; +use super::id::TaskUserRes; +use super::{kstack_alloc, KernelStack, ProcessControlBlock, TaskContext}; +use crate::trap::TrapContext; +use crate::{ + mm::PhysPageNum, + sync::{UPIntrFreeCell, UPIntrRefMut}, +}; +use alloc::sync::{Arc, Weak}; -#[derive(Copy, Clone)] pub struct TaskControlBlock { - pub task_status: TaskStatus, + // immutable + pub process: Weak, + pub kstack: KernelStack, + // mutable + pub inner: UPIntrFreeCell, +} + +impl TaskControlBlock { + pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, TaskControlBlockInner> { + self.inner.exclusive_access() + } + + pub fn get_user_token(&self) -> usize { + let process = self.process.upgrade().unwrap(); + let inner = process.inner_exclusive_access(); + inner.memory_set.token() + } +} + +pub struct TaskControlBlockInner { + pub res: Option, + pub trap_cx_ppn: PhysPageNum, pub task_cx: TaskContext, + pub task_status: TaskStatus, + pub exit_code: Option, +} + +impl TaskControlBlockInner { + pub fn get_trap_cx(&self) -> &'static mut TrapContext { + self.trap_cx_ppn.get_mut() + } + + #[allow(unused)] + fn get_status(&self) -> TaskStatus { + self.task_status + } +} + +impl TaskControlBlock { + pub fn new( + process: Arc, + ustack_base: usize, + alloc_user_res: bool, + ) -> Self { + let res = TaskUserRes::new(Arc::clone(&process), ustack_base, alloc_user_res); + let trap_cx_ppn = res.trap_cx_ppn(); + let kstack = kstack_alloc(); + let kstack_top = kstack.get_top(); + Self { + process: Arc::downgrade(&process), + kstack, + inner: unsafe { + UPIntrFreeCell::new(TaskControlBlockInner { + res: Some(res), + trap_cx_ppn, + task_cx: TaskContext::goto_trap_return(kstack_top), + task_status: TaskStatus::Ready, + exit_code: None, + }) + }, + } + } } #[derive(Copy, Clone, PartialEq)] pub enum TaskStatus { - UnInit, Ready, Running, - Exited, -} \ No newline at end of file + Blocked, +} diff --git a/os/src/timer.rs b/os/src/timer.rs index 8b908bc..83c969d 100644 --- a/os/src/timer.rs +++ b/os/src/timer.rs @@ -1,18 +1,74 @@ +use core::cmp::Ordering; + use crate::config::CLOCK_FREQ; use crate::sbi::set_timer; +use crate::sync::UPIntrFreeCell; +use crate::task::{wakeup_task, TaskControlBlock}; +use alloc::collections::BinaryHeap; +use alloc::sync::Arc; +use lazy_static::*; use riscv::register::time; const TICKS_PER_SEC: usize = 100; -const MICRO_PER_SEC: usize = 1_000_000; +const MSEC_PER_SEC: usize = 1000; pub fn get_time() -> usize { time::read() } -pub fn get_time_us() -> usize { - time::read() / (CLOCK_FREQ / MICRO_PER_SEC) +pub fn get_time_ms() -> usize { + time::read() / (CLOCK_FREQ / MSEC_PER_SEC) } pub fn set_next_trigger() { set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC); } + +pub struct TimerCondVar { + pub expire_ms: usize, + pub task: Arc, +} + +impl PartialEq for TimerCondVar { + fn eq(&self, other: &Self) -> bool { + self.expire_ms == other.expire_ms + } +} +impl Eq for TimerCondVar {} +impl PartialOrd for TimerCondVar { + fn partial_cmp(&self, other: &Self) -> Option { + let a = -(self.expire_ms as isize); + let b = -(other.expire_ms as isize); + Some(a.cmp(&b)) + } +} + +impl Ord for TimerCondVar { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +lazy_static! { + static ref TIMERS: UPIntrFreeCell> = + unsafe { UPIntrFreeCell::new(BinaryHeap::::new()) }; +} + +pub fn add_timer(expire_ms: usize, task: Arc) { + let mut timers = TIMERS.exclusive_access(); + timers.push(TimerCondVar { expire_ms, task }); +} + +pub fn check_timer() { + let current_ms = get_time_ms(); + TIMERS.exclusive_session(|timers| { + while let Some(timer) = timers.peek() { + if timer.expire_ms <= current_ms { + wakeup_task(Arc::clone(&timer.task)); + timers.pop(); + } else { + break; + } + } + }); +} diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index e2575de..011b7fb 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -1,21 +1,37 @@ -use riscv::register::sstatus::{Sstatus, self, SPP}; +use riscv::register::sstatus::{self, Sstatus, SPP}; #[repr(C)] +#[derive(Debug)] pub struct TrapContext { pub x: [usize; 32], pub sstatus: Sstatus, pub sepc: usize, + pub kernel_satp: usize, + pub kernel_sp: usize, + pub trap_handler: usize, } impl TrapContext { - pub fn set_sp(&mut self, sp: usize) { self.x[2] = sp; } - pub fn app_init_context(entry: usize, sp: usize) -> Self { + pub fn set_sp(&mut self, sp: usize) { + self.x[2] = sp; + } + pub fn app_init_context( + entry: usize, + sp: usize, + kernel_satp: usize, + kernel_sp: usize, + trap_handler: usize, + ) -> Self { let mut sstatus = sstatus::read(); + // set CPU privilege to User after trapping back sstatus.set_spp(SPP::User); let mut cx = Self { x: [0; 32], sstatus, sepc: entry, + kernel_satp, + kernel_sp, + trap_handler, }; cx.set_sp(sp); cx diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 7718d77..3287011 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -1,64 +1,169 @@ mod context; -use riscv::register::{ - mtvec::TrapMode, - stvec, - scause::{ - self, - Trap, - Exception, - Interrupt, - }, - stval, - sie, -}; +use crate::config::TRAMPOLINE; use crate::syscall::syscall; use crate::task::{ - exit_current_and_run_next, - suspend_current_and_run_next, + check_signals_of_current, current_add_signal, current_trap_cx, current_trap_cx_user_va, + current_user_token, exit_current_and_run_next, suspend_current_and_run_next, SignalFlags, +}; +use crate::timer::{check_timer, set_next_trigger}; +use core::arch::{asm, global_asm}; +use riscv::register::{ + mtvec::TrapMode, + scause::{self, Exception, Interrupt, Trap}, + sie, sip, sscratch, sstatus, stval, stvec, }; -use crate::timer::set_next_trigger; global_asm!(include_str!("trap.S")); pub fn init() { - extern "C" { fn __alltraps(); } + set_kernel_trap_entry(); +} + +fn set_kernel_trap_entry() { + extern "C" { + fn __alltraps(); + fn __alltraps_k(); + } + let __alltraps_k_va = __alltraps_k as usize - __alltraps as usize + TRAMPOLINE; unsafe { - stvec::write(__alltraps as usize, TrapMode::Direct); + stvec::write(__alltraps_k_va, TrapMode::Direct); + sscratch::write(trap_from_kernel as usize); + } +} + +fn set_user_trap_entry() { + unsafe { + stvec::write(TRAMPOLINE as usize, TrapMode::Direct); } } pub fn enable_timer_interrupt() { - unsafe { sie::set_stimer(); } + unsafe { + sie::set_stimer(); + } +} + +fn enable_supervisor_interrupt() { + unsafe { + sstatus::set_sie(); + } +} + +fn disable_supervisor_interrupt() { + unsafe { + sstatus::clear_sie(); + } } #[no_mangle] -pub fn trap_handler(cx: &mut TrapContext) -> &mut TrapContext { +pub fn trap_handler() -> ! { + set_kernel_trap_entry(); let scause = scause::read(); let stval = stval::read(); + // println!("into {:?}", scause.cause()); match scause.cause() { Trap::Exception(Exception::UserEnvCall) => { + // jump to next instruction anyway + let mut cx = current_trap_cx(); cx.sepc += 4; - cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize; + + enable_supervisor_interrupt(); + + // get system call return value + let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]); + // cx is changed during sys_exec, so we have to call it again + cx = current_trap_cx(); + cx.x[10] = result as usize; } - Trap::Exception(Exception::StoreFault) | - Trap::Exception(Exception::StorePageFault) => { - println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.", stval, cx.sepc); - exit_current_and_run_next(); + Trap::Exception(Exception::StoreFault) + | Trap::Exception(Exception::StorePageFault) + | Trap::Exception(Exception::InstructionFault) + | Trap::Exception(Exception::InstructionPageFault) + | Trap::Exception(Exception::LoadFault) + | Trap::Exception(Exception::LoadPageFault) => { + /* + println!( + "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", + scause.cause(), + stval, + current_trap_cx().sepc, + ); + */ + current_add_signal(SignalFlags::SIGSEGV); } Trap::Exception(Exception::IllegalInstruction) => { - println!("[kernel] IllegalInstruction in application, core dumped."); - exit_current_and_run_next(); + current_add_signal(SignalFlags::SIGILL); } Trap::Interrupt(Interrupt::SupervisorTimer) => { set_next_trigger(); + check_timer(); suspend_current_and_run_next(); } + Trap::Interrupt(Interrupt::SupervisorExternal) => { + crate::board::irq_handler(); + } _ => { - panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval); + panic!( + "Unsupported trap {:?}, stval = {:#x}!", + scause.cause(), + stval + ); } } - cx + // check signals + if let Some((errno, msg)) = check_signals_of_current() { + println!("[kernel] {}", msg); + exit_current_and_run_next(errno); + } + trap_return(); } -pub use context::TrapContext; \ No newline at end of file +#[no_mangle] +pub fn trap_return() -> ! { + disable_supervisor_interrupt(); + set_user_trap_entry(); + let trap_cx_user_va = current_trap_cx_user_va(); + let user_satp = current_user_token(); + extern "C" { + fn __alltraps(); + fn __restore(); + } + let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE; + //println!("before return"); + unsafe { + asm!( + "fence.i", + "jr {restore_va}", + restore_va = in(reg) restore_va, + in("a0") trap_cx_user_va, + in("a1") user_satp, + options(noreturn) + ); + } +} + +#[no_mangle] +pub fn trap_from_kernel(_trap_cx: &TrapContext) { + let scause = scause::read(); + let stval = stval::read(); + match scause.cause() { + Trap::Interrupt(Interrupt::SupervisorExternal) => { + crate::board::irq_handler(); + } + Trap::Interrupt(Interrupt::SupervisorTimer) => { + set_next_trigger(); + check_timer(); + // do not schedule now + } + _ => { + panic!( + "Unsupported trap from kernel: {:?}, stval = {:#x}!", + scause.cause(), + stval + ); + } + } +} + +pub use context::TrapContext; diff --git a/os/src/trap/trap.S b/os/src/trap/trap.S index 70a18a9..407307c 100644 --- a/os/src/trap/trap.S +++ b/os/src/trap/trap.S @@ -5,16 +5,16 @@ .macro LOAD_GP n ld x\n, \n*8(sp) .endm - .section .text + .section .text.trampoline .globl __alltraps .globl __restore + .globl __alltraps_k + .globl __restore_k .align 2 __alltraps: csrrw sp, sscratch, sp - # now sp->kernel stack, sscratch->user stack - # allocate a TrapContext on kernel stack - addi sp, sp, -34*8 - # save general-purpose registers + # now sp->*TrapContext in user space, sscratch->user stack + # save other general purpose registers sd x1, 1*8(sp) # skip sp(x2), we will save it later sd x3, 3*8(sp) @@ -25,28 +25,40 @@ __alltraps: SAVE_GP %n .set n, n+1 .endr - # we can use t0/t1/t2 freely, because they were saved on kernel stack + # we can use t0/t1/t2 freely, because they have been saved in TrapContext csrr t0, sstatus csrr t1, sepc sd t0, 32*8(sp) sd t1, 33*8(sp) - # read user stack from sscratch and save it on the kernel stack + # read user stack from sscratch and save it in TrapContext csrr t2, sscratch sd t2, 2*8(sp) - # set input argument of trap_handler(cx: &mut TrapContext) - mv a0, sp - call trap_handler + # load kernel_satp into t0 + ld t0, 34*8(sp) + # load trap_handler into t1 + ld t1, 36*8(sp) + # move to kernel_sp + ld sp, 35*8(sp) + # switch to kernel space + csrw satp, t0 + sfence.vma + # jump to trap_handler + jr t1 __restore: - # now sp->kernel stack(after allocated), sscratch->user stack + # a0: *TrapContext in user space(Constant); a1: user space token + # switch to user space + csrw satp, a1 + sfence.vma + csrw sscratch, a0 + mv sp, a0 + # now sp points to TrapContext in user space, start restoring based on it # restore sstatus/sepc ld t0, 32*8(sp) ld t1, 33*8(sp) - ld t2, 2*8(sp) csrw sstatus, t0 csrw sepc, t1 - csrw sscratch, t2 - # restore general-purpuse registers except sp/tp + # restore general purpose registers except x0/sp/tp ld x1, 1*8(sp) ld x3, 3*8(sp) .set n, 5 @@ -54,8 +66,39 @@ __restore: LOAD_GP %n .set n, n+1 .endr - # release TrapContext on kernel stack - addi sp, sp, 34*8 - # now sp->kernel stack, sscratch->user stack - csrrw sp, sscratch, sp + # back to user stack + ld sp, 2*8(sp) + sret + + .align 2 +__alltraps_k: + addi sp, sp, -34*8 + sd x1, 1*8(sp) + sd x3, 3*8(sp) + .set n, 5 + .rept 27 + SAVE_GP %n + .set n, n+1 + .endr + csrr t0, sstatus + csrr t1, sepc + sd t0, 32*8(sp) + sd t1, 33*8(sp) + mv a0, sp + csrr t2, sscratch + jalr t2 + +__restore_k: + ld t0, 32*8(sp) + ld t1, 33*8(sp) + csrw sstatus, t0 + csrw sepc, t1 + ld x1, 1*8(sp) + ld x3, 3*8(sp) + .set n, 5 + .rept 27 + LOAD_GP %n + .set n, n+1 + .endr + addi sp, sp, 34*8 sret diff --git a/ping.py b/ping.py new file mode 100644 index 0000000..c68fc71 --- /dev/null +++ b/ping.py @@ -0,0 +1,18 @@ +import socket +import sys +import time + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +addr = ('localhost', 26099) +sock.bind(addr) + + +print("pinging...", file=sys.stderr) +while True: + buf, raddr = sock.recvfrom(4096) + print("receive: " + buf.decode("utf-8")) + buf = "this is a ping to port 6200!".encode('utf-8') + sock.sendto(buf, ("127.0.0.1", 6200)) + buf = "this is a ping to reply!".encode('utf-8') + sock.sendto(buf, raddr) + time.sleep(1) diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index fd47275..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2021-10-15 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..c65d258 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +profile = "minimal" +# use the nightly version of the last stable toolchain, see +channel = "nightly-2024-05-01" +components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] +targets = ["riscv64gc-unknown-none-elf"] diff --git a/setenv.sh b/setenv.sh new file mode 100644 index 0000000..e3842d5 --- /dev/null +++ b/setenv.sh @@ -0,0 +1,2 @@ +export PATH=$(rustc --print sysroot)/bin:$PATH +export RUST_SRC_PATH=$(rustc --print sysroot)/lib/rustlib/src/rust/library/ diff --git a/user/.cargo/config b/user/.cargo/config.toml similarity index 60% rename from user/.cargo/config rename to user/.cargo/config.toml index e5ded8a..334d01e 100644 --- a/user/.cargo/config +++ b/user/.cargo/config.toml @@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf" [target.riscv64gc-unknown-none-elf] rustflags = [ - "-Clink-args=-Tsrc/linker.ld", + "-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes" ] diff --git a/user/Cargo.toml b/user/Cargo.toml index 01b75c2..1d7b2ef 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -9,5 +9,11 @@ edition = "2018" [dependencies] buddy_system_allocator = "0.6" bitflags = "1.2.1" -spin = "0.9" +riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +embedded-graphics = "0.7.1" +oorandom ="11" +virtio-input-decoder = "0.1.4" + +[profile.release] +debug = true diff --git a/user/Makefile b/user/Makefile index 284b240..5170327 100644 --- a/user/Makefile +++ b/user/Makefile @@ -2,39 +2,28 @@ TARGET := riscv64gc-unknown-none-elf MODE := release APP_DIR := src/bin TARGET_DIR := target/$(TARGET)/$(MODE) -BUILD_DIR := build +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 -PY := python3 +CP := cp -TEST ?= 0 -ifeq ($(TEST), 0) - APPS := $(filter-out $(wildcard $(APP_DIR)/test*.rs), $(wildcard $(APP_DIR)/*.rs)) -else - APPS := $(wildcard $(APP_DIR)/test$(TEST)*.rs) -endif -ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS)) +TEST ?= elf: $(APPS) - @python3 build.py + @cargo build --release +ifeq ($(TEST), 1) + @$(CP) $(TARGET_DIR)/usertests $(TARGET_DIR)/initproc +endif binary: elf - @$(foreach elf, $(ELFS), \ - $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf)); \ - cp $(elf) $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.elf, $(elf));) + @$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) -pre: - @mkdir -p $(BUILD_DIR)/bin/ - @mkdir -p $(BUILD_DIR)/elf/ - @mkdir -p $(BUILD_DIR)/app/ - @$(foreach t, $(APPS), cp $(t) $(BUILD_DIR)/app/;) - -build: clean pre binary - @$(foreach t, $(ELFS), cp $(t).bin $(BUILD_DIR)/bin/;) - @$(foreach t, $(ELFS), cp $(t).elf $(BUILD_DIR)/elf/;) +build: binary clean: @cargo clean - @rm -rf $(BUILD_DIR) -.PHONY: elf binary build clean \ No newline at end of file +.PHONY: elf binary build clean diff --git a/user/build.py b/user/build.py deleted file mode 100644 index aababef..0000000 --- a/user/build.py +++ /dev/null @@ -1,25 +0,0 @@ -import os - -base_address = 0x80400000 -step = 0x20000 -linker = 'src/linker.ld' - -app_id = 0 -apps = os.listdir('build/app') -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 diff --git a/user/src/bin/adder.rs b/user/src/bin/adder.rs new file mode 100644 index 0000000..1cdce95 --- /dev/null +++ b/user/src/bin/adder.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + critical_section(&mut t); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_atomic.rs b/user/src/bin/adder_atomic.rs new file mode 100644 index 0000000..5e216c6 --- /dev/null +++ b/user/src/bin/adder_atomic.rs @@ -0,0 +1,75 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use core::sync::atomic::{AtomicBool, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static OCCUPIED: AtomicBool = AtomicBool::new(false); +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +fn lock() { + while OCCUPIED + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + yield_(); + } +} + +fn unlock() { + OCCUPIED.store(false, Ordering::Relaxed); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_blocking.rs b/user/src/bin/adder_mutex_blocking.rs new file mode 100644 index 0000000..7fcb80b --- /dev/null +++ b/user/src/bin/adder_mutex_blocking.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + let start = get_time(); + assert_eq!(mutex_blocking_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_spin.rs b/user/src/bin/adder_mutex_spin.rs new file mode 100644 index 0000000..f5750af --- /dev/null +++ b/user/src/bin/adder_mutex_spin.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + let start = get_time(); + assert_eq!(mutex_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_spin.rs b/user/src/bin/adder_peterson_spin.rs new file mode 100644 index 0000000..44e8096 --- /dev/null +++ b/user/src/bin/adder_peterson_spin.rs @@ -0,0 +1,95 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use core::sync::atomic::{compiler_fence, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + // Why do we need to use volatile_read here? + // Otherwise the compiler will assume that they will never + // be changed on this thread. Thus, they will be accessed + // only once! + while vload!(FLAG[j]) && vload!(TURN) == j {} +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + let start = get_time(); + let mut v = Vec::new(); + assert_eq!( + thread_count, 2, + "Peterson works when there are only 2 threads." + ); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_yield.rs b/user/src/bin/adder_peterson_yield.rs new file mode 100644 index 0000000..b25adf6 --- /dev/null +++ b/user/src/bin/adder_peterson_yield.rs @@ -0,0 +1,96 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::{ + ptr::addr_of_mut, + sync::atomic::{compiler_fence, Ordering}, +}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + while FLAG[j] && TURN == j { + yield_(); + } +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + + let start = get_time(); + let mut v = Vec::new(); + assert_eq!( + thread_count, 2, + "Peterson works when there are only 2 threads." + ); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_spin.rs b/user/src/bin/adder_simple_spin.rs new file mode 100644 index 0000000..be99d28 --- /dev/null +++ b/user/src/bin/adder_simple_spin.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while vload!(OCCUPIED) {} + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_yield.rs b/user/src/bin/adder_simple_yield.rs new file mode 100644 index 0000000..2de7924 --- /dev/null +++ b/user/src/bin/adder_simple_yield.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while OCCUPIED { + yield_(); + } + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/barrier_condvar.rs b/user/src/bin/barrier_condvar.rs new file mode 100644 index 0000000..7157772 --- /dev/null +++ b/user/src/bin/barrier_condvar.rs @@ -0,0 +1,83 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::cell::UnsafeCell; +use lazy_static::*; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, exit, mutex_create, mutex_lock, mutex_unlock, + thread_create, waittid, +}; + +const THREAD_NUM: usize = 3; + +struct Barrier { + mutex_id: usize, + condvar_id: usize, + count: UnsafeCell, +} + +impl Barrier { + pub fn new() -> Self { + Self { + mutex_id: mutex_create() as usize, + condvar_id: condvar_create() as usize, + count: UnsafeCell::new(0), + } + } + pub fn block(&self) { + mutex_lock(self.mutex_id); + let count = self.count.get(); + // SAFETY: Here, the accesses of the count is in the + // critical section protected by the mutex. + unsafe { + *count = *count + 1; + } + if unsafe { *count } == THREAD_NUM { + condvar_signal(self.condvar_id); + } else { + condvar_wait(self.condvar_id, self.mutex_id); + condvar_signal(self.condvar_id); + } + mutex_unlock(self.mutex_id); + } +} + +unsafe impl Sync for Barrier {} + +lazy_static! { + static ref BARRIER_AB: Barrier = Barrier::new(); + static ref BARRIER_BC: Barrier = Barrier::new(); +} + +fn thread_fn() { + for _ in 0..300 { + print!("a"); + } + BARRIER_AB.block(); + for _ in 0..300 { + print!("b"); + } + BARRIER_BC.block(); + for _ in 0..300 { + print!("c"); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/barrier_fail.rs b/user/src/bin/barrier_fail.rs new file mode 100644 index 0000000..11604ca --- /dev/null +++ b/user/src/bin/barrier_fail.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, thread_create, waittid}; + +const THREAD_NUM: usize = 3; + +fn thread_fn() { + for ch in 'a'..='c' { + for _ in 0..300 { + print!("{}", ch); + } + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/cat.rs b/user/src/bin/cat.rs new file mode 100644 index 0000000..fea5a24 --- /dev/null +++ b/user/src/bin/cat.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{close, open, read, OpenFlags}; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for (i, arg) in argv.iter().enumerate() { + println!("argv[{}] = {}", i, arg); + } + assert!(argc == 2); + let fd = open(argv[1], OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occurred when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 256]; + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { + break; + } + print!("{}", core::str::from_utf8(&buf[..size]).unwrap()); + } + close(fd); + 0 +} diff --git a/user/src/bin/cmdline_args.rs b/user/src/bin/cmdline_args.rs new file mode 100644 index 0000000..9a22745 --- /dev/null +++ b/user/src/bin/cmdline_args.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for (i, arg) in argv.iter().enumerate() { + println!("argv[{}] = {}", i, arg); + } + 0 +} diff --git a/user/src/bin/condsync_condvar.rs b/user/src/bin/condsync_condvar.rs new file mode 100644 index 0000000..78605ad --- /dev/null +++ b/user/src/bin/condsync_condvar.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const CONDVAR_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + condvar_signal(CONDVAR_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + mutex_lock(MUTEX_ID); + while A == 0 { + println!("Second: A is {}", A); + condvar_wait(CONDVAR_ID, MUTEX_ID); + } + println!("A is {}, Second can work now", A); + mutex_unlock(MUTEX_ID); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create condvar & mutex + assert_eq!(condvar_create() as usize, CONDVAR_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/condsync_sem.rs b/user/src/bin/condsync_sem.rs new file mode 100644 index 0000000..ee08fac --- /dev/null +++ b/user/src/bin/condsync_sem.rs @@ -0,0 +1,64 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + mutex_blocking_create, mutex_lock, mutex_unlock, semaphore_create, semaphore_down, semaphore_up, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const SEM_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + semaphore_up(SEM_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + loop { + mutex_lock(MUTEX_ID); + if A == 0 { + println!("Second: A is {}", A); + mutex_unlock(MUTEX_ID); + semaphore_down(SEM_ID); + } else { + mutex_unlock(MUTEX_ID); + break; + } + } + println!("A is {}, Second can work now", A); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphore & mutex + assert_eq!(semaphore_create(0) as usize, SEM_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/count_lines.rs b/user/src/bin/count_lines.rs new file mode 100644 index 0000000..276e12e --- /dev/null +++ b/user/src/bin/count_lines.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::read; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> i32 { + let mut buf = [0u8; 256]; + let mut lines = 0usize; + let mut total_size = 0usize; + loop { + let len = read(0, &mut buf) as usize; + if len == 0 { + break; + } + total_size += len; + let string = core::str::from_utf8(&buf[..len]).unwrap(); + lines += string + .chars() + .fold(0, |acc, c| acc + if c == '\n' { 1 } else { 0 }); + } + if total_size > 0 { + lines += 1; + } + println!("{}", lines); + 0 +} diff --git a/user/src/bin/eisenberg.rs b/user/src/bin/eisenberg.rs new file mode 100644 index 0000000..49a1d45 --- /dev/null +++ b/user/src/bin/eisenberg.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use alloc::vec::Vec; +use core::{ + sync::atomic::{AtomicUsize, Ordering}, +}; +use user_lib::{exit, sleep, thread_create, waittid}; + +const N: usize = 2; +const THREAD_NUM: usize = 10; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum FlagState { + Out, + Want, + In, +} + +static mut TURN: usize = 0; +static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM]; + +static GUARD: AtomicUsize = AtomicUsize::new(0); + +fn critical_test_enter() { + assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); +} + +fn critical_test_claim() { + assert_eq!(GUARD.load(Ordering::SeqCst), 1); +} + +fn critical_test_exit() { + assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); +} + +unsafe fn eisenberg_enter_critical(id: usize) { + /* announce that we want to enter */ + loop { + println!("Thread[{}] try enter", id); + vstore!(FLAG[id], FlagState::Want); + loop { + /* check if any with higher priority is `Want` or `In` */ + let mut prior_thread: Option = None; + let turn = vload!(TURN); + let ring_id = if id < turn { id + THREAD_NUM } else { id }; + // FLAG.iter() may lead to some errors, use for-loop instead + for i in turn..ring_id { + if vload!(FLAG[i % THREAD_NUM]) != FlagState::Out { + prior_thread = Some(i % THREAD_NUM); + break; + } + } + if prior_thread.is_none() { + break; + } + println!( + "Thread[{}]: prior thread {} exist, sleep and retry", + id, + prior_thread.unwrap() + ); + sleep(1); + } + /* now tentatively claim the resource */ + vstore!(FLAG[id], FlagState::In); + /* enforce the order of `claim` and `conflict check`*/ + memory_fence!(); + /* check if anthor thread is also `In`, which imply a conflict*/ + let mut conflict = false; + for i in 0..THREAD_NUM { + if i != id && vload!(FLAG[i]) == FlagState::In { + conflict = true; + } + } + if !conflict { + break; + } + println!("Thread[{}]: CONFLECT!", id); + /* no need to sleep */ + } + /* clain the trun */ + vstore!(TURN, id); + println!("Thread[{}] enter", id); +} + +unsafe fn eisenberg_exit_critical(id: usize) { + /* find next one who wants to enter and give the turn to it*/ + let mut next = id; + let ring_id = id + THREAD_NUM; + for i in (id + 1)..ring_id { + let idx = i % THREAD_NUM; + if vload!(FLAG[idx]) == FlagState::Want { + next = idx; + break; + } + } + vstore!(TURN, next); + /* All done */ + vstore!(FLAG[id], FlagState::Out); + println!("Thread[{}] exit, give turn to {}", id, next); +} + +pub unsafe fn thread_fn(id: usize) -> ! { + println!("Thread[{}] init.", id); + for _ in 0..N { + eisenberg_enter_critical(id); + critical_test_enter(); + for _ in 0..3 { + critical_test_claim(); + sleep(2); + } + critical_test_exit(); + eisenberg_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + // TODO: really shuffle + assert_eq!(THREAD_NUM, 10); + let shuffle: [usize; 10] = [0, 7, 4, 6, 2, 9, 8, 1, 3, 5]; + for i in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, shuffle[i])); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/exit.rs b/user/src/bin/exit.rs new file mode 100644 index 0000000..60510c9 --- /dev/null +++ b/user/src/bin/exit.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use user_lib::{exit, fork, wait, waitpid, yield_}; + +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 +} diff --git a/user/src/bin/fantastic_text.rs b/user/src/bin/fantastic_text.rs new file mode 100644 index 0000000..a3402ff --- /dev/null +++ b/user/src/bin/fantastic_text.rs @@ -0,0 +1,44 @@ +#![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 +} diff --git a/user/src/bin/filetest_simple.rs b/user/src/bin/filetest_simple.rs new file mode 100644 index 0000000..3406d55 --- /dev/null +++ b/user/src/bin/filetest_simple.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, open, read, write, OpenFlags}; + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "Hello, world!"; + let filea = "filea\0"; + let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY); + assert!(fd > 0); + let fd = fd as usize; + write(fd, test_str.as_bytes()); + close(fd); + + let fd = open(filea, OpenFlags::RDONLY); + assert!(fd > 0); + let fd = fd as usize; + let mut buffer = [0u8; 100]; + let read_len = read(fd, &mut buffer) as usize; + close(fd); + + assert_eq!(test_str, core::str::from_utf8(&buffer[..read_len]).unwrap(),); + println!("file_test passed!"); + 0 +} diff --git a/user/src/bin/forktest.rs b/user/src/bin/forktest.rs new file mode 100644 index 0000000..5374a56 --- /dev/null +++ b/user/src/bin/forktest.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, wait}; + +const MAX_CHILD: usize = 30; + +#[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 +} diff --git a/user/src/bin/forktest2.rs b/user/src/bin/forktest2.rs new file mode 100644 index 0000000..c91ce15 --- /dev/null +++ b/user/src/bin/forktest2.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, get_time, getpid, sleep, wait}; + +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 exit_code: i32 = 0; + for _ in 0..NUM { + assert!(wait(&mut exit_code) > 0); + assert_eq!(exit_code, 0); + } + assert!(wait(&mut exit_code) < 0); + println!("forktest2 test passed!"); + 0 +} diff --git a/user/src/bin/forktest_simple.rs b/user/src/bin/forktest_simple.rs new file mode 100644 index 0000000..29a624b --- /dev/null +++ b/user/src/bin/forktest_simple.rs @@ -0,0 +1,28 @@ +#![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 exit_code: i32 = 0; + println!("ready waiting on parent process!"); + assert_eq!(pid, wait(&mut exit_code)); + assert_eq!(exit_code, 100); + println!("child process pid = {}, exit code = {}", pid, exit_code); + 0 + } +} diff --git a/user/src/bin/forktree.rs b/user/src/bin/forktree.rs new file mode 100644 index 0000000..6b120ee --- /dev/null +++ b/user/src/bin/forktree.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, getpid, sleep, wait, 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; + } + 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'); + let mut exit_code: i32 = 0; + for _ in 0..2 { + wait(&mut exit_code); + } +} + +#[no_mangle] +pub fn main() -> i32 { + fork_tree(""); + let mut exit_code: i32 = 0; + for _ in 0..2 { + wait(&mut exit_code); + } + sleep(3000); + 0 +} diff --git a/user/src/bin/gui_move.rs b/user/src/bin/gui_move.rs new file mode 100644 index 0000000..240ff8f --- /dev/null +++ b/user/src/bin/gui_move.rs @@ -0,0 +1,82 @@ +#![no_std] +#![no_main] + +extern crate user_lib; +extern crate alloc; + +use user_lib::console::getchar; +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::draw_target::DrawTarget; + +const INIT_X: i32 = 640; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 40; + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), + latest_pos: Point::new(INIT_X, INIT_Y), + } + } + fn paint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1)) + .draw(&mut self.disp) + .ok(); + } + fn unpaint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1)) + .draw(&mut self.disp) + .ok(); + } + pub fn move_rect(&mut self, dx: i32, dy: i32) { + let new_x = self.latest_pos.x + dx; + let new_y = self.latest_pos.y + dy; + let r = (RECT_SIZE / 2) as i32; + if new_x > r && new_x + r < (VIRTGPU_XRES as i32) && new_y > r && new_y + r < (VIRTGPU_YRES as i32) { + self.unpaint(); + self.latest_pos.x = new_x; + self.latest_pos.y = new_y; + self.paint(); + } + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + board.disp.flush(); + loop { + let c = getchar(); + if c == LF || c == CR { + break; + } + let mut moved = true; + match c { + b'w' => board.move_rect(0, -10), + b'a' => board.move_rect(-10, 0), + b's' => board.move_rect(0, 10), + b'd' => board.move_rect(10, 0), + _ => moved = false, + } + if moved { + board.disp.flush(); + } + } + 0 +} diff --git a/user/src/bin/gui_shape.rs b/user/src/bin/gui_shape.rs new file mode 100644 index 0000000..df03df3 --- /dev/null +++ b/user/src/bin/gui_shape.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate user_lib; + +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle, Triangle}; + +const INIT_X: i32 = 80; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 150; + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), + latest_pos: Point::new(INIT_X, INIT_Y), + } + } + fn paint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10)) + .draw(&mut self.disp) + .ok(); + Circle::new(self.latest_pos + Point::new(-70, -300), 150) + .into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE)) + .draw(&mut self.disp) + .ok(); + Triangle::new( + self.latest_pos + Point::new(0, 150), + self.latest_pos + Point::new(80, 200), + self.latest_pos + Point::new(-120, 300), + ) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10)) + .draw(&mut self.disp) + .ok(); + } +} + +#[no_mangle] +pub fn main() -> i32 { + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + for _ in 0..5 { + board.latest_pos.x += RECT_SIZE as i32 + 20; + board.paint(); + } + board.disp.flush(); + 0 +} diff --git a/user/src/bin/gui_snake.rs b/user/src/bin/gui_snake.rs new file mode 100644 index 0000000..6dd4052 --- /dev/null +++ b/user/src/bin/gui_snake.rs @@ -0,0 +1,352 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate user_lib; + +use user_lib::console::getchar; +use user_lib::{key_pressed, sleep, Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::*; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::Pixel; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use oorandom; //random generator + +struct Snake { + parts: [Pixel; MAX_SIZE], + len: usize, + direction: Direction, + size_x: u32, + size_y: u32, +} + +struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> { + snake: &'a Snake, + index: usize, +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake { + type Item = Pixel; + type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>; + + fn into_iter(self) -> Self::IntoIter { + SnakeIntoIterator { + snake: self, + index: 0, + } + } +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> { + type Item = Pixel; + + fn next(&mut self) -> Option { + let cur = self.snake.parts[self.index]; + if self.index < self.snake.len { + self.index += 1; + return Some(cur); + } + None + } +} + +impl Snake { + fn new(color: T, size_x: u32, size_y: u32) -> Snake { + Snake { + parts: [Pixel::(Point { x: 0, y: 0 }, color); MAX_SIZE], + len: 1, + direction: Direction::None, + size_x, + size_y, + } + } + fn set_direction(&mut self, direction: Direction) { + self.direction = direction; + } + fn contains(&self, this: Point) -> bool { + for part in self.into_iter() { + if part.0 == this { + return true; + }; + } + false + } + fn grow(&mut self) { + if self.len < MAX_SIZE - 1 { + self.len += 1; + } + } + fn make_step(&mut self) { + let mut i = self.len; + while i > 0 { + self.parts[i] = self.parts[i - 1]; + i -= 1; + } + match self.direction { + Direction::Left => { + if self.parts[0].0.x == 0 { + self.parts[0].0.x = (self.size_x - 1) as i32; + } else { + self.parts[0].0.x -= 1; + } + } + Direction::Right => { + if self.parts[0].0.x == (self.size_x - 1) as i32 { + self.parts[0].0.x = 0; + } else { + self.parts[0].0.x += 1; + } + } + Direction::Up => { + if self.parts[0].0.y == 0 { + self.parts[0].0.y = (self.size_y - 1) as i32; + } else { + self.parts[0].0.y -= 1; + } + } + Direction::Down => { + if self.parts[0].0.y == (self.size_y - 1) as i32 { + self.parts[0].0.y = 0; + } else { + self.parts[0].0.y += 1; + } + } + Direction::None => {} + } + } +} + +struct Food { + size_x: u32, + size_y: u32, + place: Pixel, + rng: oorandom::Rand32, +} + +impl Food { + pub fn new(color: T, size_x: u32, size_y: u32) -> Self { + let seed = 4; + let rng = oorandom::Rand32::new(seed); + Food { + size_x, + size_y, + place: Pixel(Point { x: 0, y: 0 }, color), + rng, + } + } + fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake) { + let mut p: Point; + 'outer: loop { + let random_number = self.rng.rand_u32(); + let blocked_positions = iter_source.into_iter(); + p = Point { + x: ((random_number >> 24) as u16 % self.size_x as u16).into(), + y: ((random_number >> 16) as u16 % self.size_y as u16).into(), + }; + for blocked_position in blocked_positions { + if p == blocked_position.0 { + continue 'outer; + } + } + break; + } + self.place = Pixel:: { + 0: p, + 1: self.place.1, + } + } + fn get_pixel(&self) -> Pixel { + self.place + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum Direction { + Left, + Right, + Up, + Down, + None, +} + +pub struct SnakeGame { + snake: Snake, + food: Food, + food_age: u32, + food_lifetime: u32, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl SnakeGame { + pub fn new( + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, + snake_color: T, + food_color: T, + food_lifetime: u32, + ) -> Self { + let snake = Snake::::new(snake_color, size_x / scale_x, size_y / scale_y); + let mut food = Food::::new(food_color, size_x / scale_x, size_y / scale_y); + food.replace(&snake); + SnakeGame { + snake, + food, + food_age: 0, + food_lifetime, + size_x, + size_y, + scale_x, + scale_y, + } + } + pub fn set_direction(&mut self, direction: Direction) { + self.snake.set_direction(direction); + } + pub fn draw(&mut self, target: &mut D) -> () + where + D: DrawTarget, + { + self.snake.make_step(); + let hit = self.snake.contains(self.food.get_pixel().0); + if hit { + self.snake.grow(); + } + self.food_age += 1; + if self.food_age >= self.food_lifetime || hit { + self.food.replace(&self.snake); + self.food_age = 0; + } + + let mut scaled_display = ScaledDisplay:: { + real_display: target, + size_x: self.size_x / self.scale_x, + size_y: self.size_y / self.scale_y, + scale_x: self.scale_x, + scale_y: self.scale_y, + }; + + for part in self.snake.into_iter() { + _ = part.draw(&mut scaled_display); + } + _ = self.food.get_pixel().draw(&mut scaled_display); + } +} + +/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things +struct ScaledDisplay<'a, T: DrawTarget> { + real_display: &'a mut T, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> { + type Color = T::Color; + type Error = T::Error; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + let style = PrimitiveStyle::with_fill(pixel.1); + Rectangle::new( + Point::new( + pixel.0.x * self.scale_x as i32, + pixel.0.y * self.scale_y as i32, + ), + Size::new(self.scale_x as u32, self.scale_y as u32), + ) + .into_styled(style) + .draw(self.real_display)?; + } + Ok(()) + } +} + +impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> { + fn size(&self) -> Size { + Size::new(self.size_x as u32, self.size_y as u32) + } +} + +#[cfg(test)] +mod tests { + + use crate::Snake; + use embedded_graphics::pixelcolor::*; + use embedded_graphics::prelude::*; + + #[test] + fn snake_basic() { + let mut snake = Snake::::new(Rgb888::RED, 8, 8); + snake.set_direction(crate::Direction::Right); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().next().unwrap() + ); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + snake.set_direction(crate::Direction::Down); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 1 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(2).unwrap() + ); + assert_eq!(true, snake.contains(Point { x: 0, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 1 })); + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 200); + let _ = disp.clear(Rgb888::BLACK).unwrap(); + loop { + if key_pressed() { + let c = getchar(); + match c { + LF => break, + CR => break, + b'w' => game.set_direction(Direction::Up), + b's' => game.set_direction(Direction::Down), + b'a' => game.set_direction(Direction::Left), + b'd' => game.set_direction(Direction::Right), + _ => (), + } + } + let _ = disp.clear(Rgb888::BLACK).unwrap(); + game.draw(&mut disp); + disp.flush(); + sleep(40); + } + 0 +} diff --git a/user/src/bin/gui_tri.rs b/user/src/bin/gui_tri.rs new file mode 100644 index 0000000..69d47f5 --- /dev/null +++ b/user/src/bin/gui_tri.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +use embedded_graphics::prelude::Size; +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + disp.paint_on_framebuffer(|fb| { + for y in 0..VIRTGPU_YRES as usize { + for x in 0..VIRTGPU_XRES as usize { + let idx = (y * VIRTGPU_XRES as usize + x) * 4; + fb[idx] = x as u8; + fb[idx + 1] = y as u8; + fb[idx + 2] = (x + y) as u8; + } + } + }); + disp.flush(); + 0 +} diff --git a/user/src/bin/hello_world.rs b/user/src/bin/hello_world.rs new file mode 100644 index 0000000..10d3f26 --- /dev/null +++ b/user/src/bin/hello_world.rs @@ -0,0 +1,11 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main() -> i32 { + println!("Hello world from user mode program!"); + 0 +} diff --git a/user/src/bin/huge_write.rs b/user/src/bin/huge_write.rs new file mode 100644 index 0000000..2a977c9 --- /dev/null +++ b/user/src/bin/huge_write.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, get_time, open, write, OpenFlags}; + +#[no_mangle] +pub fn main() -> i32 { + let mut buffer = [0u8; 1024]; // 1KiB + for (i, ch) in buffer.iter_mut().enumerate() { + *ch = i as u8; + } + let f = open("testf\0", OpenFlags::CREATE | OpenFlags::WRONLY); + if f < 0 { + panic!("Open test file failed!"); + } + let f = f as usize; + let start = get_time(); + let size_mb = 1usize; + for _ in 0..1024 * size_mb { + write(f, &buffer); + } + close(f); + let time_ms = (get_time() - start) as usize; + let speed_kbs = (size_mb << 20) / time_ms; + println!( + "{}MiB written, time cost = {}ms, write speed = {}KiB/s", + size_mb, time_ms, speed_kbs + ); + 0 +} diff --git a/user/src/bin/huge_write_mt.rs b/user/src/bin/huge_write_mt.rs new file mode 100644 index 0000000..0a60fd8 --- /dev/null +++ b/user/src/bin/huge_write_mt.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::{fmt::format, vec::Vec}; +use user_lib::{close, get_time, gettid, open, write, OpenFlags}; +use user_lib::{exit, thread_create, waittid}; + +fn worker(size_kib: usize) { + let mut buffer = [0u8; 1024]; // 1KiB + for (i, ch) in buffer.iter_mut().enumerate() { + *ch = i as u8; + } + let filename = format(format_args!("testf{}\0", gettid())); + let f = open(filename.as_str(), OpenFlags::CREATE | OpenFlags::WRONLY); + if f < 0 { + panic!("Open test file failed!"); + } + let f = f as usize; + for _ in 0..size_kib { + write(f, &buffer); + } + close(f); + exit(0) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert_eq!(argc, 2, "wrong argument"); + let size_mb = 1usize; + let size_kb = size_mb << 10; + let workers = argv[1].parse::().expect("wrong argument"); + assert!(workers >= 1 && size_kb % workers == 0, "wrong argument"); + + let start = get_time(); + + let mut v = Vec::new(); + let size_mb = 1usize; + for _ in 0..workers { + v.push(thread_create(worker as usize, size_kb / workers)); + } + for tid in v.iter() { + assert_eq!(0, waittid(*tid as usize)); + } + + let time_ms = (get_time() - start) as usize; + let speed_kbs = size_kb * 1000 / time_ms; + println!( + "{}MiB written by {} threads, time cost = {}ms, write speed = {}KiB/s", + size_mb, workers, time_ms, speed_kbs + ); + 0 +} diff --git a/user/src/bin/infloop.rs b/user/src/bin/infloop.rs new file mode 100644 index 0000000..1f24853 --- /dev/null +++ b/user/src/bin/infloop.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] +#![allow(clippy::empty_loop)] + +extern crate user_lib; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> ! { + loop {} +} diff --git a/user/src/bin/initproc.rs b/user/src/bin/initproc.rs new file mode 100644 index 0000000..d25aee1 --- /dev/null +++ b/user/src/bin/initproc.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +use user_lib::{exec, fork, wait, yield_}; + +#[no_mangle] +fn main() -> i32 { + if fork() == 0 { + exec("user_shell\0", &[core::ptr::null::()]); + } 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 +} diff --git a/user/src/bin/inputdev_event.rs b/user/src/bin/inputdev_event.rs new file mode 100644 index 0000000..ea56f3a --- /dev/null +++ b/user/src/bin/inputdev_event.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use user_lib::{event_get, DecodeType, Key, KeyType}; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main() -> i32 { + println!("Input device event test"); + loop { + if let Some(event) = event_get() { + if let Some(decoder_type) = event.decode() { + println!("{:?}", decoder_type); + if let DecodeType::Key(key, keytype) = decoder_type { + if key == Key::Enter && keytype == KeyType::Press { + break; + } + } + } + } + } + 0 +} diff --git a/user/src/bin/matrix.rs b/user/src/bin/matrix.rs new file mode 100644 index 0000000..9ebf48f --- /dev/null +++ b/user/src/bin/matrix.rs @@ -0,0 +1,69 @@ +#![no_std] +#![no_main] +#![allow(clippy::needless_range_loop)] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exit, fork, get_time, getpid, wait, yield_}; + +static NUM: usize = 30; +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 exit_code: i32 = 0; + for _ in 0..NUM { + if wait(&mut exit_code) < 0 { + panic!("wait failed."); + } + } + assert!(wait(&mut exit_code) < 0); + println!("matrix passed."); + 0 +} diff --git a/user/src/bin/mpsc_sem.rs b/user/src/bin/mpsc_sem.rs new file mode 100644 index 0000000..7b72bbb --- /dev/null +++ b/user/src/bin/mpsc_sem.rs @@ -0,0 +1,73 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{thread_create, waittid}; + +const SEM_MUTEX: usize = 0; +const SEM_EMPTY: usize = 1; +const SEM_AVAIL: usize = 2; +const BUFFER_SIZE: usize = 8; +static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; +static mut FRONT: usize = 0; +static mut TAIL: usize = 0; +const PRODUCER_COUNT: usize = 4; +const NUMBER_PER_PRODUCER: usize = 100; + +unsafe fn producer(id: *const usize) -> ! { + let id = *id; + for _ in 0..NUMBER_PER_PRODUCER { + semaphore_down(SEM_EMPTY); + semaphore_down(SEM_MUTEX); + BUFFER[TAIL] = id; + TAIL = (TAIL + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_AVAIL); + } + exit(0) +} + +unsafe fn consumer() -> ! { + for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { + semaphore_down(SEM_AVAIL); + semaphore_down(SEM_MUTEX); + print!("{} ", BUFFER[FRONT]); + FRONT = (FRONT + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_EMPTY); + } + println!(""); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); + assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); + assert_eq!(semaphore_create(0) as usize, SEM_AVAIL); + // create threads + let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); + let mut threads = Vec::new(); + for i in 0..PRODUCER_COUNT { + threads.push(thread_create( + producer as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + threads.push(thread_create(consumer as usize, 0)); + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("mpsc_sem passed!"); + 0 +} diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs new file mode 100644 index 0000000..e03e2a4 --- /dev/null +++ b/user/src/bin/peterson.rs @@ -0,0 +1,79 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use alloc::vec::Vec; +use core::sync::atomic::{AtomicUsize, Ordering}; +use user_lib::{exit, sleep, thread_create, waittid}; +const N: usize = 1000; + +static mut TURN: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static GUARD: AtomicUsize = AtomicUsize::new(0); + +fn critical_test_enter() { + assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); +} + +fn critical_test_claim() { + assert_eq!(GUARD.load(Ordering::SeqCst), 1); +} + +fn critical_test_exit() { + assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); +} + +unsafe fn peterson_enter_critical(id: usize, peer_id: usize) { + // println!("Thread[{}] try enter", id); + vstore!(FLAG[id], true); + vstore!(TURN, peer_id); + memory_fence!(); + while vload!(FLAG[peer_id]) && vload!(TURN) == peer_id { + // println!("Thread[{}] enter fail", id); + sleep(1); + // println!("Thread[{}] retry enter", id); + } + // println!("Thread[{}] enter", id); +} + +unsafe fn peterson_exit_critical(id: usize) { + vstore!(FLAG[id], false); + // println!("Thread[{}] exit", id); +} + +pub unsafe fn thread_fn(id: usize) -> ! { + // println!("Thread[{}] init.", id); + let peer_id: usize = id ^ 1; + for iter in 0..N { + if iter % 10 == 0 { + println!("[{}] it={}", id, iter); + } + peterson_enter_critical(id, peer_id); + critical_test_enter(); + for _ in 0..3 { + critical_test_claim(); + sleep(2); + } + critical_test_exit(); + peterson_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + v.push(thread_create(thread_fn as usize, 0)); + // v.push(thread_create(thread_fn as usize, 1)); + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/phil_din_mutex.rs b/user/src/bin/phil_din_mutex.rs new file mode 100644 index 0000000..8e7b566 --- /dev/null +++ b/user/src/bin/phil_din_mutex.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, sleep}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; +use user_lib::{thread_create, waittid}; + +const N: usize = 5; +const ROUND: usize = 4; +// A round: think -> wait for forks -> eat +const GRAPH_SCALE: usize = 100; + +fn get_time_u() -> usize { + get_time() as usize +} + +// Time unit: ms +const ARR: [[usize; ROUND * 2]; N] = [ + [700, 800, 1000, 400, 500, 600, 200, 400], + [300, 600, 200, 700, 1000, 100, 300, 600], + [500, 200, 900, 200, 400, 600, 1200, 400], + [500, 1000, 600, 500, 800, 600, 200, 900], + [600, 100, 600, 600, 200, 500, 600, 200], +]; +static mut THINK: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; +static mut EAT: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; + +fn philosopher_dining_problem(id: *const usize) { + let id = unsafe { *id }; + let left = id; + let right = if id == N - 1 { 0 } else { id + 1 }; + let min = if left < right { left } else { right }; + let max = left + right - min; + for round in 0..ROUND { + // thinking + unsafe { + THINK[id][2 * round] = get_time_u(); + } + sleep(ARR[id][2 * round]); + unsafe { + THINK[id][2 * round + 1] = get_time_u(); + } + // wait for forks + mutex_lock(min); + mutex_lock(max); + // eating + unsafe { + EAT[id][2 * round] = get_time_u(); + } + sleep(ARR[id][2 * round + 1]); + unsafe { + EAT[id][2 * round + 1] = get_time_u(); + } + mutex_unlock(max); + mutex_unlock(min); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + let ids: Vec<_> = (0..N).collect(); + let start = get_time_u(); + for i in 0..N { + assert_eq!(mutex_blocking_create(), i as isize); + v.push(thread_create( + philosopher_dining_problem as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + for tid in v.iter() { + waittid(*tid as usize); + } + let time_cost = get_time_u() - start; + println!("time cost = {}", time_cost); + println!("'-' -> THINKING; 'x' -> EATING; ' ' -> WAITING "); + for id in (0..N).into_iter().chain(0..=0) { + print!("#{}:", id); + for j in 0..time_cost / GRAPH_SCALE { + let current_time = j * GRAPH_SCALE + start; + if (0..ROUND).any(|round| unsafe { + let start_thinking = THINK[id][2 * round]; + let end_thinking = THINK[id][2 * round + 1]; + start_thinking <= current_time && current_time <= end_thinking + }) { + print!("-"); + } else if (0..ROUND).any(|round| unsafe { + let start_eating = EAT[id][2 * round]; + let end_eating = EAT[id][2 * round + 1]; + start_eating <= current_time && current_time <= end_eating + }) { + print!("x"); + } else { + print!(" "); + }; + } + println!(""); + } + 0 +} diff --git a/user/src/bin/pipe_large_test.rs b/user/src/bin/pipe_large_test.rs new file mode 100644 index 0000000..e85d0a6 --- /dev/null +++ b/user/src/bin/pipe_large_test.rs @@ -0,0 +1,71 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::format; +use user_lib::{close, fork, get_time, pipe, read, wait, write}; + +const LENGTH: usize = 3000; +#[no_mangle] +pub fn main() -> i32 { + // create pipes + // parent write to child + let mut down_pipe_fd = [0usize; 2]; + // child write to parent + let mut up_pipe_fd = [0usize; 2]; + pipe(&mut down_pipe_fd); + pipe(&mut up_pipe_fd); + let mut random_str = [0u8; LENGTH]; + if fork() == 0 { + // close write end of down pipe + close(down_pipe_fd[1]); + // close read end of up pipe + close(up_pipe_fd[0]); + assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH); + close(down_pipe_fd[0]); + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(child)", sum); + let sum_str = format!("{}", sum); + write(up_pipe_fd[1], sum_str.as_bytes()); + close(up_pipe_fd[1]); + println!("Child process exited!"); + 0 + } else { + // close read end of down pipe + close(down_pipe_fd[0]); + // close write end of up pipe + close(up_pipe_fd[1]); + // generate a long random string + for ch in random_str.iter_mut() { + *ch = get_time() as u8; + } + // send it + assert_eq!( + write(down_pipe_fd[1], &random_str) as usize, + random_str.len() + ); + // close write end of down pipe + close(down_pipe_fd[1]); + // calculate sum(parent) + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(parent)", sum); + // recv sum(child) + let mut child_result = [0u8; 32]; + let result_len = read(up_pipe_fd[0], &mut child_result) as usize; + close(up_pipe_fd[0]); + // check + assert_eq!( + sum, + str::parse::(core::str::from_utf8(&child_result[..result_len]).unwrap()) + .unwrap() + ); + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("pipe_large_test passed!"); + 0 + } +} diff --git a/user/src/bin/pipetest.rs b/user/src/bin/pipetest.rs new file mode 100644 index 0000000..5436c63 --- /dev/null +++ b/user/src/bin/pipetest.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, fork, pipe, read, wait, write}; + +static STR: &str = "Hello, world!"; + +#[no_mangle] +pub fn main() -> i32 { + // create pipe + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + // read end + assert_eq!(pipe_fd[0], 3); + // write end + assert_eq!(pipe_fd[1], 4); + if fork() == 0 { + // child process, read from parent + // close write_end + close(pipe_fd[1]); + let mut buffer = [0u8; 32]; + let len_read = read(pipe_fd[0], &mut buffer) as usize; + // close read_end + close(pipe_fd[0]); + assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR); + println!("Read OK, child process exited!"); + 0 + } else { + // parent process, write to child + // close read end + close(pipe_fd[0]); + assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize); + // close write end + close(pipe_fd[1]); + let mut child_exit_code: i32 = 0; + wait(&mut child_exit_code); + assert_eq!(child_exit_code, 0); + println!("pipetest passed!"); + 0 + } +} diff --git a/user/src/bin/power_3.rs b/user/src/bin/power_3.rs deleted file mode 100644 index e0ec5f3..0000000 --- a/user/src/bin/power_3.rs +++ /dev/null @@ -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!("{}^{} = {}(MOD {})", p, iter, s[cur], m); - println!("Test power_3 OK!"); - 0 -} \ No newline at end of file diff --git a/user/src/bin/power_5.rs b/user/src/bin/power_5.rs deleted file mode 100644 index e47761b..0000000 --- a/user/src/bin/power_5.rs +++ /dev/null @@ -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!("{}^{} = {}(MOD {})", p, iter, s[cur], m); - println!("Test power_5 OK!"); - 0 -} diff --git a/user/src/bin/power_7.rs b/user/src/bin/power_7.rs deleted file mode 100644 index a97d2f5..0000000 --- a/user/src/bin/power_7.rs +++ /dev/null @@ -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!("{}^{} = {}(MOD {})", p, iter, s[cur], m); - println!("Test power_7 OK!"); - 0 -} diff --git a/user/src/bin/priv_csr.rs b/user/src/bin/priv_csr.rs new file mode 100644 index 0000000..fbd678f --- /dev/null +++ b/user/src/bin/priv_csr.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use riscv::register::sstatus::{self, SPP}; + +#[no_mangle] +fn main() -> i32 { + println!("Try to access privileged CSR in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + sstatus::set_spp(SPP::User); + } + 0 +} diff --git a/user/src/bin/priv_inst.rs b/user/src/bin/priv_inst.rs new file mode 100644 index 0000000..04dac37 --- /dev/null +++ b/user/src/bin/priv_inst.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use core::arch::asm; + +#[no_mangle] +fn main() -> i32 { + println!("Try to execute privileged instruction in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + asm!("sret"); + } + 0 +} diff --git a/user/src/bin/race_adder_arg.rs b/user/src/bin/race_adder_arg.rs new file mode 100644 index 0000000..f33c660 --- /dev/null +++ b/user/src/bin/race_adder_arg.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use crate::alloc::string::ToString; +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f(count: usize) -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..count { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let count: usize; + if argc == 1 { + count = THREAD_COUNT; + } else if argc == 2 { + count = argv[1].to_string().parse::().unwrap(); + } else { + println!( + "ERROR in argv, argc is {}, argv[0] {} , argv[1] {} , argv[2] {}", + argc, argv[0], argv[1], argv[2] + ); + exit(-1); + } + + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, count) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + 0 +} diff --git a/user/src/bin/random_num.rs b/user/src/bin/random_num.rs new file mode 100644 index 0000000..d68bb13 --- /dev/null +++ b/user/src/bin/random_num.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use oorandom; + +#[no_mangle] +pub fn main() -> i32 { + println!("random num program!"); + let seed = 4; + let mut rng = oorandom::Rand32::new(seed); + println!("OORandom: Random number 32bit: {}", rng.rand_i32()); + println!("OORandom: Random number range: {}", rng.rand_range(1..100)); + 0 +} diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs new file mode 100644 index 0000000..d3e329d --- /dev/null +++ b/user/src/bin/run_pipe_test.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait}; + +#[no_mangle] +pub fn main() -> i32 { + for i in 0..5 { + if fork() == 0 { + exec("pipe_large_test\0", &[core::ptr::null::()]); + } else { + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("Iter {} OK.", i); + } + } + 0 +} diff --git a/user/src/bin/sleep.rs b/user/src/bin/sleep.rs index 1312f6c..641a4f9 100644 --- a/user/src/bin/sleep.rs +++ b/user/src/bin/sleep.rs @@ -4,21 +4,27 @@ #[macro_use] extern crate user_lib; -use user_lib::{get_time, yield_}; +use user_lib::{exit, fork, get_time, sleep, waitpid}; -/// 正确输出:(无报错信息) -/// get_time OK! {...} -/// Test sleep OK! +fn sleepy() { + let time: usize = 100; + for i in 0..5 { + sleep(time); + println!("sleep {} x {} msecs.", i + 1, time); + } + exit(0); +} #[no_mangle] -fn main() -> i32 { +pub fn main() -> i32 { let current_time = get_time(); - assert!(current_time > 0); - println!("get_time OK! {}", current_time); - let wait_for = current_time + 3000; - while get_time() < wait_for { - yield_(); + let pid = fork(); + let mut exit_code: i32 = 0; + if pid == 0 { + sleepy(); } - println!("Test sleep OK!"); + assert!(waitpid(pid as usize, &mut exit_code) == pid && exit_code == 0); + println!("use {} msecs.", get_time() - current_time); + println!("sleep pass."); 0 } diff --git a/user/src/bin/sleep1.rs b/user/src/bin/sleep_simple.rs similarity index 84% rename from user/src/bin/sleep1.rs rename to user/src/bin/sleep_simple.rs index da0cae3..7015a3d 100644 --- a/user/src/bin/sleep1.rs +++ b/user/src/bin/sleep_simple.rs @@ -8,6 +8,7 @@ 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); @@ -17,6 +18,6 @@ pub fn main() -> i32 { end, end - start ); - println!("Test sleep1 passed!"); + println!("r_sleep passed!"); 0 } diff --git a/user/src/bin/stack_overflow.rs b/user/src/bin/stack_overflow.rs new file mode 100644 index 0000000..3bec557 --- /dev/null +++ b/user/src/bin/stack_overflow.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +#[allow(unconditional_recursion)] +fn f(depth: usize) { + if depth % 10 == 0 { + println!("depth = {}", depth); + } + f(depth + 1); +} + +#[no_mangle] +pub fn main() -> i32 { + println!("It should trigger segmentation fault!"); + f(0); + 0 +} diff --git a/user/src/bin/stackful_coroutine.rs b/user/src/bin/stackful_coroutine.rs new file mode 100644 index 0000000..3f61979 --- /dev/null +++ b/user/src/bin/stackful_coroutine.rs @@ -0,0 +1,350 @@ +// we porting below codes to Rcore Tutorial v3 +// https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/ +// https://github.com/cfsamson/example-greenthreads +#![no_std] +#![no_main] +#![feature(naked_functions)] +//#![feature(asm)] + +extern crate alloc; +#[macro_use] +extern crate user_lib; + +use core::arch::asm; + +//#[macro_use] +use alloc::vec; +use alloc::vec::Vec; + +use user_lib::exit; + +// In our simple example we set most constraints here. +const DEFAULT_STACK_SIZE: usize = 4096; //128 got SEGFAULT, 256(1024, 4096) got right results. +const MAX_TASKS: usize = 5; +static mut RUNTIME: usize = 0; + +pub struct Runtime { + tasks: Vec, + current: usize, +} + +#[derive(PartialEq, Eq, Debug)] +enum State { + Available, + Running, + Ready, +} + +struct Task { + id: usize, + stack: Vec, + ctx: TaskContext, + state: State, +} + +#[derive(Debug, Default)] +#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable +pub struct TaskContext { + // 15 u64 + x1: u64, //ra: return addres + x2: u64, //sp + x8: u64, //s0,fp + x9: u64, //s1 + x18: u64, //x18-27: s2-11 + x19: u64, + x20: u64, + x21: u64, + x22: u64, + x23: u64, + x24: u64, + x25: u64, + x26: u64, + x27: u64, + nx1: u64, //new return addres +} + +impl Task { + fn new(id: usize) -> Self { + // We initialize each task here and allocate the stack. This is not neccesary, + // we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts + // to do it here. The important part is that once allocated it MUST NOT move in memory. + Task { + id: id, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Available, + } + } +} + +impl Runtime { + pub fn new() -> Self { + // This will be our base task, which will be initialized in the `running` state + let base_task = Task { + id: 0, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Running, + }; + + // We initialize the rest of our tasks. + let mut tasks = vec![base_task]; + let mut available_tasks: Vec = (1..MAX_TASKS).map(|i| Task::new(i)).collect(); + tasks.append(&mut available_tasks); + + Runtime { tasks, current: 0 } + } + + /// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if + /// we don't have a reference to it. + pub fn init(&self) { + unsafe { + let r_ptr: *const Runtime = self; + RUNTIME = r_ptr as usize; + } + } + + /// This is where we start running our runtime. If it is our base task, we call yield until + /// it returns false (which means that there are no tasks scheduled) and we are done. + pub fn run(&mut self) { + while self.t_yield() {} + println!("All tasks finished!"); + } + + /// This is our return function. The only place we use this is in our `guard` function. + /// If the current task is not our base task we set its state to Available. It means + /// we're finished with it. Then we yield which will schedule a new task to be run. + fn t_return(&mut self) { + if self.current != 0 { + self.tasks[self.current].state = State::Available; + self.t_yield(); + } + } + + /// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state. + /// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm. + /// + /// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`. + /// Then we call switch which will save the current context (the old context) and load the new context + /// into the CPU which then resumes based on the context it was just passed. + /// + /// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result + #[inline(never)] + fn t_yield(&mut self) -> bool { + let mut pos = self.current; + while self.tasks[pos].state != State::Ready { + pos += 1; + if pos == self.tasks.len() { + pos = 0; + } + if pos == self.current { + return false; + } + } + + if self.tasks[self.current].state != State::Available { + self.tasks[self.current].state = State::Ready; + } + + self.tasks[pos].state = State::Running; + let old_pos = self.current; + self.current = pos; + + unsafe { + switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx); + } + + // NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler + // is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows + // and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that + // will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead, + // this code will never be run anyways and if it did it would always be `true`. + self.tasks.len() > 0 + } + + /// While `yield` is the logically interesting function I think this the technically most interesting. + /// + /// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state). + /// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that. + /// We keep things simple for now. + /// + /// When we find an available task we get the stack length and a pointer to our u8 bytearray. + /// + /// The next part we have to use some unsafe functions. First we write an address to our `guard` function + /// that will be called if the function we provide returns. Then we set the address to the function we + /// pass inn. + /// + /// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start + /// executing that first when we are scheuled to run. + /// + /// Lastly we set the state as `Ready` which means we have work to do and is ready to do it. + pub fn spawn(&mut self, f: fn()) { + let available = self + .tasks + .iter_mut() + .find(|t| t.state == State::Available) + .expect("no available task."); + + println!("RUNTIME: spawning task {}\n", available.id); + let size = available.stack.len(); + unsafe { + let s_ptr = available.stack.as_mut_ptr().offset(size as isize); + + // make sure our stack itself is 8 byte aligned - it will always + // offset to a lower memory address. Since we know we're at the "high" + // memory address of our allocated space, we know that offsetting to + // a lower one will be a valid address (given that we actually allocated) + // enough space to actually get an aligned pointer in the first place). + let s_ptr = (s_ptr as usize & !7) as *mut u8; + + available.ctx.x1 = guard as u64; //ctx.x1 is old return address + available.ctx.nx1 = f as u64; //ctx.nx2 is new return address + available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp + } + available.state = State::Ready; + } +} + +/// This is our guard function that we place on top of the stack. All this function does is set the +/// state of our current task and then `yield` which will then schedule a new task to be run. +fn guard() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_return(); + }; +} + +/// We know that Runtime is alive the length of the program and that we only access from one core +/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our +/// Runtime and then calling `t_yield` +pub fn yield_task() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_yield(); + }; +} + +/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first +/// read out the values of all the registers we need and then sets all the register values to the register values we +/// saved when we suspended exceution on the "new" task. +/// +/// This is essentially all we need to do to save and resume execution. +/// +/// Some details about inline assembly. +/// +/// The assembly commands in the string literal is called the assemblt template. It is preceeded by +/// zero or up to four segments indicated by ":": +/// +/// - First ":" we have our output parameters, this parameters that this function will return. +/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context +/// but we modify the "old" context saving our registers there (see volatile option below) +/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely +/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel" +/// +/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to +/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects" +/// therefore we should use the `volatile` option. I **think** this is actually set for us by default +/// when there are no output parameters given (my own assumption after going through the source code) +/// for the `asm` macro, but we should make it explicit anyway. +/// +/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full +/// control over the stack layout since normal functions has a prologue-and epilogue added by the +/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked". +/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else +/// the compiler decides to inline this function (curiously this currently only happens on Windows). +/// If the function is inlined we get a curious runtime error where it fails when switching back +/// to as saved context and in general our assembly will not work as expected. +/// +/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md +/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html +/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html +#[naked] +#[no_mangle] +unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) { + // a0: _old, a1: _new + asm!( + " + sd x1, 0x00(a0) + sd x2, 0x08(a0) + sd x8, 0x10(a0) + sd x9, 0x18(a0) + sd x18, 0x20(a0) + sd x19, 0x28(a0) + sd x20, 0x30(a0) + sd x21, 0x38(a0) + sd x22, 0x40(a0) + sd x23, 0x48(a0) + sd x24, 0x50(a0) + sd x25, 0x58(a0) + sd x26, 0x60(a0) + sd x27, 0x68(a0) + sd x1, 0x70(a0) + + ld x1, 0x00(a1) + ld x2, 0x08(a1) + ld x8, 0x10(a1) + ld x9, 0x18(a1) + ld x18, 0x20(a1) + ld x19, 0x28(a1) + ld x20, 0x30(a1) + ld x21, 0x38(a1) + ld x22, 0x40(a1) + ld x23, 0x48(a1) + ld x24, 0x50(a1) + ld x25, 0x58(a1) + ld x26, 0x60(a1) + ld x27, 0x68(a1) + ld t0, 0x70(a1) + + jr t0 + ", + options(noreturn) + ); +} + +#[no_mangle] +pub fn main() { + println!("stackful_coroutine begin..."); + println!("TASK 0(Runtime) STARTING"); + let mut runtime = Runtime::new(); + runtime.init(); + runtime.spawn(|| { + println!("TASK 1 STARTING"); + let id = 1; + for i in 0..4 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 1 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 2 STARTING"); + let id = 2; + for i in 0..8 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 2 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 3 STARTING"); + let id = 3; + for i in 0..12 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 3 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 4 STARTING"); + let id = 4; + for i in 0..16 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 4 FINISHED"); + }); + runtime.run(); + println!("stackful_coroutine PASSED"); + exit(0); +} diff --git a/user/src/bin/stackless_coroutine.rs b/user/src/bin/stackless_coroutine.rs new file mode 100644 index 0000000..43aeb2d --- /dev/null +++ b/user/src/bin/stackless_coroutine.rs @@ -0,0 +1,129 @@ +// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/ +// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86 +#![no_std] +#![no_main] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +extern crate alloc; +use alloc::collections::VecDeque; + +use alloc::boxed::Box; + +#[macro_use] +extern crate user_lib; + +enum State { + Halted, + Running, +} + +struct Task { + state: State, +} + +impl Task { + fn waiter<'a>(&'a mut self) -> Waiter<'a> { + Waiter { task: self } + } +} + +struct Waiter<'a> { + task: &'a mut Task, +} + +impl<'a> Future for Waiter<'a> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + match self.task.state { + State::Halted => { + self.task.state = State::Running; + Poll::Ready(()) + } + State::Running => { + self.task.state = State::Halted; + Poll::Pending + } + } + } +} + +struct Executor { + tasks: VecDeque>>>, +} + +impl Executor { + fn new() -> Self { + Executor { + tasks: VecDeque::new(), + } + } + + fn push(&mut self, closure: C) + where + F: Future + 'static, + C: FnOnce(Task) -> F, + { + let task = Task { + state: State::Running, + }; + self.tasks.push_back(Box::pin(closure(task))); + } + + fn run(&mut self) { + let waker = create_waker(); + let mut context = Context::from_waker(&waker); + + while let Some(mut task) = self.tasks.pop_front() { + match task.as_mut().poll(&mut context) { + Poll::Pending => { + self.tasks.push_back(task); + } + Poll::Ready(()) => {} + } + } + } +} + +pub fn create_waker() -> Waker { + // Safety: The waker points to a vtable with functions that do nothing. Doing + // nothing is memory-safe. + unsafe { Waker::from_raw(RAW_WAKER) } +} + +const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE); +const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + +unsafe fn clone(_: *const ()) -> RawWaker { + RAW_WAKER +} +unsafe fn wake(_: *const ()) {} +unsafe fn wake_by_ref(_: *const ()) {} +unsafe fn drop(_: *const ()) {} + +#[no_mangle] +pub fn main() -> i32 { + println!("stackless coroutine Begin.."); + let mut exec = Executor::new(); + println!(" Create futures"); + for instance in 1..=3 { + exec.push(move |mut task| async move { + println!(" Task {}: begin state", instance); + task.waiter().await; + println!(" Task {}: next state", instance); + task.waiter().await; + println!(" Task {}: end state", instance); + }); + } + + println!(" Running"); + exec.run(); + println!(" Done"); + println!("stackless coroutine PASSED"); + + 0 +} diff --git a/user/src/bin/store_fault.rs b/user/src/bin/store_fault.rs new file mode 100644 index 0000000..f8023eb --- /dev/null +++ b/user/src/bin/store_fault.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +fn main() -> i32 { + println!("Into Test store_fault, we will insert an invalid store operation..."); + println!("Kernel should kill this application!"); + unsafe { + core::ptr::null_mut::().write_volatile(0); + } + 0 +} diff --git a/user/src/bin/sync_sem.rs b/user/src/bin/sync_sem.rs new file mode 100644 index 0000000..b8d1f79 --- /dev/null +++ b/user/src/bin/sync_sem.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{sleep, thread_create, waittid}; + +const SEM_SYNC: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work and wakeup Second"); + semaphore_up(SEM_SYNC); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait first"); + semaphore_down(SEM_SYNC); + println!("Second can work now"); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(0) as usize, SEM_SYNC); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("sync_sem passed!"); + 0 +} diff --git a/user/src/bin/tcp_simplehttp.rs b/user/src/bin/tcp_simplehttp.rs new file mode 100644 index 0000000..3e61950 --- /dev/null +++ b/user/src/bin/tcp_simplehttp.rs @@ -0,0 +1,188 @@ +#![no_std] +#![no_main] + +use alloc::string::{String, ToString}; + +#[macro_use] +extern crate user_lib; +#[macro_use] +extern crate alloc; + +// use http://localhost:6201/ to access the http server + +use user_lib::{accept, listen, read, write}; + +// get url from the tcp request list. +fn get_url_from_tcp_request(req: &[u8]) -> String { + let mut index = 0; + for i in 4..req.len() { + if req[i] == 0x20 { + index = i; + break; + } + } + + String::from_utf8_lossy(&req[4..index]).to_string() +} + +// just receive GET requests +fn handle_tcp_client(client_fd: usize) -> bool { + // a buf to receive the data from the server + let mut buf = vec![0u8; 1024]; + + let len = read(client_fd as usize, &mut buf); + + println!("receive {} bytes", len); + hexdump(&buf[..len as usize]); + + // verify whether it is a valid HTTP request simply, [0x47,0x45,0x54, 0x20] is GET + if len < 4 || buf[..4] != [0x47, 0x45, 0x54, 0x20] { + println!("it's not a valid http request"); + return false; + } + + let url = get_url_from_tcp_request(&buf); + + if url == "/close" { + let content = r#" + + + + + + + + + + +
+

server closed

+
+ + "#; + let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnecion: Close\r\n\r\n{}", content.len(),content); + write(client_fd, response.as_bytes()); + // terminate the connection immediately. + return true; + } + + let content = r#" + + + + + + + + + + +
+

rCore-tutorial-V3

+

rCore-tutorial-V3 是一个 基于 RISC-V 架构的 类 Unix 内核.

+
+ +
+
+
+

Rust

+

Rust

+

Rust是一门系统编程语言,专注于安全,尤其是并发安全,支持函数式和命令式以及泛型等编程范式的多范式语言

+
+
+

仓库地址

+

repo url

+

https://github.com/rcore-os/rCore-Tutorial-v3

+
+
+

QQ 群号

+

Official QQ group number

+

735045051

+
+
+
+ +
+

点击下列按钮即可关闭服务器。

+ +
+ + + "#; + + let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnecion: Close\r\n\r\n{}", content.len(),content); + + // write a response + write(client_fd, response.as_bytes()); + + false +} + +#[no_mangle] +pub fn main() -> i32 { + println!("This is a very simple http server"); + + let tcp_fd = listen(80); + + if tcp_fd < 0 { + println!("Failed to listen on port 80"); + return -1; + } + + loop { + let client = accept(tcp_fd as usize); + println!("client connected: {}", client); + + if client < 1 { + println!("Failed to accept a client on port 80"); + return -1; + } + + if handle_tcp_client(client as usize) { + break; + } + } + + println!("finish tcp test"); + + // String::from_utf8_lossy(&buf[..len as usize]) + + 0 +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("{:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("{:-^1$}", " hexdump end ", PRELAND_WIDTH); +} diff --git a/user/src/bin/test1_write0.rs b/user/src/bin/test1_write0.rs deleted file mode 100644 index c4a77cc..0000000 --- a/user/src/bin/test1_write0.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![no_std] -#![no_main] -#![feature(asm)] - -#[macro_use] -extern crate user_lib; -extern crate core; -use core::slice; -use user_lib::{write, STDOUT}; - -/// 正确输出: -/// Test write0 OK! - -const STACK_SIZE: usize = 0x1000; - -unsafe fn r_sp() -> usize { - let mut sp: usize; - asm!("mv {}, sp", out(reg) sp); - sp -} - -unsafe fn stack_range() -> (usize, usize) { - let sp = r_sp(); - let top = (sp + STACK_SIZE - 1) & (!(STACK_SIZE - 1)); - (top - STACK_SIZE, top) -} - -#[no_mangle] -pub fn main() -> i32 { - assert_eq!( - write(STDOUT, unsafe { - #[allow(clippy::zero_ptr)] - slice::from_raw_parts(0x0 as *const _, 10) - }), - -1 - ); - let (bottom, top) = unsafe { stack_range() }; - assert_eq!( - write(STDOUT, unsafe { - slice::from_raw_parts((top - 5) as *const _, 10) - }), - -1 - ); - assert_eq!( - write(STDOUT, unsafe { - slice::from_raw_parts((bottom - 5) as *const _, 10) - }), - -1 - ); - // TODO: test string located in .data section - println!("Test write0 OK!"); - 0 -} diff --git a/user/src/bin/test1_write1.rs b/user/src/bin/test1_write1.rs deleted file mode 100644 index 276ad9c..0000000 --- a/user/src/bin/test1_write1.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{write, STDOUT}; -const DATA_STRING: &str = "string from data section\n"; - -/// 正确输出: -/// string from data section -/// strinstring from stack section -/// strin -/// Test write1 OK! - -#[no_mangle] -pub fn main() -> i32 { - assert_eq!(write(1234, DATA_STRING.as_bytes()), -1); - assert_eq!( - write(STDOUT, DATA_STRING.as_bytes()), - DATA_STRING.len() as isize - ); - assert_eq!(write(STDOUT, &DATA_STRING.as_bytes()[..5]), 5); - let stack_string = "string from stack section\n"; - assert_eq!( - write(STDOUT, stack_string.as_bytes()), - stack_string.len() as isize - ); - assert_eq!(write(STDOUT, &stack_string.as_bytes()[..5]), 5); - println!("\nTest write1 OK!"); - 0 -} diff --git a/user/src/bin/test2_setprio.rs b/user/src/bin/test2_setprio.rs deleted file mode 100644 index 6aa1064..0000000 --- a/user/src/bin/test2_setprio.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::set_priority; - -/// 正确输出:(无报错信息) -/// Test set_priority OK! - -#[no_mangle] -pub fn main() -> i32 { - assert_eq!(set_priority(10), 10); - assert_eq!(set_priority(isize::MAX), isize::MAX); - assert_eq!(set_priority(0), -1); - assert_eq!(set_priority(1), -1); - assert_eq!(set_priority(-10), -1); - println!("Test set_priority OK!"); - 0 -} diff --git a/user/src/bin/test3_stride0.rs b/user/src/bin/test3_stride0.rs deleted file mode 100644 index 7fd9764..0000000 --- a/user/src/bin/test3_stride0.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -/* -理想结果:6个进程退出时,输出 count 基本正比于 priority -*/ - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -pub fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 5; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} diff --git a/user/src/bin/test3_stride1.rs b/user/src/bin/test3_stride1.rs deleted file mode 100644 index 52eb18a..0000000 --- a/user/src/bin/test3_stride1.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 6; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} \ No newline at end of file diff --git a/user/src/bin/test3_stride2.rs b/user/src/bin/test3_stride2.rs deleted file mode 100644 index e2c032c..0000000 --- a/user/src/bin/test3_stride2.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 7; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} \ No newline at end of file diff --git a/user/src/bin/test3_stride3.rs b/user/src/bin/test3_stride3.rs deleted file mode 100644 index 71348cc..0000000 --- a/user/src/bin/test3_stride3.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 8; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} \ No newline at end of file diff --git a/user/src/bin/test3_stride4.rs b/user/src/bin/test3_stride4.rs deleted file mode 100644 index 40369e7..0000000 --- a/user/src/bin/test3_stride4.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 9; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} \ No newline at end of file diff --git a/user/src/bin/test3_stride5.rs b/user/src/bin/test3_stride5.rs deleted file mode 100644 index 580494a..0000000 --- a/user/src/bin/test3_stride5.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -use user_lib::{get_time, set_priority}; - -fn spin_delay() { - let mut j = true; - for _ in 0..10 { - j = !j; - } -} - -// to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. -const MAX_TIME: isize = 1000; -fn count_during(prio: isize) -> isize { - let start_time = get_time(); - let mut acc = 0; - set_priority(prio); - loop { - spin_delay(); - acc += 1; - if acc % 400 == 0 { - let time = get_time() - start_time; - if time > MAX_TIME { - return acc; - } - } - } -} - -#[no_mangle] -pub fn main() -> usize { - let prio = 10; - let count = count_during(prio); - println!("priority = {}, exitcode = {}", prio, count); - 0 -} \ No newline at end of file diff --git a/user/src/bin/threads.rs b/user/src/bin/threads.rs new file mode 100644 index 0000000..3d374fe --- /dev/null +++ b/user/src/bin/threads.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec; +use user_lib::{exit, thread_create, waittid}; + +pub fn thread_a() -> ! { + for _ in 0..1000 { + print!("a"); + } + exit(1) +} + +pub fn thread_b() -> ! { + for _ in 0..1000 { + print!("b"); + } + exit(2) +} + +pub fn thread_c() -> ! { + for _ in 0..1000 { + print!("c"); + } + exit(3) +} + +#[no_mangle] +pub fn main() -> i32 { + let v = vec![ + thread_create(thread_a as usize, 0), + thread_create(thread_b as usize, 0), + thread_create(thread_c as usize, 0), + ]; + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/threads_arg.rs b/user/src/bin/threads_arg.rs new file mode 100644 index 0000000..c5b26ed --- /dev/null +++ b/user/src/bin/threads_arg.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, thread_create, waittid}; + +struct Argument { + pub ch: char, + pub rc: i32, +} + +fn thread_print(arg: *const Argument) -> ! { + let arg = unsafe { &*arg }; + for _ in 0..1000 { + print!("{}", arg.ch); + } + exit(arg.rc) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + let args = [ + Argument { ch: 'a', rc: 1 }, + Argument { ch: 'b', rc: 2 }, + Argument { ch: 'c', rc: 3 }, + ]; + for arg in args.iter() { + v.push(thread_create( + thread_print as usize, + arg as *const _ as usize, + )); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/udp.rs b/user/src/bin/udp.rs new file mode 100644 index 0000000..68a1a10 --- /dev/null +++ b/user/src/bin/udp.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +use alloc::string::String; + +#[macro_use] +extern crate user_lib; +#[macro_use] +extern crate alloc; + +use user_lib::{connect, read, write}; + +#[no_mangle] +pub fn main() -> i32 { + println!("udp test open!"); + + let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099); + + if udp_fd < 0 { + println!("failed to create udp connection."); + return -1; + } + + let buf = "Hello rCoreOS user program!"; + + println!("send <{}>", buf); + + write(udp_fd as usize, buf.as_bytes()); + + println!("udp send done, waiting for reply."); + + let mut buf = vec![0u8; 1024]; + + let len = read(udp_fd as usize, &mut buf); + + if len < 0 { + println!("can't receive udp packet"); + return -1; + } + + let recv_str = String::from_utf8_lossy(&buf[..len as usize]); + + println!("receive reply <{}>", recv_str); + + 0 +} diff --git a/user/src/bin/until_timeout.rs b/user/src/bin/until_timeout.rs new file mode 100644 index 0000000..a0007cb --- /dev/null +++ b/user/src/bin/until_timeout.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, get_time, kill, waitpid, waitpid_nb, SignalFlags}; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert_eq!(argc, 3, "argc must be 3!"); + let timeout_ms = argv[2] + .parse::() + .expect("Error when parsing timeout!"); + let pid = fork() as usize; + if pid == 0 { + if exec(argv[1], &[core::ptr::null::()]) != 0 { + println!("Error when executing '{}'", argv[1]); + return -4; + } + } else { + let start_time = get_time(); + let mut child_exited = false; + let mut exit_code: i32 = 0; + loop { + if get_time() - start_time > timeout_ms { + break; + } + if waitpid_nb(pid, &mut exit_code) as usize == pid { + child_exited = true; + println!( + "child exited in {}ms, exit_code = {}", + get_time() - start_time, + exit_code, + ); + } + } + if !child_exited { + println!("child has run for {}ms, kill it!", timeout_ms); + kill(pid, SignalFlags::SIGINT.bits()); + assert_eq!(waitpid(pid, &mut exit_code) as usize, pid); + println!("exit code of the child is {}", exit_code); + } + } + 0 +} diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs new file mode 100644 index 0000000..4288248 --- /dev/null +++ b/user/src/bin/user_shell.rs @@ -0,0 +1,214 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +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; +const LINE_START: &str = ">> "; + +use alloc::string::String; +use alloc::vec::Vec; +use user_lib::console::getchar; +use user_lib::{close, dup, exec, fork, open, pipe, waitpid, OpenFlags}; + +#[derive(Debug)] +struct ProcessArguments { + input: String, + output: String, + args_copy: Vec, + args_addr: Vec<*const u8>, +} + +impl ProcessArguments { + pub fn new(command: &str) -> Self { + let args: Vec<_> = command.split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .filter(|&arg| !arg.is_empty()) + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string.push('\0'); + string + }) + .collect(); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") + { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") + { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy.iter().map(|arg| arg.as_ptr()).collect(); + args_addr.push(core::ptr::null::()); + + Self { + input, + output, + args_copy, + args_addr, + } + } +} + +#[no_mangle] +pub fn main() -> i32 { + println!("Rust user shell"); + let mut line: String = String::new(); + print!("{}", LINE_START); + loop { + let c = getchar(); + match c { + LF | CR => { + println!(""); + if !line.is_empty() { + let splited: Vec<_> = line.as_str().split('|').collect(); + let process_arguments_list: Vec<_> = splited + .iter() + .map(|&cmd| ProcessArguments::new(cmd)) + .collect(); + let mut valid = true; + for (i, process_args) in process_arguments_list.iter().enumerate() { + if i == 0 { + if !process_args.output.is_empty() { + valid = false; + } + } else if i == process_arguments_list.len() - 1 { + if !process_args.input.is_empty() { + valid = false; + } + } else if !process_args.output.is_empty() || !process_args.input.is_empty() + { + valid = false; + } + } + if process_arguments_list.len() == 1 { + valid = true; + } + if !valid { + println!("Invalid command: Inputs/Outputs cannot be correctly binded!"); + } else { + // create pipes + let mut pipes_fd: Vec<[usize; 2]> = Vec::new(); + if !process_arguments_list.is_empty() { + for _ in 0..process_arguments_list.len() - 1 { + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + pipes_fd.push(pipe_fd); + } + } + let mut children: Vec<_> = Vec::new(); + for (i, process_argument) in process_arguments_list.iter().enumerate() { + let pid = fork(); + if pid == 0 { + let input = &process_argument.input; + let output = &process_argument.output; + let args_copy = &process_argument.args_copy; + let args_addr = &process_argument.args_addr; + // redirect input + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // redirect output + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY, + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } + // receive input from the previous process + if i > 0 { + close(0); + let read_end = pipes_fd.get(i - 1).unwrap()[0]; + assert_eq!(dup(read_end), 0); + } + // send output to the next process + if i < process_arguments_list.len() - 1 { + close(1); + let write_end = pipes_fd.get(i).unwrap()[1]; + assert_eq!(dup(write_end), 1); + } + // close all pipe ends inherited from the parent process + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + // execute new application + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + children.push(pid); + } + } + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + let mut exit_code: i32 = 0; + for pid in children.into_iter() { + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + //println!("Shell: Process {} exited with code {}", pid, exit_code); + } + } + line.clear(); + } + print!("{}", LINE_START); + } + 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); + } + } + } +} diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs new file mode 100644 index 0000000..b522af2 --- /dev/null +++ b/user/src/bin/usertests.rs @@ -0,0 +1,145 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +// not in SUCC_TESTS & FAIL_TESTS +// count_lines, infloop, user_shell, usertests + +// item of TESTS : app_name(argv_0), argv_1, argv_2, argv_3, exit_code +static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[ + ("filetest_simple\0", "\0", "\0", "\0", 0), + ("cat\0", "filea\0", "\0", "\0", 0), + ("cmdline_args\0", "1\0", "2\0", "3\0", 0), + ("eisenberg\0", "\0", "\0", "\0", 0), + ("exit\0", "\0", "\0", "\0", 0), + ("fantastic_text\0", "\0", "\0", "\0", 0), + ("forktest_simple\0", "\0", "\0", "\0", 0), + ("forktest\0", "\0", "\0", "\0", 0), + ("forktest2\0", "\0", "\0", "\0", 0), + ("forktree\0", "\0", "\0", "\0", 0), + ("hello_world\0", "\0", "\0", "\0", 0), + ("huge_write\0", "\0", "\0", "\0", 0), + ("matrix\0", "\0", "\0", "\0", 0), + ("mpsc_sem\0", "\0", "\0", "\0", 0), + ("peterson\0", "\0", "\0", "\0", 0), + ("phil_din_mutex\0", "\0", "\0", "\0", 0), + ("pipe_large_test\0", "\0", "\0", "\0", 0), + ("pipetest\0", "\0", "\0", "\0", 0), + ("adder_peterson_spin\0", "\0", "\0", "\0", 0), + ("adder_peterson_yield\0", "\0", "\0", "\0", 0), + ("adder_mutex_blocking\0", "\0", "\0", "\0", 0), + ("adder_mutex_spin\0", "\0", "\0", "\0", 0), + ("run_pipe_test\0", "\0", "\0", "\0", 0), + ("sleep_simple\0", "\0", "\0", "\0", 0), + ("sleep\0", "\0", "\0", "\0", 0), + ("sleep_simple\0", "\0", "\0", "\0", 0), + ("sync_sem\0", "\0", "\0", "\0", 0), + ("condsync_sem\0", "\0", "\0", "\0", 0), + ("condsync_condvar\0", "\0", "\0", "\0", 0), + ("threads_arg\0", "\0", "\0", "\0", 0), + ("threads\0", "\0", "\0", "\0", 0), + ("yield\0", "\0", "\0", "\0", 0), + ("barrier_fail\0", "\0", "\0", "\0", 0), + ("barrier_condvar\0", "\0", "\0", "\0", 0), +]; + +static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[ + ("stack_overflow\0", "\0", "\0", "\0", -11), + ("race_adder_loop\0", "\0", "\0", "\0", -6), + ("priv_csr\0", "\0", "\0", "\0", -4), + ("priv_inst\0", "\0", "\0", "\0", -4), + ("store_fault\0", "\0", "\0", "\0", -11), + ("until_timeout\0", "\0", "\0", "\0", -6), + ("adder\0", "\0", "\0", "\0", -6), + ("adder_simple_spin\0", "\0", "\0", "\0", -6), + ("adder_simple_yield\0", "\0", "\0", "\0", -6), +]; + +use user_lib::{exec, fork, waitpid}; + +fn run_tests(tests: &[(&str, &str, &str, &str, i32)]) -> i32 { + let mut pass_num = 0; + let mut arr: [*const u8; 4] = [ + core::ptr::null::(), + core::ptr::null::(), + core::ptr::null::(), + core::ptr::null::(), + ]; + + for test in tests { + println!("Usertests: Running {}", test.0); + arr[0] = test.0.as_ptr(); + if test.1 != "\0" { + arr[1] = test.1.as_ptr(); + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + if test.2 != "\0" { + arr[2] = test.2.as_ptr(); + arr[3] = core::ptr::null::(); + if test.3 != "\0" { + arr[3] = test.3.as_ptr(); + } else { + arr[3] = core::ptr::null::(); + } + } else { + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + } + } else { + arr[1] = core::ptr::null::(); + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + } + + let pid = fork(); + if pid == 0 { + exec(test.0, &arr[..]); + panic!("unreachable!"); + } else { + let mut exit_code: i32 = Default::default(); + let wait_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, wait_pid); + if exit_code == test.4 { + // summary apps with exit_code + pass_num = pass_num + 1; + } + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test.0, pid, exit_code + ); + } + } + pass_num +} + +#[no_mangle] +pub fn main() -> i32 { + let succ_num = run_tests(SUCC_TESTS); + let err_num = run_tests(FAIL_TESTS); + if succ_num == SUCC_TESTS.len() as i32 && err_num == FAIL_TESTS.len() as i32 { + println!( + "{} of sueecssed apps, {} of failed apps run correctly. \nUsertests passed!", + SUCC_TESTS.len(), + FAIL_TESTS.len() + ); + return 0; + } + if succ_num != SUCC_TESTS.len() as i32 { + println!( + "all successed app_num is {} , but only passed {}", + SUCC_TESTS.len(), + succ_num + ); + } + if err_num != FAIL_TESTS.len() as i32 { + println!( + "all failed app_num is {} , but only passed {}", + FAIL_TESTS.len(), + err_num + ); + } + println!(" Usertests failed!"); + return -1; +} diff --git a/user/src/bin/yield.rs b/user/src/bin/yield.rs new file mode 100644 index 0000000..78b1468 --- /dev/null +++ b/user/src/bin/yield.rs @@ -0,0 +1,17 @@ +#![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 +} diff --git a/user/src/bin/yield0.rs b/user/src/bin/yield0.rs deleted file mode 100644 index 1e09edd..0000000 --- a/user/src/bin/yield0.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; - -use user_lib::yield_; - -const WIDTH: usize = 10; -const HEIGHT: usize = 5; - -/* -理想结果:三个程序交替输出 ABC -*/ - -#[no_mangle] -fn main() -> i32 { - for i in 0..HEIGHT { - let buf = ['A' as u8; WIDTH]; - println!( - "{} [{}/{}]", - core::str::from_utf8(&buf).unwrap(), - i + 1, - HEIGHT - ); - yield_(); - } - println!("Test write A OK!"); - 0 -} diff --git a/user/src/bin/yield1.rs b/user/src/bin/yield1.rs deleted file mode 100644 index 3d91adf..0000000 --- a/user/src/bin/yield1.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; - -use user_lib::yield_; - -const WIDTH: usize = 10; -const HEIGHT: usize = 5; - -/* -理想结果:三个程序交替输出 ABC -*/ - -#[no_mangle] -#[no_mangle] -fn main() -> i32 { - for i in 0..HEIGHT { - let buf = ['B' as u8; WIDTH]; - println!( - "{} [{}/{}]", - core::str::from_utf8(&buf).unwrap(), - i + 1, - HEIGHT - ); - yield_(); - } - println!("Test write B OK!"); - 0 -} diff --git a/user/src/bin/yield2.rs b/user/src/bin/yield2.rs deleted file mode 100644 index 05ea13a..0000000 --- a/user/src/bin/yield2.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; - -use user_lib::yield_; - -/* -理想结果:三个程序交替输出 ABC -*/ - -const WIDTH: usize = 10; -const HEIGHT: usize = 5; - -#[no_mangle] -#[no_mangle] -fn main() -> i32 { - for i in 0..HEIGHT { - let buf = ['C' as u8; WIDTH]; - println!( - "{} [{}/{}]", - core::str::from_utf8(&buf).unwrap(), - i + 1, - HEIGHT - ); - yield_(); - } - println!("Test write C OK!"); - 0 -} diff --git a/user/src/console.rs b/user/src/console.rs index 6d6d1f3..810ebba 100644 --- a/user/src/console.rs +++ b/user/src/console.rs @@ -1,52 +1,21 @@ -use alloc::collections::vec_deque::VecDeque; -use alloc::sync::Arc; use core::fmt::{self, Write}; -use spin::mutex::Mutex; -pub const STDIN: usize = 0; -pub const STDOUT: usize = 1; - -const CONSOLE_BUFFER_SIZE: usize = 256 * 10; +const STDIN: usize = 0; +const STDOUT: usize = 1; use super::{read, write}; -use lazy_static::*; -struct ConsoleBuffer(VecDeque); +struct Stdout; -lazy_static! { - static ref CONSOLE_BUFFER: Arc> = { - let buffer = VecDeque::::with_capacity(CONSOLE_BUFFER_SIZE); - Arc::new(Mutex::new(ConsoleBuffer(buffer))) - }; -} - -impl ConsoleBuffer { - fn flush(&mut self) -> isize { - let s: &[u8] = self.0.make_contiguous(); - let ret = write(STDOUT, s); - self.0.clear(); - ret - } -} - -impl Write for ConsoleBuffer { +impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.as_bytes().iter() { - self.0.push_back(*c); - if (*c == b'\n' || self.0.len() == CONSOLE_BUFFER_SIZE) && -1 == self.flush() { - return Err(fmt::Error); - } - } + write(STDOUT, s.as_bytes()); Ok(()) } } -#[allow(unused)] pub fn print(args: fmt::Arguments) { - let mut buf = CONSOLE_BUFFER.lock(); - // buf.write_fmt(args).unwrap(); - // BUG FIX: 关闭 stdout 后,本函数不能触发 panic,否则会造成死锁 - buf.write_fmt(args); + Stdout.write_fmt(args).unwrap(); } #[macro_export] @@ -68,8 +37,3 @@ pub fn getchar() -> u8 { read(STDIN, &mut c); c[0] } - -pub fn flush() { - let mut buf = CONSOLE_BUFFER.lock(); - buf.flush(); -} diff --git a/user/src/file.rs b/user/src/file.rs new file mode 100644 index 0000000..19e2564 --- /dev/null +++ b/user/src/file.rs @@ -0,0 +1,30 @@ +use super::*; + +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +pub fn dup(fd: usize) -> isize { + sys_dup(fd) +} +pub fn open(path: &str, flags: OpenFlags) -> isize { + sys_open(path, flags.bits) +} +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) +} diff --git a/user/src/io.rs b/user/src/io.rs new file mode 100644 index 0000000..d2c2a0a --- /dev/null +++ b/user/src/io.rs @@ -0,0 +1,117 @@ +use super::*; +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{RgbColor, Size}; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use virtio_input_decoder::Decoder; +pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse}; + +pub const VIRTGPU_XRES: u32 = 1280; +pub const VIRTGPU_YRES: u32 = 800; +pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize; + +pub fn framebuffer() -> isize { + sys_framebuffer() +} +pub fn framebuffer_flush() -> isize { + sys_framebuffer_flush() +} + +pub struct Display { + pub size: Size, + pub fb: &'static mut [u8], +} + +impl Display { + pub fn new(size: Size) -> Self { + let fb_ptr = framebuffer() as *mut u8; + let fb = unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) }; + Self { size, fb } + } + pub fn framebuffer(&mut self) -> &mut [u8] { + self.fb + } + pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) { + p(self.framebuffer()); + } + pub fn flush(&self) { + framebuffer_flush(); + } +} + +impl OriginDimensions for Display { + fn size(&self) -> Size { + self.size + } +} + +impl DrawTarget for Display { + type Color = Rgb888; + + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels.into_iter().for_each(|px| { + let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x) as usize * 4; + if idx + 2 >= self.fb.len() { + return; + } + self.fb[idx] = px.1.b(); + self.fb[idx + 1] = px.1.g(); + self.fb[idx + 2] = px.1.r(); + }); + Ok(()) + } +} + +pub fn event_get() -> Option { + let raw_value = sys_event_get(); + if raw_value == 0 { + None + } else { + Some((raw_value as u64).into()) + } +} + +pub fn key_pressed() -> bool { + if sys_key_pressed() == 1 { + true + } else { + false + } +} + +#[repr(C)] +pub struct InputEvent { + pub event_type: u16, + pub code: u16, + pub value: u32, +} + +impl From for InputEvent { + fn from(mut v: u64) -> Self { + let value = v as u32; + v >>= 32; + let code = v as u16; + v >>= 16; + let event_type = v as u16; + Self { + event_type, + code, + value, + } + } +} + +impl InputEvent { + pub fn decode(&self) -> Option { + Decoder::decode( + self.event_type as usize, + self.code as usize, + self.value as usize, + ) + .ok() + } +} diff --git a/user/src/lang_items.rs b/user/src/lang_items.rs index 2d81a1d..df0467c 100644 --- a/user/src/lang_items.rs +++ b/user/src/lang_items.rs @@ -1,4 +1,4 @@ -use crate::exit; +use super::{getpid, kill, SignalFlags}; #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { @@ -13,5 +13,6 @@ fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { } else { println!("Panicked: {}", err); } - exit(-1); + kill(getpid() as usize, SignalFlags::SIGABRT.bits()); + unreachable!() } diff --git a/user/src/lib.rs b/user/src/lib.rs index d97fbf3..8c709fc 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -1,24 +1,32 @@ #![no_std] -#![feature(asm)] #![feature(linkage)] #![feature(panic_info_message)] #![feature(alloc_error_handler)] #[macro_use] pub mod console; +mod file; +mod io; mod lang_items; +mod net; +mod sync; mod syscall; +mod task; extern crate alloc; -extern crate core; #[macro_use] extern crate bitflags; +use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; -pub use console::{flush, STDIN, STDOUT}; -pub use syscall::*; +pub use file::*; +pub use io::*; +pub use net::*; +pub use sync::*; +use syscall::*; +pub use task::*; -const USER_HEAP_SIZE: usize = 16384; +const USER_HEAP_SIZE: usize = 32768; static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; @@ -32,208 +40,53 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { #[no_mangle] #[link_section = ".text.entry"] -pub extern "C" fn _start() -> ! { +pub extern "C" fn _start(argc: usize, argv: usize) -> ! { unsafe { HEAP.lock() .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); } - exit(main()); + let mut v: Vec<&'static str> = Vec::new(); + for i in 0..argc { + let str_start = + unsafe { ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() }; + let len = (0usize..) + .find(|i| unsafe { ((str_start + *i) as *const u8).read_volatile() == 0 }) + .unwrap(); + v.push( + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(str_start as *const u8, len) + }) + .unwrap(), + ); + } + exit(main(argc, v.as_slice())); } #[linkage = "weak"] #[no_mangle] -fn main() -> i32 { +fn main(_argc: usize, _argv: &[&str]) -> i32 { panic!("Cannot find main!"); } -bitflags! { - pub struct OpenFlags: u32 { - const RDONLY = 0; - const WRONLY = 1 << 0; - const RDWR = 1 << 1; - const CREATE = 1 << 9; - const TRUNC = 1 << 10; - } +#[macro_export] +macro_rules! vstore { + ($var: expr, $value: expr) => { + // unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } + unsafe { core::ptr::write_volatile(core::ptr::addr_of_mut!($var), $value); } + }; } -#[repr(C)] -#[derive(Debug, Default)] -pub struct TimeVal { - pub sec: usize, - pub usec: usize, +#[macro_export] +macro_rules! vload { + ($var: expr) => { + // unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } + unsafe { core::ptr::read_volatile(core::ptr::addr_of!($var)) } + }; } -impl TimeVal { - pub fn new() -> Self { - Self::default() - } -} - -#[repr(C)] -#[derive(Debug)] -pub struct Stat { - /// ID of device containing file - pub dev: u64, - /// inode number - pub ino: u64, - /// file type and mode - pub mode: StatMode, - /// number of hard links - pub nlink: u32, - /// unused pad - pad: [u64; 7], -} - -impl Stat { - pub fn new() -> Self { - Stat { - dev: 0, - ino: 0, - mode: StatMode::NULL, - nlink: 0, - pad: [0; 7], - } - } -} - -impl Default for Stat { - fn default() -> Self { - Self::new() - } -} - -bitflags! { - pub struct StatMode: u32 { - const NULL = 0; - /// directory - const DIR = 0o040000; - /// ordinary regular file - const FILE = 0o100000; - } -} - -const AT_FDCWD: isize = -100; - -pub fn open(path: &str, flags: OpenFlags) -> isize { - sys_openat(AT_FDCWD as usize, path, flags.bits, OpenFlags::RDWR.bits) -} - -pub fn close(fd: usize) -> isize { - if fd == STDOUT { - console::flush(); - } - sys_close(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 link(old_path: &str, new_path: &str) -> isize { - sys_linkat(AT_FDCWD as usize, old_path, AT_FDCWD as usize, new_path, 0) -} - -pub fn unlink(path: &str) -> isize { - sys_unlinkat(AT_FDCWD as usize, path, 0) -} - -pub fn fstat(fd: usize, st: &Stat) -> isize { - sys_fstat(fd, st) -} - -pub fn mail_read(buf: &mut [u8]) -> isize { - sys_mail_read(buf) -} - -pub fn mail_write(pid: usize, buf: &[u8]) -> isize { - sys_mail_write(pid, buf) -} - -pub fn exit(exit_code: i32) -> ! { - console::flush(); - sys_exit(exit_code); -} - -pub fn yield_() -> isize { - sys_yield() -} - -pub fn get_time() -> isize { - let time = TimeVal::new(); - match sys_get_time(&time, 0) { - 0 => ((time.sec & 0xffff) * 1000 + time.usec / 1000) as isize, - _ => -1, - } -} - -pub fn getpid() -> isize { - sys_getpid() -} - -pub fn fork() -> isize { - sys_fork() -} - -pub fn exec(path: &str) -> isize { - sys_exec(path) -} - -pub fn set_priority(prio: isize) -> isize { - sys_set_priority(prio) -} - -pub fn wait(exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(-1, exit_code as *mut _) { - -2 => { - sys_yield(); - } - n => { - return n; - } - } - } -} - -pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(pid as isize, exit_code as *mut _) { - -2 => { - sys_yield(); - } - n => { - return n; - } - } - } -} - -pub fn sleep(period_ms: usize) { - let start = get_time(); - while get_time() < start + period_ms as isize { - sys_yield(); - } -} - -pub fn mmap(start: usize, len: usize, prot: usize) -> isize { - sys_mmap(start, len, prot) -} - -pub fn munmap(start: usize, len: usize) -> isize { - sys_munmap(start, len) -} - -pub fn spawn(path: &str) -> isize { - sys_spawn(path) -} - -pub fn dup(fd: usize) -> isize { - sys_dup(fd) -} -pub fn pipe(pipe_fd: &mut [usize]) -> isize { - sys_pipe(pipe_fd) +#[macro_export] +macro_rules! memory_fence { + () => { + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst) + }; } diff --git a/user/src/linker.ld b/user/src/linker.ld index 75438fb..02f7b6b 100644 --- a/user/src/linker.ld +++ b/user/src/linker.ld @@ -2,7 +2,7 @@ OUTPUT_ARCH(riscv) ENTRY(_start) -BASE_ADDRESS = 0x80400000; +BASE_ADDRESS = 0x10000; SECTIONS { @@ -11,22 +11,22 @@ SECTIONS *(.text.entry) *(.text .text.*) } + . = ALIGN(4K); .rodata : { *(.rodata .rodata.*) *(.srodata .srodata.*) } + . = ALIGN(4K); .data : { *(.data .data.*) *(.sdata .sdata.*) } .bss : { - start_bss = .; *(.bss .bss.*) *(.sbss .sbss.*) - end_bss = .; } /DISCARD/ : { *(.eh_frame) *(.debug*) } -} \ No newline at end of file +} diff --git a/user/src/net.rs b/user/src/net.rs new file mode 100644 index 0000000..78529d0 --- /dev/null +++ b/user/src/net.rs @@ -0,0 +1,13 @@ +use super::*; + +pub fn connect(ip: u32, sport: u16, dport: u16) -> isize { + sys_connect(ip, sport, dport) +} + +pub fn listen(sport: u16) -> isize { + sys_listen(sport) +} + +pub fn accept(socket_fd: usize) -> isize { + sys_accept(socket_fd) +} diff --git a/user/src/sync.rs b/user/src/sync.rs new file mode 100644 index 0000000..82cd2a0 --- /dev/null +++ b/user/src/sync.rs @@ -0,0 +1,32 @@ +use super::*; + +pub fn mutex_create() -> isize { + sys_mutex_create(false) +} +pub fn mutex_blocking_create() -> isize { + sys_mutex_create(true) +} +pub fn mutex_lock(mutex_id: usize) { + sys_mutex_lock(mutex_id); +} +pub fn mutex_unlock(mutex_id: usize) { + sys_mutex_unlock(mutex_id); +} +pub fn semaphore_create(res_count: usize) -> isize { + sys_semaphore_create(res_count) +} +pub fn semaphore_up(sem_id: usize) { + sys_semaphore_up(sem_id); +} +pub fn semaphore_down(sem_id: usize) { + sys_semaphore_down(sem_id); +} +pub fn condvar_create() -> isize { + sys_condvar_create() +} +pub fn condvar_signal(condvar_id: usize) { + sys_condvar_signal(condvar_id); +} +pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { + sys_condvar_wait(condvar_id, mutex_id); +} diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 7aaa584..48b3e99 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,32 +1,42 @@ -use super::{Stat, TimeVal}; +const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; +const SYSCALL_LISTEN: usize = 30; +const SYSCALL_ACCEPT: usize = 31; +const SYSCALL_OPEN: usize = 56; +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_SLEEP: usize = 101; +const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; +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; +const SYSCALL_THREAD_CREATE: usize = 1000; +const SYSCALL_GETTID: usize = 1001; +const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; +const SYSCALL_SEMAPHORE_CREATE: usize = 1020; +const SYSCALL_SEMAPHORE_UP: usize = 1021; +const SYSCALL_SEMAPHORE_DOWN: usize = 1022; +const SYSCALL_CONDVAR_CREATE: usize = 1030; +const SYSCALL_CONDVAR_SIGNAL: usize = 1031; +const SYSCALL_CONDVAR_WAIT: usize = 1032; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; -pub const SYSCALL_OPENAT: usize = 56; -pub const SYSCALL_CLOSE: usize = 57; -pub const SYSCALL_READ: usize = 63; -pub const SYSCALL_WRITE: usize = 64; -pub const SYSCALL_UNLINKAT: usize = 35; -pub const SYSCALL_LINKAT: usize = 37; -pub const SYSCALL_FSTAT: usize = 80; -pub const SYSCALL_EXIT: usize = 93; -pub const SYSCALL_YIELD: usize = 124; -pub const SYSCALL_GETTIMEOFDAY: usize = 169; -pub const SYSCALL_GETPID: usize = 172; -pub const SYSCALL_FORK: usize = 220; -pub const SYSCALL_EXEC: usize = 221; -pub const SYSCALL_WAITPID: usize = 260; -pub const SYSCALL_SET_PRIORITY: usize = 140; -pub const SYSCALL_MUNMAP: usize = 215; -pub const SYSCALL_MMAP: usize = 222; -pub const SYSCALL_SPAWN: usize = 400; -pub const SYSCALL_MAIL_READ: usize = 401; -pub const SYSCALL_MAIL_WRITE: usize = 402; -pub const SYSCALL_DUP: usize = 24; -pub const SYSCALL_PIPE: usize = 59; - -pub fn syscall(id: usize, args: [usize; 3]) -> isize { +fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; unsafe { - asm!( + core::arch::asm!( "ecall", inlateout("x10") args[0] => ret, in("x11") args[1], @@ -37,41 +47,38 @@ pub fn syscall(id: usize, args: [usize; 3]) -> isize { ret } -pub fn syscall6(id: usize, args: [usize; 6]) -> isize { - let mut ret: isize; - unsafe { - asm!( - "ecall", - inlateout("x10") args[0] => ret, - in("x11") args[1], - in("x12") args[2], - in("x13") args[3], - in("x14") args[4], - in("x15") args[5], - in("x17") id - ); - } - ret +pub fn sys_dup(fd: usize) -> isize { + syscall(SYSCALL_DUP, [fd, 0, 0]) } -pub fn sys_openat(dirfd: usize, path: &str, flags: u32, mode: u32) -> isize { - syscall6( - SYSCALL_OPENAT, - [ - dirfd, - path.as_ptr() as usize, - flags as usize, - mode as usize, - 0, - 0, - ], +pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize { + syscall( + SYSCALL_CONNECT, + [dest as usize, sport as usize, dport as usize], ) } +// just listen for tcp connections now +pub fn sys_listen(sport: u16) -> isize { + syscall(SYSCALL_LISTEN, [sport as usize, 0, 0]) +} + +pub fn sys_accept(socket_fd: usize) -> isize { + syscall(SYSCALL_ACCEPT, [socket_fd, 0, 0]) +} + +pub fn sys_open(path: &str, flags: u32) -> isize { + syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) +} + 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, @@ -83,59 +90,25 @@ pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) } -pub fn sys_linkat( - old_dirfd: usize, - old_path: &str, - new_dirfd: usize, - new_path: &str, - flags: usize, -) -> isize { - syscall6( - SYSCALL_LINKAT, - [ - old_dirfd, - old_path.as_ptr() as usize, - new_dirfd, - new_path.as_ptr() as usize, - flags, - 0, - ], - ) -} - -pub fn sys_unlinkat(dirfd: usize, path: &str, flags: usize) -> isize { - syscall(SYSCALL_UNLINKAT, [dirfd, path.as_ptr() as usize, flags]) -} - -pub fn sys_fstat(fd: usize, st: &Stat) -> isize { - syscall(SYSCALL_FSTAT, [fd, st as *const _ as usize, 0]) -} - -pub fn sys_mail_read(buffer: &mut [u8]) -> isize { - syscall( - SYSCALL_MAIL_READ, - [buffer.as_ptr() as usize, buffer.len(), 0], - ) -} - -pub fn sys_mail_write(pid: usize, buffer: &[u8]) -> isize { - syscall( - SYSCALL_MAIL_WRITE, - [pid, 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_sleep(sleep_ms: usize) -> isize { + syscall(SYSCALL_SLEEP, [sleep_ms, 0, 0]) +} + pub fn sys_yield() -> isize { syscall(SYSCALL_YIELD, [0, 0, 0]) } -pub fn sys_get_time(time: &TimeVal, tz: usize) -> isize { - syscall(SYSCALL_GETTIMEOFDAY, [time as *const _ as usize, tz, 0]) +pub fn sys_kill(pid: usize, signal: i32) -> isize { + syscall(SYSCALL_KILL, [pid, signal as usize, 0]) +} + +pub fn sys_get_time() -> isize { + syscall(SYSCALL_GET_TIME, [0, 0, 0]) } pub fn sys_getpid() -> isize { @@ -146,34 +119,77 @@ 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_exec(path: &str, args: &[*const u8]) -> isize { + syscall( + SYSCALL_EXEC, + [path.as_ptr() as usize, args.as_ptr() as usize, 0], + ) } -pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize { - syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0]) +pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize { + syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0]) } -pub fn sys_set_priority(prio: isize) -> isize { - syscall(SYSCALL_SET_PRIORITY, [prio as usize, 0, 0]) +pub fn sys_thread_create(entry: usize, arg: usize) -> isize { + syscall(SYSCALL_THREAD_CREATE, [entry, arg, 0]) } -pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { - syscall(SYSCALL_MMAP, [start, len, prot]) +pub fn sys_gettid() -> isize { + syscall(SYSCALL_GETTID, [0; 3]) } -pub fn sys_munmap(start: usize, len: usize) -> isize { - syscall(SYSCALL_MUNMAP, [start, len, 0]) +pub fn sys_waittid(tid: usize) -> isize { + syscall(SYSCALL_WAITTID, [tid, 0, 0]) } -pub fn sys_spawn(path: &str) -> isize { - syscall(SYSCALL_SPAWN, [path.as_ptr() as usize, 0, 0]) +pub fn sys_mutex_create(blocking: bool) -> isize { + syscall(SYSCALL_MUTEX_CREATE, [blocking as usize, 0, 0]) } -pub fn sys_dup(fd: usize) -> isize { - syscall(SYSCALL_DUP, [fd, 0, 0]) +pub fn sys_mutex_lock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_LOCK, [id, 0, 0]) } -pub fn sys_pipe(pipe: &mut [usize]) -> isize { - syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0]) +pub fn sys_mutex_unlock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_UNLOCK, [id, 0, 0]) +} + +pub fn sys_semaphore_create(res_count: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_CREATE, [res_count, 0, 0]) +} + +pub fn sys_semaphore_up(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_UP, [sem_id, 0, 0]) +} + +pub fn sys_semaphore_down(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0]) +} + +pub fn sys_condvar_create() -> isize { + syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0]) +} + +pub fn sys_condvar_signal(condvar_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_SIGNAL, [condvar_id, 0, 0]) +} + +pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0]) +} + +pub fn sys_framebuffer() -> isize { + syscall(SYSCALL_FRAMEBUFFER, [0, 0, 0]) +} + +pub fn sys_framebuffer_flush() -> isize { + syscall(SYSCALL_FRAMEBUFFER_FLUSH, [0, 0, 0]) +} + +pub fn sys_event_get() -> isize { + syscall(SYSCALL_EVENT_GET, [0, 0, 0]) +} + +pub fn sys_key_pressed() -> isize { + syscall(SYSCALL_KEY_PRESSED, [0, 0, 0]) } diff --git a/user/src/task.rs b/user/src/task.rs new file mode 100644 index 0000000..9e72b32 --- /dev/null +++ b/user/src/task.rs @@ -0,0 +1,83 @@ +use super::*; + +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, args: &[*const u8]) -> isize { + sys_exec(path, args) +} + +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 waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { + sys_waitpid(pid as isize, exit_code as *mut _) +} + +bitflags! { + pub struct SignalFlags: i32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +pub fn kill(pid: usize, signal: i32) -> isize { + sys_kill(pid, signal) +} + +pub fn sleep(sleep_ms: usize) { + sys_sleep(sleep_ms); +} + +pub fn thread_create(entry: usize, arg: usize) -> isize { + sys_thread_create(entry, arg) +} +pub fn gettid() -> isize { + sys_gettid() +} +pub fn waittid(tid: usize) -> isize { + loop { + match sys_waittid(tid) { + -2 => { + yield_(); + } + exit_code => return exit_code, + } + } +}