...
 
Commits (23)
    https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/3a3eba4f6e1a02bcd96d49e53bf129d7790b9078 update README 2022-06-23T22:41:20+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/67915087a276bfb2733404531d848a7504f51d7f support rust-analyzer for board_qemu features 2022-07-14T09:37:06+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/96b02e3b72952484aaacd238281cf05fd4bfc6e4 update .gitignore 2022-07-14T09:44:20+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/1b72573dcdeb7238102b0bd20c7ee748c5495da2 update Dockerfile: ubuntu 18.04-->20.04, QEMU-5.0-->7.0 2022-07-18T11:24:38+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/022b5bab6ba0c7b3dafc90340f3d8e14c33565c8 udpate rust-toolchain: nightly-2022-07-20, cargo-utils 0.36 2022-07-25T11:48:01+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/453a08fc0c5044002afd6617f867c88e12570b9c Merged PR #87 2022-09-02T02:54:55-07:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/648907524e41136775ec01f7e39e6c76d68ee792 Lock virtio-drivers version at 4ee80e5 2022-09-05T02:33:37-07:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/00d1d1bf3ab38f4e3defc1dc5ba1606703f2f6ca Update Docker 2022-10-01T22:20:43+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/dd9ef8c9c50ff84afb239e45c7c2ee22dcea6fb5 Bump rustsbi-qemu to 701e891 2022-10-09T05:41:25+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/2d92cd428cfc2b412dd9138e539b0a8cc131324a build: update toolchain 2022-10-20T11:47:37+08:00 YdrMaster ydrml@hotmail.com Signed-off-by: <span data-trailer="Signed-off-by:"><a href="mailto:ydrml@hotmail.com" title="ydrml@hotmail.com"></a><a href="javascript:void(0)" class="avatar s16 avatar-inline identicon bg1" style="text-decoration: none">N</a><a href="mailto:ydrml@hotmail.com" title="ydrml@hotmail.com">YdrMaster</a> &lt;<a href="mailto:ydrml@hotmail.com" title="ydrml@hotmail.com">ydrml@hotmail.com</a>&gt;</span> https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/414dbb3a09aca326e388cd0d5e8ad04ab099ca54 in entry.asm: boot_stack->boot_stack_lower_bound 2022-11-29T10:33:55+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/3042f7461187d322bc62697d2adc0b1a735f59cb Update stack_overflow 2022-11-29T11:14:51+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/d7e5d2f63021b1370f8a69f7df2e4608c0714717 Remove K210 support. 2022-12-13T23:52:27+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/cc3ed1aa2b18d3c967f0b1862fa1f99e56fcdba7 Modify run_pipe_test. 2022-12-14T00:20:45+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/2ca2132df28b84038d62714ae5fbdc4481d1f7c2 Workflow: Remove k210 support. 2022-12-14T00:41:59+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/20d4f5fe080d2b3b9ef3bcce17a44042c396d3ef feat: simple drawing board GUI 2022-12-19T03:55:58-08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/fd0db55621e9e39f7b7a79eb3cc01c3f7803a310 Remove a dependency 2022-12-19T04:48:45-08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/3d69c6053e06d14ec041b01cf495e44eed3fff4e Add coroutine examples 2022-12-21T11:11:50+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/bcfdcf33e3574e9a916f27e8980e37c5a743845e user add fp support 2022-12-21T21:27:17+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/95d853d8d2204c6c6d794fb65eae27b735c101d8 Fix pipe impl 2022-12-23T11:23:54+08:00 Yifan Wu shinbokuow@163.com https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/e0e201a131c1a6418edecec6768de04b52f42c29 fix rust-analyzer warning 2022-12-31T10:36:35+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/683a4fdc55bf104a9600856c8b878f1b322d52ca Merge branch 'ch9' into ch9-dev 2023-01-02T10:16:41+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn https://gitcode.net/rcore-os/rCore-Tutorial-v3/-/commit/1722d0d4d708c94a3f6d6674846bb48fdf063835 run in qemu -bios none ENV 2023-01-02T11:28:06+08:00 Yu Chen yuchen@mail.tsinghua.edu.cn
