diff --git a/.github/workflows/doc-and-test.yml b/.github/workflows/doc-and-test.yml index 98fdc66..6da799d 100644 --- a/.github/workflows/doc-and-test.yml +++ b/.github/workflows/doc-and-test.yml @@ -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 diff --git a/.gitignore b/.gitignore index f411b22..852c161 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.vscode/settings.json b/.vscode/settings.json index 11de111..6a40655 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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 diff --git a/Dockerfile b/Dockerfile index ac784bc..284db4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,85 @@ -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 +# 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 -#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 +ARG QEMU_VERSION=7.0.0 +ARG HOME=/root -#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 \ +# 0. Install general tools +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install -y \ + curl \ + git \ + python3 \ + wget - #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 +# 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 -#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 +# 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} diff --git a/Makefile b/Makefile index a657cd9..bd267f4 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ -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 .. + 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 9822f54..022c7f2 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 index ee0ef97..5c5e68d 100644 --- a/easy-fs-fuse/Cargo.toml +++ b/easy-fs-fuse/Cargo.toml @@ -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 diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml index c969077..7a2f38e 100644 --- a/easy-fs/Cargo.toml +++ b/easy-fs/Cargo.toml @@ -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 diff --git a/os/Cargo.toml b/os/Cargo.toml index 4ebae8a..f4b2d80 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -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 diff --git a/os/Makefile b/os/Makefile index b38c23a..e02bc81 100644 --- a/os/Makefile +++ b/os/Makefile @@ -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 := 0x80200000 # 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: @@ -89,28 +73,12 @@ disasm-vim: kernel run: run-inner -gui: build -ifeq ($(BOARD),qemu) - @qemu-system-riscv64 \ - -M 128m \ - -machine virt \ - -bios $(BOOTLOADER) \ - -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 \ - -serial stdio -endif - 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 +86,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 +104,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 diff --git a/os/run-fdt.sh b/os/run-fdt.sh deleted file mode 100755 index c7c886d..0000000 --- a/os/run-fdt.sh +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/os/run-nodisp.sh b/os/run-nodisp.sh deleted file mode 100755 index 7f2a5bb..0000000 --- a/os/run-nodisp.sh +++ /dev/null @@ -1,10 +0,0 @@ -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 diff --git a/os/run.sh b/os/run.sh deleted file mode 100755 index f9e96d6..0000000 --- a/os/run.sh +++ /dev/null @@ -1,9 +0,0 @@ -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 diff --git a/os/src/boards/k210.rs b/os/src/boards/k210.rs deleted file mode 100644 index 249e49f..0000000 --- a/os/src/boards/k210.rs +++ /dev/null @@ -1,30 +0,0 @@ -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!(); -} diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index 1dae9f3..ffabff6 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -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); } diff --git a/os/src/console.rs b/os/src/console.rs index 5c8daaf..085637b 100644 --- a/os/src/console.rs +++ b/os/src/console.rs @@ -1,8 +1,5 @@ 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(()) } diff --git a/os/src/drivers/block/mod.rs b/os/src/drivers/block/mod.rs index 7361ec8..add8da0 100644 --- a/os/src/drivers/block/mod.rs +++ b/os/src/drivers/block/mod.rs @@ -1,7 +1,5 @@ -mod sdcard; mod virtio_blk; -pub use sdcard::SDCardWrapper; pub use virtio_blk::VirtIOBlock; use crate::board::BlockDeviceImpl; diff --git a/os/src/drivers/block/sdcard.rs b/os/src/drivers/block/sdcard.rs deleted file mode 100644 index 756e9a0..0000000 --- a/os/src/drivers/block/sdcard.rs +++ /dev/null @@ -1,767 +0,0 @@ -#![allow(non_snake_case)] -#![allow(non_camel_case_types)] -#![allow(unused)] - -use super::BlockDevice; -use crate::sync::UPIntrFreeCell; -use core::convert::TryInto; -use k210_hal::prelude::*; -use k210_pac::{Peripherals, SPI0}; -use k210_soc::{ - fpioa::{self, io}, - //dmac::{dma_channel, DMAC, DMACExt}, - gpio, - gpiohs, - sleep::usleep, - spi::{aitm, frame_format, tmod, work_mode, SPIExt, SPIImpl, SPI}, - sysctl, -}; -use lazy_static::*; - -pub struct SDCard { - spi: SPI, - spi_cs: u32, - cs_gpionum: u8, - //dmac: &'a DMAC, - //channel: dma_channel, -} - -/* - * Start Data tokens: - * Tokens (necessary because at nop/idle (and CS active) only 0xff is - * on the data/command line) - */ -/** Data token start byte, Start Single Block Read */ -pub const SD_START_DATA_SINGLE_BLOCK_READ: u8 = 0xFE; -/** Data token start byte, Start Multiple Block Read */ -pub const SD_START_DATA_MULTIPLE_BLOCK_READ: u8 = 0xFE; -/** Data token start byte, Start Single Block Write */ -pub const SD_START_DATA_SINGLE_BLOCK_WRITE: u8 = 0xFE; -/** Data token start byte, Start Multiple Block Write */ -pub const SD_START_DATA_MULTIPLE_BLOCK_WRITE: u8 = 0xFC; - -pub const SEC_LEN: usize = 512; - -/** SD commands */ -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[allow(unused)] -pub enum CMD { - /** Software reset */ - CMD0 = 0, - /** Check voltage range (SDC V2) */ - CMD8 = 8, - /** Read CSD register */ - CMD9 = 9, - /** Read CID register */ - CMD10 = 10, - /** Stop to read data */ - CMD12 = 12, - /** Change R/W block size */ - CMD16 = 16, - /** Read block */ - CMD17 = 17, - /** Read multiple blocks */ - CMD18 = 18, - /** Number of blocks to erase (SDC) */ - ACMD23 = 23, - /** Write a block */ - CMD24 = 24, - /** Write multiple blocks */ - CMD25 = 25, - /** Initiate initialization process (SDC) */ - ACMD41 = 41, - /** Leading command for ACMD* */ - CMD55 = 55, - /** Read OCR */ - CMD58 = 58, - /** Enable/disable CRC check */ - CMD59 = 59, -} - -#[allow(unused)] -#[derive(Debug, Copy, Clone)] -pub enum InitError { - CMDFailed(CMD, u8), - CardCapacityStatusNotSet([u8; 4]), - CannotGetCardInfo, -} - -/** - * Card Specific Data: CSD Register - */ -#[derive(Debug, Copy, Clone)] -pub struct SDCardCSD { - pub CSDStruct: u8, /* CSD structure */ - pub SysSpecVersion: u8, /* System specification version */ - pub Reserved1: u8, /* Reserved */ - pub TAAC: u8, /* Data read access-time 1 */ - pub NSAC: u8, /* Data read access-time 2 in CLK cycles */ - pub MaxBusClkFrec: u8, /* Max. bus clock frequency */ - pub CardComdClasses: u16, /* Card command classes */ - pub RdBlockLen: u8, /* Max. read data block length */ - pub PartBlockRead: u8, /* Partial blocks for read allowed */ - pub WrBlockMisalign: u8, /* Write block misalignment */ - pub RdBlockMisalign: u8, /* Read block misalignment */ - pub DSRImpl: u8, /* DSR implemented */ - pub Reserved2: u8, /* Reserved */ - pub DeviceSize: u32, /* Device Size */ - //MaxRdCurrentVDDMin: u8, /* Max. read current @ VDD min */ - //MaxRdCurrentVDDMax: u8, /* Max. read current @ VDD max */ - //MaxWrCurrentVDDMin: u8, /* Max. write current @ VDD min */ - //MaxWrCurrentVDDMax: u8, /* Max. write current @ VDD max */ - //DeviceSizeMul: u8, /* Device size multiplier */ - pub EraseGrSize: u8, /* Erase group size */ - pub EraseGrMul: u8, /* Erase group size multiplier */ - pub WrProtectGrSize: u8, /* Write protect group size */ - pub WrProtectGrEnable: u8, /* Write protect group enable */ - pub ManDeflECC: u8, /* Manufacturer default ECC */ - pub WrSpeedFact: u8, /* Write speed factor */ - pub MaxWrBlockLen: u8, /* Max. write data block length */ - pub WriteBlockPaPartial: u8, /* Partial blocks for write allowed */ - pub Reserved3: u8, /* Reserded */ - pub ContentProtectAppli: u8, /* Content protection application */ - pub FileFormatGroup: u8, /* File format group */ - pub CopyFlag: u8, /* Copy flag (OTP) */ - pub PermWrProtect: u8, /* Permanent write protection */ - pub TempWrProtect: u8, /* Temporary write protection */ - pub FileFormat: u8, /* File Format */ - pub ECC: u8, /* ECC code */ - pub CSD_CRC: u8, /* CSD CRC */ - pub Reserved4: u8, /* always 1*/ -} - -/** - * Card Identification Data: CID Register - */ -#[derive(Debug, Copy, Clone)] -pub struct SDCardCID { - pub ManufacturerID: u8, /* ManufacturerID */ - pub OEM_AppliID: u16, /* OEM/Application ID */ - pub ProdName1: u32, /* Product Name part1 */ - pub ProdName2: u8, /* Product Name part2*/ - pub ProdRev: u8, /* Product Revision */ - pub ProdSN: u32, /* Product Serial Number */ - pub Reserved1: u8, /* Reserved1 */ - pub ManufactDate: u16, /* Manufacturing Date */ - pub CID_CRC: u8, /* CID CRC */ - pub Reserved2: u8, /* always 1 */ -} - -/** - * Card information - */ -#[derive(Debug, Copy, Clone)] -pub struct SDCardInfo { - pub SD_csd: SDCardCSD, - pub SD_cid: SDCardCID, - pub CardCapacity: u64, /* Card Capacity */ - pub CardBlockSize: u64, /* Card Block Size */ -} - -impl SDCard { - pub fn new( - spi: X, - spi_cs: u32, - cs_gpionum: u8, /*, dmac: &'a DMAC, channel: dma_channel*/ - ) -> Self { - Self { - spi, - spi_cs, - cs_gpionum, - /* - dmac, - channel, - */ - } - } - - fn CS_HIGH(&self) { - gpiohs::set_pin(self.cs_gpionum, true); - } - - fn CS_LOW(&self) { - gpiohs::set_pin(self.cs_gpionum, false); - } - - fn HIGH_SPEED_ENABLE(&self) { - self.spi.set_clk_rate(10000000); - } - - fn lowlevel_init(&self) { - gpiohs::set_direction(self.cs_gpionum, gpio::direction::OUTPUT); - self.spi.set_clk_rate(200000); - } - - fn write_data(&self, data: &[u8]) { - self.spi.configure( - work_mode::MODE0, - frame_format::STANDARD, - 8, /* data bits */ - 0, /* endian */ - 0, /*instruction length*/ - 0, /*address length*/ - 0, /*wait cycles*/ - aitm::STANDARD, - tmod::TRANS, - ); - self.spi.send_data(self.spi_cs, data); - } - - /* - fn write_data_dma(&self, data: &[u32]) { - self.spi.configure( - work_mode::MODE0, - frame_format::STANDARD, - 8, /* data bits */ - 0, /* endian */ - 0, /*instruction length*/ - 0, /*address length*/ - 0, /*wait cycles*/ - aitm::STANDARD, - tmod::TRANS, - ); - self.spi - .send_data_dma(self.dmac, self.channel, self.spi_cs, data); - } - */ - - fn read_data(&self, data: &mut [u8]) { - self.spi.configure( - work_mode::MODE0, - frame_format::STANDARD, - 8, /* data bits */ - 0, /* endian */ - 0, /*instruction length*/ - 0, /*address length*/ - 0, /*wait cycles*/ - aitm::STANDARD, - tmod::RECV, - ); - self.spi.recv_data(self.spi_cs, data); - } - - /* - fn read_data_dma(&self, data: &mut [u32]) { - self.spi.configure( - work_mode::MODE0, - frame_format::STANDARD, - 8, /* data bits */ - 0, /* endian */ - 0, /*instruction length*/ - 0, /*address length*/ - 0, /*wait cycles*/ - aitm::STANDARD, - tmod::RECV, - ); - self.spi - .recv_data_dma(self.dmac, self.channel, self.spi_cs, data); - } - */ - - /* - * Send 5 bytes command to the SD card. - * @param cmd: The user expected command to send to SD card. - * @param arg: The command argument. - * @param crc: The CRC. - * @retval None - */ - fn send_cmd(&self, cmd: CMD, arg: u32, crc: u8) { - /* SD chip select low */ - self.CS_LOW(); - /* Send the Cmd bytes */ - self.write_data(&[ - /* Construct byte 1 */ - ((cmd as u8) | 0x40), - /* Construct byte 2 */ - (arg >> 24) as u8, - /* Construct byte 3 */ - ((arg >> 16) & 0xff) as u8, - /* Construct byte 4 */ - ((arg >> 8) & 0xff) as u8, - /* Construct byte 5 */ - (arg & 0xff) as u8, - /* Construct CRC: byte 6 */ - crc, - ]); - } - - /* Send end-command sequence to SD card */ - fn end_cmd(&self) { - /* SD chip select high */ - self.CS_HIGH(); - /* Send the cmd byte */ - self.write_data(&[0xff]); - } - - /* - * Returns the SD response. - * @param None - * @retval The SD Response: - * - 0xFF: Sequence failed - * - 0: Sequence succeed - */ - fn get_response(&self) -> u8 { - let result = &mut [0u8]; - let mut timeout = 0x0FFF; - /* Check if response is got or a timeout is happen */ - while timeout != 0 { - self.read_data(result); - /* Right response got */ - if result[0] != 0xFF { - return result[0]; - } - timeout -= 1; - } - /* After time out */ - 0xFF - } - - /* - * Get SD card data response. - * @param None - * @retval The SD status: Read data response xxx01 - * - status 010: Data accepted - * - status 101: Data rejected due to a crc error - * - status 110: Data rejected due to a Write error. - * - status 111: Data rejected due to other error. - */ - fn get_dataresponse(&self) -> u8 { - let response = &mut [0u8]; - /* Read response */ - self.read_data(response); - /* Mask unused bits */ - response[0] &= 0x1F; - if response[0] != 0x05 { - return 0xFF; - } - /* Wait null data */ - self.read_data(response); - while response[0] == 0 { - self.read_data(response); - } - /* Return response */ - 0 - } - - /* - * Read the CSD card register - * Reading the contents of the CSD register in SPI mode is a simple - * read-block transaction. - * @param SD_csd: pointer on an SCD register structure - * @retval The SD Response: - * - `Err()`: Sequence failed - * - `Ok(info)`: Sequence succeed - */ - fn get_csdregister(&self) -> Result { - let mut csd_tab = [0u8; 18]; - /* Send CMD9 (CSD register) */ - self.send_cmd(CMD::CMD9, 0, 0); - /* Wait for response in the R1 format (0x00 is no errors) */ - if self.get_response() != 0x00 { - self.end_cmd(); - return Err(()); - } - if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { - self.end_cmd(); - return Err(()); - } - /* Store CSD register value on csd_tab */ - /* Get CRC bytes (not really needed by us, but required by SD) */ - self.read_data(&mut csd_tab); - self.end_cmd(); - /* see also: https://cdn-shop.adafruit.com/datasheets/TS16GUSDHC6.pdf */ - Ok(SDCardCSD { - /* Byte 0 */ - CSDStruct: (csd_tab[0] & 0xC0) >> 6, - SysSpecVersion: (csd_tab[0] & 0x3C) >> 2, - Reserved1: csd_tab[0] & 0x03, - /* Byte 1 */ - TAAC: csd_tab[1], - /* Byte 2 */ - NSAC: csd_tab[2], - /* Byte 3 */ - MaxBusClkFrec: csd_tab[3], - /* Byte 4, 5 */ - CardComdClasses: (u16::from(csd_tab[4]) << 4) | ((u16::from(csd_tab[5]) & 0xF0) >> 4), - /* Byte 5 */ - RdBlockLen: csd_tab[5] & 0x0F, - /* Byte 6 */ - PartBlockRead: (csd_tab[6] & 0x80) >> 7, - WrBlockMisalign: (csd_tab[6] & 0x40) >> 6, - RdBlockMisalign: (csd_tab[6] & 0x20) >> 5, - DSRImpl: (csd_tab[6] & 0x10) >> 4, - Reserved2: 0, - // DeviceSize: (csd_tab[6] & 0x03) << 10, - /* Byte 7, 8, 9 */ - DeviceSize: ((u32::from(csd_tab[7]) & 0x3F) << 16) - | (u32::from(csd_tab[8]) << 8) - | u32::from(csd_tab[9]), - /* Byte 10 */ - EraseGrSize: (csd_tab[10] & 0x40) >> 6, - /* Byte 10, 11 */ - EraseGrMul: ((csd_tab[10] & 0x3F) << 1) | ((csd_tab[11] & 0x80) >> 7), - /* Byte 11 */ - WrProtectGrSize: (csd_tab[11] & 0x7F), - /* Byte 12 */ - WrProtectGrEnable: (csd_tab[12] & 0x80) >> 7, - ManDeflECC: (csd_tab[12] & 0x60) >> 5, - WrSpeedFact: (csd_tab[12] & 0x1C) >> 2, - /* Byte 12,13 */ - MaxWrBlockLen: ((csd_tab[12] & 0x03) << 2) | ((csd_tab[13] & 0xC0) >> 6), - /* Byte 13 */ - WriteBlockPaPartial: (csd_tab[13] & 0x20) >> 5, - Reserved3: 0, - ContentProtectAppli: (csd_tab[13] & 0x01), - /* Byte 14 */ - FileFormatGroup: (csd_tab[14] & 0x80) >> 7, - CopyFlag: (csd_tab[14] & 0x40) >> 6, - PermWrProtect: (csd_tab[14] & 0x20) >> 5, - TempWrProtect: (csd_tab[14] & 0x10) >> 4, - FileFormat: (csd_tab[14] & 0x0C) >> 2, - ECC: (csd_tab[14] & 0x03), - /* Byte 15 */ - CSD_CRC: (csd_tab[15] & 0xFE) >> 1, - Reserved4: 1, - /* Return the response */ - }) - } - - /* - * Read the CID card register. - * Reading the contents of the CID register in SPI mode is a simple - * read-block transaction. - * @param SD_cid: pointer on an CID register structure - * @retval The SD Response: - * - `Err()`: Sequence failed - * - `Ok(info)`: Sequence succeed - */ - fn get_cidregister(&self) -> Result { - let mut cid_tab = [0u8; 18]; - /* Send CMD10 (CID register) */ - self.send_cmd(CMD::CMD10, 0, 0); - /* Wait for response in the R1 format (0x00 is no errors) */ - if self.get_response() != 0x00 { - self.end_cmd(); - return Err(()); - } - if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { - self.end_cmd(); - return Err(()); - } - /* Store CID register value on cid_tab */ - /* Get CRC bytes (not really needed by us, but required by SD) */ - self.read_data(&mut cid_tab); - self.end_cmd(); - Ok(SDCardCID { - /* Byte 0 */ - ManufacturerID: cid_tab[0], - /* Byte 1, 2 */ - OEM_AppliID: (u16::from(cid_tab[1]) << 8) | u16::from(cid_tab[2]), - /* Byte 3, 4, 5, 6 */ - ProdName1: (u32::from(cid_tab[3]) << 24) - | (u32::from(cid_tab[4]) << 16) - | (u32::from(cid_tab[5]) << 8) - | u32::from(cid_tab[6]), - /* Byte 7 */ - ProdName2: cid_tab[7], - /* Byte 8 */ - ProdRev: cid_tab[8], - /* Byte 9, 10, 11, 12 */ - ProdSN: (u32::from(cid_tab[9]) << 24) - | (u32::from(cid_tab[10]) << 16) - | (u32::from(cid_tab[11]) << 8) - | u32::from(cid_tab[12]), - /* Byte 13, 14 */ - Reserved1: (cid_tab[13] & 0xF0) >> 4, - ManufactDate: ((u16::from(cid_tab[13]) & 0x0F) << 8) | u16::from(cid_tab[14]), - /* Byte 15 */ - CID_CRC: (cid_tab[15] & 0xFE) >> 1, - Reserved2: 1, - }) - } - - /* - * Returns information about specific card. - * @param cardinfo: pointer to a SD_CardInfo structure that contains all SD - * card information. - * @retval The SD Response: - * - `Err(())`: Sequence failed - * - `Ok(info)`: Sequence succeed - */ - fn get_cardinfo(&self) -> Result { - let mut info = SDCardInfo { - SD_csd: self.get_csdregister()?, - SD_cid: self.get_cidregister()?, - CardCapacity: 0, - CardBlockSize: 0, - }; - info.CardBlockSize = 1 << u64::from(info.SD_csd.RdBlockLen); - info.CardCapacity = (u64::from(info.SD_csd.DeviceSize) + 1) * 1024 * info.CardBlockSize; - - Ok(info) - } - - /* - * Initializes the SD/SD communication in SPI mode. - * @param None - * @retval The SD Response info if succeeeded, otherwise Err - */ - pub fn init(&self) -> Result { - /* Initialize SD_SPI */ - self.lowlevel_init(); - /* SD chip select high */ - self.CS_HIGH(); - /* NOTE: this reset doesn't always seem to work if the SD access was broken off in the - * middle of an operation: CMDFailed(CMD0, 127). */ - - /* Send dummy byte 0xFF, 10 times with CS high */ - /* Rise CS and MOSI for 80 clocks cycles */ - /* Send dummy byte 0xFF */ - self.write_data(&[0xff; 10]); - /*------------Put SD in SPI mode--------------*/ - /* SD initialized and set to SPI mode properly */ - - /* Send software reset */ - self.send_cmd(CMD::CMD0, 0, 0x95); - let result = self.get_response(); - self.end_cmd(); - if result != 0x01 { - return Err(InitError::CMDFailed(CMD::CMD0, result)); - } - - /* Check voltage range */ - self.send_cmd(CMD::CMD8, 0x01AA, 0x87); - /* 0x01 or 0x05 */ - let result = self.get_response(); - let mut frame = [0u8; 4]; - self.read_data(&mut frame); - self.end_cmd(); - if result != 0x01 { - return Err(InitError::CMDFailed(CMD::CMD8, result)); - } - let mut index = 255; - while index != 0 { - /* */ - self.send_cmd(CMD::CMD55, 0, 0); - let result = self.get_response(); - self.end_cmd(); - if result != 0x01 { - return Err(InitError::CMDFailed(CMD::CMD55, result)); - } - /* Initiate SDC initialization process */ - self.send_cmd(CMD::ACMD41, 0x40000000, 0); - let result = self.get_response(); - self.end_cmd(); - if result == 0x00 { - break; - } - index -= 1; - } - if index == 0 { - return Err(InitError::CMDFailed(CMD::ACMD41, result)); - } - index = 255; - let mut frame = [0u8; 4]; - while index != 0 { - /* Read OCR */ - self.send_cmd(CMD::CMD58, 0, 1); - let result = self.get_response(); - self.read_data(&mut frame); - self.end_cmd(); - if result == 0 { - break; - } - index -= 1; - } - if index == 0 { - return Err(InitError::CMDFailed(CMD::CMD58, result)); - } - if (frame[0] & 0x40) == 0 { - return Err(InitError::CardCapacityStatusNotSet(frame)); - } - self.HIGH_SPEED_ENABLE(); - self.get_cardinfo() - .map_err(|_| InitError::CannotGetCardInfo) - } - - /* - * Reads a block of data from the SD. - * @param data_buf: slice that receives the data read from the SD. - * @param sector: SD's internal address to read from. - * @retval The SD Response: - * - `Err(())`: Sequence failed - * - `Ok(())`: Sequence succeed - */ - pub fn read_sector(&self, data_buf: &mut [u8], sector: u32) -> Result<(), ()> { - assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0); - /* Send CMD17 to read one block, or CMD18 for multiple */ - let flag = if data_buf.len() == SEC_LEN { - self.send_cmd(CMD::CMD17, sector, 0); - false - } else { - self.send_cmd(CMD::CMD18, sector, 0); - true - }; - /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ - if self.get_response() != 0x00 { - self.end_cmd(); - return Err(()); - } - let mut error = false; - //let mut dma_chunk = [0u32; SEC_LEN]; - let mut tmp_chunk = [0u8; SEC_LEN]; - for chunk in data_buf.chunks_mut(SEC_LEN) { - if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { - error = true; - break; - } - /* Read the SD block data : read NumByteToRead data */ - //self.read_data_dma(&mut dma_chunk); - self.read_data(&mut tmp_chunk); - /* Place the data received as u32 units from DMA into the u8 target buffer */ - for (a, b) in chunk.iter_mut().zip(/*dma_chunk*/ tmp_chunk.iter()) { - //*a = (b & 0xff) as u8; - *a = *b; - } - /* Get CRC bytes (not really needed by us, but required by SD) */ - let mut frame = [0u8; 2]; - self.read_data(&mut frame); - } - self.end_cmd(); - if flag { - self.send_cmd(CMD::CMD12, 0, 0); - self.get_response(); - self.end_cmd(); - self.end_cmd(); - } - /* It is an error if not everything requested was read */ - if error { - Err(()) - } else { - Ok(()) - } - } - - /* - * Writes a block to the SD - * @param data_buf: slice containing the data to be written to the SD. - * @param sector: address to write on. - * @retval The SD Response: - * - `Err(())`: Sequence failed - * - `Ok(())`: Sequence succeed - */ - pub fn write_sector(&self, data_buf: &[u8], sector: u32) -> Result<(), ()> { - assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0); - let mut frame = [0xff, 0x00]; - if data_buf.len() == SEC_LEN { - frame[1] = SD_START_DATA_SINGLE_BLOCK_WRITE; - self.send_cmd(CMD::CMD24, sector, 0); - } else { - frame[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE; - self.send_cmd( - CMD::ACMD23, - (data_buf.len() / SEC_LEN).try_into().unwrap(), - 0, - ); - self.get_response(); - self.end_cmd(); - self.send_cmd(CMD::CMD25, sector, 0); - } - /* Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ - if self.get_response() != 0x00 { - self.end_cmd(); - return Err(()); - } - //let mut dma_chunk = [0u32; SEC_LEN]; - let mut tmp_chunk = [0u8; SEC_LEN]; - for chunk in data_buf.chunks(SEC_LEN) { - /* Send the data token to signify the start of the data */ - self.write_data(&frame); - /* Write the block data to SD : write count data by block */ - for (a, &b) in /*dma_chunk*/ tmp_chunk.iter_mut().zip(chunk.iter()) { - //*a = b.into(); - *a = b; - } - //self.write_data_dma(&mut dma_chunk); - self.write_data(&tmp_chunk); - /* Put dummy CRC bytes */ - self.write_data(&[0xff, 0xff]); - /* Read data response */ - if self.get_dataresponse() != 0x00 { - self.end_cmd(); - return Err(()); - } - } - self.end_cmd(); - self.end_cmd(); - Ok(()) - } -} - -/** GPIOHS GPIO number to use for controlling the SD card CS pin */ -const SD_CS_GPIONUM: u8 = 7; -/** CS value passed to SPI controller, this is a dummy value as SPI0_CS3 is not mapping to anything - * in the FPIOA */ -const SD_CS: u32 = 3; - -/** Connect pins to internal functions */ -fn io_init() { - fpioa::set_function(io::SPI0_SCLK, fpioa::function::SPI0_SCLK); - fpioa::set_function(io::SPI0_MOSI, fpioa::function::SPI0_D0); - fpioa::set_function(io::SPI0_MISO, fpioa::function::SPI0_D1); - fpioa::set_function(io::SPI0_CS0, fpioa::function::gpiohs(SD_CS_GPIONUM)); - fpioa::set_io_pull(io::SPI0_CS0, fpioa::pull::DOWN); // GPIO output=pull down -} - -lazy_static! { - static ref PERIPHERALS: UPIntrFreeCell = - unsafe { UPIntrFreeCell::new(Peripherals::take().unwrap()) }; -} - -fn init_sdcard() -> SDCard> { - // wait previous output - usleep(100000); - let peripherals = unsafe { Peripherals::steal() }; - sysctl::pll_set_freq(sysctl::pll::PLL0, 800_000_000).unwrap(); - sysctl::pll_set_freq(sysctl::pll::PLL1, 300_000_000).unwrap(); - sysctl::pll_set_freq(sysctl::pll::PLL2, 45_158_400).unwrap(); - let clocks = k210_hal::clock::Clocks::new(); - peripherals.UARTHS.configure(115_200.bps(), &clocks); - io_init(); - - let spi = peripherals.SPI0.constrain(); - let sd = SDCard::new(spi, SD_CS, SD_CS_GPIONUM); - let info = sd.init().unwrap(); - let num_sectors = info.CardCapacity / 512; - assert!(num_sectors > 0); - - println!("init sdcard!"); - sd -} - -pub struct SDCardWrapper(UPIntrFreeCell>>); - -impl SDCardWrapper { - pub fn new() -> Self { - unsafe { Self(UPIntrFreeCell::new(init_sdcard())) } - } -} - -impl BlockDevice for SDCardWrapper { - fn read_block(&self, block_id: usize, buf: &mut [u8]) { - self.0 - .exclusive_access() - .read_sector(buf, block_id as u32) - .unwrap(); - } - fn write_block(&self, block_id: usize, buf: &[u8]) { - self.0 - .exclusive_access() - .write_sector(buf, block_id as u32) - .unwrap(); - } - fn handle_irq(&self) { - unimplemented!(); - } -} diff --git a/os/src/drivers/block/virtio_blk.rs b/os/src/drivers/block/virtio_blk.rs index ff460d7..fb89084 100644 --- a/os/src/drivers/block/virtio_blk.rs +++ b/os/src/drivers/block/virtio_blk.rs @@ -1,29 +1,19 @@ 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>, + virtio_blk: UPIntrFreeCell>, condvars: BTreeMap, } -lazy_static! { - static ref QUEUE_FRAMES: UPIntrFreeCell> = - 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::::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() -} 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..6871eb8 --- /dev/null +++ b/os/src/drivers/bus/virtio.rs @@ -0,0 +1,52 @@ +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> = + 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 + } +} diff --git a/os/src/drivers/chardev/mod.rs b/os/src/drivers/chardev/mod.rs index 2a04f8e..de1446d 100644 --- a/os/src/drivers/chardev/mod.rs +++ b/os/src/drivers/chardev/mod.rs @@ -1,6 +1,5 @@ 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 = Arc::new(CharDeviceImpl::new()); } diff --git a/os/src/drivers/gpu/mod.rs b/os/src/drivers/gpu/mod.rs index 2dc80d4..759633f 100644 --- a/os/src/drivers/gpu/mod.rs +++ b/os/src/drivers/gpu/mod.rs @@ -1,3 +1,4 @@ +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 = Arc::new(VirtIOGPU::new()); + pub static ref GPU_DEVICE: Arc = Arc::new(VirtIOGpuWrapper::new()); ); -pub struct VirtIOGPU { - gpu: UPIntrFreeCell>, +pub struct VirtIOGpuWrapper { + gpu: UPIntrFreeCell>, 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::::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()) diff --git a/os/src/drivers/input/mod.rs b/os/src/drivers/input/mod.rs index 0acb034..cbe8933 100644 --- a/os/src/drivers/input/mod.rs +++ b/os/src/drivers/input/mod.rs @@ -1,74 +1,75 @@ +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>); +struct VirtIOInputWrapper(UPIntrFreeCell>); -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 = Arc::new(VirtIOINPUT::new(VIRTIO5)); - pub static ref MOUSE_DEVICE: Arc = Arc::new(VirtIOINPUT::new(VIRTIO6)); + pub static ref KEYBOARD_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO5)); + pub static ref MOUSE_DEVICE: Arc = 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::::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), } } } diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs index 4ecf8a9..57a15f0 100644 --- a/os/src/drivers/mod.rs +++ b/os/src/drivers/mod.rs @@ -1,14 +1,12 @@ 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::*; diff --git a/os/src/entry.asm b/os/src/entry.asm index a28dc8f..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: diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs index 18c74d5..531eec4 100644 --- a/os/src/fs/pipe.rs +++ b/os/src/fs/pipe.rs @@ -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 diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs index 33af603..66f4c5a 100644 --- a/os/src/fs/stdio.rs +++ b/os/src/fs/stdio.rs @@ -1,12 +1,7 @@ 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!"); } diff --git a/os/src/gui/button.rs b/os/src/gui/button.rs deleted file mode 100644 index dee2f7c..0000000 --- a/os/src/gui/button.rs +++ /dev/null @@ -1,79 +0,0 @@ -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, -} - -pub struct ButtonInner { - graphic: Graphics, - text: String, - parent: Option>, -} - -impl Button { - pub fn new(size: Size, point: Point, parent: Option>, 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) { - unreachable!() - } - - fn bound( - &self, - ) -> ( - embedded_graphics::prelude::Size, - embedded_graphics::prelude::Point, - ) { - let inner = self.inner.exclusive_access(); - (inner.graphic.size, inner.graphic.point) - } -} diff --git a/os/src/gui/graphic.rs b/os/src/gui/graphic.rs index cda7520..d13ad61 100644 --- a/os/src/gui/graphic.rs +++ b/os/src/gui/graphic.rs @@ -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, + pub drv: Arc, } 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>, { - 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; } diff --git a/os/src/gui/icon.rs b/os/src/gui/icon.rs deleted file mode 100644 index 8d78470..0000000 --- a/os/src/gui/icon.rs +++ /dev/null @@ -1,79 +0,0 @@ -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, -} - -pub struct IconControllerInner { - files: Vec, - graphic: Graphics, - parent: Option>, -} - -impl IconController { - pub fn new(files: Vec, parent: Option>) -> 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::::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) { - todo!() - } - - fn bound(&self) -> (Size, Point) { - todo!() - } -} diff --git a/os/src/gui/image.rs b/os/src/gui/image.rs deleted file mode 100644 index aac5829..0000000 --- a/os/src/gui/image.rs +++ /dev/null @@ -1,80 +0,0 @@ -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, -} - -pub struct ImageInner { - image: &'static [u8], - graphic: Graphics, - parent: Option>, -} - -impl ImageComp { - pub fn new( - size: Size, - point: Point, - v: &'static [u8], - parent: Option>, - ) -> 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::::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) { - todo!() - } - - fn bound(&self) -> (Size, Point) { - let inner = self.inner.exclusive_access(); - (inner.graphic.size, inner.graphic.point) - } -} diff --git a/os/src/gui/mod.rs b/os/src/gui/mod.rs index da3aae9..766ded8 100644 --- a/os/src/gui/mod.rs +++ b/os/src/gui/mod.rs @@ -1,21 +1,5 @@ -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); - fn bound(&self) -> (Size, Point); -} +use graphic::Graphics; +pub use paint::{init_paint, move_rect, reset}; diff --git a/os/src/gui/paint.rs b/os/src/gui/paint.rs new file mode 100644 index 0000000..0cdf60d --- /dev/null +++ b/os/src/gui/paint.rs @@ -0,0 +1,62 @@ +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 = 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(); + }); +} diff --git a/os/src/gui/panel.rs b/os/src/gui/panel.rs deleted file mode 100644 index 2af2961..0000000 --- a/os/src/gui/panel.rs +++ /dev/null @@ -1,66 +0,0 @@ -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, -} -struct PanelInner { - graphic: Graphics, - comps: VecDeque>, -} - -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) { - 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) - } -} diff --git a/os/src/gui/terminal.rs b/os/src/gui/terminal.rs deleted file mode 100644 index 8d233f5..0000000 --- a/os/src/gui/terminal.rs +++ /dev/null @@ -1,105 +0,0 @@ -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, -} - -pub struct TerminalInner { - pub text: String, - titel: Option, - graphic: Graphics, - comps: VecDeque>, -} - -impl Terminal { - pub fn new( - size: Size, - point: Point, - parent: Option>, - titel: Option, - 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) { - 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) - } -} diff --git a/os/src/linker-k210.ld b/os/src/linker-k210.ld deleted file mode 100644 index eaa2c9f..0000000 --- a/os/src/linker-k210.ld +++ /dev/null @@ -1,53 +0,0 @@ -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 diff --git a/os/src/main.rs b/os/src/main.rs index 014b029..299e4cf 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -2,7 +2,7 @@ #![no_main] #![feature(panic_info_message)] #![feature(alloc_error_handler)] -#[cfg(feature = "board_qemu")] + use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; extern crate alloc; @@ -10,10 +10,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 +18,6 @@ mod console; mod config; mod drivers; mod fs; -#[cfg(feature = "board_qemu")] mod gui; mod lang_items; mod mm; @@ -33,7 +28,7 @@ mod task; mod timer; mod trap; -// use syscall::create_desktop; //for test +//use syscall::create_desktop; //for test core::arch::global_asm!(include_str!("entry.asm")); @@ -61,21 +56,18 @@ 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(); 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(); diff --git a/os/src/mm/frame_allocator.rs b/os/src/mm/frame_allocator.rs index 3c62324..1ecfe99 100644 --- a/os/src/mm/frame_allocator.rs +++ b/os/src/mm/frame_allocator.rs @@ -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 { diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 0862dc7..663b68d 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -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( diff --git a/os/src/sbi.rs b/os/src/sbi.rs index a218ec8..5113c90 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -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() } diff --git a/os/src/syscall/gui.rs b/os/src/syscall/gui.rs deleted file mode 100644 index 82c42af..0000000 --- a/os/src/syscall/gui.rs +++ /dev/null @@ -1,63 +0,0 @@ -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> = unsafe { - UPIntrFreeCell::new(Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0)))) - }; - pub static ref PAD:UPIntrFreeCell>> = unsafe { - UPIntrFreeCell::new(None) - }; -); - -pub fn create_desktop() -> isize { - let mut p: Arc = - 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); -} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 70a1f16..fa4a4cf 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -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]), diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 31c792c..97e76bd 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -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 {} ...", diff --git a/os/src/timer.rs b/os/src/timer.rs index 06a70de..7be2d01 100644 --- a/os/src/timer.rs +++ b/os/src/timer.rs @@ -61,13 +61,14 @@ pub fn add_timer(expire_ms: usize, task: Arc) { 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; + } } - } + }); } diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index ce2aae8..cb4a7ad 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -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 diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index abcacd9..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2022-04-11 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..553747b --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +profile = "minimal" +# use the nightly version of the last stable toolchain, see +channel = "nightly-2022-08-05" +components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] diff --git a/user/.cargo/config b/user/.cargo/config index e5ded8a..334d01e 100644 --- a/user/.cargo/config +++ b/user/.cargo/config @@ -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 542a624..a609f4d 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -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 diff --git a/user/src/bin/gui.rs b/user/src/bin/gui.rs deleted file mode 100644 index e3f7ec2..0000000 --- a/user/src/bin/gui.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![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 -} - diff --git a/user/src/bin/huge_write_mt.rs b/user/src/bin/huge_write_mt.rs index 8ca7578..0a60fd8 100644 --- a/user/src/bin/huge_write_mt.rs +++ b/user/src/bin/huge_write_mt.rs @@ -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}; diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs index 5551997..089841d 100644 --- a/user/src/bin/peterson.rs +++ b/user/src/bin/peterson.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![feature(core_intrinsics)] -#![feature(asm)] #[macro_use] extern crate user_lib; diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs index 5f50b0d..d3e329d 100644 --- a/user/src/bin/run_pipe_test.rs +++ b/user/src/bin/run_pipe_test.rs @@ -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::()]); } else { diff --git a/user/src/bin/stack_overflow.rs b/user/src/bin/stack_overflow.rs index 5d365f5..3bec557 100644 --- a/user/src/bin/stack_overflow.rs +++ b/user/src/bin/stack_overflow.rs @@ -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] diff --git a/user/src/bin/stackful_coroutine.rs b/user/src/bin/stackful_coroutine.rs new file mode 100644 index 0000000..d9f3b94 --- /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/lib.rs b/user/src/lib.rs index 6f57edd..729eaef 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -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) => { diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 3f36f53..b4bb67a 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -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