diff --git a/.github/workflows/doc-and-test.yml b/.github/workflows/doc-and-test.yml index 1e6f0b1c..6da799db 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-07-20 + 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-07-20 + 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 f411b223..852c161c 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 bf81ab53..6a406554 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ // For Rust Analyzer plugin users: "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.cargo.features": [ - "board_qemu" - ] + // "rust-analyzer.cargo.features": [ + // "board_qemu" + // ] } \ No newline at end of file diff --git a/README.md b/README.md index e74826f4..2df01b07 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,4 @@ # rCore-Tutorial-v3 - -![](figures/logo.png) - -**Welcome to JOIN** [**Open-Source-OS-Training-Camp-2022 !**](https://learningos.github.io/rust-based-os-comp2022/) - rCore-Tutorial version 3.6. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/). rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS) diff --git a/bootloader/rustsbi-k210.bin b/bootloader/rustsbi-k210.bin deleted file mode 100755 index c53ed1fc..00000000 Binary files a/bootloader/rustsbi-k210.bin and /dev/null differ diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml index 0527e9b8..d8d4e7fe 100644 --- a/easy-fs-fuse/Cargo.toml +++ b/easy-fs-fuse/Cargo.toml @@ -11,6 +11,6 @@ clap = "2.33.3" easy-fs = { path = "../easy-fs" } rand = "0.8.0" -[features] -board_qemu = [] -board_k210 = [] \ No newline at end of file +# [features] +# board_qemu = [] +# board_k210 = [] diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs index 17b24654..bb9ff761 100644 --- a/easy-fs-fuse/src/main.rs +++ b/easy-fs-fuse/src/main.rs @@ -85,9 +85,9 @@ fn easy_fs_pack() -> std::io::Result<()> { inode.write_at(0, all_data.as_slice()); } // list apps - for app in root_inode.ls() { - println!("{}", app); - } + // for app in root_inode.ls() { + // println!("{}", app); + // } Ok(()) } diff --git a/os/Cargo.toml b/os/Cargo.toml index 915e5f92..fbcff883 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -10,21 +10,15 @@ edition = "2021" riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] } buddy_system_allocator = "0.6" +bit_field = "0.10.0" bitflags = "1.2.1" xmas-elf = "0.7.0" volatile = "0.3" virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } -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" } +lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "3f467dd" } 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 0658a5e0..ac0fd273 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 := 0x80000000 # Binutils OBJDUMP := rust-objdump --arch-name=riscv64 @@ -40,14 +36,7 @@ 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) @@ -55,11 +44,6 @@ env: 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,44 +73,53 @@ disasm-vim: kernel run: run-inner -gui: build -ifeq ($(BOARD),qemu) +run-inner-none: build @qemu-system-riscv64 \ -M 128m \ -machine virt \ - -bios $(BOOTLOADER) \ - -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ + -bios none \ + $(GUI_OPTION) \ + -kernel $(KERNEL_ELF) \ -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ -device virtio-blk-device,drive=x0 \ - -device virtio-gpu-device \ +# -device virtio-gpu-device \ -device virtio-keyboard-device \ -device virtio-mouse-device \ - -serial stdio -endif + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000 \ + -serial stdio run-inner: build -ifeq ($(BOARD),qemu) @qemu-system-riscv64 \ -M 128m \ -machine virt \ -bios $(BOOTLOADER) \ - -display none \ + $(GUI_OPTION) \ -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ -device virtio-blk-device,drive=x0 \ -device virtio-gpu-device \ -device virtio-keyboard-device \ -device virtio-mouse-device \ + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000 \ -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-none: build + @tmux new-session -d \ + "qemu-system-riscv64 -machine virt -nographic -bios none -kernel $(KERNEL_ELF) \ + -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0 \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ + -serial stdio \ + -s -S" && \ + tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ + tmux -2 attach-session -d debug: build @tmux new-session -d \ @@ -141,4 +134,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 c7c886d1..00000000 --- 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 7f2a5bb4..00000000 --- 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 f9e96d6c..00000000 --- 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 249e49fe..00000000 --- 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 1dae9f3d..ffb88539 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -2,7 +2,7 @@ pub const CLOCK_FREQ: usize = 12500000; pub const MMIO: &[(usize, usize)] = &[ (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine - (0x2000000, 0x10000), + (0x2000000, 0x10000), // core local interrupter (CLINT) (0xc000000, 0x210000), // VIRT_PLIC in virt machine (0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine ]; @@ -12,8 +12,9 @@ pub type CharDeviceImpl = crate::drivers::chardev::NS16550a; pub const VIRT_PLIC: usize = 0xC00_0000; pub const VIRT_UART: usize = 0x1000_0000; - +#[allow(unused)] pub const VIRTGPU_XRES: u32 = 1280; +#[allow(unused)] pub const VIRTGPU_YRES: u32 = 800; use crate::drivers::block::BLOCK_DEVICE; @@ -30,7 +31,7 @@ pub fn device_init() { plic.set_threshold(hart_id, supervisor, 0); plic.set_threshold(hart_id, machine, 1); //irq nums: 5 keyboard, 6 mouse, 8 block, 10 uart - for intr_src_id in [5usize, 6, 8 , 10] { + for intr_src_id in [5usize, 6, 8, 10] { plic.enable(hart_id, supervisor, intr_src_id); plic.set_priority(intr_src_id, 1); } @@ -52,6 +53,58 @@ pub fn irq_handler() { plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); } + +// core local interrupter (CLINT), which contains the timer +pub const CLINT: usize = 0x2000000; +// pub const fn clint_mtimecmp(hartid: usize) -> usize { +// CLINT + 0x4000 + 8 * hartid +// } +pub const CLINT_MTIME: usize = CLINT + 0xBFF8; // Cycles since boot. +pub const CLINT_MTIMECMP: usize = CLINT + 0x4000; + +#[naked] +#[repr(align(16))] // if miss this alignment, a load access fault will occur. +#[no_mangle] +pub unsafe extern "C" fn timervec() -> ! { + // start.rs has set up the memory that mscratch points to: + // scratch[0,8,16] : register save area. + // scratch[24] : address of CLINT's MTIMECMP register. + // scratch[32] : desired interval between interrupts. + + // Now, mscrach has a pointer to an additional scratch space. + // to aboid overwriting the contents of the integer registers, + // the prologue of an interrupts handler usually begins by swapping + // an integer register(say a0) with mscratch CSR. + // The interrupt handler stores the integer registers + // used for processing in this scratch space. + // a0 saved in mscrach, a1 ~ a3 saved in scratch space. + //loop {} + asm!( + "csrrw a0, mscratch, a0", + "sd a1, 0(a0)", + "sd a2, 8(a0)", + "sd a3, 16(a0)", + // schedule the next timer interrupt + // by adding interval to mtimecmp. + "ld a1, 24(a0)", // CLINT_MTIMECMP(hartid) contents + "ld a2, 32(a0)", // interval + "ld a3, 0(a1)", + "add a3, a3, a2", + "sd a3, 0(a1)", + // raise a supervisor software interrupt. + "li a1, 2", + "csrw sip, a1", + // restore and return + "ld a3, 16(a0)", + "ld a2, 8(a0)", + "ld a1, 0(a0)", + "csrrw a0, mscratch, a0", + "mret", + options(noreturn) + ); +} + + //ref:: https://github.com/andre-richter/qemu-exit use core::arch::asm; diff --git a/os/src/console.rs b/os/src/console.rs index 5c8daaf7..085637ba 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 7361ec83..add8da00 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 756e9a00..00000000 --- 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 2d853e0c..34a93e0d 100644 --- a/os/src/drivers/block/virtio_blk.rs +++ b/os/src/drivers/block/virtio_blk.rs @@ -1,4 +1,5 @@ use super::BlockDevice; +use crate::drivers::bus::virtio::VirtioHal; use crate::sync::{Condvar, UPIntrFreeCell}; use crate::task::schedule; use crate::DEV_NON_BLOCKING_ACCESS; @@ -69,7 +70,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(); @@ -83,4 +86,3 @@ impl VirtIOBlock { } } } - diff --git a/os/src/drivers/bus/mod.rs b/os/src/drivers/bus/mod.rs index ab8f38fd..d43f3046 100644 --- a/os/src/drivers/bus/mod.rs +++ b/os/src/drivers/bus/mod.rs @@ -1 +1 @@ -pub mod virtio; \ No newline at end of file +pub mod virtio; diff --git a/os/src/drivers/bus/virtio.rs b/os/src/drivers/bus/virtio.rs index fe7bc127..d9a61219 100644 --- a/os/src/drivers/bus/virtio.rs +++ b/os/src/drivers/bus/virtio.rs @@ -1,9 +1,9 @@ -use alloc::vec::Vec; use crate::mm::{ frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum, - StepByOne, VirtAddr, + StepByOne, VirtAddr, frame_alloc_more, }; use crate::sync::UPIntrFreeCell; +use alloc::vec::Vec; use lazy_static::*; use virtio_drivers::Hal; @@ -16,15 +16,9 @@ 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 trakcers = frame_alloc_more(pages); + let ppn_base = trakcers.as_ref().unwrap().last().unwrap().ppn; + QUEUE_FRAMES.exclusive_access().append(&mut trakcers.unwrap()); let pa: PhysAddr = ppn_base.into(); pa.0 } @@ -49,4 +43,4 @@ impl Hal for VirtioHal { .unwrap() .0 } -} \ No newline at end of file +} diff --git a/os/src/drivers/chardev/mod.rs b/os/src/drivers/chardev/mod.rs index 2a04f8ed..64c168f3 100644 --- a/os/src/drivers/chardev/mod.rs +++ b/os/src/drivers/chardev/mod.rs @@ -1,17 +1,17 @@ mod ns16550a; -#[cfg(feature = "board_qemu")] use crate::board::CharDeviceImpl; use alloc::sync::Arc; use lazy_static::*; pub use ns16550a::NS16550a; pub trait CharDevice { + fn init(&self); fn read(&self) -> u8; fn write(&self, ch: u8); fn handle_irq(&self); } -#[cfg(feature = "board_qemu")] + lazy_static! { pub static ref UART: Arc = Arc::new(CharDeviceImpl::new()); } diff --git a/os/src/drivers/chardev/ns16550a.rs b/os/src/drivers/chardev/ns16550a.rs index da290633..7122cec1 100644 --- a/os/src/drivers/chardev/ns16550a.rs +++ b/os/src/drivers/chardev/ns16550a.rs @@ -131,19 +131,30 @@ pub struct NS16550a { impl NS16550a { pub fn new() -> Self { - let mut inner = NS16550aInner { + let inner = NS16550aInner { ns16550a: NS16550aRaw::new(BASE_ADDR), read_buffer: VecDeque::new(), }; - inner.ns16550a.init(); + //inner.ns16550a.init(); Self { inner: unsafe { UPIntrFreeCell::new(inner) }, condvar: Condvar::new(), } } + + pub fn read_buffer_is_empty(&self) -> bool { + self.inner + .exclusive_session(|inner| inner.read_buffer.is_empty()) + } } impl CharDevice for NS16550a { + fn init(&self) { + let mut inner = self.inner.exclusive_access(); + inner.ns16550a.init(); + drop(inner); + } + fn read(&self) -> u8 { loop { let mut inner = self.inner.exclusive_access(); diff --git a/os/src/drivers/gpu/mod.rs b/os/src/drivers/gpu/mod.rs index 29028213..b1281e1d 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; @@ -6,25 +7,26 @@ use tinybmp::Bmp; use virtio_drivers::{VirtIOGpu, VirtIOHeader}; use crate::drivers::bus::virtio::VirtioHal; 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 { +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(); @@ -53,11 +55,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 1da66a6d..a9f5f0bd 100644 --- a/os/src/drivers/input/mod.rs +++ b/os/src/drivers/input/mod.rs @@ -1,75 +1,83 @@ -use crate::{ - gui::{Button, Component}, - sync::UPIntrFreeCell, - syscall::PAD, -}; -use alloc::{string::ToString, 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 crate::drivers::bus::virtio::VirtioHal; -use virtio_input_decoder::{Decoder, Key, KeyType}; - -use super::GPU_DEVICE; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use core::any::Any; +use virtio_drivers::{VirtIOHeader, VirtIOInput}; const VIRTIO5: usize = 0x10005000; const VIRTIO6: usize = 0x10006000; -struct VirtIOINPUT(UPIntrFreeCell>); +struct VirtIOInputInner { + virtio_input: VirtIOInput<'static, VirtioHal>, + events: VecDeque, +} -pub trait INPUTDevice: Send + Sync + Any { +struct VirtIOInputWrapper { + inner: UPIntrFreeCell, + condvar: Condvar, +} + +pub trait InputDevice: Send + Sync + Any { + fn read_event(&self) -> u64; fn handle_irq(&self); + fn is_empty(&self) -> bool; } lazy_static::lazy_static!( - pub static ref KEYBOARD_DEVICE: Arc = Arc::new(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()) - }) - } -} - -impl INPUTDevice for VirtIOINPUT { - 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, + let inner = VirtIOInputInner { + virtio_input: unsafe { + VirtIOInput::::new(&mut *(addr as *mut VirtIOHeader)).unwrap() + }, + events: VecDeque::new(), }; - 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()) - } - } - Err(_) => {} - } - } - } - virtio_input_decoder::DecodeType::Mouse(mouse) => println!("{:?}", mouse), + Self { + inner: unsafe { UPIntrFreeCell::new(inner) }, + condvar: Condvar::new(), } } } + +impl InputDevice for VirtIOInputWrapper { + fn is_empty(&self) -> bool { + self.inner.exclusive_access().events.is_empty() + } + + fn read_event(&self) -> u64 { + loop { + let mut inner = self.inner.exclusive_access(); + if let Some(event) = inner.events.pop_front() { + return event; + } else { + let task_cx_ptr = self.condvar.wait_no_sched(); + drop(inner); + schedule(task_cx_ptr); + } + } + } + + fn handle_irq(&self) { + let mut count = 0; + let mut result = 0; + self.inner.exclusive_session(|inner| { + inner.virtio_input.ack_interrupt(); + while let Some(event) = inner.virtio_input.pop_pending_event() { + count += 1; + result = (event.event_type as u64) << 48 + | (event.code as u64) << 32 + | (event.value) as u64; + inner.events.push_back(result); + } + }); + if count > 0 { + self.condvar.signal(); + }; + } +} diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs index cdd37574..e16c51ea 100644 --- a/os/src/drivers/mod.rs +++ b/os/src/drivers/mod.rs @@ -1,18 +1,14 @@ pub mod block; -pub mod chardev; -#[cfg(feature = "board_qemu")] -pub mod gpu; -#[cfg(feature = "board_qemu")] -pub mod input; -#[cfg(feature = "board_qemu")] pub mod bus; +pub mod chardev; +pub mod gpu; +pub mod input; +pub mod net; pub mod plic; + pub use block::BLOCK_DEVICE; -#[cfg(feature = "board_qemu")] -pub use chardev::UART; -#[cfg(feature = "board_qemu")] -pub use gpu::*; -#[cfg(feature = "board_qemu")] -pub use input::*; -#[cfg(feature = "board_qemu")] pub use bus::*; +pub use chardev::UART; +pub use gpu::*; +pub use input::*; +pub use net::*; diff --git a/os/src/drivers/net/mod.rs b/os/src/drivers/net/mod.rs new file mode 100644 index 00000000..e1f76b00 --- /dev/null +++ b/os/src/drivers/net/mod.rs @@ -0,0 +1,41 @@ +use core::any::Any; + +use alloc::sync::Arc; +use lazy_static::*; +use virtio_drivers::{VirtIONet, VirtIOHeader}; +use crate::drivers::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; + +const VIRTIO8: usize = 0x10004000; + +lazy_static! { + pub static ref NET_DEVICE: Arc = Arc::new(VirtIONetWrapper::new()); +} + +pub trait NetDevice: Send + Sync + Any { + fn transmit(&self, data: &[u8]); + fn receive(&self, data: &mut [u8]) -> usize; +} + +pub struct VirtIONetWrapper(UPIntrFreeCell>); + +impl NetDevice for VirtIONetWrapper { + fn transmit(&self, data: &[u8]) { + self.0.exclusive_access().send(data).expect("can't send data") + } + + fn receive(&self, data: &mut [u8]) -> usize { + self.0.exclusive_access().recv(data).expect("can't receive data") + } +} + +impl VirtIONetWrapper { + pub fn new() -> Self { + unsafe { + let virtio = + VirtIONet::::new(&mut *(VIRTIO8 as *mut VirtIOHeader)) + .expect("can't create net device by virtio"); + VirtIONetWrapper(UPIntrFreeCell::new(virtio)) + } + } +} \ No newline at end of file diff --git a/os/src/entry.asm b/os/src/entry.asm index a28dc8ff..1d1a1752 100644 --- a/os/src/entry.asm +++ b/os/src/entry.asm @@ -2,11 +2,11 @@ .globl _start _start: la sp, boot_stack_top - call rust_main + call rust_start .section .bss.stack - .globl boot_stack -boot_stack: + .globl boot_stack_lower_bound +boot_stack_lower_bound: .space 4096 * 16 .globl boot_stack_top boot_stack_top: diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs index 18c74d51..d10dc33b 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,9 +160,12 @@ 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; } } } diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs index 33af6033..66f4c5ae 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 dee2f7c2..00000000 --- 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 deleted file mode 100644 index cda75206..00000000 --- a/os/src/gui/graphic.rs +++ /dev/null @@ -1,57 +0,0 @@ -use alloc::sync::Arc; -use embedded_graphics::{ - draw_target::DrawTarget, - pixelcolor::Rgb888, - prelude::{OriginDimensions, Point, RgbColor, Size}, -}; - -use crate::drivers::{GPUDevice, GPU_DEVICE,}; -use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES}; - -#[derive(Clone)] -pub struct Graphics { - pub size: Size, - pub point: Point, - pub drv: Arc, -} - -impl Graphics { - pub fn new(size: Size, point: Point) -> Self { - Self { - size, - point, - drv: GPU_DEVICE.clone(), - } - } -} - -impl OriginDimensions for Graphics { - fn size(&self) -> Size { - self.size - } -} - -impl DrawTarget for Graphics { - type Color = Rgb888; - - type Error = core::convert::Infallible; - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - let fb = self.drv.getfreambuffer(); - - 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; - if idx + 2 >= fb.len() { - return; - } - fb[idx] = px.1.b(); - fb[idx + 1] = px.1.g(); - fb[idx + 2] = px.1.r(); - }); - self.drv.flush(); - Ok(()) - } -} diff --git a/os/src/gui/icon.rs b/os/src/gui/icon.rs deleted file mode 100644 index 8d784709..00000000 --- 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 aac5829a..00000000 --- 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 deleted file mode 100644 index da3aae9b..00000000 --- a/os/src/gui/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -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::*; - -pub trait Component: Send + Sync + Any { - fn paint(&self); - fn add(&self, comp: Arc); - fn bound(&self) -> (Size, Point); -} diff --git a/os/src/gui/panel.rs b/os/src/gui/panel.rs deleted file mode 100644 index 2af2961a..00000000 --- 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 8d233f56..00000000 --- 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 eaa2c9ff..00000000 --- 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/linker-qemu.ld b/os/src/linker-qemu.ld index 5baafbd0..92dd51cd 100644 --- a/os/src/linker-qemu.ld +++ b/os/src/linker-qemu.ld @@ -1,6 +1,6 @@ OUTPUT_ARCH(riscv) ENTRY(_start) -BASE_ADDRESS = 0x80200000; +BASE_ADDRESS = 0x80000000; SECTIONS { diff --git a/os/src/main.rs b/os/src/main.rs index 014b0299..83554ae7 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -2,28 +2,24 @@ #![no_main] #![feature(panic_info_message)] #![feature(alloc_error_handler)] -#[cfg(feature = "board_qemu")] -use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; +#![feature(naked_functions)] +#![feature(fn_align)] +//use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE, INPUT_CONDVAR}; +use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; extern crate alloc; #[macro_use] extern crate bitflags; -#[cfg(feature = "board_k210")] -#[path = "boards/k210.rs"] -mod board; -#[cfg(not(any(feature = "board_k210")))] #[path = "boards/qemu.rs"] mod board; - +use board::*; #[macro_use] mod console; mod config; mod drivers; mod fs; -#[cfg(feature = "board_qemu")] -mod gui; mod lang_items; mod mm; mod sbi; @@ -32,8 +28,17 @@ mod syscall; mod task; mod timer; mod trap; +mod net; -// use syscall::create_desktop; //for test +use riscv::register::*; +// mod riscvreg; +// use riscvreg::{ +// mstatus, mepc, satp, medeleg, mideleg, sie, mhartid, tp, clint, +// mscratch, mtvec, mie, sstatus +// }; +// use riscvregs::registers::*; +// use riscvregs::registers::pmpcfg0::*; +//use syscall::create_desktop; //for test core::arch::global_asm!(include_str!("entry.asm")); @@ -56,26 +61,212 @@ lazy_static! { unsafe { UPIntrFreeCell::new(false) }; } + +#[repr(C, align(16))] +struct Stack([u8; 4096 * 4 * 1]); + +#[no_mangle] +static mut STACK0: Stack = Stack([0; 4096 * 4 * 1]); + +#[inline] +pub unsafe fn medeleg_write(medeleg: usize){ + core::arch::asm!("csrw medeleg, {}",in(reg)medeleg); +} + +pub unsafe fn mideleg_write(mideleg: usize) { + core::arch::asm!("csrw mideleg, {}", in(reg)mideleg); +} + +pub enum SIE { + SEIE = 1 << 9, // external + STIE = 1 << 5, // timer + SSIE = 1 << 1, // software +} + +#[inline] +pub unsafe fn sie_read() -> usize { + let ret:usize; + core::arch::asm!("csrr {}, sie", out(reg)ret); + ret +} + +#[inline] +pub unsafe fn sie_write(x:usize) { + core::arch::asm!("csrw sie, {}", in(reg)x); +} + +/// enable all software interrupts +/// still need to set SIE bit in sstatus +pub unsafe fn intr_on() { + let mut sie = sie_read(); + sie |= SIE::SSIE as usize | SIE::STIE as usize | SIE::SEIE as usize; + sie_write(sie); +} + +#[no_mangle] +pub unsafe fn rust_start() -> ! { + // set MPP mode to Supervisor, for mret + mstatus::set_mpp(mstatus::MPP::Supervisor); + + // set MEPC to main, for mret + mepc::write(rust_main as usize); + + // disable paging for now. + satp::write(0); + + // delegate all interrupts and exceptions to supervisor mode. + medeleg_write(0xffff); + mideleg_write(0xffff); + intr_on(); + + // configure Physical Memory Protection to give supervisor mode + // access to all of physical memory. + pmpaddr0::write(0x3fffffffffffff); + pmpcfg0::write(0xf); + //pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0 + + // ask for clock interrupts. + timer_init(); + + // keep each CPU's hartid in its tp register, for cpuid(). + // let id = mhartid::read(); + // core::arch::asm!("mv tp, {0}", in(reg) id); + + // switch to supervisor mode and jump to main(). + core::arch::asm!("mret"); + + extern "C" { + fn rust_main() -> !; + } + core::hint::unreachable_unchecked(); +} + +use core::convert::Into; +use core::ptr; + +// a scratch area per CPU for machine-mode timer interrupts. +static mut TIMER_SCRATCH: [u64; 5] = [0; 5]; + +#[inline] +unsafe fn read_mtime() -> u64 { + ptr::read_volatile(Into::::into(CLINT_MTIME) as *const u64) +} + +unsafe fn write_mtimecmp(value: u64) { + let offset = Into::::into(CLINT_MTIMECMP); + ptr::write_volatile(offset as *mut u64, value); +} + +pub unsafe fn add_mtimecmp(interval:u64){ + let value = read_mtime(); + write_mtimecmp(value+interval); +} + +pub fn count_mtiecmp() -> usize{ + let ret:usize; + ret = Into::::into(CLINT) + 0x4000; + ret +} + +#[inline] +pub unsafe fn mtvec_write(x:usize){ + core::arch::asm!("csrw mtvec, {}",in(reg)x); +} + +use bit_field::BitField; + +#[inline] +unsafe fn mstatus_read() -> usize { + let ret:usize; + core::arch::asm!("csrr {}, mstatus",out(reg)ret); + ret +} + +#[inline] +unsafe fn mstatus_write(x: usize) { + core::arch::asm!("csrw mstatus, {}",in(reg)x); +} + +// enable machine-mode interrupts. +pub unsafe fn mstatus_enable_interrupt(){ + let mut mstatus = mstatus_read(); + mstatus.set_bit(3, true); + mstatus_write(mstatus); +} + + +pub enum MIE { + MEIE = 1 << 11, // external + MTIE = 1 << 7, // timer + MSIE = 1 << 3 // software +} + +#[inline] +pub unsafe fn mie_read() -> usize { + let ret:usize; + core::arch::asm!("csrr {}, mie", out(reg)ret); + ret +} + +#[inline] +pub unsafe fn mie_write(x:usize){ + core::arch::asm!("csrw mie, {}",in(reg)x); +} + +unsafe fn timer_init() { + clear_bss(); + // each CPU has a separate source of timer interrupts + //let id = mhartid::read(); + + // ask the CLINT for a timer interrupts + let interval = 1000000u64; // cycles; about 1/10th second in qemu. + add_mtimecmp(interval); + // let mtimecmp = board::clint_mtimecmp(0) as *mut u64; + // let mtime = board::CLINT_MTIME as *const u64; + // mtimecmp.write_volatile(mtime.read_volatile() + interval); + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + let scratch = &mut TIMER_SCRATCH; + scratch[3] = count_mtiecmp() as u64; + scratch[4] = interval; + mscratch::write(scratch.as_mut_ptr() as usize); + + // set the machine-mode trap handler + mtvec_write(timervec as usize); + //mtvec::write(board::timervec as usize, mtvec::TrapMode::Direct); + + // enable machine-mode interrupts. + mstatus_enable_interrupt(); + //mstatus::set_mie(); + + // enable machine-mode timer interrupts. + mie_write(mie_read() | MIE::MTIE as usize); + //mie::set_mtimer(); +} + +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; #[no_mangle] pub fn rust_main() -> ! { - clear_bss(); + + //clear_bss(); mm::init(); + UART.init(); println!("KERN: init gpu"); - #[cfg(feature = "board_qemu")] - GPU_DEVICE.clone(); + let _gpu = GPU_DEVICE.clone(); println!("KERN: init keyboard"); - #[cfg(feature = "board_qemu")] - KEYBOARD_DEVICE.clone(); + let _keyboard = KEYBOARD_DEVICE.clone(); println!("KERN: init mouse"); - #[cfg(feature = "board_qemu")] - MOUSE_DEVICE.clone(); + let _mouse = MOUSE_DEVICE.clone(); println!("KERN: init trap"); trap::init(); - trap::enable_timer_interrupt(); - timer::set_next_trigger(); + //trap::enable_timer_interrupt(); + //timer::set_next_trigger(); board::device_init(); fs::list_apps(); - //syscall::create_desktop(); //for test 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 3c62324b..3635e18e 100644 --- a/os/src/mm/frame_allocator.rs +++ b/os/src/mm/frame_allocator.rs @@ -35,6 +35,7 @@ impl Drop for FrameTracker { trait FrameAllocator { fn new() -> Self; fn alloc(&mut self) -> Option; + fn alloc_more(&mut self, pages: usize) -> Option>; fn dealloc(&mut self, ppn: PhysPageNum); } @@ -48,7 +49,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 { @@ -69,6 +70,16 @@ impl FrameAllocator for StackFrameAllocator { Some((self.current - 1).into()) } } + fn alloc_more(&mut self, pages: usize) -> Option> { + if self.current + pages >= self.end { + None + } else { + self.current += pages; + let arr:Vec = (1..pages + 1).collect(); + let v = arr.iter().map(|x| (self.current - x).into()).collect(); + Some(v) + } + } fn dealloc(&mut self, ppn: PhysPageNum) { let ppn = ppn.0; // validity check @@ -104,6 +115,13 @@ pub fn frame_alloc() -> Option { .map(FrameTracker::new) } +pub fn frame_alloc_more(num: usize) -> Option> { + FRAME_ALLOCATOR + .exclusive_access() + .alloc_more(num) + .map(|x| x.iter().map(|&t| FrameTracker::new(t)).collect()) +} + pub fn frame_dealloc(ppn: PhysPageNum) { FRAME_ALLOCATOR.exclusive_access().dealloc(ppn); } @@ -125,3 +143,21 @@ pub fn frame_allocator_test() { drop(v); println!("frame_allocator_test passed!"); } + + +#[allow(unused)] +pub fn frame_allocator_alloc_more_test() { + let mut v: Vec = Vec::new(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + v.extend(frames); + v.clear(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + drop(v); + println!("frame_allocator_test passed!"); +} diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 0862dc7a..7cdd394a 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -71,7 +71,10 @@ impl MemorySet { self.areas.remove(idx); } } - fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { + /// Add a new MapArea into this MemorySet. + /// Assuming that there are no conflicts in the virtual address + /// space. + pub fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { map_area.map(&mut self.page_table); if let Some(data) = data { map_area.copy_data(&mut self.page_table, data); @@ -92,14 +95,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 +112,7 @@ impl MemorySet { ), None, ); - println!("mapping .rodata section"); + // println!("mapping .rodata section"); memory_set.push( MapArea::new( (srodata as usize).into(), @@ -119,7 +122,7 @@ impl MemorySet { ), None, ); - println!("mapping .data section"); + // println!("mapping .data section"); memory_set.push( MapArea::new( (sdata as usize).into(), @@ -129,7 +132,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 +142,7 @@ impl MemorySet { ), None, ); - println!("mapping physical memory"); + // println!("mapping physical memory"); memory_set.push( MapArea::new( (ekernel as usize).into(), @@ -149,7 +152,7 @@ impl MemorySet { ), None, ); - println!("mapping memory-mapped registers"); + //println!("mapping memory-mapped registers"); for pair in MMIO { memory_set.push( MapArea::new( @@ -286,6 +289,11 @@ impl MapArea { ppn = frame.ppn; self.data_frames.insert(vpn, frame); } + MapType::Linear(pn_offset) => { + // check for sv39 + assert!(vpn.0 < (1usize << 27)); + ppn = PhysPageNum((vpn.0 as isize + pn_offset) as usize); + } } let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); page_table.map(vpn, ppn, pte_flags); @@ -334,6 +342,8 @@ impl MapArea { pub enum MapType { Identical, Framed, + /// offset of page num + Linear(isize), } bitflags! { diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 34220c4a..574cea02 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -4,11 +4,11 @@ mod heap_allocator; mod memory_set; mod page_table; -use address::VPNRange; +pub use address::VPNRange; pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; -pub use frame_allocator::{frame_alloc, frame_dealloc, FrameTracker}; +pub use frame_allocator::{frame_alloc, frame_alloc_more, frame_dealloc, FrameTracker}; pub use memory_set::remap_test; -pub use memory_set::{kernel_token, MapPermission, MemorySet, KERNEL_SPACE}; +pub use memory_set::{kernel_token, MapArea, MapPermission, MapType, MemorySet, KERNEL_SPACE}; use page_table::PTEFlags; pub use page_table::{ translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable, diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs new file mode 100644 index 00000000..57673d3e --- /dev/null +++ b/os/src/net/mod.rs @@ -0,0 +1,92 @@ +pub mod udp; +pub mod socket; + +pub use lose_net_stack::IPv4; + +use alloc::{vec, sync::Arc}; +use lose_net_stack::{LoseStack, MacAddress, results::Packet}; + +use crate::{drivers::NET_DEVICE, sync::UPIntrFreeCell, net::socket::{get_socket, push_data}}; + +pub struct NetStack(UPIntrFreeCell); + +impl NetStack { + pub fn new() -> Self { + unsafe { + NetStack(UPIntrFreeCell::new(LoseStack::new( + IPv4::new(10, 0, 2, 15), + MacAddress::new([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]) + ))) + } + } +} + +lazy_static::lazy_static! { + static ref LOSE_NET_STACK: Arc = Arc::new(NetStack::new()); +} + + +pub fn net_interrupt_handler() { + let mut recv_buf = vec![0u8; 1024]; + + let len = NET_DEVICE.receive(&mut recv_buf); + + let packet = LOSE_NET_STACK.0.exclusive_access().analysis(&recv_buf[..len]); + + // println!("[kernel] receive a packet"); + // hexdump(&recv_buf[..len]); + + match packet { + Packet::ARP(arp_packet) => { + let lose_stack = LOSE_NET_STACK.0.exclusive_access(); + let reply_packet = arp_packet.reply_packet(lose_stack.ip, lose_stack.mac).expect("can't build reply"); + let reply_data = reply_packet.build_data(); + NET_DEVICE.transmit(&reply_data) + }, + + Packet::UDP(udp_packet) => { + let target = udp_packet.source_ip; + let lport = udp_packet.dest_port; + let rport = udp_packet.source_port; + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, udp_packet.data.to_vec()); + } + } + _ => {} + } +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("[kernel] {:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + print!("[kernel] "); + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("[kernel] {:-^1$}", " hexdump end ", PRELAND_WIDTH); +} \ No newline at end of file diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs new file mode 100644 index 00000000..4e92a00e --- /dev/null +++ b/os/src/net/socket.rs @@ -0,0 +1,93 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use lose_net_stack::IPv4; + +use crate::sync::UPIntrFreeCell; + + +// TODO: specify the protocol, TCP or UDP +pub struct Socket { + pub raddr: IPv4, // remote address + pub lport: u16, // local port + pub rport: u16, // rempote port + pub buffers: VecDeque> // datas +} + +lazy_static! { + static ref SOCKET_TABLE:UPIntrFreeCell>> = unsafe { + UPIntrFreeCell::new(Vec::new()) + }; +} + +pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + let socket_table = SOCKET_TABLE.exclusive_access(); + for i in 0..socket_table.len() { + let sock = &socket_table[i]; + if sock.is_none() { + continue; + } + + let sock = sock.as_ref().unwrap(); + if sock.raddr == raddr && sock.lport == lport && sock.rport == rport { + return Some(i) + } + } + None +} + +pub fn add_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + if get_socket(raddr, lport, rport).is_some() { + return None; + } + + let mut socket_table = SOCKET_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..socket_table.len() { + if socket_table[i].is_none() { + index = i; + break; + } + } + + let socket = Socket { + raddr, + lport, + rport, + buffers: VecDeque::new() + }; + + if index == usize::MAX { + socket_table.push(Some(socket)); + Some(socket_table.len() - 1) + } else { + socket_table[index] = Some(socket); + Some(index) + } +} + +pub fn remove_socket(index: usize) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + + socket_table[index] = None; +} + +pub fn push_data(index: usize, data: Vec) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.push_back(data); +} + +pub fn pop_data(index: usize) -> Option> { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.pop_front() +} \ No newline at end of file diff --git a/os/src/net/udp.rs b/os/src/net/udp.rs new file mode 100644 index 00000000..afa26d0e --- /dev/null +++ b/os/src/net/udp.rs @@ -0,0 +1,94 @@ +use alloc::vec; +use lose_net_stack::MacAddress; +use lose_net_stack::packets::udp::UDPPacket; +use lose_net_stack::IPv4; +use crate::fs::File; +use super::net_interrupt_handler; +use super::socket::{add_socket, remove_socket, pop_data}; +use super::LOSE_NET_STACK; +use super::NET_DEVICE; + +pub struct UDP{ + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub socket_index: usize +} + +impl UDP { + pub fn new(target: IPv4, sport: u16, dport: u16) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + socket_index: index + } + } +} + +impl File for UDP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len].copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + let udp_packet = UDPPacket::new( + lose_net_stack.ip, + lose_net_stack.mac, + self.sport, + self.target, + MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + self.dport, + len, + data.as_ref() + ); + NET_DEVICE.transmit(&udp_packet.build_data()); + len + } +} + +impl Drop for UDP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} \ No newline at end of file diff --git a/os/src/riscvregs.rs b/os/src/riscvregs.rs new file mode 100644 index 00000000..18712422 --- /dev/null +++ b/os/src/riscvregs.rs @@ -0,0 +1,614 @@ +// RISC-V registers +pub mod registers { + // hart (core) id registers + pub mod mhartid { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let id: usize; + unsafe { + asm!("csrr {}, mhartid", out(reg) id); + } + id + } + } + + // Machine Status Register, mstatus + pub mod mstatus { + use core::arch::asm; + + // Machine Status Register bit + const MPP_MASK: usize = 3 << 11; + const MIE: usize = 1 << 3; + + // Machine Previous Privilege mode + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum MPP { + Machine = 3, + Supervisor = 1, + User = 0, + } + + #[inline] + unsafe fn _read() -> usize { + let bits: usize; + asm!("csrr {}, mstatus", out(reg) bits); + bits + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw mstatus, {}", in(reg) bits); + } + + // Machine Previous Privilege Mode + #[inline] + pub fn set_mpp(mpp: MPP) { + unsafe { + let mut value = _read(); + value &= !MPP_MASK; + value |= (mpp as usize) << 11; + _write(value); + } + } + + #[inline] + pub fn set_mie() { + unsafe { + asm!("csrs mstatus, {}", in(reg) MIE); + } + } + } + + // machine exception program counter, holds the + // instruction address to which a return from + // exception will go. + pub mod mepc { + use core::arch::asm; + + #[inline] + pub fn write(x: usize) { + unsafe { + asm!("csrw mepc, {}", in(reg) x); + } + } + } + + // Supervisor Status Register, sstatus + pub mod sstatus { + use core::arch::asm; + + // Supervisor Status Register bit + const SPP: usize = 1 << 8; // Previous mode, 1=Supervisor, 0=user + const SPIE: usize = 1 << 5; // Supervisor Previous Interrupt Enable + const SIE: usize = 1 << 1; // Supervisor Interrupt Enable + + #[derive(Clone, Copy, Debug)] + pub struct Sstatus { + bits: usize, + } + + impl Sstatus { + // Supervisor Interrupt Enable + #[inline] + pub(in crate::riscvregs) fn sie(&self) -> bool { + self.bits & SIE != 0 + } + + // Supervisor Previous Privilege mode + #[inline] + pub fn spp(&self) -> SPP { + match self.bits & SPP { + 0 => SPP::User, + _ => SPP::Supervisor, + } + } + + // restore status bits + #[inline] + pub fn restore(&self) { + unsafe { + _write(self.bits); + } + } + } + + // Supervisor Previous Privilege Mode + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum SPP { + Supervisor = 1, + User = 0, + } + + #[inline] + pub fn read() -> Sstatus { + let bits: usize; + unsafe { asm!("csrr {}, sstatus", out(reg) bits) } + Sstatus { bits } + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw sstatus, {}", in(reg) bits); + } + + // bit set + #[inline] + unsafe fn _set(bits: usize) { + asm!("csrs sstatus, {}", in(reg) bits); + } + + // bit clear + #[inline] + unsafe fn _clear(bits: usize) { + asm!("csrc sstatus, {}", in(reg) bits); + } + + #[inline] + pub(in crate::riscvregs) unsafe fn set_sie() { + _set(SIE) + } + + #[inline] + pub(in crate::riscvregs) unsafe fn clear_sie() { + _clear(SIE) + } + + #[inline] + pub unsafe fn set_spie() { + _set(SPIE); + } + + #[inline] + pub unsafe fn set_spp(spp: SPP) { + match spp { + SPP::Supervisor => _set(SPP), + SPP::User => _clear(SPP), + } + } + } + + // Supervisor Interrupt Pending + pub mod sip { + use core::arch::asm; + + const SSIP: usize = 1 << 1; + + // Supervisor Software Interrupt Pending + #[inline] + pub unsafe fn clear_ssoft() { + asm!("csrc sip, {}", in(reg) SSIP); + } + } + + // Supervisor Interrupt Enable + pub mod sie { + use core::arch::asm; + + const SEIE: usize = 1 << 9; // external + const STIE: usize = 1 << 5; // timer + const SSIE: usize = 1 << 1; // software + + #[inline] + unsafe fn _set(bits: usize) { + asm!("csrs sie, {}", in(reg) bits); + } + + #[inline] + pub unsafe fn set_sext() { + _set(SEIE); + } + + #[inline] + pub unsafe fn set_stimer() { + _set(STIE); + } + + #[inline] + pub unsafe fn set_ssoft() { + _set(SSIE); + } + } + + // Machine-mode Interrupt Enable + pub mod mie { + use core::arch::asm; + + const MTIE: usize = 1 << 7; + + #[inline] + pub unsafe fn set_mtimer() { + asm!("csrs mie, {}", in(reg) MTIE); + } + } + + // supervisor exceptions program counter, holds the + // instruction address to which a return from + // exception will go. + pub mod sepc { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let bits: usize; + unsafe { + asm!("csrr {}, sepc", out(reg) bits); + } + bits + } + + #[inline] + pub fn write(bits: usize) { + unsafe { + asm!("csrw sepc, {}", in(reg) bits); + } + } + } + + // Machine Exception Delegation + pub mod medeleg { + use core::arch::asm; + + pub unsafe fn set_all() { + asm!("csrw medeleg, {}", in(reg) 0xffff); + } + } + + // Machine Interrupt Delegation + pub mod mideleg { + use core::arch::asm; + + #[inline] + pub unsafe fn set_all() { + asm!("csrw mideleg, {}", in(reg) 0xffff); + } + } + + // Supervisor Trap-Vector Base Address + // low two bits are mode. + pub mod stvec { + pub use super::mtvec::TrapMode; + use core::arch::asm; + + #[inline] + pub unsafe fn write(addr: usize, mode: TrapMode) { + asm!("csrw stvec, {}", in(reg) addr + mode as usize); + } + } + + // Machine-mode interrupt vector + pub mod mtvec { + use core::arch::asm; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum TrapMode { + Direct = 0, + Vectored = 1, + } + + #[inline] + pub unsafe fn write(addr: usize, mode: TrapMode) { + asm!("csrw mtvec, {}", in(reg) addr + mode as usize); + } + } + + // Physical Memory Protection Configuration + pub mod pmpcfg0 { + use core::arch::asm; + + // Permission enum contains all possible permission modes for pmp registers + #[derive(Clone, Copy, Debug)] + pub enum Permission { + NONE = 0b000, + R = 0b001, + W = 0b010, + RW = 0b011, + X = 0b100, + RX = 0b101, + WX = 0b110, + RWX = 0b111, + } + + // Range enum contains all possible addressing modes for pmp registers + pub enum Range { + OFF = 0b00, + TOR = 0b01, + NA4 = 0b10, + NAPOT = 0b11, + } + + // Set the pmp configuration corresponging to the index + #[inline] + pub unsafe fn set_pmp(index: usize, range: Range, permission: Permission, locked: bool) { + assert!(index < 8); + let mut value = _read(); + let byte = (locked as usize) << 7 | (range as usize) << 3 | (permission as usize); + value |= byte << (8 * index); + _write(value); + } + + #[inline] + unsafe fn _read() -> usize { + let bits: usize; + asm!("csrr {}, pmpcfg0", out(reg) bits); + bits + } + + #[inline] + unsafe fn _write(bits: usize) { + asm!("csrw pmpcfg0, {}", in(reg) bits); + } + } + + // Physical memory protection address register + pub mod pmpaddr0 { + use core::arch::asm; + + pub fn write(bits: usize) { + unsafe { + asm!("csrw pmpaddr0, {}", in(reg) bits); + } + } + } + + // Supervisor address translation and protection; + // holds the address of the page table. + pub mod satp { + use core::arch::asm; + + // stap register + #[derive(Clone, Copy, Debug)] + pub struct Satp { + bits: usize, + } + + // 64-bit satp mode + pub enum Mode { + // No translation or protection + Bare = 0, + // Page-based 39-bit virtual addressing + Sv39 = 8, + // Page-based 48-bit virtual addressing + Sv48 = 9, + // Page-based 57-bit virtual addressing + Sv57 = 10, + // Page-based 64-bit virtual addressing + Sv64 = 11, + } + + impl Satp { + // Return the contents of the register as raw bits + #[inline] + pub fn bits(&self) -> usize { + self.bits + } + } + + #[inline] + pub unsafe fn read() -> Satp { + let bits: usize; + asm!("csrr {}, satp", out(reg) bits); + Satp { bits } + } + + #[inline] + pub unsafe fn write(bits: usize) { + asm!("csrw satp, {}", in(reg) bits); + } + + #[inline] + pub fn make(mode: Mode, asid: usize, ppn: usize) -> usize { + let mut bits: usize = 0; + bits |= (mode as usize) << 60; + bits |= asid << 44; + bits |= ppn >> 12; + bits + } + } + + // mscratch register + pub mod mscratch { + use core::arch::asm; + + #[inline] + pub fn write(bits: usize) { + unsafe { + asm!("csrw mscratch, {}", in(reg) bits); + } + } + } + + // Supervisor Trap Cause + pub mod scause { + use core::{arch::asm, mem::size_of}; + + // scause register + #[derive(Clone, Copy)] + pub struct Scause { + bits: usize, + } + + // Trap Cause + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Trap { + Interrupt(Interrupt), + Exception(Exception), + } + + // Interrupt + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Interrupt { + UserSoft, + SupervisorSoft, + UserTimer, + SupervisorTimer, + UserExternal, + SupervisorExternal, + Unknown, + } + + // Exception + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum Exception { + InstructionMisaligned, + InstructionFault, + IllegalInstruction, + Breakpoint, + LoadFault, + StoreMisaligned, + StoreFault, + UserEnvCall, + InstructionPageFault, + LoadPageFault, + StorePageFault, + Unknown, + } + + impl Interrupt { + #[inline] + pub fn from(nr: usize) -> Self { + match nr { + 0 => Interrupt::UserSoft, + 1 => Interrupt::SupervisorSoft, + 4 => Interrupt::UserTimer, + 5 => Interrupt::SupervisorTimer, + 8 => Interrupt::UserExternal, + 9 => Interrupt::SupervisorExternal, + _ => Interrupt::Unknown, + } + } + } + + impl Exception { + #[inline] + pub fn from(nr: usize) -> Self { + match nr { + 0 => Exception::InstructionMisaligned, + 1 => Exception::InstructionFault, + 2 => Exception::IllegalInstruction, + 3 => Exception::Breakpoint, + 5 => Exception::LoadFault, + 6 => Exception::StoreMisaligned, + 7 => Exception::StoreFault, + 8 => Exception::UserEnvCall, + 12 => Exception::InstructionPageFault, + 13 => Exception::LoadPageFault, + 15 => Exception::StorePageFault, + _ => Exception::Unknown, + } + } + } + + impl Scause { + // Returns the contents of the register as raw bits + #[inline] + pub fn bits(&self) -> usize { + self.bits + } + + // Returns the code field + #[inline] + pub fn code(&self) -> usize { + let bit = 1 << (size_of::() * 8 - 1); + self.bits & !bit + } + + // Trap cause + #[inline] + pub fn cause(&self) -> Trap { + if self.is_interrupt() { + Trap::Interrupt(Interrupt::from(self.code())) + } else { + Trap::Exception(Exception::from(self.code())) + } + } + + // Is trap cause an interrupt. + #[inline] + pub fn is_interrupt(&self) -> bool { + self.bits & (1 << (size_of::() * 8 - 1)) != 0 + } + + // Is trap cause an exception. + #[inline] + pub fn is_exception(&self) -> bool { + !self.is_interrupt() + } + } + + #[inline] + pub fn read() -> Scause { + let bits: usize; + unsafe { + asm!("csrr {}, scause", out(reg) bits); + } + Scause { bits } + } + } + + // Supervisor Trap Value + pub mod stval { + use core::arch::asm; + + #[inline] + pub fn read() -> usize { + let bits: usize; + unsafe { asm!("csrr {}, stval", out(reg) bits) } + bits + } + } +} + +use core::arch::asm; + +use registers::*; + +// enable device interrupts +#[inline] +pub fn intr_on() { + unsafe { + sstatus::set_sie(); + } +} + +// disable device interrupts +#[inline] +pub fn intr_off() { + unsafe { + sstatus::clear_sie(); + } +} + +// are device interrupts enabled? +#[inline] +pub fn intr_get() -> bool { + sstatus::read().sie() +} + +// flush the TLB. +#[inline] +pub unsafe fn sfence_vma() { + // the zero, zero means flush all TLB entries + asm!("sfence.vma zero, zero"); +} + +pub const PGSIZE: usize = 4096; // bytes per page +pub const PGSHIFT: usize = 12; // bits of offset within a page + +pub const fn pgroundup(sz: usize) -> usize { + (sz + PGSIZE - 1) & !(PGSIZE - 1) +} + +pub const fn pgrounddown(sz: usize) -> usize { + sz & !(PGSIZE - 1) +} + +// PTE flags +pub mod pteflags { + pub const PTE_V: usize = 1 << 0; // valid + pub const PTE_R: usize = 1 << 1; + pub const PTE_W: usize = 1 << 2; + pub const PTE_X: usize = 1 << 3; + pub const PTE_U: usize = 1 << 4; // user can access +} diff --git a/os/src/sbi.rs b/os/src/sbi.rs index a218ec8b..5113c901 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/start.rs b/os/src/start.rs new file mode 100644 index 00000000..3a6d3912 --- /dev/null +++ b/os/src/start.rs @@ -0,0 +1,86 @@ +//use crate::kernelvec::*; +//use crate::memlayout::*; +//use crate::param::NCPU; +//use super::main::*; +//use crate::riscv::registers::{pmpcfg0::*, *}; +use core::arch::asm; +use core::hint::unreachable_unchecked; + +mod riscv; + +#[repr(C, align(16))] +struct Stack([u8; 4096 * 4 * NCPU]); + +#[no_mangle] +static mut STACK0: Stack = Stack([0; 4096 * 4 * NCPU]); + +#[no_mangle] +pub unsafe fn rust_start() -> ! { + // set MPP mode to Supervisor, for mret + mstatus::set_mpp(mstatus::MPP::Supervisor); + + // set MEPC to main, for mret + mepc::write(rust_main as usize); + + // disable paging for now. + satp::write(0); + + // delegate all interrupts and exceptions to supervisor mode. + medeleg::set_all(); + mideleg::set_all(); + sie::set_sext(); + sie::set_ssoft(); + sie::set_stimer(); + + // configure Physical Memory Protection to give supervisor mode + // access to all of physical memory. + pmpaddr0::write(0x3fffffffffffff); + pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0 + + // ask for clock interrupts. + timerinit(); + + // keep each CPU's hartid in its tp register, for cpuid(). + let id = mhartid::read(); + asm!("mv tp, {0}", in(reg) id); + + // switch to supervisor mode and jump to main(). + asm!("mret"); + + extern "C" { + fn rust_main() -> !; + } + unreachable_unchecked(); +} + +// a scratch area per CPU for machine-mode timer interrupts. +static mut TIMER_SCRATCH: [[u64; 5]; 1] = [[0; 5]; 1]; + +unsafe fn timerinit() { + // each CPU has a separate source of timer interrupts + let id = mhartid::read(); + + // ask the CLINT for a timer interrupts + let interval = 1000000u64; // cycles; about 1/10th second in qemu. + let mtimecmp = clint_mtimecmp(id) as *mut u64; + let mtime = CLINT_MTIME as *const u64; + mtimecmp.write_volatile(mtime.read_volatile() + interval); + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + let scratch = &mut TIMER_SCRATCH[id]; + scratch[3] = mtimecmp as u64; + scratch[4] = interval; + mscratch::write(scratch.as_mut_ptr() as usize); + + // set the machine-mode trap handler + mtvec::write(timervec as usize, mtvec::TrapMode::Direct); + + // enable machine-mode interrupts. + mstatus::set_mie(); + + // enable machime-mode timer interrupts. + mie::set_mtimer(); +} \ No newline at end of file diff --git a/os/src/sync/condvar.rs b/os/src/sync/condvar.rs index 714782c2..b8ad07b7 100644 --- a/os/src/sync/condvar.rs +++ b/os/src/sync/condvar.rs @@ -1,6 +1,6 @@ use crate::sync::{Mutex, UPIntrFreeCell}; use crate::task::{ - add_task, block_current_and_run_next, block_current_task, current_task, TaskContext, + wakeup_task, block_current_and_run_next, block_current_task, current_task, TaskContext, TaskControlBlock, }; use alloc::{collections::VecDeque, sync::Arc}; @@ -27,7 +27,7 @@ impl Condvar { pub fn signal(&self) { let mut inner = self.inner.exclusive_access(); if let Some(task) = inner.wait_queue.pop_front() { - add_task(task); + wakeup_task(task); } } diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs index 463638b3..2b2db108 100644 --- a/os/src/sync/mutex.rs +++ b/os/src/sync/mutex.rs @@ -1,6 +1,6 @@ use super::UPIntrFreeCell; use crate::task::TaskControlBlock; -use crate::task::{add_task, current_task}; +use crate::task::{wakeup_task, current_task}; use crate::task::{block_current_and_run_next, suspend_current_and_run_next}; use alloc::{collections::VecDeque, sync::Arc}; @@ -80,7 +80,7 @@ impl Mutex for MutexBlocking { let mut mutex_inner = self.inner.exclusive_access(); assert!(mutex_inner.locked); if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { - add_task(waking_task); + wakeup_task(waking_task); } else { mutex_inner.locked = false; } diff --git a/os/src/sync/semaphore.rs b/os/src/sync/semaphore.rs index 354db29f..cb62c201 100644 --- a/os/src/sync/semaphore.rs +++ b/os/src/sync/semaphore.rs @@ -1,5 +1,5 @@ use crate::sync::UPIntrFreeCell; -use crate::task::{add_task, block_current_and_run_next, current_task, TaskControlBlock}; +use crate::task::{wakeup_task, block_current_and_run_next, current_task, TaskControlBlock}; use alloc::{collections::VecDeque, sync::Arc}; pub struct Semaphore { @@ -28,7 +28,7 @@ impl Semaphore { inner.count += 1; if inner.count <= 0 { if let Some(task) = inner.wait_queue.pop_front() { - add_task(task); + wakeup_task(task); } } } diff --git a/os/src/syscall/gui.rs b/os/src/syscall/gui.rs index 82c42af4..c5ba8a4e 100644 --- a/os/src/syscall/gui.rs +++ b/os/src/syscall/gui.rs @@ -1,63 +1,34 @@ -use alloc::{string::ToString, sync::Arc, vec::Vec}; -use embedded_graphics::{ - prelude::{Point, Size}, - primitives::arc, -}; +use crate::drivers::GPU_DEVICE; +use crate::mm::{MapArea, MapPermission, MapType, PhysAddr, VirtAddr}; +use crate::task::current_process; -use crate::{ - fs::ROOT_INODE, - gui::{Button, Component, IconController, ImageComp, Panel, Terminal}, - sync::UPIntrFreeCell, -}; +const FB_VADDR: usize = 0x10000000; -use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES}; +pub fn sys_framebuffer() -> isize { + let fb = GPU_DEVICE.get_framebuffer(); + let len = fb.len(); + // println!("[kernel] FrameBuffer: addr 0x{:X}, len {}", fb.as_ptr() as usize , len); + let fb_start_pa = PhysAddr::from(fb.as_ptr() as usize); + assert!(fb_start_pa.aligned()); + let fb_start_ppn = fb_start_pa.floor(); + let fb_start_vpn = VirtAddr::from(FB_VADDR).floor(); + let pn_offset = fb_start_ppn.0 as isize - fb_start_vpn.0 as isize; -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(), + let current_process = current_process(); + let mut inner = current_process.inner_exclusive_access(); + inner.memory_set.push( + MapArea::new( + (FB_VADDR as usize).into(), + (FB_VADDR + len as usize).into(), + MapType::Linear(pn_offset), + MapPermission::R | MapPermission::W | MapPermission::U, + ), + None, ); - 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); + FB_VADDR as isize +} + +pub fn sys_framebuffer_flush() -> isize { + GPU_DEVICE.flush(); + 0 } diff --git a/os/src/syscall/input.rs b/os/src/syscall/input.rs new file mode 100644 index 00000000..ee86bd39 --- /dev/null +++ b/os/src/syscall/input.rs @@ -0,0 +1,28 @@ +//use crate::drivers::{KEYBOARD_DEVICE,MOUSE_DEVICE,INPUT_CONDVAR,read_input_event}; +use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; + +pub fn sys_event_get() -> isize { + let kb = KEYBOARD_DEVICE.clone(); + let mouse = MOUSE_DEVICE.clone(); + //let input=INPUT_CONDVAR.clone(); + //read_input_event() as isize + if !kb.is_empty() { + kb.read_event() as isize + } else if !mouse.is_empty() { + mouse.read_event() as isize + } else { + 0 + } +} + +use crate::drivers::chardev::UART; + +/// check UART's read-buffer is empty or not +pub fn sys_key_pressed() -> isize { + let res = !UART.read_buffer_is_empty(); + if res { + 1 + } else { + 0 + } +} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 70a1f167..a336a96f 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,4 +1,5 @@ const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -25,27 +26,31 @@ 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; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; + mod fs; -#[cfg(feature = "board_qemu")] mod gui; +mod input; mod process; mod sync; mod thread; -#[cfg(feature = "board_qemu")] -pub use self::gui::create_desktop; -use fs::*; +mod net; -#[cfg(feature = "board_qemu")] -pub use gui::PAD; +use fs::*; +use gui::*; +use input::*; use process::*; use sync::*; use thread::*; +use net::*; -#[cfg(feature = "board_qemu")] pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { SYSCALL_DUP => sys_dup(args[0]), + SYSCALL_CONNECT => sys_connect(args[0] as _, args[1] as _, args[2] as _), SYSCALL_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), @@ -69,44 +74,13 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { 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]), - 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_CREATE => sys_condvar_create(), SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]), SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]), + SYSCALL_FRAMEBUFFER => sys_framebuffer(), + SYSCALL_FRAMEBUFFER_FLUSH => sys_framebuffer_flush(), + SYSCALL_EVENT_GET => sys_event_get(), + SYSCALL_KEY_PRESSED => sys_key_pressed(), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/net.rs b/os/src/syscall/net.rs new file mode 100644 index 00000000..4ed4466d --- /dev/null +++ b/os/src/syscall/net.rs @@ -0,0 +1,14 @@ +use alloc::sync::Arc; +use crate::net::IPv4; +use crate::net::udp::UDP; +use crate::task::current_process; + +// just support udp +pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); + inner.fd_table[fd] = Some(Arc::new(udp_node)); + fd as isize +} \ No newline at end of file diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs index e08f3296..11806699 100644 --- a/os/src/syscall/sync.rs +++ b/os/src/syscall/sync.rs @@ -93,7 +93,7 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize { 0 } -pub fn sys_condvar_create(_arg: usize) -> isize { +pub fn sys_condvar_create() -> isize { let process = current_process(); let mut process_inner = process.inner_exclusive_access(); let id = if let Some(id) = process_inner diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 168ba32e..7672cf53 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -1,4 +1,4 @@ -use super::{ProcessControlBlock, TaskControlBlock}; +use super::{ProcessControlBlock, TaskControlBlock, TaskStatus}; use crate::sync::UPIntrFreeCell; use alloc::collections::{BTreeMap, VecDeque}; use alloc::sync::Arc; @@ -34,6 +34,13 @@ pub fn add_task(task: Arc) { TASK_MANAGER.exclusive_access().add(task); } +pub fn wakeup_task(task: Arc) { + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Ready; + drop(task_inner); + add_task(task); +} + pub fn fetch_task() -> Option> { TASK_MANAGER.exclusive_access().fetch() } diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 31c792c9..c8354805 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -18,7 +18,7 @@ use switch::__switch; pub use context::TaskContext; pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle, IDLE_PID}; -pub use manager::{add_task, pid2process, remove_from_pid2process}; +pub use manager::{add_task, wakeup_task, pid2process, remove_from_pid2process}; pub use processor::{ current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va, current_user_token, run_tasks, schedule, take_current_task, @@ -48,7 +48,7 @@ pub fn suspend_current_and_run_next() { pub fn block_current_task() -> *mut TaskContext { let task = take_current_task().unwrap(); let mut task_inner = task.inner_exclusive_access(); - task_inner.task_status = TaskStatus::Blocking; + task_inner.task_status = TaskStatus::Blocked; &mut task_inner.task_cx as *mut TaskContext } @@ -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/task/task.rs b/os/src/task/task.rs index c620890e..ffc5c0e0 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -76,5 +76,5 @@ impl TaskControlBlock { pub enum TaskStatus { Ready, Running, - Blocking, + Blocked, } diff --git a/os/src/timer.rs b/os/src/timer.rs index 06a70dea..83c969d4 100644 --- a/os/src/timer.rs +++ b/os/src/timer.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use crate::config::CLOCK_FREQ; use crate::sbi::set_timer; use crate::sync::UPIntrFreeCell; -use crate::task::{add_task, TaskControlBlock}; +use crate::task::{wakeup_task, TaskControlBlock}; use alloc::collections::BinaryHeap; use alloc::sync::Arc; use lazy_static::*; @@ -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 { + wakeup_task(Arc::clone(&timer.task)); + timers.pop(); + } else { + break; + } } - } + }); } diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index ce2aae8e..6040c7fa 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -11,7 +11,7 @@ use core::arch::{asm, global_asm}; use riscv::register::{ mtvec::TrapMode, scause::{self, Exception, Interrupt, Trap}, - sie, sscratch, sstatus, stval, stvec, + sie, sscratch, sstatus, stval, stvec,sip }; global_asm!(include_str!("trap.S")); @@ -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 @@ -95,10 +95,20 @@ pub fn trap_handler() -> ! { Trap::Exception(Exception::IllegalInstruction) => { current_add_signal(SignalFlags::SIGILL); } - Trap::Interrupt(Interrupt::SupervisorTimer) => { - set_next_trigger(); + // Trap::Interrupt(Interrupt::SupervisorTimer) => { + // set_next_trigger(); + // check_timer(); + // suspend_current_and_run_next(); + // } + Trap::Interrupt(Interrupt::SupervisorSoft) => { + //set_next_trigger(); + const SSIP: usize = 1 << 1; + unsafe { + asm!("csrc sip, {}", in(reg) SSIP); + } + //println!("TRAP: ssoft in Kern"); check_timer(); - suspend_current_and_run_next(); + // do not schedule now } Trap::Interrupt(Interrupt::SupervisorExternal) => { crate::board::irq_handler(); @@ -151,8 +161,18 @@ pub fn trap_from_kernel(_trap_cx: &TrapContext) { Trap::Interrupt(Interrupt::SupervisorExternal) => { crate::board::irq_handler(); } - Trap::Interrupt(Interrupt::SupervisorTimer) => { - set_next_trigger(); + // Trap::Interrupt(Interrupt::SupervisorTimer) => { + // //set_next_trigger(); + // check_timer(); + // // do not schedule now + // } + Trap::Interrupt(Interrupt::SupervisorSoft) => { + //set_next_trigger(); + const SSIP: usize = 1 << 1; + unsafe { + asm!("csrc sip, {}", in(reg) SSIP); + } + //println!("TRAP: ssoft in Kern"); check_timer(); // do not schedule now } diff --git a/ping.py b/ping.py new file mode 100644 index 00000000..c68fc71d --- /dev/null +++ b/ping.py @@ -0,0 +1,18 @@ +import socket +import sys +import time + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +addr = ('localhost', 26099) +sock.bind(addr) + + +print("pinging...", file=sys.stderr) +while True: + buf, raddr = sock.recvfrom(4096) + print("receive: " + buf.decode("utf-8")) + buf = "this is a ping to port 6200!".encode('utf-8') + sock.sendto(buf, ("127.0.0.1", 6200)) + buf = "this is a ping to reply!".encode('utf-8') + sock.sendto(buf, raddr) + time.sleep(1) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c56a59e0..553747b2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,5 @@ [toolchain] profile = "minimal" -channel = "nightly-2022-07-20" +# 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 e5ded8a1..334d01e2 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 18634c3c..1d7b2efc 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -10,10 +10,10 @@ edition = "2018" buddy_system_allocator = "0.6" bitflags = "1.2.1" riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +embedded-graphics = "0.7.1" +oorandom ="11" +virtio-input-decoder = "0.1.4" [profile.release] debug = true - -[features] -board_qemu = [] -board_k210 = [] \ No newline at end of file diff --git a/user/Makefile b/user/Makefile index c09b5e96..51703278 100644 --- a/user/Makefile +++ b/user/Makefile @@ -19,7 +19,7 @@ ifeq ($(TEST), 1) endif binary: elf - $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) + @$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) build: binary diff --git a/user/src/bin/adder.rs b/user/src/bin/adder.rs new file mode 100644 index 00000000..e7b16117 --- /dev/null +++ b/user/src/bin/adder.rs @@ -0,0 +1,55 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + critical_section(&mut t); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_atomic.rs b/user/src/bin/adder_atomic.rs new file mode 100644 index 00000000..fe2cdb17 --- /dev/null +++ b/user/src/bin/adder_atomic.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::sync::atomic::{AtomicBool, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static OCCUPIED: AtomicBool = AtomicBool::new(false); +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +fn lock() { + while OCCUPIED + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + yield_(); + } +} + +fn unlock() { + OCCUPIED.store(false, Ordering::Relaxed); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_blocking.rs b/user/src/bin/adder_mutex_blocking.rs new file mode 100644 index 00000000..963dd753 --- /dev/null +++ b/user/src/bin/adder_mutex_blocking.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + + let start = get_time(); + assert_eq!(mutex_blocking_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_spin.rs b/user/src/bin/adder_mutex_spin.rs new file mode 100644 index 00000000..b96f933c --- /dev/null +++ b/user/src/bin/adder_mutex_spin.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + + let start = get_time(); + assert_eq!(mutex_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_spin.rs b/user/src/bin/adder_peterson_spin.rs new file mode 100644 index 00000000..bba1f239 --- /dev/null +++ b/user/src/bin/adder_peterson_spin.rs @@ -0,0 +1,90 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; +use core::sync::atomic::{compiler_fence, Ordering}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + // Why do we need to use volatile_read here? + // Otherwise the compiler will assume that they will never + // be changed on this thread. Thus, they will be accessed + // only once! + while vload!(&FLAG[j]) && vload!(&TURN) == j {} +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + let start = get_time(); + let mut v = Vec::new(); + assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads."); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_yield.rs b/user/src/bin/adder_peterson_yield.rs new file mode 100644 index 00000000..fc5ff189 --- /dev/null +++ b/user/src/bin/adder_peterson_yield.rs @@ -0,0 +1,89 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; +use core::sync::atomic::{compiler_fence, Ordering}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + while FLAG[j] && TURN == j { + yield_(); + } +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + + let start = get_time(); + let mut v = Vec::new(); + assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads."); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_spin.rs b/user/src/bin/adder_simple_spin.rs new file mode 100644 index 00000000..d950eca5 --- /dev/null +++ b/user/src/bin/adder_simple_spin.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while vload!(&OCCUPIED) {} + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_yield.rs b/user/src/bin/adder_simple_yield.rs new file mode 100644 index 00000000..1c777779 --- /dev/null +++ b/user/src/bin/adder_simple_yield.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while OCCUPIED { + yield_(); + } + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { PER_THREAD = per_thread; } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/barrier_condvar.rs b/user/src/bin/barrier_condvar.rs new file mode 100644 index 00000000..ca0953b3 --- /dev/null +++ b/user/src/bin/barrier_condvar.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{thread_create, exit, waittid, mutex_create, mutex_lock, mutex_unlock, condvar_create, condvar_signal, condvar_wait}; +use alloc::vec::Vec; +use core::cell::UnsafeCell; +use lazy_static::*; + +const THREAD_NUM: usize = 3; + +struct Barrier { + mutex_id: usize, + condvar_id: usize, + count: UnsafeCell, +} + +impl Barrier { + pub fn new() -> Self { + Self { + mutex_id: mutex_create() as usize, + condvar_id: condvar_create() as usize, + count: UnsafeCell::new(0), + } + } + pub fn block(&self) { + mutex_lock(self.mutex_id); + let count = self.count.get(); + // SAFETY: Here, the accesses of the count is in the + // critical section protected by the mutex. + unsafe { *count = *count + 1; } + if unsafe { *count } == THREAD_NUM { + condvar_signal(self.condvar_id); + } else { + condvar_wait(self.condvar_id, self.mutex_id); + condvar_signal(self.condvar_id); + } + mutex_unlock(self.mutex_id); + } +} + +unsafe impl Sync for Barrier {} + +lazy_static! { + static ref BARRIER_AB: Barrier = Barrier::new(); + static ref BARRIER_BC: Barrier = Barrier::new(); +} + +fn thread_fn() { + for _ in 0..300 { print!("a"); } + BARRIER_AB.block(); + for _ in 0..300 { print!("b"); } + BARRIER_BC.block(); + for _ in 0..300 { print!("c"); } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/barrier_fail.rs b/user/src/bin/barrier_fail.rs new file mode 100644 index 00000000..0eb4b17f --- /dev/null +++ b/user/src/bin/barrier_fail.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{thread_create, exit, waittid}; +use alloc::vec::Vec; + +const THREAD_NUM: usize = 3; + +fn thread_fn() { + for ch in 'a'..='c' { + for _ in 0..300 { + print!("{}", ch); + } + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/test_condvar.rs b/user/src/bin/condsync_condvar.rs similarity index 95% rename from user/src/bin/test_condvar.rs rename to user/src/bin/condsync_condvar.rs index 2db9d8a4..78605ad0 100644 --- a/user/src/bin/test_condvar.rs +++ b/user/src/bin/condsync_condvar.rs @@ -1,59 +1,59 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; - -extern crate alloc; - -use alloc::vec; -use user_lib::exit; -use user_lib::{ - condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, -}; -use user_lib::{sleep, thread_create, waittid}; - -static mut A: usize = 0; - -const CONDVAR_ID: usize = 0; -const MUTEX_ID: usize = 0; - -unsafe fn first() -> ! { - sleep(10); - println!("First work, Change A --> 1 and wakeup Second"); - mutex_lock(MUTEX_ID); - A = 1; - condvar_signal(CONDVAR_ID); - mutex_unlock(MUTEX_ID); - exit(0) -} - -unsafe fn second() -> ! { - println!("Second want to continue,but need to wait A=1"); - mutex_lock(MUTEX_ID); - while A == 0 { - println!("Second: A is {}", A); - condvar_wait(CONDVAR_ID, MUTEX_ID); - } - mutex_unlock(MUTEX_ID); - println!("A is {}, Second can work now", A); - exit(0) -} - -#[no_mangle] -pub fn main() -> i32 { - // create condvar & mutex - assert_eq!(condvar_create() as usize, CONDVAR_ID); - assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); - // create threads - let threads = vec![ - thread_create(first as usize, 0), - thread_create(second as usize, 0), - ]; - // wait for all threads to complete - for thread in threads.iter() { - waittid(*thread as usize); - } - println!("test_condvar passed!"); - 0 -} +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const CONDVAR_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + condvar_signal(CONDVAR_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + mutex_lock(MUTEX_ID); + while A == 0 { + println!("Second: A is {}", A); + condvar_wait(CONDVAR_ID, MUTEX_ID); + } + println!("A is {}, Second can work now", A); + mutex_unlock(MUTEX_ID); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create condvar & mutex + assert_eq!(condvar_create() as usize, CONDVAR_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/condsync_sem.rs b/user/src/bin/condsync_sem.rs new file mode 100644 index 00000000..d7b875a7 --- /dev/null +++ b/user/src/bin/condsync_sem.rs @@ -0,0 +1,64 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + semaphore_create, semaphore_down, semaphore_up, mutex_blocking_create, mutex_lock, mutex_unlock, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const SEM_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + semaphore_up(SEM_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + loop { + mutex_lock(MUTEX_ID); + if A == 0 { + println!("Second: A is {}", A); + mutex_unlock(MUTEX_ID); + semaphore_down(SEM_ID); + } else { + mutex_unlock(MUTEX_ID); + break; + } + } + println!("A is {}, Second can work now", A); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphore & mutex + assert_eq!(semaphore_create(0) as usize, SEM_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/gui.rs b/user/src/bin/gui.rs deleted file mode 100644 index e3f7ec2c..00000000 --- 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/gui_rect.rs b/user/src/bin/gui_rect.rs new file mode 100644 index 00000000..a459dbc3 --- /dev/null +++ b/user/src/bin/gui_rect.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate user_lib; + +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle,Triangle}; + +const INIT_X: i32 = 80; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 150; + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), + latest_pos: Point::new(INIT_X, INIT_Y), + } + } + fn paint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10)) + .draw(&mut self.disp) + .ok(); + Circle::new(self.latest_pos + Point::new(-70, -300), 150) + .into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE)) + .draw(&mut self.disp) + .ok(); + Triangle::new(self.latest_pos + Point::new(0, 150), self.latest_pos + Point::new(80, 200), self.latest_pos + Point::new(-120, 300)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10)) + .draw(&mut self.disp) + .ok(); + } + fn unpaint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 10)) + .draw(&mut self.disp) + .ok(); + } + pub fn move_rect(&mut self, dx: i32, dy: i32) { + self.unpaint(); + self.latest_pos.x += dx; + self.latest_pos.y += dy; + self.paint(); + } +} + +#[no_mangle] +pub fn main() -> i32 { + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + for i in 0..5 { + board.latest_pos.x += (RECT_SIZE as i32 + 20); + //board.latest_pos.y += i; + board.paint(); + } + 0 +} diff --git a/user/src/bin/gui_simple.rs b/user/src/bin/gui_simple.rs new file mode 100644 index 00000000..ccf5b89d --- /dev/null +++ b/user/src/bin/gui_simple.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +use user_lib::{VIRTGPU_XRES, VIRTGPU_YRES, Display}; +use embedded_graphics::prelude::Size; + +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + disp.paint_on_framebuffer(|fb| { + for y in 0..VIRTGPU_YRES as usize { + for x in 0..VIRTGPU_XRES as usize { + let idx = (y * VIRTGPU_XRES as usize + x) * 4; + fb[idx] = x as u8; + fb[idx + 1] = y as u8; + fb[idx + 2] = (x + y) as u8; + } + } + }); + 0 +} diff --git a/user/src/bin/gui_snake.rs b/user/src/bin/gui_snake.rs new file mode 100644 index 00000000..080c4725 --- /dev/null +++ b/user/src/bin/gui_snake.rs @@ -0,0 +1,351 @@ +#![no_std] +#![no_main] + +extern crate user_lib; +extern crate alloc; + +use user_lib::console::getchar; +use user_lib::{Display, key_pressed, sleep, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::*; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::Pixel; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use oorandom; //random generator + +struct Snake { + parts: [Pixel; MAX_SIZE], + len: usize, + direction: Direction, + size_x: u32, + size_y: u32, +} + +struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> { + snake: &'a Snake, + index: usize, +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake { + type Item = Pixel; + type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>; + + fn into_iter(self) -> Self::IntoIter { + SnakeIntoIterator { + snake: self, + index: 0, + } + } +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> { + type Item = Pixel; + + fn next(&mut self) -> Option { + let cur = self.snake.parts[self.index]; + if self.index < self.snake.len { + self.index += 1; + return Some(cur); + } + None + } +} + +impl Snake { + fn new(color: T, size_x: u32, size_y: u32) -> Snake { + Snake { + parts: [Pixel::(Point { x: 0, y: 0 }, color); MAX_SIZE], + len: 1, + direction: Direction::None, + size_x, + size_y, + } + } + fn set_direction(&mut self, direction: Direction) { + self.direction = direction; + } + fn contains(&self, this: Point) -> bool { + for part in self.into_iter() { + if part.0 == this { + return true; + }; + } + false + } + fn grow(&mut self) { + if self.len < MAX_SIZE - 1 { + self.len += 1; + } + } + fn make_step(&mut self) { + let mut i = self.len; + while i > 0 { + self.parts[i] = self.parts[i - 1]; + i -= 1; + } + match self.direction { + Direction::Left => { + if self.parts[0].0.x == 0 { + self.parts[0].0.x = (self.size_x - 1) as i32; + } else { + self.parts[0].0.x -= 1; + } + } + Direction::Right => { + if self.parts[0].0.x == (self.size_x - 1) as i32 { + self.parts[0].0.x = 0; + } else { + self.parts[0].0.x += 1; + } + } + Direction::Up => { + if self.parts[0].0.y == 0 { + self.parts[0].0.y = (self.size_y - 1) as i32; + } else { + self.parts[0].0.y -= 1; + } + } + Direction::Down => { + if self.parts[0].0.y == (self.size_y - 1) as i32 { + self.parts[0].0.y = 0; + } else { + self.parts[0].0.y += 1; + } + } + Direction::None => {} + } + } +} + +struct Food { + size_x: u32, + size_y: u32, + place: Pixel, + rng: oorandom::Rand32, +} + +impl Food { + pub fn new(color: T, size_x: u32, size_y: u32) -> Self { + let seed = 4; + let rng = oorandom::Rand32::new(seed); + Food { + size_x, + size_y, + place: Pixel(Point { x: 0, y: 0 }, color), + rng, + } + } + fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake) { + let mut p: Point; + 'outer: loop { + let random_number = self.rng.rand_u32(); + let blocked_positions = iter_source.into_iter(); + p = Point { + x: ((random_number >> 24) as u16 % self.size_x as u16).into(), + y: ((random_number >> 16) as u16 % self.size_y as u16).into(), + }; + for blocked_position in blocked_positions { + if p == blocked_position.0 { + continue 'outer; + } + } + break; + } + self.place = Pixel:: { + 0: p, + 1: self.place.1, + } + } + fn get_pixel(&self) -> Pixel { + self.place + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum Direction { + Left, + Right, + Up, + Down, + None, +} + +pub struct SnakeGame { + snake: Snake, + food: Food, + food_age: u32, + food_lifetime: u32, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl SnakeGame { + pub fn new( + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, + snake_color: T, + food_color: T, + food_lifetime: u32, + ) -> Self { + let snake = Snake::::new(snake_color, size_x / scale_x, size_y / scale_y); + let mut food = Food::::new(food_color, size_x / scale_x, size_y / scale_y); + food.replace(&snake); + SnakeGame { + snake, + food, + food_age: 0, + food_lifetime, + size_x, + size_y, + scale_x, + scale_y, + } + } + pub fn set_direction(&mut self, direction: Direction) { + self.snake.set_direction(direction); + } + pub fn draw(&mut self, target: &mut D) -> () + where + D: DrawTarget, + { + self.snake.make_step(); + let hit = self.snake.contains(self.food.get_pixel().0); + if hit { + self.snake.grow(); + } + self.food_age += 1; + if self.food_age >= self.food_lifetime || hit { + self.food.replace(&self.snake); + self.food_age = 0; + } + + let mut scaled_display = ScaledDisplay:: { + real_display: target, + size_x: self.size_x / self.scale_x, + size_y: self.size_y / self.scale_y, + scale_x: self.scale_x, + scale_y: self.scale_y, + }; + + for part in self.snake.into_iter() { + _ = part.draw(&mut scaled_display); + } + _ = self.food.get_pixel().draw(&mut scaled_display); + } +} + +/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things +struct ScaledDisplay<'a, T: DrawTarget> { + real_display: &'a mut T, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> { + type Color = T::Color; + type Error = T::Error; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + let style = PrimitiveStyle::with_fill(pixel.1); + Rectangle::new( + Point::new( + pixel.0.x * self.scale_x as i32, + pixel.0.y * self.scale_y as i32, + ), + Size::new(self.scale_x as u32, self.scale_y as u32), + ) + .into_styled(style) + .draw(self.real_display)?; + } + Ok(()) + } +} + +impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> { + fn size(&self) -> Size { + Size::new(self.size_x as u32, self.size_y as u32) + } +} + +#[cfg(test)] +mod tests { + + use crate::Snake; + use embedded_graphics::pixelcolor::*; + use embedded_graphics::prelude::*; + + #[test] + fn snake_basic() { + let mut snake = Snake::::new(Rgb888::RED, 8, 8); + snake.set_direction(crate::Direction::Right); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().next().unwrap() + ); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + snake.set_direction(crate::Direction::Down); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 1 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(2).unwrap() + ); + assert_eq!(true, snake.contains(Point { x: 0, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 1 })); + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 50); + let _ = disp.clear(Rgb888::BLACK).unwrap(); + loop { + if key_pressed() { + let c = getchar(); + match c { + LF => break, + CR => break, + b'w' => game.set_direction(Direction::Up), + b's' => game.set_direction(Direction::Down), + b'a' => game.set_direction(Direction::Left), + b'd' => game.set_direction(Direction::Right), + _ => (), + } + } + let _ = disp.clear(Rgb888::BLACK).unwrap(); + game.draw(&mut disp); + sleep(10); + } + 0 +} diff --git a/user/src/bin/gui_uart.rs b/user/src/bin/gui_uart.rs new file mode 100644 index 00000000..c9577a4f --- /dev/null +++ b/user/src/bin/gui_uart.rs @@ -0,0 +1,125 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::console::getchar; +use user_lib::{framebuffer, framebuffer_flush}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; + +pub const VIRTGPU_XRES: usize = 1280; +pub const VIRTGPU_YRES: usize = 800; +pub const VIRTGPU_LEN: usize = VIRTGPU_XRES * VIRTGPU_YRES * 4; + +const INIT_X: i32 = 640; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 40; + +pub struct Display { + pub size: Size, + pub point: Point, + //pub fb: Arc<&'static mut [u8]>, + pub fb: &'static mut [u8], +} + +impl Display { + pub fn new(size: Size, point: Point) -> Self { + let fb_ptr = framebuffer() as *mut u8; + println!( + "Hello world from user mode program! 0x{:X} , len {}", + fb_ptr as usize, VIRTGPU_LEN + ); + let fb = + unsafe { core::slice::from_raw_parts_mut(fb_ptr as *mut u8, VIRTGPU_LEN as usize) }; + Self { size, point, fb } + } +} + +impl OriginDimensions for Display { + fn size(&self) -> Size { + self.size + } +} + +impl DrawTarget for Display { + type Color = Rgb888; + + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels.into_iter().for_each(|px| { + let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x) + as usize + * 4; + if idx + 2 >= self.fb.len() { + return; + } + self.fb[idx] = px.1.b(); + self.fb[idx + 1] = px.1.g(); + self.fb[idx + 2] = px.1.r(); + }); + framebuffer_flush(); + Ok(()) + } +} + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::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.disp) + .ok(); + } + fn unpaint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1)) + .draw(&mut self.disp) + .ok(); + } + pub fn move_rect(&mut self, dx: i32, dy: i32) { + self.unpaint(); + self.latest_pos.x += dx; + self.latest_pos.y += dy; + self.paint(); + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + // let fb_ptr = framebuffer() as *mut u8; + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + for i in 0..20 { + let c=getchar(); + if c == LF || c == CR { + break; + } + board.latest_pos.x += i; + board.latest_pos.y += i; + board.paint(); + } + 0 +} diff --git a/user/src/bin/huge_write_mt.rs b/user/src/bin/huge_write_mt.rs index 8ca75783..0a60fd8a 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/inputdev_event.rs b/user/src/bin/inputdev_event.rs new file mode 100644 index 00000000..5b820d80 --- /dev/null +++ b/user/src/bin/inputdev_event.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use user_lib::{event_get, DecodeType, Key, KeyType}; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main() -> i32 { + println!("Input device event test"); + loop { + if let Some(event) = event_get() { + if let Some(decoder_type) = event.decode() { + println!("{:?}", decoder_type); + if let DecodeType::Key(key, keytype) = decoder_type { + if key == Key::Enter && keytype == KeyType::Press { + break; + } + } + } + } + } + 0 +} \ No newline at end of file diff --git a/user/src/bin/mpsc_sem.rs b/user/src/bin/mpsc_sem.rs index 7b92b9b4..7b72bbb1 100644 --- a/user/src/bin/mpsc_sem.rs +++ b/user/src/bin/mpsc_sem.rs @@ -14,7 +14,7 @@ use user_lib::{thread_create, waittid}; const SEM_MUTEX: usize = 0; const SEM_EMPTY: usize = 1; -const SEM_EXISTED: usize = 2; +const SEM_AVAIL: usize = 2; const BUFFER_SIZE: usize = 8; static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; static mut FRONT: usize = 0; @@ -27,20 +27,20 @@ unsafe fn producer(id: *const usize) -> ! { for _ in 0..NUMBER_PER_PRODUCER { semaphore_down(SEM_EMPTY); semaphore_down(SEM_MUTEX); - BUFFER[FRONT] = id; - FRONT = (FRONT + 1) % BUFFER_SIZE; + BUFFER[TAIL] = id; + TAIL = (TAIL + 1) % BUFFER_SIZE; semaphore_up(SEM_MUTEX); - semaphore_up(SEM_EXISTED); + semaphore_up(SEM_AVAIL); } exit(0) } unsafe fn consumer() -> ! { for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { - semaphore_down(SEM_EXISTED); + semaphore_down(SEM_AVAIL); semaphore_down(SEM_MUTEX); - print!("{} ", BUFFER[TAIL]); - TAIL = (TAIL + 1) % BUFFER_SIZE; + print!("{} ", BUFFER[FRONT]); + FRONT = (FRONT + 1) % BUFFER_SIZE; semaphore_up(SEM_MUTEX); semaphore_up(SEM_EMPTY); } @@ -53,7 +53,7 @@ pub fn main() -> i32 { // create semaphores assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); - assert_eq!(semaphore_create(0) as usize, SEM_EXISTED); + assert_eq!(semaphore_create(0) as usize, SEM_AVAIL); // create threads let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); let mut threads = Vec::new(); diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs index 55519970..1fa2c416 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; @@ -11,7 +10,7 @@ extern crate core; use alloc::vec::Vec; use core::sync::atomic::{AtomicUsize, Ordering}; use user_lib::{exit, sleep, thread_create, waittid}; -const N: usize = 3; +const N: usize = 1000; static mut TURN: usize = 0; static mut FLAG: [bool; 2] = [false; 2]; @@ -30,27 +29,30 @@ fn critical_test_exit() { } fn peterson_enter_critical(id: usize, peer_id: usize) { - println!("Thread[{}] try enter", id); + // println!("Thread[{}] try enter", id); vstore!(&FLAG[id], true); vstore!(&TURN, peer_id); memory_fence!(); while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id { - println!("Thread[{}] enter fail", id); + // println!("Thread[{}] enter fail", id); sleep(1); - println!("Thread[{}] retry enter", id); + // println!("Thread[{}] retry enter", id); } - println!("Thread[{}] enter", id); + // println!("Thread[{}] enter", id); } fn peterson_exit_critical(id: usize) { vstore!(&FLAG[id], false); - println!("Thread[{}] exit", id); + // println!("Thread[{}] exit", id); } pub fn thread_fn(id: usize) -> ! { - println!("Thread[{}] init.", id); + // println!("Thread[{}] init.", id); let peer_id: usize = id ^ 1; - for _ in 0..N { + for iter in 0..N { + if iter % 10 == 0 { + println!("[{}] it={}", id, iter); + } peterson_enter_critical(id, peer_id); critical_test_enter(); for _ in 0..3 { @@ -75,4 +77,4 @@ pub fn main() -> i32 { } println!("main thread exited."); 0 -} +} \ No newline at end of file diff --git a/user/src/bin/race_adder.rs b/user/src/bin/race_adder.rs deleted file mode 100644 index c7b6747e..00000000 --- a/user/src/bin/race_adder.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use alloc::vec::Vec; -use user_lib::{exit, get_time, thread_create, waittid}; - -static mut A: usize = 0; -const PER_THREAD: usize = 1000; -const THREAD_COUNT: usize = 16; - -unsafe fn f() -> ! { - let mut t = 2usize; - for _ in 0..PER_THREAD { - let a = &mut A as *mut usize; - let cur = a.read_volatile(); - for _ in 0..500 { - t = t * t % 10007; - } - a.write_volatile(cur + 1); - } - exit(t as i32) -} - -#[no_mangle] -pub fn main() -> i32 { - let start = get_time(); - let mut v = Vec::new(); - for _ in 0..THREAD_COUNT { - v.push(thread_create(f as usize, 0) as usize); - } - let mut time_cost = Vec::new(); - for tid in v.iter() { - time_cost.push(waittid(*tid)); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_atomic.rs b/user/src/bin/race_adder_atomic.rs deleted file mode 100644 index 2feaed0d..00000000 --- a/user/src/bin/race_adder_atomic.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use alloc::vec::Vec; -use core::sync::atomic::{AtomicBool, Ordering}; -use user_lib::{exit, get_time, thread_create, waittid, yield_}; - -static mut A: usize = 0; -static OCCUPIED: AtomicBool = AtomicBool::new(false); -const PER_THREAD: usize = 1000; -const THREAD_COUNT: usize = 16; - -unsafe fn f() -> ! { - let mut t = 2usize; - for _ in 0..PER_THREAD { - while OCCUPIED - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_err() - { - yield_(); - } - let a = &mut A as *mut usize; - let cur = a.read_volatile(); - for _ in 0..500 { - t = t * t % 10007; - } - a.write_volatile(cur + 1); - OCCUPIED.store(false, Ordering::Relaxed); - } - exit(t as i32) -} - -#[no_mangle] -pub fn main() -> i32 { - let start = get_time(); - let mut v = Vec::new(); - for _ in 0..THREAD_COUNT { - v.push(thread_create(f as usize, 0) as usize); - } - let mut time_cost = Vec::new(); - for tid in v.iter() { - time_cost.push(waittid(*tid)); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_loop.rs b/user/src/bin/race_adder_loop.rs deleted file mode 100644 index 0e4fe838..00000000 --- a/user/src/bin/race_adder_loop.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use alloc::vec::Vec; -use user_lib::{exit, get_time, thread_create, waittid, yield_}; - -static mut A: usize = 0; -static mut OCCUPIED: bool = false; -const PER_THREAD: usize = 1000; -const THREAD_COUNT: usize = 16; - -unsafe fn f() -> ! { - let mut t = 2usize; - for _ in 0..PER_THREAD { - while OCCUPIED { - yield_(); - } - OCCUPIED = true; - // enter critical section - let a = &mut A as *mut usize; - let cur = a.read_volatile(); - for _ in 0..500 { - t = t * t % 10007; - } - a.write_volatile(cur + 1); - // exit critical section - OCCUPIED = false; - } - - exit(t as i32) -} - -#[no_mangle] -pub fn main() -> i32 { - let start = get_time(); - let mut v = Vec::new(); - for _ in 0..THREAD_COUNT { - v.push(thread_create(f as usize, 0) as usize); - } - let mut time_cost = Vec::new(); - for tid in v.iter() { - time_cost.push(waittid(*tid)); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_mutex_blocking.rs b/user/src/bin/race_adder_mutex_blocking.rs deleted file mode 100644 index e5affc42..00000000 --- a/user/src/bin/race_adder_mutex_blocking.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use alloc::vec::Vec; -use user_lib::{exit, get_time, thread_create, waittid}; -use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; - -static mut A: usize = 0; -const PER_THREAD: usize = 1000; -const THREAD_COUNT: usize = 16; - -unsafe fn f() -> ! { - let mut t = 2usize; - for _ in 0..PER_THREAD { - mutex_lock(0); - let a = &mut A as *mut usize; - let cur = a.read_volatile(); - for _ in 0..500 { - t = t * t % 10007; - } - a.write_volatile(cur + 1); - mutex_unlock(0); - } - exit(t as i32) -} - -#[no_mangle] -pub fn main() -> i32 { - let start = get_time(); - assert_eq!(mutex_blocking_create(), 0); - let mut v = Vec::new(); - for _ in 0..THREAD_COUNT { - v.push(thread_create(f as usize, 0) as usize); - } - let mut time_cost = Vec::new(); - for tid in v.iter() { - time_cost.push(waittid(*tid)); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_mutex_spin.rs b/user/src/bin/race_adder_mutex_spin.rs deleted file mode 100644 index ed3bcec9..00000000 --- a/user/src/bin/race_adder_mutex_spin.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use alloc::vec::Vec; -use user_lib::{exit, get_time, thread_create, waittid}; -use user_lib::{mutex_create, mutex_lock, mutex_unlock}; - -static mut A: usize = 0; -const PER_THREAD: usize = 1000; -const THREAD_COUNT: usize = 16; - -unsafe fn f() -> ! { - let mut t = 2usize; - for _ in 0..PER_THREAD { - mutex_lock(0); - let a = &mut A as *mut usize; - let cur = a.read_volatile(); - for _ in 0..500 { - t = t * t % 10007; - } - a.write_volatile(cur + 1); - mutex_unlock(0); - } - exit(t as i32) -} - -#[no_mangle] -pub fn main() -> i32 { - let start = get_time(); - assert_eq!(mutex_create(), 0); - let mut v = Vec::new(); - for _ in 0..THREAD_COUNT { - v.push(thread_create(f as usize, 0) as usize); - } - let mut time_cost = Vec::new(); - for tid in v.iter() { - time_cost.push(waittid(*tid)); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/random_num.rs b/user/src/bin/random_num.rs new file mode 100644 index 00000000..dd9ac00d --- /dev/null +++ b/user/src/bin/random_num.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use oorandom; + +#[no_mangle] +pub fn main() -> i32 { + println!("random num program!"); + let seed = 4; + let mut rng = oorandom::Rand32::new(seed); + println!("OORandom: Random number 32bit: {}", rng.rand_i32()); + println!("OORandom: Random number range: {}", rng.rand_range(1..100)); + 0 +} \ No newline at end of file diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs index 5f50b0d6..d3e329d1 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 5d365f5d..3bec557a 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 00000000..d9f3b947 --- /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 00000000..43aeb2de --- /dev/null +++ b/user/src/bin/stackless_coroutine.rs @@ -0,0 +1,129 @@ +// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/ +// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86 +#![no_std] +#![no_main] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +extern crate alloc; +use alloc::collections::VecDeque; + +use alloc::boxed::Box; + +#[macro_use] +extern crate user_lib; + +enum State { + Halted, + Running, +} + +struct Task { + state: State, +} + +impl Task { + fn waiter<'a>(&'a mut self) -> Waiter<'a> { + Waiter { task: self } + } +} + +struct Waiter<'a> { + task: &'a mut Task, +} + +impl<'a> Future for Waiter<'a> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + match self.task.state { + State::Halted => { + self.task.state = State::Running; + Poll::Ready(()) + } + State::Running => { + self.task.state = State::Halted; + Poll::Pending + } + } + } +} + +struct Executor { + tasks: VecDeque>>>, +} + +impl Executor { + fn new() -> Self { + Executor { + tasks: VecDeque::new(), + } + } + + fn push(&mut self, closure: C) + where + F: Future + 'static, + C: FnOnce(Task) -> F, + { + let task = Task { + state: State::Running, + }; + self.tasks.push_back(Box::pin(closure(task))); + } + + fn run(&mut self) { + let waker = create_waker(); + let mut context = Context::from_waker(&waker); + + while let Some(mut task) = self.tasks.pop_front() { + match task.as_mut().poll(&mut context) { + Poll::Pending => { + self.tasks.push_back(task); + } + Poll::Ready(()) => {} + } + } + } +} + +pub fn create_waker() -> Waker { + // Safety: The waker points to a vtable with functions that do nothing. Doing + // nothing is memory-safe. + unsafe { Waker::from_raw(RAW_WAKER) } +} + +const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE); +const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + +unsafe fn clone(_: *const ()) -> RawWaker { + RAW_WAKER +} +unsafe fn wake(_: *const ()) {} +unsafe fn wake_by_ref(_: *const ()) {} +unsafe fn drop(_: *const ()) {} + +#[no_mangle] +pub fn main() -> i32 { + println!("stackless coroutine Begin.."); + let mut exec = Executor::new(); + println!(" Create futures"); + for instance in 1..=3 { + exec.push(move |mut task| async move { + println!(" Task {}: begin state", instance); + task.waiter().await; + println!(" Task {}: next state", instance); + task.waiter().await; + println!(" Task {}: end state", instance); + }); + } + + println!(" Running"); + exec.run(); + println!(" Done"); + println!("stackless coroutine PASSED"); + + 0 +} diff --git a/user/src/bin/udp.rs b/user/src/bin/udp.rs new file mode 100644 index 00000000..f2f00936 --- /dev/null +++ b/user/src/bin/udp.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +use alloc::string::String; + +#[macro_use] +extern crate user_lib; +#[macro_use] +extern crate alloc; + +use user_lib::{connect, write, read}; + +#[no_mangle] +pub fn main() -> i32 { + println!("udp test open!"); + + let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099); + + if udp_fd < 0 { + println!("failed to create udp connection."); + return -1; + } + + let buf = "Hello rCoreOS user program!"; + + println!("send <{}>", buf); + + write(udp_fd as usize, buf.as_bytes()); + + println!("udp send done, waiting for reply."); + + let mut buf = vec![0u8; 1024]; + + let len = read(udp_fd as usize, &mut buf); + + if len < 0 { + println!("can't receive udp packet"); + return -1; + } + + let recv_str = String::from_utf8_lossy(&buf[..len as usize]); + + println!("receive reply <{}>", recv_str); + + 0 +} \ No newline at end of file diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs index eb706fd5..b522af24 100644 --- a/user/src/bin/usertests.rs +++ b/user/src/bin/usertests.rs @@ -27,19 +27,22 @@ static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[ ("phil_din_mutex\0", "\0", "\0", "\0", 0), ("pipe_large_test\0", "\0", "\0", "\0", 0), ("pipetest\0", "\0", "\0", "\0", 0), - ("race_adder_arg\0", "3\0", "\0", "\0", 0), - ("race_adder_atomic\0", "\0", "\0", "\0", 0), - ("race_adder_mutex_blocking\0", "\0", "\0", "\0", 0), - ("race_adder_mutex_spin\0", "\0", "\0", "\0", 0), + ("adder_peterson_spin\0", "\0", "\0", "\0", 0), + ("adder_peterson_yield\0", "\0", "\0", "\0", 0), + ("adder_mutex_blocking\0", "\0", "\0", "\0", 0), + ("adder_mutex_spin\0", "\0", "\0", "\0", 0), ("run_pipe_test\0", "\0", "\0", "\0", 0), ("sleep_simple\0", "\0", "\0", "\0", 0), ("sleep\0", "\0", "\0", "\0", 0), ("sleep_simple\0", "\0", "\0", "\0", 0), ("sync_sem\0", "\0", "\0", "\0", 0), - ("test_condvar\0", "\0", "\0", "\0", 0), + ("condsync_sem\0", "\0", "\0", "\0", 0), + ("condsync_condvar\0", "\0", "\0", "\0", 0), ("threads_arg\0", "\0", "\0", "\0", 0), ("threads\0", "\0", "\0", "\0", 0), ("yield\0", "\0", "\0", "\0", 0), + ("barrier_fail\0", "\0", "\0", "\0", 0), + ("barrier_condvar\0", "\0", "\0", "\0", 0), ]; static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[ @@ -49,8 +52,9 @@ static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[ ("priv_inst\0", "\0", "\0", "\0", -4), ("store_fault\0", "\0", "\0", "\0", -11), ("until_timeout\0", "\0", "\0", "\0", -6), - ("race_adder\0", "\0", "\0", "\0", -6), - ("huge_write_mt\0", "\0", "\0", "\0", -6), + ("adder\0", "\0", "\0", "\0", -6), + ("adder_simple_spin\0", "\0", "\0", "\0", -6), + ("adder_simple_yield\0", "\0", "\0", "\0", -6), ]; use user_lib::{exec, fork, waitpid}; diff --git a/user/src/file.rs b/user/src/file.rs new file mode 100644 index 00000000..bf77667a --- /dev/null +++ b/user/src/file.rs @@ -0,0 +1,30 @@ +use super::*; + +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +pub fn dup(fd: usize) -> isize { + sys_dup(fd) +} +pub fn open(path: &str, flags: OpenFlags) -> isize { + sys_open(path, flags.bits) +} +pub fn close(fd: usize) -> isize { + sys_close(fd) +} +pub fn pipe(pipe_fd: &mut [usize]) -> isize { + sys_pipe(pipe_fd) +} +pub fn read(fd: usize, buf: &mut [u8]) -> isize { + sys_read(fd, buf) +} +pub fn write(fd: usize, buf: &[u8]) -> isize { + sys_write(fd, buf) +} \ No newline at end of file diff --git a/user/src/io.rs b/user/src/io.rs new file mode 100644 index 00000000..baaa3382 --- /dev/null +++ b/user/src/io.rs @@ -0,0 +1,118 @@ +use super::*; +use embedded_graphics::prelude::{RgbColor, Size}; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use embedded_graphics::pixelcolor::Rgb888; +use virtio_input_decoder::Decoder; +pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse}; + +pub const VIRTGPU_XRES: u32 = 1280; +pub const VIRTGPU_YRES: u32 = 800; +pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize; + +pub fn framebuffer() -> isize { + sys_framebuffer() +} +pub fn framebuffer_flush() -> isize { + sys_framebuffer_flush() +} + +pub struct Display { + pub size: Size, + pub fb: &'static mut [u8], +} + +impl Display { + pub fn new(size: Size) -> Self { + let fb_ptr = framebuffer() as *mut u8; + let fb = + unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) }; + Self { size, fb} + } + pub fn framebuffer(&mut self) -> &mut [u8] { + self.fb + } + pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) { + p(self.framebuffer()); + framebuffer_flush(); + } +} + +impl OriginDimensions for Display { + fn size(&self) -> Size { + self.size + } +} + +impl DrawTarget for Display { + type Color = Rgb888; + + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels.into_iter().for_each(|px| { + let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x) + as usize + * 4; + if idx + 2 >= self.fb.len() { + return; + } + self.fb[idx] = px.1.b(); + self.fb[idx + 1] = px.1.g(); + self.fb[idx + 2] = px.1.r(); + }); + framebuffer_flush(); + Ok(()) + } +} + +pub fn event_get() -> Option { + let raw_value = sys_event_get(); + if raw_value == 0 { + None + } else { + Some((raw_value as u64).into()) + } +} + +pub fn key_pressed() -> bool { + if sys_key_pressed() == 1 { + true + } else { + false + } +} + +#[repr(C)] +pub struct InputEvent { + pub event_type: u16, + pub code: u16, + pub value: u32, +} + +impl From for InputEvent { + fn from(mut v: u64) -> Self { + let value = v as u32; + v >>= 32; + let code = v as u16; + v >>= 16; + let event_type = v as u16; + Self { + event_type, + code, + value, + } + } +} + +impl InputEvent { + pub fn decode(&self) -> Option { + Decoder::decode( + self.event_type as usize, + self.code as usize, + self.value as usize, + ).ok() + } +} \ No newline at end of file diff --git a/user/src/lib.rs b/user/src/lib.rs index 6f57edd4..1b5b2f38 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -8,6 +8,11 @@ pub mod console; mod lang_items; mod syscall; +mod file; +mod task; +mod sync; +mod io; +mod net; extern crate alloc; #[macro_use] @@ -16,6 +21,11 @@ extern crate bitflags; use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; use syscall::*; +pub use file::*; +pub use task::*; +pub use sync::*; +pub use io::*; +pub use net::*; const USER_HEAP_SIZE: usize = 32768; @@ -59,148 +69,6 @@ fn main(_argc: usize, _argv: &[&str]) -> i32 { panic!("Cannot find main!"); } -bitflags! { - pub struct OpenFlags: u32 { - const RDONLY = 0; - const WRONLY = 1 << 0; - const RDWR = 1 << 1; - const CREATE = 1 << 9; - const TRUNC = 1 << 10; - } -} - -pub fn dup(fd: usize) -> isize { - sys_dup(fd) -} -pub fn open(path: &str, flags: OpenFlags) -> isize { - sys_open(path, flags.bits) -} -pub fn close(fd: usize) -> isize { - sys_close(fd) -} -pub fn pipe(pipe_fd: &mut [usize]) -> isize { - sys_pipe(pipe_fd) -} -pub fn read(fd: usize, buf: &mut [u8]) -> isize { - sys_read(fd, buf) -} -pub fn write(fd: usize, buf: &[u8]) -> isize { - sys_write(fd, buf) -} -pub fn exit(exit_code: i32) -> ! { - sys_exit(exit_code); -} -pub fn yield_() -> isize { - sys_yield() -} -pub fn get_time() -> isize { - sys_get_time() -} -pub fn getpid() -> isize { - sys_getpid() -} -pub fn fork() -> isize { - sys_fork() -} -pub fn exec(path: &str, args: &[*const u8]) -> isize { - sys_exec(path, args) -} -pub fn wait(exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(-1, exit_code as *mut _) { - -2 => { - yield_(); - } - // -1 or a real pid - exit_pid => return exit_pid, - } - } -} - -pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(pid as isize, exit_code as *mut _) { - -2 => { - yield_(); - } - // -1 or a real pid - exit_pid => return exit_pid, - } - } -} - -pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { - sys_waitpid(pid as isize, exit_code as *mut _) -} - -bitflags! { - pub struct SignalFlags: i32 { - const SIGINT = 1 << 2; - const SIGILL = 1 << 4; - const SIGABRT = 1 << 6; - const SIGFPE = 1 << 8; - const SIGSEGV = 1 << 11; - } -} - -pub fn kill(pid: usize, signal: i32) -> isize { - sys_kill(pid, signal) -} - -pub fn sleep(sleep_ms: usize) { - sys_sleep(sleep_ms); -} - -pub fn thread_create(entry: usize, arg: usize) -> isize { - sys_thread_create(entry, arg) -} -pub fn gettid() -> isize { - sys_gettid() -} -pub fn waittid(tid: usize) -> isize { - loop { - match sys_waittid(tid) { - -2 => { - yield_(); - } - exit_code => return exit_code, - } - } -} - -pub fn mutex_create() -> isize { - sys_mutex_create(false) -} -pub fn mutex_blocking_create() -> isize { - sys_mutex_create(true) -} -pub fn mutex_lock(mutex_id: usize) { - sys_mutex_lock(mutex_id); -} -pub fn mutex_unlock(mutex_id: usize) { - sys_mutex_unlock(mutex_id); -} -pub fn semaphore_create(res_count: usize) -> isize { - sys_semaphore_create(res_count) -} -pub fn semaphore_up(sem_id: usize) { - sys_semaphore_up(sem_id); -} -pub fn semaphore_down(sem_id: usize) { - sys_semaphore_down(sem_id); -} -pub fn condvar_create() -> isize { - sys_condvar_create(0) -} -pub fn condvar_signal(condvar_id: usize) { - sys_condvar_signal(condvar_id); -} -pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { - sys_condvar_wait(condvar_id, mutex_id); -} -pub fn create_desktop() { - sys_create_desktop(); -} #[macro_export] macro_rules! vstore { ($var_ref: expr, $value: expr) => { diff --git a/user/src/net.rs b/user/src/net.rs new file mode 100644 index 00000000..40ebca45 --- /dev/null +++ b/user/src/net.rs @@ -0,0 +1,5 @@ +use super::*; + +pub fn connect(ip: u32, sport: u16, dport: u16) -> isize { + sys_connect(ip, sport, dport) +} \ No newline at end of file diff --git a/user/src/sync.rs b/user/src/sync.rs new file mode 100644 index 00000000..c41e4d6b --- /dev/null +++ b/user/src/sync.rs @@ -0,0 +1,32 @@ +use super::*; + +pub fn mutex_create() -> isize { + sys_mutex_create(false) +} +pub fn mutex_blocking_create() -> isize { + sys_mutex_create(true) +} +pub fn mutex_lock(mutex_id: usize) { + sys_mutex_lock(mutex_id); +} +pub fn mutex_unlock(mutex_id: usize) { + sys_mutex_unlock(mutex_id); +} +pub fn semaphore_create(res_count: usize) -> isize { + sys_semaphore_create(res_count) +} +pub fn semaphore_up(sem_id: usize) { + sys_semaphore_up(sem_id); +} +pub fn semaphore_down(sem_id: usize) { + sys_semaphore_down(sem_id); +} +pub fn condvar_create() -> isize { + sys_condvar_create() +} +pub fn condvar_signal(condvar_id: usize) { + sys_condvar_signal(condvar_id); +} +pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { + sys_condvar_wait(condvar_id, mutex_id); +} \ No newline at end of file diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 3f36f533..c5fb48dc 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,4 +1,5 @@ const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -25,6 +26,10 @@ const SYSCALL_SEMAPHORE_DOWN: usize = 1022; const SYSCALL_CONDVAR_CREATE: usize = 1030; const SYSCALL_CONDVAR_SIGNAL: usize = 1031; const SYSCALL_CONDVAR_WAIT: usize = 1032; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -44,6 +49,10 @@ pub fn sys_dup(fd: usize) -> isize { syscall(SYSCALL_DUP, [fd, 0, 0]) } +pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize { + syscall(SYSCALL_CONNECT, [dest as usize, sport as usize, dport as usize]) +} + pub fn sys_open(path: &str, flags: u32) -> isize { syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) } @@ -143,8 +152,8 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize { syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0]) } -pub fn sys_condvar_create(_arg: usize) -> isize { - syscall(SYSCALL_CONDVAR_CREATE, [_arg, 0, 0]) +pub fn sys_condvar_create() -> isize { + syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0]) } pub fn sys_condvar_signal(condvar_id: usize) -> isize { @@ -154,6 +163,20 @@ 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]) + + +pub fn sys_framebuffer() -> isize { + syscall(SYSCALL_FRAMEBUFFER, [0, 0, 0]) +} + +pub fn sys_framebuffer_flush() -> isize { + syscall(SYSCALL_FRAMEBUFFER_FLUSH, [0, 0, 0]) +} + +pub fn sys_event_get() -> isize { + syscall(SYSCALL_EVENT_GET, [0, 0, 0]) +} + +pub fn sys_key_pressed() -> isize { + syscall(SYSCALL_KEY_PRESSED, [0, 0, 0]) } \ No newline at end of file diff --git a/user/src/task.rs b/user/src/task.rs new file mode 100644 index 00000000..46eee79c --- /dev/null +++ b/user/src/task.rs @@ -0,0 +1,83 @@ +use super::*; + +pub fn exit(exit_code: i32) -> ! { + sys_exit(exit_code); +} +pub fn yield_() -> isize { + sys_yield() +} +pub fn get_time() -> isize { + sys_get_time() +} +pub fn getpid() -> isize { + sys_getpid() +} +pub fn fork() -> isize { + sys_fork() +} +pub fn exec(path: &str, args: &[*const u8]) -> isize { + sys_exec(path, args) +} + +pub fn wait(exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(-1, exit_code as *mut _) { + -2 => { + yield_(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} + +pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(pid as isize, exit_code as *mut _) { + -2 => { + yield_(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} + +pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { + sys_waitpid(pid as isize, exit_code as *mut _) +} + +bitflags! { + pub struct SignalFlags: i32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +pub fn kill(pid: usize, signal: i32) -> isize { + sys_kill(pid, signal) +} + +pub fn sleep(sleep_ms: usize) { + sys_sleep(sleep_ms); +} + +pub fn thread_create(entry: usize, arg: usize) -> isize { + sys_thread_create(entry, arg) +} +pub fn gettid() -> isize { + sys_gettid() +} +pub fn waittid(tid: usize) -> isize { + loop { + match sys_waittid(tid) { + -2 => { + yield_(); + } + exit_code => return exit_code, + } + } +} \ No newline at end of file