......@@ -4,6 +4,7 @@ on: [push]
env:
CARGO_TERM_COLOR: always
rust_toolchain: nightly-2022-08-05
jobs:
build-doc:
......@@ -13,11 +14,11 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-04-11
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 --features "board_qemu"
run: cd os && cargo doc --no-deps --verbose
- name: Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v3
with:
......@@ -32,7 +33,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-04-11
toolchain: ${{ env.rust_toolchain }}
components: rust-src, llvm-tools-preview
target: riscv64gc-unknown-none-elf
- uses: actions-rs/install@v0.1
......@@ -65,5 +66,3 @@ jobs:
run: cd os && make run TEST=1
timeout-minutes: 10
- name: Build for k210
run: cd os && make build BOARD=k210
\ No newline at end of file
......@@ -9,5 +9,7 @@ os/src/link_app.S
os/src/linker.ld
os/last-*
os/.gdb_history
os/virt.out
tools/
pushall.sh
.vscode/*.log
......@@ -6,5 +6,8 @@
"rust.all_targets": false,
// For Rust Analyzer plugin users:
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
"rust-analyzer.checkOnSave.allTargets": false
}
"rust-analyzer.checkOnSave.allTargets": false,
// "rust-analyzer.cargo.features": [
// "board_qemu"
// ]
}
\ No newline at end of file
FROM ubuntu:18.04
LABEL maintainer="dinghao188" \
version="1.1" \
description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3"
#install some deps
RUN set -x \
&& apt-get update \
&& apt-get install -y curl wget autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
gawk build-essential bison flex texinfo gperf libtool patchutils bc xz-utils \
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3
#install rust and qemu
RUN set -x; \
RUSTUP='/root/rustup.sh' \
&& cd $HOME \
#install rust
&& curl https://sh.rustup.rs -sSf > $RUSTUP && chmod +x $RUSTUP \
&& $RUSTUP -y --default-toolchain nightly --profile minimal \
#compile qemu
&& wget https://ftp.osuosl.org/pub/blfs/conglomeration/qemu/qemu-5.0.0.tar.xz \
&& tar xvJf qemu-5.0.0.tar.xz \
&& cd qemu-5.0.0 \
&& ./configure --target-list=riscv64-softmmu,riscv64-linux-user \
&& make -j$(nproc) install \
&& cd $HOME && rm -rf qemu-5.0.0 qemu-5.0.0.tar.xz
#for chinese network
RUN set -x; \
APT_CONF='/etc/apt/sources.list'; \
CARGO_CONF='/root/.cargo/config'; \
BASHRC='/root/.bashrc' \
&& echo 'export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static' >> $BASHRC \
&& echo 'export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup' >> $BASHRC \
&& touch $CARGO_CONF \
&& echo '[source.crates-io]' > $CARGO_CONF \
&& echo "replace-with = 'ustc'" >> $CARGO_CONF \
&& echo '[source.ustc]' >> $CARGO_CONF \
&& echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_CONF
\ No newline at end of file
# syntax=docker/dockerfile:1
# This Dockerfile is adapted from https://github.com/LearningOS/rCore-Tutorial-v3/blob/main/Dockerfile
# with the following major updates:
# - ubuntu 18.04 -> 20.04
# - qemu 5.0.0 -> 7.0.0
# - Extensive comments linking to relevant documentation
FROM ubuntu:20.04
ARG QEMU_VERSION=7.0.0
ARG HOME=/root
# 0. Install general tools
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y \
curl \
git \
python3 \
wget
# 1. Set up QEMU RISC-V
# - https://learningos.github.io/rust-based-os-comp2022/0setup-devel-env.html#qemu
# - https://www.qemu.org/download/
# - https://wiki.qemu.org/Documentation/Platforms/RISCV
# - https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html
# 1.1. Download source
WORKDIR ${HOME}
RUN wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz && \
tar xvJf qemu-${QEMU_VERSION}.tar.xz
# 1.2. Install dependencies
# - https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html#prerequisites
RUN apt-get install -y \
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 git \
ninja-build pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev
# 1.3. Build and install from source
WORKDIR ${HOME}/qemu-${QEMU_VERSION}
RUN ./configure --target-list=riscv64-softmmu,riscv64-linux-user && \
make -j$(nproc) && \
make install
# 1.4. Clean up
WORKDIR ${HOME}
RUN rm -rf qemu-${QEMU_VERSION} qemu-${QEMU_VERSION}.tar.xz
# 1.5. Sanity checking
RUN qemu-system-riscv64 --version && \
qemu-riscv64 --version
# 2. Set up Rust
# - https://learningos.github.io/rust-based-os-comp2022/0setup-devel-env.html#qemu
# - https://www.rust-lang.org/tools/install
# - https://github.com/rust-lang/docker-rust/blob/master/Dockerfile-debian.template
# 2.1. Install
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH \
RUST_VERSION=nightly
RUN set -eux; \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup-init; \
chmod +x rustup-init; \
./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION; \
rm rustup-init; \
chmod -R a+w $RUSTUP_HOME $CARGO_HOME;
# 2.2. Sanity checking
RUN rustup --version && \
cargo --version && \
rustc --version
# 3. Build env for labs
# See os1/Makefile `env:` for example.
# This avoids having to wait for these steps each time using a new container.
RUN rustup target add riscv64gc-unknown-none-elf && \
cargo install cargo-binutils --vers ~0.2 && \
rustup component add rust-src && \
rustup component add llvm-tools-preview
# Ready to go
WORKDIR ${HOME}
DOCKER_NAME ?= dinghao188/rcore-tutorial
DOCKER_NAME ?= rcore-tutorial-v3
.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 ${DOCKER_NAME} bash
build_docker:
docker build -t ${DOCKER_NAME} .
fmt:
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..
\ No newline at end of file
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..
......@@ -9,4 +9,8 @@ edition = "2018"
[dependencies]
clap = "2.33.3"
easy-fs = { path = "../easy-fs" }
rand = "0.8.0"
\ No newline at end of file
rand = "0.8.0"
# [features]
# board_qemu = []
# board_k210 = []
\ No newline at end of file
......@@ -12,3 +12,7 @@ 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
......@@ -13,18 +13,11 @@ 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" }
k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" }
easy-fs = { path = "../easy-fs" }
virtio-input-decoder = "0.1.4"
embedded-graphics = "0.7.1"
tinybmp = "0.3.1"
[features]
board_qemu = []
board_k210 = []
[profile.release]
debug = true
......@@ -5,14 +5,18 @@ 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
SDCARD := /dev/sdb
APPS := ../user/src/bin/*
# BOARD
BOARD ?= qemu
BOARD := qemu
SBI ?= rustsbi
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
K210_BOOTLOADER_SIZE := 131072
# GUI
GUI ?= off
ifeq ($(GUI), off)
GUI_OPTION := -display none
endif
# Building mode argument
ifeq ($(MODE), release)
......@@ -20,15 +24,7 @@ ifeq ($(MODE), release)
endif
# KERNEL ENTRY
ifeq ($(BOARD), qemu)
KERNEL_ENTRY_PA := 0x80200000
else ifeq ($(BOARD), k210)
KERNEL_ENTRY_PA := 0x80020000
endif
# Run K210
K210-SERIALPORT = /dev/ttyUSB0
K210-BURNER = ../tools/kflash.py
KERNEL_ENTRY_PA := 0x80000000
# Binutils
OBJDUMP := rust-objdump --arch-name=riscv64
......@@ -40,26 +36,14 @@ DISASM ?= -x
# Run usertests or usershell
TEST ?=
build: env switch-check $(KERNEL_BIN) fs-img
switch-check:
ifeq ($(BOARD), qemu)
(which last-qemu) || (rm -f last-k210 && touch last-qemu && make clean)
else ifeq ($(BOARD), k210)
(which last-k210) || (rm -f last-qemu && 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
sdcard: fs-img
@echo "Are you sure write to $(SDCARD) ? [y/N] " && read ans && [ $${ans:-N} = y ]
@sudo dd if=/dev/zero of=$(SDCARD) bs=1048576 count=32
@sudo dd if=$(FS_IMG) of=$(SDCARD)
$(KERNEL_BIN): kernel
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
......@@ -73,7 +57,7 @@ $(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:
......@@ -87,30 +71,28 @@ disasm-vim: kernel
@nvim $(DISASM_TMP)
@rm $(DISASM_TMP)
run: run-inner
run: run-inner-none
gui: build
ifeq ($(BOARD),qemu)
run-inner-none: build
@qemu-system-riscv64 \
-M 128m \
-machine virt \
-bios $(BOOTLOADER) \
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
-bios none \
$(GUI_OPTION) \
-kernel $(KERNEL_ELF) \
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
-device virtio-blk-device,drive=x0 \
-device virtio-gpu-device \
# -device virtio-gpu-device \
-device virtio-keyboard-device \
-device virtio-mouse-device \
-serial stdio
endif
-serial stdio
run-inner: build
ifeq ($(BOARD),qemu)
@qemu-system-riscv64 \
-M 128m \
-machine virt \
-bios $(BOOTLOADER) \
-display none \
$(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 \
......@@ -118,15 +100,10 @@ ifeq ($(BOARD),qemu)
-device virtio-keyboard-device \
-device virtio-mouse-device \
-serial stdio
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
@tmux new-session -d \
......@@ -141,4 +118,4 @@ gdbserver: build
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 switch-check fs-img gdbserver gdbclient
.PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient fdt
qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out \
-bios ../bootloader/rustsbi-qemu.bin \
-device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \
-drive file=../user/target/riscv64gc-unknown-none-elf/release/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 \
-serial stdio
fdtdump virt.out
\ No newline at end of file
qemu-system-riscv64 -M 128m -machine virt \
-bios ../bootloader/rustsbi-qemu.bin \
-display none \
-device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \
-drive file=../user/target/riscv64gc-unknown-none-elf/release/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 \
-serial stdio
qemu-system-riscv64 -M 128m -machine virt \
-bios ../bootloader/rustsbi-qemu.bin \
-device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \
-drive file=../user/target/riscv64gc-unknown-none-elf/release/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 \
-serial stdio
pub const CLOCK_FREQ: usize = 403000000 / 62;
pub const MMIO: &[(usize, usize)] = &[
// we don't need clint in S priv when running
// we only need claim/complete for target0 after initializing
(0x0C00_0000, 0x3000), /* PLIC */
(0x0C20_0000, 0x1000), /* PLIC */
(0x3800_0000, 0x1000), /* UARTHS */
(0x3800_1000, 0x1000), /* GPIOHS */
(0x5020_0000, 0x1000), /* GPIO */
(0x5024_0000, 0x1000), /* SPI_SLAVE */
(0x502B_0000, 0x1000), /* FPIOA */
(0x502D_0000, 0x1000), /* TIMER0 */
(0x502E_0000, 0x1000), /* TIMER1 */
(0x502F_0000, 0x1000), /* TIMER2 */
(0x5044_0000, 0x1000), /* SYSCTL */
(0x5200_0000, 0x1000), /* SPI0 */
(0x5300_0000, 0x1000), /* SPI1 */
(0x5400_0000, 0x1000), /* SPI2 */
];
pub type BlockDeviceImpl = crate::drivers::block::SDCardWrapper;
pub fn device_init() {
unimplemented!();
}
pub fn irq_handler() {
unimplemented!();
}
......@@ -2,7 +2,7 @@ pub const CLOCK_FREQ: usize = 12500000;
pub const MMIO: &[(usize, usize)] = &[
(0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine
(0x2000000, 0x10000),
(0x2000000, 0x10000), // core local interrupter (CLINT)
(0xc000000, 0x210000), // VIRT_PLIC in virt machine
(0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine
];
......@@ -14,6 +14,7 @@ pub const VIRT_PLIC: usize = 0xC00_0000;
pub const VIRT_UART: usize = 0x1000_0000;
pub const VIRTGPU_XRES: u32 = 1280;
#[allow(unused)]
pub const VIRTGPU_YRES: u32 = 800;
use crate::drivers::block::BLOCK_DEVICE;
......@@ -30,7 +31,7 @@ pub fn device_init() {
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] {
for intr_src_id in [5usize, 6, 8, 10] {
plic.enable(hart_id, supervisor, intr_src_id);
plic.set_priority(intr_src_id, 1);
}
......@@ -52,6 +53,57 @@ pub fn irq_handler() {
plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id);
}
// core local interrupter (CLINT), which contains the timer
pub const CLINT: usize = 0x2000000;
pub const fn clint_mtimecmp(hartid: usize) -> usize {
CLINT + 0x4000 + 8 * hartid
}
pub const CLINT_MTIME: usize = CLINT + 0xBFF8; // Cycles since boot.
#[naked]
#[repr(align(16))] // if miss this alignment, a load access fault will occur.
#[no_mangle]
pub unsafe extern "C" fn timervec() -> ! {
// start.rs has set up the memory that mscratch points to:
// scratch[0,8,16] : register save area.
// scratch[24] : address of CLINT's MTIMECMP register.
// scratch[32] : desired interval between interrupts.
// Now, mscrach has a pointer to an additional scratch space.
// to aboid overwriting the contents of the integer registers,
// the prologue of an interrupts handler usually begins by swapping
// an integer register(say a0) with mscratch CSR.
// The interrupt handler stores the integer registers
// used for processing in this scratch space.
// a0 saved in mscrach, a1 ~ a3 saved in scratch space.
//loop {}
asm!(
"csrrw a0, mscratch, a0",
"sd a1, 0(a0)",
"sd a2, 8(a0)",
"sd a3, 16(a0)",
// schedule the next timer interrupt
// by adding interval to mtimecmp.
"ld a1, 24(a0)", // CLINT_MTIMECMP(hartid) contents
"ld a2, 32(a0)", // interval
"ld a3, 0(a1)",
"add a3, a3, a2",
"sd a3, 0(a1)",
// raise a supervisor software interrupt.
"li a1, 2",
"csrw sip, a1",
// restore and return
"ld a3, 16(a0)",
"ld a2, 8(a0)",
"ld a1, 0(a0)",
"csrrw a0, mscratch, a0",
"mret",
options(noreturn)
);
}
//ref:: https://github.com/andre-richter/qemu-exit
use core::arch::asm;
......
use crate::drivers::chardev::CharDevice;
#[cfg(feature = "board_qemu")]
use crate::drivers::chardev::UART;
#[cfg(feature = "board_k210")]
use crate::sbi::console_putchar;
use core::fmt::{self, Write};
struct Stdout;
......@@ -10,10 +7,7 @@ struct Stdout;
impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
#[cfg(feature = "board_qemu")]
UART.write(c as u8);
#[cfg(feature = "board_k210")]
console_putchar(c as usize);
}
Ok(())
}
......
mod sdcard;
mod virtio_blk;
pub use sdcard::SDCardWrapper;
pub use virtio_blk::VirtIOBlock;
use crate::board::BlockDeviceImpl;
......
此差异已折叠。
use super::BlockDevice;
use crate::mm::{
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
StepByOne, VirtAddr,
};
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 alloc::vec::Vec;
use lazy_static::*;
use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader};
#[allow(unused)]
const VIRTIO0: usize = 0x10008000;
pub struct VirtIOBlock {
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static>>,
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static, VirtioHal>>,
condvars: BTreeMap<u16, Condvar>,
}
lazy_static! {
static ref QUEUE_FRAMES: UPIntrFreeCell<Vec<FrameTracker>> =
unsafe { UPIntrFreeCell::new(Vec::new()) };
}
impl BlockDevice for VirtIOBlock {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access();
......@@ -79,7 +69,9 @@ impl BlockDevice for VirtIOBlock {
impl VirtIOBlock {
pub fn new() -> Self {
let virtio_blk = unsafe {
UPIntrFreeCell::new(VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap())
UPIntrFreeCell::new(
VirtIOBlk::<VirtioHal>::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(),
)
};
let mut condvars = BTreeMap::new();
let channels = virtio_blk.exclusive_access().virt_queue_size();
......@@ -93,39 +85,3 @@ impl VirtIOBlock {
}
}
}
#[no_mangle]
pub extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
let mut ppn_base = PhysPageNum(0);
for i in 0..pages {
let frame = frame_alloc().unwrap();
if i == 0 {
ppn_base = frame.ppn;
}
assert_eq!(frame.ppn.0, ppn_base.0 + i);
QUEUE_FRAMES.exclusive_access().push(frame);
}
ppn_base.into()
}
#[no_mangle]
pub extern "C" fn virtio_dma_dealloc(pa: PhysAddr, pages: usize) -> i32 {
let mut ppn_base: PhysPageNum = pa.into();
for _ in 0..pages {
frame_dealloc(ppn_base);
ppn_base.step();
}
0
}
#[no_mangle]
pub extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
VirtAddr(paddr.0)
}
#[no_mangle]
pub extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
PageTable::from_token(kernel_token())
.translate_va(vaddr)
.unwrap()
}
use crate::mm::{
frame_alloc, 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<Vec<FrameTracker>> =
unsafe { UPIntrFreeCell::new(Vec::new()) };
}
pub struct VirtioHal;
impl Hal for VirtioHal {
fn dma_alloc(pages: usize) -> usize {
let mut ppn_base = PhysPageNum(0);
for i in 0..pages {
let frame = frame_alloc().unwrap();
if i == 0 {
ppn_base = frame.ppn;
}
assert_eq!(frame.ppn.0, ppn_base.0 + i);
QUEUE_FRAMES.exclusive_access().push(frame);
}
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
}
}
mod ns16550a;
#[cfg(feature = "board_qemu")]
use crate::board::CharDeviceImpl;
use alloc::sync::Arc;
use lazy_static::*;
......@@ -11,7 +10,7 @@ pub trait CharDevice {
fn write(&self, ch: u8);
fn handle_irq(&self);
}
#[cfg(feature = "board_qemu")]
lazy_static! {
pub static ref UART: Arc<CharDeviceImpl> = Arc::new(CharDeviceImpl::new());
}
use crate::drivers::bus::virtio::VirtioHal;
use crate::sync::UPIntrFreeCell;
use alloc::{sync::Arc, vec::Vec};
use core::any::Any;
......@@ -5,25 +6,26 @@ use embedded_graphics::pixelcolor::Rgb888;
use tinybmp::Bmp;
use virtio_drivers::{VirtIOGpu, VirtIOHeader};
const VIRTIO7: usize = 0x10007000;
pub trait GPUDevice: Send + Sync + Any {
pub trait GpuDevice: Send + Sync + Any {
fn update_cursor(&self);
fn getfreambuffer(&self) -> &mut [u8];
fn get_framebuffer(&self) -> &mut [u8];
fn flush(&self);
}
lazy_static::lazy_static!(
pub static ref GPU_DEVICE: Arc<dyn GPUDevice> = Arc::new(VirtIOGPU::new());
pub static ref GPU_DEVICE: Arc<dyn GpuDevice> = Arc::new(VirtIOGpuWrapper::new());
);
pub struct VirtIOGPU {
gpu: UPIntrFreeCell<VirtIOGpu<'static>>,
pub struct VirtIOGpuWrapper {
gpu: UPIntrFreeCell<VirtIOGpu<'static, VirtioHal>>,
fb: &'static [u8],
}
static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp");
impl VirtIOGPU {
impl VirtIOGpuWrapper {
pub fn new() -> Self {
unsafe {
let mut virtio = VirtIOGpu::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
let mut virtio =
VirtIOGpu::<VirtioHal>::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
let fbuffer = virtio.setup_framebuffer().unwrap();
let len = fbuffer.len();
......@@ -52,11 +54,11 @@ impl VirtIOGPU {
}
}
impl GPUDevice for VirtIOGPU {
impl GpuDevice for VirtIOGpuWrapper {
fn flush(&self) {
self.gpu.exclusive_access().flush().unwrap();
}
fn getfreambuffer(&self) -> &mut [u8] {
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())
......
use crate::drivers::bus::virtio::VirtioHal;
use crate::{
gui::{Button, Component},
gui::{move_rect, reset},
sync::UPIntrFreeCell,
syscall::PAD,
};
use alloc::{string::ToString, sync::Arc};
use alloc::sync::Arc;
use core::any::Any;
use embedded_graphics::{
prelude::{Point, Size},
text::Text,
};
use k210_hal::cache::Uncache;
use virtio_drivers::{VirtIOHeader, VirtIOInput};
use virtio_input_decoder::{Decoder, Key, KeyType};
use super::GPU_DEVICE;
const VIRTIO5: usize = 0x10005000;
const VIRTIO6: usize = 0x10006000;
struct VirtIOINPUT(UPIntrFreeCell<VirtIOInput<'static>>);
struct VirtIOInputWrapper(UPIntrFreeCell<VirtIOInput<'static, VirtioHal>>);
pub trait INPUTDevice: Send + Sync + Any {
pub trait InputDevice: Send + Sync + Any {
fn handle_irq(&self);
}
lazy_static::lazy_static!(
pub static ref KEYBOARD_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO5));
pub static ref MOUSE_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO6));
pub static ref KEYBOARD_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO5));
pub static ref MOUSE_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO6));
);
impl VirtIOINPUT {
impl VirtIOInputWrapper {
pub fn new(addr: usize) -> Self {
Self(unsafe {
UPIntrFreeCell::new(VirtIOInput::new(&mut *(addr as *mut VirtIOHeader)).unwrap())
UPIntrFreeCell::new(
VirtIOInput::<VirtioHal>::new(&mut *(addr as *mut VirtIOHeader)).unwrap(),
)
})
}
}
impl INPUTDevice for VirtIOINPUT {
impl InputDevice for VirtIOInputWrapper {
fn handle_irq(&self) {
let mut input = self.0.exclusive_access();
input.ack_interrupt();
let event = input.pop_pending_event().unwrap();
let dtype = match Decoder::decode(
event.event_type as usize,
event.code as usize,
event.value as usize,
) {
Ok(dtype) => dtype,
Err(_) => return,
};
match dtype {
virtio_input_decoder::DecodeType::Key(key, r#type) => {
println!("{:?} {:?}", key, r#type);
if r#type == KeyType::Press {
let mut inner = PAD.exclusive_access();
let a = inner.as_ref().unwrap();
match key.to_char() {
Ok(mut k) => {
if k == '\r' {
a.repaint(k.to_string() + "\n")
} else {
a.repaint(k.to_string())
while let Some(event) = input.pop_pending_event() {
let dtype = match Decoder::decode(
event.event_type as usize,
event.code as usize,
event.value as usize,
) {
Ok(dtype) => dtype,
Err(_) => break,
};
match dtype {
virtio_input_decoder::DecodeType::Key(key, r#type) => {
if r#type == KeyType::Press {
match key {
Key::C | Key::MouseLeft => {
reset();
}
Key::W => {
move_rect(0, -10);
}
Key::S => {
move_rect(0, 10);
}
Key::A => {
move_rect(-10, 0);
}
Key::D => {
move_rect(10, 0);
}
_ => {}
}
Err(_) => {}
}
}
_ => {}
}
virtio_input_decoder::DecodeType::Mouse(mouse) => println!("{:?}", mouse),
}
}
}
pub mod block;
pub mod bus;
pub mod chardev;
#[cfg(feature = "board_qemu")]
pub mod gpu;
#[cfg(feature = "board_qemu")]
pub mod input;
pub mod plic;
pub use block::BLOCK_DEVICE;
#[cfg(feature = "board_qemu")]
pub use bus::*;
pub use chardev::UART;
#[cfg(feature = "board_qemu")]
pub use gpu::*;
#[cfg(feature = "board_qemu")]
pub use input::*;
......@@ -2,11 +2,11 @@
.globl _start
_start:
la sp, boot_stack_top
call rust_main
call rust_start
.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:
......@@ -114,36 +114,40 @@ impl File for Pipe {
}
fn read(&self, buf: UserBuffer) -> usize {
assert!(self.readable());
let want_to_read = buf.len();
let mut buf_iter = buf.into_iter();
let mut read_size = 0usize;
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 read_size;
return already_read;
}
drop(ring_buffer);
suspend_current_and_run_next();
continue;
}
// read at most loop_read bytes
for _ in 0..loop_read {
if let Some(byte_ref) = buf_iter.next() {
unsafe {
*byte_ref = ring_buffer.read_byte();
}
read_size += 1;
already_read += 1;
if already_read == want_to_read {
return want_to_read;
}
} else {
return read_size;
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 write_size = 0usize;
let mut already_write = 0usize;
loop {
let mut ring_buffer = self.buffer.exclusive_access();
let loop_write = ring_buffer.available_write();
......@@ -156,11 +160,14 @@ impl File for Pipe {
for _ in 0..loop_write {
if let Some(byte_ref) = buf_iter.next() {
ring_buffer.write_byte(unsafe { *byte_ref });
write_size += 1;
already_write += 1;
if already_write == want_to_write {
return want_to_write;
}
} else {
return write_size;
return already_write;
}
}
}
}
}
}
\ No newline at end of file
use super::File;
use crate::drivers::chardev::CharDevice;
#[cfg(feature = "board_qemu")]
use crate::drivers::chardev::UART;
use crate::mm::UserBuffer;
#[cfg(feature = "board_k210")]
use crate::sbi::console_getchar;
#[cfg(feature = "board_k210")]
use crate::task::suspend_current_and_run_next;
pub struct Stdin;
pub struct Stdout;
......@@ -18,7 +13,6 @@ impl File for Stdin {
fn writable(&self) -> bool {
false
}
#[cfg(feature = "board_qemu")]
fn read(&self, mut user_buf: UserBuffer) -> usize {
assert_eq!(user_buf.len(), 1);
//println!("before UART.read() in Stdin::read()");
......@@ -28,27 +22,6 @@ impl File for Stdin {
}
1
}
#[cfg(feature = "board_k210")]
fn read(&self, mut user_buf: UserBuffer) -> usize {
assert_eq!(user_buf.len(), 1);
// busy loop
let mut c: usize;
loop {
c = console_getchar();
if c == 0 {
suspend_current_and_run_next();
continue;
} else {
break;
}
}
let ch = c as u8;
unsafe {
user_buf.buffers[0].as_mut_ptr().write_volatile(ch);
}
1
}
fn write(&self, _user_buf: UserBuffer) -> usize {
panic!("Cannot write to stdin!");
}
......
use alloc::{string::String, sync::Arc};
use embedded_graphics::{
mono_font::{
ascii::{FONT_10X20, FONT_6X10},
MonoTextStyle,
},
pixelcolor::Rgb888,
prelude::{Dimensions, Point, Primitive, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle},
text::{Alignment, Text},
Drawable,
};
use crate::{drivers::GPU_DEVICE, sync::UPIntrFreeCell};
use super::{Component, Graphics};
pub struct Button {
inner: UPIntrFreeCell<ButtonInner>,
}
pub struct ButtonInner {
graphic: Graphics,
text: String,
parent: Option<Arc<dyn Component>>,
}
impl Button {
pub fn new(size: Size, point: Point, parent: Option<Arc<dyn Component>>, text: String) -> Self {
let point = match &parent {
Some(p) => {
let (_, p) = p.bound();
Point::new(p.x + point.x, p.y + point.y)
}
None => point,
};
Self {
inner: unsafe {
UPIntrFreeCell::new(ButtonInner {
graphic: Graphics {
size,
point,
drv: GPU_DEVICE.clone(),
},
text,
parent,
})
},
}
}
}
impl Component for Button {
fn paint(&self) {
let mut inner = self.inner.exclusive_access();
let text = inner.text.clone();
Text::with_alignment(
text.as_str(),
inner.graphic.bounding_box().center(),
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
Alignment::Center,
)
.draw(&mut inner.graphic);
}
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
unreachable!()
}
fn bound(
&self,
) -> (
embedded_graphics::prelude::Size,
embedded_graphics::prelude::Point,
) {
let inner = self.inner.exclusive_access();
(inner.graphic.size, inner.graphic.point)
}
}
......@@ -5,14 +5,14 @@ use embedded_graphics::{
prelude::{OriginDimensions, Point, RgbColor, Size},
};
use crate::drivers::{GPUDevice, GPU_DEVICE,};
use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES};
use crate::board::VIRTGPU_XRES;
use crate::drivers::{GpuDevice, GPU_DEVICE};
#[derive(Clone)]
pub struct Graphics {
pub size: Size,
pub point: Point,
pub drv: Arc<dyn GPUDevice>,
pub drv: Arc<dyn GpuDevice>,
}
impl Graphics {
......@@ -23,6 +23,10 @@ impl Graphics {
drv: GPU_DEVICE.clone(),
}
}
pub fn reset(&self) {
let fb = self.drv.get_framebuffer();
fb.fill(0u8);
}
}
impl OriginDimensions for Graphics {
......@@ -40,10 +44,12 @@ impl DrawTarget for Graphics {
where
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
{
let fb = self.drv.getfreambuffer();
let fb = self.drv.get_framebuffer();
pixels.into_iter().for_each(|px| {
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x) as usize * 4;
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x)
as usize
* 4;
if idx + 2 >= fb.len() {
return;
}
......
use alloc::{string::String, sync::Arc, vec::Vec};
use embedded_graphics::{
image::Image,
mono_font::{ascii::FONT_10X20, iso_8859_13::FONT_6X12, MonoTextStyle},
pixelcolor::Rgb888,
prelude::{Point, RgbColor, Size},
text::Text,
Drawable,
};
use tinybmp::Bmp;
use crate::{drivers::GPU_DEVICE, sync::UPIntrFreeCell};
use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES};
use super::{Component, Graphics, ImageComp};
static FILEICON: &[u8] = include_bytes!("../assert/file.bmp");
pub struct IconController {
inner: UPIntrFreeCell<IconControllerInner>,
}
pub struct IconControllerInner {
files: Vec<String>,
graphic: Graphics,
parent: Option<Arc<dyn Component>>,
}
impl IconController {
pub fn new(files: Vec<String>, parent: Option<Arc<dyn Component>>) -> Self {
IconController {
inner: unsafe {
UPIntrFreeCell::new(IconControllerInner {
files,
graphic: Graphics {
size: Size::new(VIRTGPU_XRES, VIRTGPU_YRES),
point: Point::new(0, 0),
drv: GPU_DEVICE.clone(),
},
parent,
})
},
}
}
}
impl Component for IconController {
fn paint(&self) {
println!("demo");
let mut inner = self.inner.exclusive_access();
let mut x = 10;
let mut y = 10;
let v = inner.files.clone();
for file in v {
println!("file");
let bmp = Bmp::<Rgb888>::from_slice(FILEICON).unwrap();
Image::new(&bmp, Point::new(x, y)).draw(&mut inner.graphic);
let text = Text::new(
file.as_str(),
Point::new(x + 20, y + 80),
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
);
text.draw(&mut inner.graphic);
if y >= 600 {
x = x + 70;
y = 10;
} else {
y = y + 90;
}
}
}
fn add(&self, comp: Arc<dyn Component>) {
todo!()
}
fn bound(&self) -> (Size, Point) {
todo!()
}
}
use alloc::{sync::Arc, vec::Vec};
use embedded_graphics::{
image::Image,
pixelcolor::Rgb888,
prelude::{Point, Size},
Drawable,
};
use tinybmp::Bmp;
use crate::{
drivers::{BLOCK_DEVICE, GPU_DEVICE},
sync::UPIntrFreeCell,
};
use super::{Component, Graphics};
pub struct ImageComp {
inner: UPIntrFreeCell<ImageInner>,
}
pub struct ImageInner {
image: &'static [u8],
graphic: Graphics,
parent: Option<Arc<dyn Component>>,
}
impl ImageComp {
pub fn new(
size: Size,
point: Point,
v: &'static [u8],
parent: Option<Arc<dyn Component>>,
) -> Self {
unsafe {
ImageComp {
inner: UPIntrFreeCell::new(ImageInner {
parent,
image: v,
graphic: Graphics {
size,
point,
drv: GPU_DEVICE.clone(),
},
}),
}
}
}
}
impl Component for ImageComp {
fn paint(&self) {
let mut inner = self.inner.exclusive_access();
let b = unsafe {
let len = inner.image.len();
let ptr = inner.image.as_ptr() as *const u8;
core::slice::from_raw_parts(ptr, len)
};
let bmp = Bmp::<Rgb888>::from_slice(b).unwrap();
let point = match &inner.parent {
Some(parent) => {
let (_, point) = parent.bound();
Point::new(
point.x + inner.graphic.point.x,
point.y + inner.graphic.point.y,
)
}
None => inner.graphic.point,
};
Image::new(&bmp, point).draw(&mut inner.graphic);
}
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
todo!()
}
fn bound(&self) -> (Size, Point) {
let inner = self.inner.exclusive_access();
(inner.graphic.size, inner.graphic.point)
}
}
mod button;
mod graphic;
mod icon;
mod image;
mod panel;
mod terminal;
use alloc::sync::Arc;
pub use button::*;
use core::any::Any;
use embedded_graphics::prelude::{Point, Size};
pub use graphic::*;
pub use icon::*;
pub use image::*;
pub use panel::*;
pub use terminal::*;
mod paint;
pub trait Component: Send + Sync + Any {
fn paint(&self);
fn add(&self, comp: Arc<dyn Component>);
fn bound(&self) -> (Size, Point);
}
use graphic::Graphics;
pub use paint::{init_paint, move_rect, reset};
use super::Graphics;
use crate::sync::UPIntrFreeCell;
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 lazy_static::*;
const INIT_X: i32 = 640;
const INIT_Y: i32 = 400;
const RECT_SIZE: u32 = 40;
pub struct DrawingBoard {
graphics: Graphics,
latest_pos: Point,
}
impl DrawingBoard {
pub fn new() -> Self {
Self {
graphics: Graphics::new(Size::new(1280, 800), Point::new(0, 0)),
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.graphics)
.ok();
}
pub fn move_rect(&mut self, dx: i32, dy: i32) {
self.latest_pos.x += dx;
self.latest_pos.y += dy;
self.paint();
}
pub fn reset(&mut self) {
self.latest_pos = Point::new(INIT_X, INIT_Y);
self.graphics.reset();
}
}
lazy_static! {
pub static ref DRAWING_BOARD: UPIntrFreeCell<DrawingBoard> = unsafe { UPIntrFreeCell::new(DrawingBoard::new()) };
}
pub fn init_paint() {
DRAWING_BOARD.exclusive_session(|ripple| {
ripple.paint();
});
}
pub fn move_rect(dx: i32, dy: i32) {
DRAWING_BOARD.exclusive_session(|ripple| {
ripple.move_rect(dx, dy);
});
}
pub fn reset() {
DRAWING_BOARD.exclusive_session(|ripple| {
ripple.reset();
});
}
use alloc::{collections::VecDeque, rc::Weak, sync::Arc};
use embedded_graphics::{
pixelcolor::Rgb888,
prelude::{Point, Primitive, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle},
Drawable,
};
use crate::{drivers::GPU_DEVICE, sync::UPIntrFreeCell};
use super::{Component, Graphics};
pub struct Panel {
inner: UPIntrFreeCell<PanelInner>,
}
struct PanelInner {
graphic: Graphics,
comps: VecDeque<Arc<dyn Component>>,
}
impl Panel {
pub fn new(size: Size, point: Point) -> Self {
Self {
inner: unsafe {
UPIntrFreeCell::new(PanelInner {
graphic: Graphics {
size,
point,
drv: GPU_DEVICE.clone(),
},
comps: VecDeque::new(),
})
},
}
}
}
impl Component for Panel {
fn paint(&self) {
let mut inner = self.inner.exclusive_access();
Rectangle::new(Point::new(0, 0), inner.graphic.size)
.into_styled(PrimitiveStyle::with_fill(Rgb888::WHITE))
.draw(&mut inner.graphic)
.unwrap();
let len = inner.comps.len();
drop(inner);
for i in 0..len {
let mut inner = self.inner.exclusive_access();
let comp = Arc::downgrade(&inner.comps[i]);
drop(inner);
comp.upgrade().unwrap().paint();
}
}
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
let mut inner = self.inner.exclusive_access();
inner.comps.push_back(comp);
}
fn bound(&self) -> (Size, Point) {
let inner = self.inner.exclusive_access();
(inner.graphic.size, inner.graphic.point)
}
}
use alloc::{
collections::VecDeque,
string::{String, ToString},
sync::Arc,
};
use embedded_graphics::{
mono_font::{ascii::FONT_10X20, MonoTextStyle},
pixelcolor::Rgb888,
prelude::{Dimensions, Point, Primitive, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle},
text::{Alignment, Text},
Drawable,
};
use crate::{drivers::GPU_DEVICE, sync::UPIntrFreeCell};
use super::{button::Button, Component, Graphics, Panel};
pub struct Terminal {
inner: UPIntrFreeCell<TerminalInner>,
}
pub struct TerminalInner {
pub text: String,
titel: Option<String>,
graphic: Graphics,
comps: VecDeque<Arc<dyn Component>>,
}
impl Terminal {
pub fn new(
size: Size,
point: Point,
parent: Option<Arc<dyn Component>>,
titel: Option<String>,
text: String,
) -> Self {
Self {
inner: unsafe {
UPIntrFreeCell::new(TerminalInner {
text,
titel,
graphic: Graphics {
size,
point,
drv: GPU_DEVICE.clone(),
},
comps: VecDeque::new(),
})
},
}
}
pub fn repaint(&self, text: String) {
let mut inner = self.inner.exclusive_access();
inner.text += text.as_str();
Text::with_alignment(
inner.text.clone().as_str(),
Point::new(20, 50),
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
Alignment::Left,
)
.draw(&mut inner.graphic);
}
}
impl Component for Terminal {
fn paint(&self) {
let mut inner = self.inner.exclusive_access();
let len = inner.comps.len();
drop(inner);
for i in 0..len {
let mut inner = self.inner.exclusive_access();
let comp = Arc::downgrade(&inner.comps[i]);
drop(inner);
comp.upgrade().unwrap().paint();
}
let mut inner = self.inner.exclusive_access();
let titel = inner.titel.get_or_insert("No Titel".to_string()).clone();
let text = Text::new(
titel.as_str(),
Point::new(20, 20),
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
);
text.draw(&mut inner.graphic);
Text::with_alignment(
inner.text.clone().as_str(),
Point::new(20, 50),
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
Alignment::Left,
)
.draw(&mut inner.graphic);
}
fn add(&self, comp: Arc<dyn Component>) {
let mut inner = self.inner.exclusive_access();
inner.comps.push_back(comp);
}
fn bound(&self) -> (Size, Point) {
let inner = self.inner.exclusive_access();
(inner.graphic.size, inner.graphic.point)
}
}
OUTPUT_ARCH(riscv)
ENTRY(_start)
BASE_ADDRESS = 0x80020000;
SECTIONS
{
. = BASE_ADDRESS;
skernel = .;
stext = .;
.text : {
*(.text.entry)
. = ALIGN(4K);
strampoline = .;
*(.text.trampoline);
. = ALIGN(4K);
*(.text .text.*)
}
. = ALIGN(4K);
etext = .;
srodata = .;
.rodata : {
*(.rodata .rodata.*)
*(.srodata .srodata.*)
}
. = ALIGN(4K);
erodata = .;
sdata = .;
.data : {
*(.data .data.*)
*(.sdata .sdata.*)
}
. = ALIGN(4K);
edata = .;
sbss_with_stack = .;
.bss : {
*(.bss.stack)
sbss = .;
*(.bss .bss.*)
*(.sbss .sbss.*)
}
. = ALIGN(4K);
ebss = .;
ekernel = .;
/DISCARD/ : {
*(.eh_frame)
}
}
\ No newline at end of file
OUTPUT_ARCH(riscv)
ENTRY(_start)
BASE_ADDRESS = 0x80200000;
BASE_ADDRESS = 0x80000000;
SECTIONS
{
......
......@@ -2,7 +2,9 @@
#![no_main]
#![feature(panic_info_message)]
#![feature(alloc_error_handler)]
#[cfg(feature = "board_qemu")]
#![feature(naked_functions)]
#![feature(fn_align)]
use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE};
extern crate alloc;
......@@ -10,10 +12,6 @@ extern crate alloc;
#[macro_use]
extern crate bitflags;
#[cfg(feature = "board_k210")]
#[path = "boards/k210.rs"]
mod board;
#[cfg(not(any(feature = "board_k210")))]
#[path = "boards/qemu.rs"]
mod board;
......@@ -22,7 +20,6 @@ mod console;
mod config;
mod drivers;
mod fs;
#[cfg(feature = "board_qemu")]
mod gui;
mod lang_items;
mod mm;
......@@ -32,8 +29,11 @@ mod syscall;
mod task;
mod timer;
mod trap;
// use syscall::create_desktop; //for test
//mod start;
mod riscvregs;
use riscvregs::registers::*;
use riscvregs::registers::pmpcfg0::*;
//use syscall::create_desktop; //for test
core::arch::global_asm!(include_str!("entry.asm"));
......@@ -56,26 +56,102 @@ lazy_static! {
unsafe { UPIntrFreeCell::new(false) };
}
#[repr(C, align(16))]
struct Stack([u8; 4096 * 4 * 1]);
#[no_mangle]
static mut STACK0: Stack = Stack([0; 4096 * 4 * 1]);
#[no_mangle]
pub unsafe fn rust_start() -> ! {
// set MPP mode to Supervisor, for mret
mstatus::set_mpp(mstatus::MPP::Supervisor);
// set MEPC to main, for mret
mepc::write(rust_main as usize);
// disable paging for now.
satp::write(0);
// delegate all interrupts and exceptions to supervisor mode.
medeleg::set_all();
mideleg::set_all();
sie::set_sext();
sie::set_ssoft();
sie::set_stimer();
// configure Physical Memory Protection to give supervisor mode
// access to all of physical memory.
pmpaddr0::write(0x3fffffffffffff);
pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0
// ask for clock interrupts.
timerinit();
// keep each CPU's hartid in its tp register, for cpuid().
let id = mhartid::read();
core::arch::asm!("mv tp, {0}", in(reg) id);
// switch to supervisor mode and jump to main().
core::arch::asm!("mret");
extern "C" {
fn rust_main() -> !;
}
core::hint::unreachable_unchecked();
}
// a scratch area per CPU for machine-mode timer interrupts.
static mut TIMER_SCRATCH: [[u64; 5]; 1] = [[0; 5]; 1];
unsafe fn timerinit() {
// each CPU has a separate source of timer interrupts
let id = mhartid::read();
// ask the CLINT for a timer interrupts
let interval = 1000000u64; // cycles; about 1/10th second in qemu.
let mtimecmp = board::clint_mtimecmp(id) as *mut u64;
let mtime = board::CLINT_MTIME as *const u64;
mtimecmp.write_volatile(mtime.read_volatile() + interval);
// prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers.
// scratch[3] : address of CLINT MTIMECMP register.
// scratch[4] : desired interval (in cycles) between timer interrupts.
let scratch = &mut TIMER_SCRATCH[id];
scratch[3] = mtimecmp as u64;
scratch[4] = interval;
mscratch::write(scratch.as_mut_ptr() as usize);
// set the machine-mode trap handler
mtvec::write(board::timervec as usize, mtvec::TrapMode::Direct);
// enable machine-mode interrupts.
mstatus::set_mie();
// enable machime-mode timer interrupts.
mie::set_mtimer();
}
#[no_mangle]
pub fn rust_main() -> ! {
clear_bss();
mm::init();
println!("KERN: init gpu");
#[cfg(feature = "board_qemu")]
GPU_DEVICE.clone();
let _gpu = GPU_DEVICE.clone();
println!("KERN: init keyboard");
#[cfg(feature = "board_qemu")]
KEYBOARD_DEVICE.clone();
let _keyboard = KEYBOARD_DEVICE.clone();
println!("KERN: init mouse");
#[cfg(feature = "board_qemu")]
MOUSE_DEVICE.clone();
let _mouse = MOUSE_DEVICE.clone();
println!("KERN: init trap");
trap::init();
trap::enable_timer_interrupt();
timer::set_next_trigger();
//trap::enable_timer_interrupt();
//timer::set_next_trigger();
board::device_init();
fs::list_apps();
//syscall::create_desktop(); //for test
//gui::init_paint();
task::add_initproc();
*DEV_NON_BLOCKING_ACCESS.exclusive_access() = true;
task::run_tasks();
......
......@@ -48,7 +48,7 @@ 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);
// println!("last {} Physical Frames.", self.end - self.current);
}
}
impl FrameAllocator for StackFrameAllocator {
......
......@@ -92,14 +92,14 @@ impl MemorySet {
// 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");
// 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(),
......@@ -109,7 +109,7 @@ impl MemorySet {
),
None,
);
println!("mapping .rodata section");
// println!("mapping .rodata section");
memory_set.push(
MapArea::new(
(srodata as usize).into(),
......@@ -119,7 +119,7 @@ impl MemorySet {
),
None,
);
println!("mapping .data section");
// println!("mapping .data section");
memory_set.push(
MapArea::new(
(sdata as usize).into(),
......@@ -129,7 +129,7 @@ impl MemorySet {
),
None,
);
println!("mapping .bss section");
// println!("mapping .bss section");
memory_set.push(
MapArea::new(
(sbss_with_stack as usize).into(),
......@@ -139,7 +139,7 @@ impl MemorySet {
),
None,
);
println!("mapping physical memory");
// println!("mapping physical memory");
memory_set.push(
MapArea::new(
(ekernel as usize).into(),
......@@ -149,7 +149,7 @@ impl MemorySet {
),
None,
);
println!("mapping memory-mapped registers");
//println!("mapping memory-mapped registers");
for pair in MMIO {
memory_set.push(
MapArea::new(
......
// RISC-V registers
pub mod registers {
// hart (core) id registers
pub mod mhartid {
use core::arch::asm;
#[inline]
pub fn read() -> usize {
let id: usize;
unsafe {
asm!("csrr {}, mhartid", out(reg) id);
}
id
}
}
// Machine Status Register, mstatus
pub mod mstatus {
use core::arch::asm;
// Machine Status Register bit
const MPP_MASK: usize = 3 << 11;
const MIE: usize = 1 << 3;
// Machine Previous Privilege mode
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MPP {
Machine = 3,
Supervisor = 1,
User = 0,
}
#[inline]
unsafe fn _read() -> usize {
let bits: usize;
asm!("csrr {}, mstatus", out(reg) bits);
bits
}
#[inline]
unsafe fn _write(bits: usize) {
asm!("csrw mstatus, {}", in(reg) bits);
}
// Machine Previous Privilege Mode
#[inline]
pub fn set_mpp(mpp: MPP) {
unsafe {
let mut value = _read();
value &= !MPP_MASK;
value |= (mpp as usize) << 11;
_write(value);
}
}
#[inline]
pub fn set_mie() {
unsafe {
asm!("csrs mstatus, {}", in(reg) MIE);
}
}
}
// machine exception program counter, holds the
// instruction address to which a return from
// exception will go.
pub mod mepc {
use core::arch::asm;
#[inline]
pub fn write(x: usize) {
unsafe {
asm!("csrw mepc, {}", in(reg) x);
}
}
}
// Supervisor Status Register, sstatus
pub mod sstatus {
use core::arch::asm;
// Supervisor Status Register bit
const SPP: usize = 1 << 8; // Previous mode, 1=Supervisor, 0=user
const SPIE: usize = 1 << 5; // Supervisor Previous Interrupt Enable
const SIE: usize = 1 << 1; // Supervisor Interrupt Enable
#[derive(Clone, Copy, Debug)]
pub struct Sstatus {
bits: usize,
}
impl Sstatus {
// Supervisor Interrupt Enable
#[inline]
pub(in crate::riscvregs) fn sie(&self) -> bool {
self.bits & SIE != 0
}
// Supervisor Previous Privilege mode
#[inline]
pub fn spp(&self) -> SPP {
match self.bits & SPP {
0 => SPP::User,
_ => SPP::Supervisor,
}
}
// restore status bits
#[inline]
pub fn restore(&self) {
unsafe {
_write(self.bits);
}
}
}
// Supervisor Previous Privilege Mode
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SPP {
Supervisor = 1,
User = 0,
}
#[inline]
pub fn read() -> Sstatus {
let bits: usize;
unsafe { asm!("csrr {}, sstatus", out(reg) bits) }
Sstatus { bits }
}
#[inline]
unsafe fn _write(bits: usize) {
asm!("csrw sstatus, {}", in(reg) bits);
}
// bit set
#[inline]
unsafe fn _set(bits: usize) {
asm!("csrs sstatus, {}", in(reg) bits);
}
// bit clear
#[inline]
unsafe fn _clear(bits: usize) {
asm!("csrc sstatus, {}", in(reg) bits);
}
#[inline]
pub(in crate::riscvregs) unsafe fn set_sie() {
_set(SIE)
}
#[inline]
pub(in crate::riscvregs) unsafe fn clear_sie() {
_clear(SIE)
}
#[inline]
pub unsafe fn set_spie() {
_set(SPIE);
}
#[inline]
pub unsafe fn set_spp(spp: SPP) {
match spp {
SPP::Supervisor => _set(SPP),
SPP::User => _clear(SPP),
}
}
}
// Supervisor Interrupt Pending
pub mod sip {
use core::arch::asm;
const SSIP: usize = 1 << 1;
// Supervisor Software Interrupt Pending
#[inline]
pub unsafe fn clear_ssoft() {
asm!("csrc sip, {}", in(reg) SSIP);
}
}
// Supervisor Interrupt Enable
pub mod sie {
use core::arch::asm;
const SEIE: usize = 1 << 9; // external
const STIE: usize = 1 << 5; // timer
const SSIE: usize = 1 << 1; // software
#[inline]
unsafe fn _set(bits: usize) {
asm!("csrs sie, {}", in(reg) bits);
}
#[inline]
pub unsafe fn set_sext() {
_set(SEIE);
}
#[inline]
pub unsafe fn set_stimer() {
_set(STIE);
}
#[inline]
pub unsafe fn set_ssoft() {
_set(SSIE);
}
}
// Machine-mode Interrupt Enable
pub mod mie {
use core::arch::asm;
const MTIE: usize = 1 << 7;
#[inline]
pub unsafe fn set_mtimer() {
asm!("csrs mie, {}", in(reg) MTIE);
}
}
// supervisor exceptions program counter, holds the
// instruction address to which a return from
// exception will go.
pub mod sepc {
use core::arch::asm;
#[inline]
pub fn read() -> usize {
let bits: usize;
unsafe {
asm!("csrr {}, sepc", out(reg) bits);
}
bits
}
#[inline]
pub fn write(bits: usize) {
unsafe {
asm!("csrw sepc, {}", in(reg) bits);
}
}
}
// Machine Exception Delegation
pub mod medeleg {
use core::arch::asm;
pub unsafe fn set_all() {
asm!("csrw medeleg, {}", in(reg) 0xffff);
}
}
// Machine Interrupt Delegation
pub mod mideleg {
use core::arch::asm;
#[inline]
pub unsafe fn set_all() {
asm!("csrw mideleg, {}", in(reg) 0xffff);
}
}
// Supervisor Trap-Vector Base Address
// low two bits are mode.
pub mod stvec {
pub use super::mtvec::TrapMode;
use core::arch::asm;
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
asm!("csrw stvec, {}", in(reg) addr + mode as usize);
}
}
// Machine-mode interrupt vector
pub mod mtvec {
use core::arch::asm;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TrapMode {
Direct = 0,
Vectored = 1,
}
#[inline]
pub unsafe fn write(addr: usize, mode: TrapMode) {
asm!("csrw mtvec, {}", in(reg) addr + mode as usize);
}
}
// Physical Memory Protection Configuration
pub mod pmpcfg0 {
use core::arch::asm;
// Permission enum contains all possible permission modes for pmp registers
#[derive(Clone, Copy, Debug)]
pub enum Permission {
NONE = 0b000,
R = 0b001,
W = 0b010,
RW = 0b011,
X = 0b100,
RX = 0b101,
WX = 0b110,
RWX = 0b111,
}
// Range enum contains all possible addressing modes for pmp registers
pub enum Range {
OFF = 0b00,
TOR = 0b01,
NA4 = 0b10,
NAPOT = 0b11,
}
// Set the pmp configuration corresponging to the index
#[inline]
pub unsafe fn set_pmp(index: usize, range: Range, permission: Permission, locked: bool) {
assert!(index < 8);
let mut value = _read();
let byte = (locked as usize) << 7 | (range as usize) << 3 | (permission as usize);
value |= byte << (8 * index);
_write(value);
}
#[inline]
unsafe fn _read() -> usize {
let bits: usize;
asm!("csrr {}, pmpcfg0", out(reg) bits);
bits
}
#[inline]
unsafe fn _write(bits: usize) {
asm!("csrw pmpcfg0, {}", in(reg) bits);
}
}
// Physical memory protection address register
pub mod pmpaddr0 {
use core::arch::asm;
pub fn write(bits: usize) {
unsafe {
asm!("csrw pmpaddr0, {}", in(reg) bits);
}
}
}
// Supervisor address translation and protection;
// holds the address of the page table.
pub mod satp {
use core::arch::asm;
// stap register
#[derive(Clone, Copy, Debug)]
pub struct Satp {
bits: usize,
}
// 64-bit satp mode
pub enum Mode {
// No translation or protection
Bare = 0,
// Page-based 39-bit virtual addressing
Sv39 = 8,
// Page-based 48-bit virtual addressing
Sv48 = 9,
// Page-based 57-bit virtual addressing
Sv57 = 10,
// Page-based 64-bit virtual addressing
Sv64 = 11,
}
impl Satp {
// Return the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
}
#[inline]
pub unsafe fn read() -> Satp {
let bits: usize;
asm!("csrr {}, satp", out(reg) bits);
Satp { bits }
}
#[inline]
pub unsafe fn write(bits: usize) {
asm!("csrw satp, {}", in(reg) bits);
}
#[inline]
pub fn make(mode: Mode, asid: usize, ppn: usize) -> usize {
let mut bits: usize = 0;
bits |= (mode as usize) << 60;
bits |= asid << 44;
bits |= ppn >> 12;
bits
}
}
// mscratch register
pub mod mscratch {
use core::arch::asm;
#[inline]
pub fn write(bits: usize) {
unsafe {
asm!("csrw mscratch, {}", in(reg) bits);
}
}
}
// Supervisor Trap Cause
pub mod scause {
use core::{arch::asm, mem::size_of};
// scause register
#[derive(Clone, Copy)]
pub struct Scause {
bits: usize,
}
// Trap Cause
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Trap {
Interrupt(Interrupt),
Exception(Exception),
}
// Interrupt
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Interrupt {
UserSoft,
SupervisorSoft,
UserTimer,
SupervisorTimer,
UserExternal,
SupervisorExternal,
Unknown,
}
// Exception
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Exception {
InstructionMisaligned,
InstructionFault,
IllegalInstruction,
Breakpoint,
LoadFault,
StoreMisaligned,
StoreFault,
UserEnvCall,
InstructionPageFault,
LoadPageFault,
StorePageFault,
Unknown,
}
impl Interrupt {
#[inline]
pub fn from(nr: usize) -> Self {
match nr {
0 => Interrupt::UserSoft,
1 => Interrupt::SupervisorSoft,
4 => Interrupt::UserTimer,
5 => Interrupt::SupervisorTimer,
8 => Interrupt::UserExternal,
9 => Interrupt::SupervisorExternal,
_ => Interrupt::Unknown,
}
}
}
impl Exception {
#[inline]
pub fn from(nr: usize) -> Self {
match nr {
0 => Exception::InstructionMisaligned,
1 => Exception::InstructionFault,
2 => Exception::IllegalInstruction,
3 => Exception::Breakpoint,
5 => Exception::LoadFault,
6 => Exception::StoreMisaligned,
7 => Exception::StoreFault,
8 => Exception::UserEnvCall,
12 => Exception::InstructionPageFault,
13 => Exception::LoadPageFault,
15 => Exception::StorePageFault,
_ => Exception::Unknown,
}
}
}
impl Scause {
// Returns the contents of the register as raw bits
#[inline]
pub fn bits(&self) -> usize {
self.bits
}
// Returns the code field
#[inline]
pub fn code(&self) -> usize {
let bit = 1 << (size_of::<usize>() * 8 - 1);
self.bits & !bit
}
// Trap cause
#[inline]
pub fn cause(&self) -> Trap {
if self.is_interrupt() {
Trap::Interrupt(Interrupt::from(self.code()))
} else {
Trap::Exception(Exception::from(self.code()))
}
}
// Is trap cause an interrupt.
#[inline]
pub fn is_interrupt(&self) -> bool {
self.bits & (1 << (size_of::<usize>() * 8 - 1)) != 0
}
// Is trap cause an exception.
#[inline]
pub fn is_exception(&self) -> bool {
!self.is_interrupt()
}
}
#[inline]
pub fn read() -> Scause {
let bits: usize;
unsafe {
asm!("csrr {}, scause", out(reg) bits);
}
Scause { bits }
}
}
// Supervisor Trap Value
pub mod stval {
use core::arch::asm;
#[inline]
pub fn read() -> usize {
let bits: usize;
unsafe { asm!("csrr {}, stval", out(reg) bits) }
bits
}
}
}
use core::arch::asm;
use registers::*;
// enable device interrupts
#[inline]
pub fn intr_on() {
unsafe {
sstatus::set_sie();
}
}
// disable device interrupts
#[inline]
pub fn intr_off() {
unsafe {
sstatus::clear_sie();
}
}
// are device interrupts enabled?
#[inline]
pub fn intr_get() -> bool {
sstatus::read().sie()
}
// flush the TLB.
#[inline]
pub unsafe fn sfence_vma() {
// the zero, zero means flush all TLB entries
asm!("sfence.vma zero, zero");
}
pub const PGSIZE: usize = 4096; // bytes per page
pub const PGSHIFT: usize = 12; // bits of offset within a page
pub const fn pgroundup(sz: usize) -> usize {
(sz + PGSIZE - 1) & !(PGSIZE - 1)
}
pub const fn pgrounddown(sz: usize) -> usize {
sz & !(PGSIZE - 1)
}
// PTE flags
pub mod pteflags {
pub const PTE_V: usize = 1 << 0; // valid
pub const PTE_R: usize = 1 << 1;
pub const PTE_W: usize = 1 << 2;
pub const PTE_X: usize = 1 << 3;
pub const PTE_U: usize = 1 << 4; // user can access
}
......@@ -40,13 +40,7 @@ pub fn console_getchar() -> usize {
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
}
#[cfg(feature = "board_qemu")]
use crate::board::QEMUExit;
pub fn shutdown(exit_code: usize) -> ! {
#[cfg(feature = "board_k210")]
sbi_call(SBI_SHUTDOWN, exit_code, 0, 0);
#[cfg(feature = "board_qemu")]
crate::board::QEMU_EXIT_HANDLE.exit_failure();
#[cfg(feature = "board_k210")]
panic!("It should shutdown!");
crate::board::QEMU_EXIT_HANDLE.exit_failure()
}
//use crate::kernelvec::*;
//use crate::memlayout::*;
//use crate::param::NCPU;
//use super::main::*;
//use crate::riscv::registers::{pmpcfg0::*, *};
use core::arch::asm;
use core::hint::unreachable_unchecked;
mod riscv;
#[repr(C, align(16))]
struct Stack([u8; 4096 * 4 * NCPU]);
#[no_mangle]
static mut STACK0: Stack = Stack([0; 4096 * 4 * NCPU]);
#[no_mangle]
pub unsafe fn rust_start() -> ! {
// set MPP mode to Supervisor, for mret
mstatus::set_mpp(mstatus::MPP::Supervisor);
// set MEPC to main, for mret
mepc::write(rust_main as usize);
// disable paging for now.
satp::write(0);
// delegate all interrupts and exceptions to supervisor mode.
medeleg::set_all();
mideleg::set_all();
sie::set_sext();
sie::set_ssoft();
sie::set_stimer();
// configure Physical Memory Protection to give supervisor mode
// access to all of physical memory.
pmpaddr0::write(0x3fffffffffffff);
pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0
// ask for clock interrupts.
timerinit();
// keep each CPU's hartid in its tp register, for cpuid().
let id = mhartid::read();
asm!("mv tp, {0}", in(reg) id);
// switch to supervisor mode and jump to main().
asm!("mret");
extern "C" {
fn rust_main() -> !;
}
unreachable_unchecked();
}
// a scratch area per CPU for machine-mode timer interrupts.
static mut TIMER_SCRATCH: [[u64; 5]; 1] = [[0; 5]; 1];
unsafe fn timerinit() {
// each CPU has a separate source of timer interrupts
let id = mhartid::read();
// ask the CLINT for a timer interrupts
let interval = 1000000u64; // cycles; about 1/10th second in qemu.
let mtimecmp = clint_mtimecmp(id) as *mut u64;
let mtime = CLINT_MTIME as *const u64;
mtimecmp.write_volatile(mtime.read_volatile() + interval);
// prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers.
// scratch[3] : address of CLINT MTIMECMP register.
// scratch[4] : desired interval (in cycles) between timer interrupts.
let scratch = &mut TIMER_SCRATCH[id];
scratch[3] = mtimecmp as u64;
scratch[4] = interval;
mscratch::write(scratch.as_mut_ptr() as usize);
// set the machine-mode trap handler
mtvec::write(timervec as usize, mtvec::TrapMode::Direct);
// enable machine-mode interrupts.
mstatus::set_mie();
// enable machime-mode timer interrupts.
mie::set_mtimer();
}
\ No newline at end of file
use alloc::{string::ToString, sync::Arc, vec::Vec};
use embedded_graphics::{
prelude::{Point, Size},
primitives::arc,
};
use crate::{
fs::ROOT_INODE,
gui::{Button, Component, IconController, ImageComp, Panel, Terminal},
sync::UPIntrFreeCell,
};
use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES};
static DT: &[u8] = include_bytes!("../assert/desktop.bmp");
lazy_static::lazy_static!(
pub static ref DESKTOP:UPIntrFreeCell<Arc<dyn Component>> = unsafe {
UPIntrFreeCell::new(Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0))))
};
pub static ref PAD:UPIntrFreeCell<Option<Arc<Terminal>>> = unsafe {
UPIntrFreeCell::new(None)
};
);
pub fn create_desktop() -> isize {
let mut p: Arc<dyn Component + 'static> =
Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0)));
let image = ImageComp::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0), DT, Some(p.clone()));
let icon = IconController::new(ROOT_INODE.ls(), Some(p.clone()));
p.add(Arc::new(image));
p.add(Arc::new(icon));
let mut desktop = DESKTOP.exclusive_access();
*desktop = p;
desktop.paint();
drop(desktop);
create_terminal();
1
}
pub fn create_terminal() {
let desktop = DESKTOP.exclusive_access();
let arc_t = Arc::new(Terminal::new(
Size::new(400, 400),
Point::new(200, 100),
Some(desktop.clone()),
Some("demo.txt".to_string()),
"".to_string(),
));
let text = Panel::new(Size::new(400, 400), Point::new(200, 100));
let button = Button::new(
Size::new(20, 20),
Point::new(370, 10),
Some(arc_t.clone()),
"X".to_string(),
);
arc_t.add(Arc::new(text));
arc_t.add(Arc::new(button));
arc_t.paint();
desktop.add(arc_t.clone());
let mut pad = PAD.exclusive_access();
*pad = Some(arc_t);
}
......@@ -25,59 +25,17 @@ 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_CREATE_DESKTOP: usize = 2000;
mod fs;
#[cfg(feature = "board_qemu")]
mod gui;
mod process;
mod sync;
mod thread;
#[cfg(feature = "board_qemu")]
pub use self::gui::create_desktop;
use fs::*;
#[cfg(feature = "board_qemu")]
pub use gui::PAD;
use fs::*;
use process::*;
use sync::*;
use thread::*;
#[cfg(feature = "board_qemu")]
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
match syscall_id {
SYSCALL_DUP => sys_dup(args[0]),
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_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(args[0]),
SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]),
SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]),
SYSCALL_CREATE_DESKTOP => create_desktop(),
_ => panic!("Unsupported syscall_id: {}", syscall_id),
}
}
#[cfg(feature = "board_k210")]
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
match syscall_id {
SYSCALL_DUP => sys_dup(args[0]),
......
......@@ -56,7 +56,6 @@ pub fn block_current_and_run_next() {
let task_cx_ptr = block_current_task();
schedule(task_cx_ptr);
}
#[cfg(feature = "board_qemu")]
use crate::board::QEMUExit;
pub fn exit_current_and_run_next(exit_code: i32) {
......@@ -75,7 +74,6 @@ pub fn exit_current_and_run_next(exit_code: i32) {
// the process should terminate at once
if tid == 0 {
let pid = process.getpid();
#[cfg(feature = "board_qemu")]
if pid == IDLE_PID {
println!(
"[kernel] Idle process exit with exit_code {} ...",
......
......@@ -61,13 +61,14 @@ pub fn add_timer(expire_ms: usize, task: Arc<TaskControlBlock>) {
pub fn check_timer() {
let current_ms = get_time_ms();
let mut timers = TIMERS.exclusive_access();
while let Some(timer) = timers.peek() {
if timer.expire_ms <= current_ms {
add_task(Arc::clone(&timer.task));
timers.pop();
} else {
break;
TIMERS.exclusive_session(|timers| {
while let Some(timer) = timers.peek() {
if timer.expire_ms <= current_ms {
add_task(Arc::clone(&timer.task));
timers.pop();
} else {
break;
}
}
}
});
}
......@@ -61,7 +61,7 @@ pub fn trap_handler() -> ! {
set_kernel_trap_entry();
let scause = scause::read();
let stval = stval::read();
//println!("into {:?}", scause.cause());
// println!("into {:?}", scause.cause());
match scause.cause() {
Trap::Exception(Exception::UserEnvCall) => {
// jump to next instruction anyway
......
[toolchain]
profile = "minimal"
# use the nightly version of the last stable toolchain, see <https://forge.rust-lang.org/>
channel = "nightly-2022-08-05"
components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"]
......@@ -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"
]
......@@ -13,3 +13,7 @@ riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
[profile.release]
debug = true
# [features]
# board_qemu = []
# board_k210 = []
\ No newline at end of file
#![no_std]
#![no_main]
use user_lib::create_desktop;
#[macro_use]
extern crate user_lib;
#[no_mangle]
pub fn main() -> i32 {
println!("gui");
create_desktop();
println!("exit pass.");
loop{}
0
}
......@@ -5,7 +5,7 @@
extern crate user_lib;
extern crate alloc;
use alloc::{fmt::format, string::String, vec::Vec};
use alloc::{fmt::format, vec::Vec};
use user_lib::{close, get_time, gettid, open, write, OpenFlags};
use user_lib::{exit, thread_create, waittid};
......
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#![feature(asm)]
#[macro_use]
extern crate user_lib;
......
......@@ -8,7 +8,7 @@ use user_lib::{exec, fork, wait};
#[no_mangle]
pub fn main() -> i32 {
for i in 0..50 {
for i in 0..5 {
if fork() == 0 {
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
} else {
......
......@@ -4,9 +4,12 @@
#[macro_use]
extern crate user_lib;
fn f(d: usize) {
println!("d = {}", d);
f(d + 1);
#[allow(unconditional_recursion)]
fn f(depth: usize) {
if depth % 10 == 0 {
println!("depth = {}", depth);
}
f(depth + 1);
}
#[no_mangle]
......
// 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<Task>,
current: usize,
}
#[derive(PartialEq, Eq, Debug)]
enum State {
Available,
Running,
Ready,
}
struct Task {
id: usize,
stack: Vec<u8>,
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<Task> = (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);
}
// 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<Self::Output> {
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<Pin<Box<dyn Future<Output = ()>>>>,
}
impl Executor {
fn new() -> Self {
Executor {
tasks: VecDeque::new(),
}
}
fn push<C, F>(&mut self, closure: C)
where
F: Future<Output = ()> + '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
}
......@@ -198,9 +198,7 @@ pub fn condvar_signal(condvar_id: usize) {
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
sys_condvar_wait(condvar_id, mutex_id);
}
pub fn create_desktop() {
sys_create_desktop();
}
#[macro_export]
macro_rules! vstore {
($var_ref: expr, $value: expr) => {
......
......@@ -154,6 +154,3 @@ pub fn sys_condvar_signal(condvar_id: usize) -> isize {
pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize {
syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0])
}
pub fn sys_create_desktop() -> isize {
syscall(2000, [0, 0, 0])
}
\ No newline at end of file