mirror of
https://github.com/rcore-os/rCore-Tutorial-v3.git
synced 2024-11-22 09:26:26 +04:00
Merge branch 'main' into fix/kstack-leak
This commit is contained in:
commit
abc19f593d
9
.github/workflows/doc-and-test.yml
vendored
9
.github/workflows/doc-and-test.yml
vendored
@ -4,6 +4,7 @@ on: [push]
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
rust_toolchain: nightly-2022-08-05
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-doc:
|
build-doc:
|
||||||
@ -13,11 +14,11 @@ jobs:
|
|||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: nightly-2022-07-20
|
toolchain: ${{ env.rust_toolchain }}
|
||||||
components: rust-src, llvm-tools-preview
|
components: rust-src, llvm-tools-preview
|
||||||
target: riscv64gc-unknown-none-elf
|
target: riscv64gc-unknown-none-elf
|
||||||
- name: Build doc
|
- 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
|
- name: Deploy to Github Pages
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
@ -32,7 +33,7 @@ jobs:
|
|||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: nightly-2022-07-20
|
toolchain: ${{ env.rust_toolchain }}
|
||||||
components: rust-src, llvm-tools-preview
|
components: rust-src, llvm-tools-preview
|
||||||
target: riscv64gc-unknown-none-elf
|
target: riscv64gc-unknown-none-elf
|
||||||
- uses: actions-rs/install@v0.1
|
- uses: actions-rs/install@v0.1
|
||||||
@ -65,5 +66,3 @@ jobs:
|
|||||||
run: cd os && make run TEST=1
|
run: cd os && make run TEST=1
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
- name: Build for k210
|
|
||||||
run: cd os && make build BOARD=k210
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,5 +9,7 @@ os/src/link_app.S
|
|||||||
os/src/linker.ld
|
os/src/linker.ld
|
||||||
os/last-*
|
os/last-*
|
||||||
os/.gdb_history
|
os/.gdb_history
|
||||||
|
os/virt.out
|
||||||
tools/
|
tools/
|
||||||
pushall.sh
|
pushall.sh
|
||||||
|
.vscode/*.log
|
||||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -7,7 +7,7 @@
|
|||||||
// For Rust Analyzer plugin users:
|
// For Rust Analyzer plugin users:
|
||||||
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
|
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
|
||||||
"rust-analyzer.checkOnSave.allTargets": false,
|
"rust-analyzer.checkOnSave.allTargets": false,
|
||||||
"rust-analyzer.cargo.features": [
|
// "rust-analyzer.cargo.features": [
|
||||||
"board_qemu"
|
// "board_qemu"
|
||||||
]
|
// ]
|
||||||
}
|
}
|
@ -1,9 +1,4 @@
|
|||||||
# rCore-Tutorial-v3
|
# 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 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)
|
rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS)
|
||||||
|
Binary file not shown.
@ -11,6 +11,6 @@ clap = "2.33.3"
|
|||||||
easy-fs = { path = "../easy-fs" }
|
easy-fs = { path = "../easy-fs" }
|
||||||
rand = "0.8.0"
|
rand = "0.8.0"
|
||||||
|
|
||||||
[features]
|
# [features]
|
||||||
board_qemu = []
|
# board_qemu = []
|
||||||
board_k210 = []
|
# board_k210 = []
|
||||||
|
@ -85,9 +85,9 @@ fn easy_fs_pack() -> std::io::Result<()> {
|
|||||||
inode.write_at(0, all_data.as_slice());
|
inode.write_at(0, all_data.as_slice());
|
||||||
}
|
}
|
||||||
// list apps
|
// list apps
|
||||||
for app in root_inode.ls() {
|
// for app in root_inode.ls() {
|
||||||
println!("{}", app);
|
// println!("{}", app);
|
||||||
}
|
// }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,21 +10,15 @@ edition = "2021"
|
|||||||
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
|
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
|
||||||
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||||
buddy_system_allocator = "0.6"
|
buddy_system_allocator = "0.6"
|
||||||
|
bit_field = "0.10.0"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
xmas-elf = "0.7.0"
|
xmas-elf = "0.7.0"
|
||||||
volatile = "0.3"
|
volatile = "0.3"
|
||||||
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" }
|
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" }
|
||||||
k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
|
lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "3f467dd" }
|
||||||
k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
|
|
||||||
k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
|
|
||||||
easy-fs = { path = "../easy-fs" }
|
easy-fs = { path = "../easy-fs" }
|
||||||
virtio-input-decoder = "0.1.4"
|
|
||||||
embedded-graphics = "0.7.1"
|
embedded-graphics = "0.7.1"
|
||||||
tinybmp = "0.3.1"
|
tinybmp = "0.3.1"
|
||||||
|
|
||||||
[features]
|
|
||||||
board_qemu = []
|
|
||||||
board_k210 = []
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
81
os/Makefile
81
os/Makefile
@ -5,14 +5,18 @@ KERNEL_ELF := target/$(TARGET)/$(MODE)/os
|
|||||||
KERNEL_BIN := $(KERNEL_ELF).bin
|
KERNEL_BIN := $(KERNEL_ELF).bin
|
||||||
DISASM_TMP := target/$(TARGET)/$(MODE)/asm
|
DISASM_TMP := target/$(TARGET)/$(MODE)/asm
|
||||||
FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img
|
FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img
|
||||||
SDCARD := /dev/sdb
|
|
||||||
APPS := ../user/src/bin/*
|
APPS := ../user/src/bin/*
|
||||||
|
|
||||||
# BOARD
|
# BOARD
|
||||||
BOARD ?= qemu
|
BOARD := qemu
|
||||||
SBI ?= rustsbi
|
SBI ?= rustsbi
|
||||||
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
|
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
|
||||||
K210_BOOTLOADER_SIZE := 131072
|
|
||||||
|
# GUI
|
||||||
|
GUI ?= off
|
||||||
|
ifeq ($(GUI), off)
|
||||||
|
GUI_OPTION := -display none
|
||||||
|
endif
|
||||||
|
|
||||||
# Building mode argument
|
# Building mode argument
|
||||||
ifeq ($(MODE), release)
|
ifeq ($(MODE), release)
|
||||||
@ -20,15 +24,7 @@ ifeq ($(MODE), release)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# KERNEL ENTRY
|
# KERNEL ENTRY
|
||||||
ifeq ($(BOARD), qemu)
|
KERNEL_ENTRY_PA := 0x80000000
|
||||||
KERNEL_ENTRY_PA := 0x80200000
|
|
||||||
else ifeq ($(BOARD), k210)
|
|
||||||
KERNEL_ENTRY_PA := 0x80020000
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Run K210
|
|
||||||
K210-SERIALPORT = /dev/ttyUSB0
|
|
||||||
K210-BURNER = ../tools/kflash.py
|
|
||||||
|
|
||||||
# Binutils
|
# Binutils
|
||||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||||
@ -40,14 +36,7 @@ DISASM ?= -x
|
|||||||
# Run usertests or usershell
|
# Run usertests or usershell
|
||||||
TEST ?=
|
TEST ?=
|
||||||
|
|
||||||
build: env switch-check $(KERNEL_BIN) fs-img
|
build: env $(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
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
(rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET)
|
(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 rust-src
|
||||||
rustup component add llvm-tools-preview
|
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
|
$(KERNEL_BIN): kernel
|
||||||
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
|
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
|
||||||
|
|
||||||
@ -73,7 +57,7 @@ $(APPS):
|
|||||||
kernel:
|
kernel:
|
||||||
@echo Platform: $(BOARD)
|
@echo Platform: $(BOARD)
|
||||||
@cp src/linker-$(BOARD).ld src/linker.ld
|
@cp src/linker-$(BOARD).ld src/linker.ld
|
||||||
@cargo build --release --features "board_$(BOARD)"
|
@cargo build --release
|
||||||
@rm src/linker.ld
|
@rm src/linker.ld
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@ -89,44 +73,53 @@ disasm-vim: kernel
|
|||||||
|
|
||||||
run: run-inner
|
run: run-inner
|
||||||
|
|
||||||
gui: build
|
run-inner-none: build
|
||||||
ifeq ($(BOARD),qemu)
|
|
||||||
@qemu-system-riscv64 \
|
@qemu-system-riscv64 \
|
||||||
-M 128m \
|
-M 128m \
|
||||||
-machine virt \
|
-machine virt \
|
||||||
-bios $(BOOTLOADER) \
|
-bios none \
|
||||||
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
|
$(GUI_OPTION) \
|
||||||
|
-kernel $(KERNEL_ELF) \
|
||||||
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
||||||
-device virtio-blk-device,drive=x0 \
|
-device virtio-blk-device,drive=x0 \
|
||||||
-device virtio-gpu-device \
|
# -device virtio-gpu-device \
|
||||||
-device virtio-keyboard-device \
|
-device virtio-keyboard-device \
|
||||||
-device virtio-mouse-device \
|
-device virtio-mouse-device \
|
||||||
|
-device virtio-net-device,netdev=net0 \
|
||||||
|
-netdev user,id=net0,hostfwd=udp::6200-:2000 \
|
||||||
-serial stdio
|
-serial stdio
|
||||||
endif
|
|
||||||
|
|
||||||
run-inner: build
|
run-inner: build
|
||||||
ifeq ($(BOARD),qemu)
|
|
||||||
@qemu-system-riscv64 \
|
@qemu-system-riscv64 \
|
||||||
-M 128m \
|
-M 128m \
|
||||||
-machine virt \
|
-machine virt \
|
||||||
-bios $(BOOTLOADER) \
|
-bios $(BOOTLOADER) \
|
||||||
-display none \
|
$(GUI_OPTION) \
|
||||||
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
|
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
|
||||||
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
||||||
-device virtio-blk-device,drive=x0 \
|
-device virtio-blk-device,drive=x0 \
|
||||||
-device virtio-gpu-device \
|
-device virtio-gpu-device \
|
||||||
-device virtio-keyboard-device \
|
-device virtio-keyboard-device \
|
||||||
-device virtio-mouse-device \
|
-device virtio-mouse-device \
|
||||||
|
-device virtio-net-device,netdev=net0 \
|
||||||
|
-netdev user,id=net0,hostfwd=udp::6200-:2000 \
|
||||||
-serial stdio
|
-serial stdio
|
||||||
else
|
|
||||||
(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
|
fdt:
|
||||||
@cp $(BOOTLOADER) $(BOOTLOADER).copy
|
@qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out
|
||||||
@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1
|
fdtdump virt.out
|
||||||
@mv $(BOOTLOADER).copy $(KERNEL_BIN)
|
|
||||||
@sudo chmod 777 $(K210-SERIALPORT)
|
debug-none: build
|
||||||
python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN)
|
@tmux new-session -d \
|
||||||
python3 -m serial.tools.miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200
|
"qemu-system-riscv64 -machine virt -nographic -bios none -kernel $(KERNEL_ELF) \
|
||||||
endif
|
-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
|
debug: build
|
||||||
@tmux new-session -d \
|
@tmux new-session -d \
|
||||||
@ -141,4 +134,4 @@ gdbserver: build
|
|||||||
gdbclient:
|
gdbclient:
|
||||||
@riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'
|
@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
|
||||||
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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!();
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ pub const CLOCK_FREQ: usize = 12500000;
|
|||||||
|
|
||||||
pub const MMIO: &[(usize, usize)] = &[
|
pub const MMIO: &[(usize, usize)] = &[
|
||||||
(0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine
|
(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
|
(0xc000000, 0x210000), // VIRT_PLIC in virt machine
|
||||||
(0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine
|
(0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine
|
||||||
];
|
];
|
||||||
@ -12,8 +12,9 @@ pub type CharDeviceImpl = crate::drivers::chardev::NS16550a<VIRT_UART>;
|
|||||||
|
|
||||||
pub const VIRT_PLIC: usize = 0xC00_0000;
|
pub const VIRT_PLIC: usize = 0xC00_0000;
|
||||||
pub const VIRT_UART: usize = 0x1000_0000;
|
pub const VIRT_UART: usize = 0x1000_0000;
|
||||||
|
#[allow(unused)]
|
||||||
pub const VIRTGPU_XRES: u32 = 1280;
|
pub const VIRTGPU_XRES: u32 = 1280;
|
||||||
|
#[allow(unused)]
|
||||||
pub const VIRTGPU_YRES: u32 = 800;
|
pub const VIRTGPU_YRES: u32 = 800;
|
||||||
|
|
||||||
use crate::drivers::block::BLOCK_DEVICE;
|
use crate::drivers::block::BLOCK_DEVICE;
|
||||||
@ -52,6 +53,58 @@ pub fn irq_handler() {
|
|||||||
plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id);
|
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
|
//ref:: https://github.com/andre-richter/qemu-exit
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use crate::drivers::chardev::CharDevice;
|
use crate::drivers::chardev::CharDevice;
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
use crate::drivers::chardev::UART;
|
use crate::drivers::chardev::UART;
|
||||||
#[cfg(feature = "board_k210")]
|
|
||||||
use crate::sbi::console_putchar;
|
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
struct Stdout;
|
struct Stdout;
|
||||||
@ -10,10 +7,7 @@ struct Stdout;
|
|||||||
impl Write for Stdout {
|
impl Write for Stdout {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
UART.write(c as u8);
|
UART.write(c as u8);
|
||||||
#[cfg(feature = "board_k210")]
|
|
||||||
console_putchar(c as usize);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
mod sdcard;
|
|
||||||
mod virtio_blk;
|
mod virtio_blk;
|
||||||
|
|
||||||
pub use sdcard::SDCardWrapper;
|
|
||||||
pub use virtio_blk::VirtIOBlock;
|
pub use virtio_blk::VirtIOBlock;
|
||||||
|
|
||||||
use crate::board::BlockDeviceImpl;
|
use crate::board::BlockDeviceImpl;
|
||||||
|
@ -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,
|
|
||||||
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</*'a,*/ X: SPI> SDCard</*'a,*/ X> {
|
|
||||||
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 xxx0<status>1
|
|
||||||
* - 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<SDCardCSD, ()> {
|
|
||||||
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<SDCardCID, ()> {
|
|
||||||
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<SDCardInfo, ()> {
|
|
||||||
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<SDCardInfo, InitError> {
|
|
||||||
/* 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 {
|
|
||||||
/* <ACMD> */
|
|
||||||
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<Peripherals> =
|
|
||||||
unsafe { UPIntrFreeCell::new(Peripherals::take().unwrap()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
|
|
||||||
// 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<SDCard<SPIImpl<SPI0>>>);
|
|
||||||
|
|
||||||
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!();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
use super::BlockDevice;
|
use super::BlockDevice;
|
||||||
|
use crate::drivers::bus::virtio::VirtioHal;
|
||||||
use crate::sync::{Condvar, UPIntrFreeCell};
|
use crate::sync::{Condvar, UPIntrFreeCell};
|
||||||
use crate::task::schedule;
|
use crate::task::schedule;
|
||||||
use crate::DEV_NON_BLOCKING_ACCESS;
|
use crate::DEV_NON_BLOCKING_ACCESS;
|
||||||
@ -69,7 +70,9 @@ impl BlockDevice for VirtIOBlock {
|
|||||||
impl VirtIOBlock {
|
impl VirtIOBlock {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let virtio_blk = unsafe {
|
let virtio_blk = unsafe {
|
||||||
UPIntrFreeCell::new(VirtIOBlk::<VirtioHal>::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap())
|
UPIntrFreeCell::new(
|
||||||
|
VirtIOBlk::<VirtioHal>::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let mut condvars = BTreeMap::new();
|
let mut condvars = BTreeMap::new();
|
||||||
let channels = virtio_blk.exclusive_access().virt_queue_size();
|
let channels = virtio_blk.exclusive_access().virt_queue_size();
|
||||||
@ -83,4 +86,3 @@ impl VirtIOBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use crate::mm::{
|
use crate::mm::{
|
||||||
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
|
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
|
||||||
StepByOne, VirtAddr,
|
StepByOne, VirtAddr, frame_alloc_more,
|
||||||
};
|
};
|
||||||
use crate::sync::UPIntrFreeCell;
|
use crate::sync::UPIntrFreeCell;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use lazy_static::*;
|
use lazy_static::*;
|
||||||
use virtio_drivers::Hal;
|
use virtio_drivers::Hal;
|
||||||
|
|
||||||
@ -16,15 +16,9 @@ pub struct VirtioHal;
|
|||||||
|
|
||||||
impl Hal for VirtioHal {
|
impl Hal for VirtioHal {
|
||||||
fn dma_alloc(pages: usize) -> usize {
|
fn dma_alloc(pages: usize) -> usize {
|
||||||
let mut ppn_base = PhysPageNum(0);
|
let trakcers = frame_alloc_more(pages);
|
||||||
for i in 0..pages {
|
let ppn_base = trakcers.as_ref().unwrap().last().unwrap().ppn;
|
||||||
let frame = frame_alloc().unwrap();
|
QUEUE_FRAMES.exclusive_access().append(&mut trakcers.unwrap());
|
||||||
if i == 0 {
|
|
||||||
ppn_base = frame.ppn;
|
|
||||||
}
|
|
||||||
assert_eq!(frame.ppn.0, ppn_base.0 + i);
|
|
||||||
QUEUE_FRAMES.exclusive_access().push(frame);
|
|
||||||
}
|
|
||||||
let pa: PhysAddr = ppn_base.into();
|
let pa: PhysAddr = ppn_base.into();
|
||||||
pa.0
|
pa.0
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
mod ns16550a;
|
mod ns16550a;
|
||||||
|
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
use crate::board::CharDeviceImpl;
|
use crate::board::CharDeviceImpl;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use lazy_static::*;
|
use lazy_static::*;
|
||||||
pub use ns16550a::NS16550a;
|
pub use ns16550a::NS16550a;
|
||||||
|
|
||||||
pub trait CharDevice {
|
pub trait CharDevice {
|
||||||
|
fn init(&self);
|
||||||
fn read(&self) -> u8;
|
fn read(&self) -> u8;
|
||||||
fn write(&self, ch: u8);
|
fn write(&self, ch: u8);
|
||||||
fn handle_irq(&self);
|
fn handle_irq(&self);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref UART: Arc<CharDeviceImpl> = Arc::new(CharDeviceImpl::new());
|
pub static ref UART: Arc<CharDeviceImpl> = Arc::new(CharDeviceImpl::new());
|
||||||
}
|
}
|
||||||
|
@ -131,19 +131,30 @@ pub struct NS16550a<const BASE_ADDR: usize> {
|
|||||||
|
|
||||||
impl<const BASE_ADDR: usize> NS16550a<BASE_ADDR> {
|
impl<const BASE_ADDR: usize> NS16550a<BASE_ADDR> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut inner = NS16550aInner {
|
let inner = NS16550aInner {
|
||||||
ns16550a: NS16550aRaw::new(BASE_ADDR),
|
ns16550a: NS16550aRaw::new(BASE_ADDR),
|
||||||
read_buffer: VecDeque::new(),
|
read_buffer: VecDeque::new(),
|
||||||
};
|
};
|
||||||
inner.ns16550a.init();
|
//inner.ns16550a.init();
|
||||||
Self {
|
Self {
|
||||||
inner: unsafe { UPIntrFreeCell::new(inner) },
|
inner: unsafe { UPIntrFreeCell::new(inner) },
|
||||||
condvar: Condvar::new(),
|
condvar: Condvar::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_buffer_is_empty(&self) -> bool {
|
||||||
|
self.inner
|
||||||
|
.exclusive_session(|inner| inner.read_buffer.is_empty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const BASE_ADDR: usize> CharDevice for NS16550a<BASE_ADDR> {
|
impl<const BASE_ADDR: usize> CharDevice for NS16550a<BASE_ADDR> {
|
||||||
|
fn init(&self) {
|
||||||
|
let mut inner = self.inner.exclusive_access();
|
||||||
|
inner.ns16550a.init();
|
||||||
|
drop(inner);
|
||||||
|
}
|
||||||
|
|
||||||
fn read(&self) -> u8 {
|
fn read(&self) -> u8 {
|
||||||
loop {
|
loop {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::drivers::bus::virtio::VirtioHal;
|
||||||
use crate::sync::UPIntrFreeCell;
|
use crate::sync::UPIntrFreeCell;
|
||||||
use alloc::{sync::Arc, vec::Vec};
|
use alloc::{sync::Arc, vec::Vec};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
@ -6,25 +7,26 @@ use tinybmp::Bmp;
|
|||||||
use virtio_drivers::{VirtIOGpu, VirtIOHeader};
|
use virtio_drivers::{VirtIOGpu, VirtIOHeader};
|
||||||
use crate::drivers::bus::virtio::VirtioHal;
|
use crate::drivers::bus::virtio::VirtioHal;
|
||||||
const VIRTIO7: usize = 0x10007000;
|
const VIRTIO7: usize = 0x10007000;
|
||||||
pub trait GPUDevice: Send + Sync + Any {
|
pub trait GpuDevice: Send + Sync + Any {
|
||||||
fn update_cursor(&self);
|
fn update_cursor(&self);
|
||||||
fn getfreambuffer(&self) -> &mut [u8];
|
fn get_framebuffer(&self) -> &mut [u8];
|
||||||
fn flush(&self);
|
fn flush(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static!(
|
lazy_static::lazy_static!(
|
||||||
pub static ref GPU_DEVICE: Arc<dyn GPUDevice> = Arc::new(VirtIOGPU::new());
|
pub static ref GPU_DEVICE: Arc<dyn GpuDevice> = Arc::new(VirtIOGpuWrapper::new());
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct VirtIOGPU {
|
pub struct VirtIOGpuWrapper {
|
||||||
gpu: UPIntrFreeCell<VirtIOGpu<'static, VirtioHal>>,
|
gpu: UPIntrFreeCell<VirtIOGpu<'static, VirtioHal>>,
|
||||||
fb: &'static [u8],
|
fb: &'static [u8],
|
||||||
}
|
}
|
||||||
static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp");
|
static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp");
|
||||||
impl VirtIOGPU {
|
impl VirtIOGpuWrapper {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut virtio = VirtIOGpu::<VirtioHal>::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
|
let mut virtio =
|
||||||
|
VirtIOGpu::<VirtioHal>::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
|
||||||
|
|
||||||
let fbuffer = virtio.setup_framebuffer().unwrap();
|
let fbuffer = virtio.setup_framebuffer().unwrap();
|
||||||
let len = fbuffer.len();
|
let len = fbuffer.len();
|
||||||
@ -53,11 +55,11 @@ impl VirtIOGPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUDevice for VirtIOGPU {
|
impl GpuDevice for VirtIOGpuWrapper {
|
||||||
fn flush(&self) {
|
fn flush(&self) {
|
||||||
self.gpu.exclusive_access().flush().unwrap();
|
self.gpu.exclusive_access().flush().unwrap();
|
||||||
}
|
}
|
||||||
fn getfreambuffer(&self) -> &mut [u8] {
|
fn get_framebuffer(&self) -> &mut [u8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = self.fb.as_ptr() as *const _ as *mut u8;
|
let ptr = self.fb.as_ptr() as *const _ as *mut u8;
|
||||||
core::slice::from_raw_parts_mut(ptr, self.fb.len())
|
core::slice::from_raw_parts_mut(ptr, self.fb.len())
|
||||||
|
@ -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 crate::drivers::bus::virtio::VirtioHal;
|
||||||
use virtio_input_decoder::{Decoder, Key, KeyType};
|
use crate::sync::{Condvar, UPIntrFreeCell};
|
||||||
|
use crate::task::schedule;
|
||||||
use super::GPU_DEVICE;
|
use alloc::collections::VecDeque;
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::any::Any;
|
||||||
|
use virtio_drivers::{VirtIOHeader, VirtIOInput};
|
||||||
|
|
||||||
const VIRTIO5: usize = 0x10005000;
|
const VIRTIO5: usize = 0x10005000;
|
||||||
const VIRTIO6: usize = 0x10006000;
|
const VIRTIO6: usize = 0x10006000;
|
||||||
|
|
||||||
struct VirtIOINPUT(UPIntrFreeCell<VirtIOInput<'static, VirtioHal>>);
|
struct VirtIOInputInner {
|
||||||
|
virtio_input: VirtIOInput<'static, VirtioHal>,
|
||||||
|
events: VecDeque<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait INPUTDevice: Send + Sync + Any {
|
struct VirtIOInputWrapper {
|
||||||
|
inner: UPIntrFreeCell<VirtIOInputInner>,
|
||||||
|
condvar: Condvar,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InputDevice: Send + Sync + Any {
|
||||||
|
fn read_event(&self) -> u64;
|
||||||
fn handle_irq(&self);
|
fn handle_irq(&self);
|
||||||
|
fn is_empty(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static!(
|
lazy_static::lazy_static!(
|
||||||
pub static ref KEYBOARD_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO5));
|
pub static ref KEYBOARD_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO5));
|
||||||
pub static ref MOUSE_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO6));
|
pub static ref MOUSE_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO6));
|
||||||
);
|
);
|
||||||
|
|
||||||
impl VirtIOINPUT {
|
impl VirtIOInputWrapper {
|
||||||
pub fn new(addr: usize) -> Self {
|
pub fn new(addr: usize) -> Self {
|
||||||
Self(unsafe {
|
let inner = VirtIOInputInner {
|
||||||
UPIntrFreeCell::new(VirtIOInput::<VirtioHal>::new(&mut *(addr as *mut VirtIOHeader)).unwrap())
|
virtio_input: unsafe {
|
||||||
})
|
VirtIOInput::<VirtioHal>::new(&mut *(addr as *mut VirtIOHeader)).unwrap()
|
||||||
|
},
|
||||||
|
events: VecDeque::new(),
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
inner: unsafe { UPIntrFreeCell::new(inner) },
|
||||||
|
condvar: Condvar::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl INPUTDevice for VirtIOINPUT {
|
impl InputDevice for VirtIOInputWrapper {
|
||||||
fn handle_irq(&self) {
|
fn is_empty(&self) -> bool {
|
||||||
let mut input = self.0.exclusive_access();
|
self.inner.exclusive_access().events.is_empty()
|
||||||
input.ack_interrupt();
|
}
|
||||||
let event = input.pop_pending_event().unwrap();
|
|
||||||
let dtype = match Decoder::decode(
|
fn read_event(&self) -> u64 {
|
||||||
event.event_type as usize,
|
loop {
|
||||||
event.code as usize,
|
let mut inner = self.inner.exclusive_access();
|
||||||
event.value as usize,
|
if let Some(event) = inner.events.pop_front() {
|
||||||
) {
|
return event;
|
||||||
Ok(dtype) => dtype,
|
|
||||||
Err(_) => return,
|
|
||||||
};
|
|
||||||
match dtype {
|
|
||||||
virtio_input_decoder::DecodeType::Key(key, r#type) => {
|
|
||||||
println!("{:?} {:?}", key, r#type);
|
|
||||||
if r#type == KeyType::Press {
|
|
||||||
let mut inner = PAD.exclusive_access();
|
|
||||||
let a = inner.as_ref().unwrap();
|
|
||||||
match key.to_char() {
|
|
||||||
Ok(mut k) => {
|
|
||||||
if k == '\r' {
|
|
||||||
a.repaint(k.to_string() + "\n")
|
|
||||||
} else {
|
} else {
|
||||||
a.repaint(k.to_string())
|
let task_cx_ptr = self.condvar.wait_no_sched();
|
||||||
}
|
drop(inner);
|
||||||
}
|
schedule(task_cx_ptr);
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtio_input_decoder::DecodeType::Mouse(mouse) => println!("{:?}", mouse),
|
|
||||||
}
|
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();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
pub mod block;
|
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 bus;
|
||||||
|
pub mod chardev;
|
||||||
|
pub mod gpu;
|
||||||
|
pub mod input;
|
||||||
|
pub mod net;
|
||||||
pub mod plic;
|
pub mod plic;
|
||||||
|
|
||||||
pub use block::BLOCK_DEVICE;
|
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 bus::*;
|
||||||
|
pub use chardev::UART;
|
||||||
|
pub use gpu::*;
|
||||||
|
pub use input::*;
|
||||||
|
pub use net::*;
|
||||||
|
41
os/src/drivers/net/mod.rs
Normal file
41
os/src/drivers/net/mod.rs
Normal file
@ -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<dyn NetDevice> = 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<VirtIONet<'static, VirtioHal>>);
|
||||||
|
|
||||||
|
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::<VirtioHal>::new(&mut *(VIRTIO8 as *mut VirtIOHeader))
|
||||||
|
.expect("can't create net device by virtio");
|
||||||
|
VirtIONetWrapper(UPIntrFreeCell::new(virtio))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,11 @@
|
|||||||
.globl _start
|
.globl _start
|
||||||
_start:
|
_start:
|
||||||
la sp, boot_stack_top
|
la sp, boot_stack_top
|
||||||
call rust_main
|
call rust_start
|
||||||
|
|
||||||
.section .bss.stack
|
.section .bss.stack
|
||||||
.globl boot_stack
|
.globl boot_stack_lower_bound
|
||||||
boot_stack:
|
boot_stack_lower_bound:
|
||||||
.space 4096 * 16
|
.space 4096 * 16
|
||||||
.globl boot_stack_top
|
.globl boot_stack_top
|
||||||
boot_stack_top:
|
boot_stack_top:
|
||||||
|
@ -114,36 +114,40 @@ impl File for Pipe {
|
|||||||
}
|
}
|
||||||
fn read(&self, buf: UserBuffer) -> usize {
|
fn read(&self, buf: UserBuffer) -> usize {
|
||||||
assert!(self.readable());
|
assert!(self.readable());
|
||||||
|
let want_to_read = buf.len();
|
||||||
let mut buf_iter = buf.into_iter();
|
let mut buf_iter = buf.into_iter();
|
||||||
let mut read_size = 0usize;
|
let mut already_read = 0usize;
|
||||||
loop {
|
loop {
|
||||||
let mut ring_buffer = self.buffer.exclusive_access();
|
let mut ring_buffer = self.buffer.exclusive_access();
|
||||||
let loop_read = ring_buffer.available_read();
|
let loop_read = ring_buffer.available_read();
|
||||||
if loop_read == 0 {
|
if loop_read == 0 {
|
||||||
if ring_buffer.all_write_ends_closed() {
|
if ring_buffer.all_write_ends_closed() {
|
||||||
return read_size;
|
return already_read;
|
||||||
}
|
}
|
||||||
drop(ring_buffer);
|
drop(ring_buffer);
|
||||||
suspend_current_and_run_next();
|
suspend_current_and_run_next();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// read at most loop_read bytes
|
|
||||||
for _ in 0..loop_read {
|
for _ in 0..loop_read {
|
||||||
if let Some(byte_ref) = buf_iter.next() {
|
if let Some(byte_ref) = buf_iter.next() {
|
||||||
unsafe {
|
unsafe {
|
||||||
*byte_ref = ring_buffer.read_byte();
|
*byte_ref = ring_buffer.read_byte();
|
||||||
}
|
}
|
||||||
read_size += 1;
|
already_read += 1;
|
||||||
|
if already_read == want_to_read {
|
||||||
|
return want_to_read;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return read_size;
|
return already_read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn write(&self, buf: UserBuffer) -> usize {
|
fn write(&self, buf: UserBuffer) -> usize {
|
||||||
assert!(self.writable());
|
assert!(self.writable());
|
||||||
|
let want_to_write = buf.len();
|
||||||
let mut buf_iter = buf.into_iter();
|
let mut buf_iter = buf.into_iter();
|
||||||
let mut write_size = 0usize;
|
let mut already_write = 0usize;
|
||||||
loop {
|
loop {
|
||||||
let mut ring_buffer = self.buffer.exclusive_access();
|
let mut ring_buffer = self.buffer.exclusive_access();
|
||||||
let loop_write = ring_buffer.available_write();
|
let loop_write = ring_buffer.available_write();
|
||||||
@ -156,9 +160,12 @@ impl File for Pipe {
|
|||||||
for _ in 0..loop_write {
|
for _ in 0..loop_write {
|
||||||
if let Some(byte_ref) = buf_iter.next() {
|
if let Some(byte_ref) = buf_iter.next() {
|
||||||
ring_buffer.write_byte(unsafe { *byte_ref });
|
ring_buffer.write_byte(unsafe { *byte_ref });
|
||||||
write_size += 1;
|
already_write += 1;
|
||||||
|
if already_write == want_to_write {
|
||||||
|
return want_to_write;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return write_size;
|
return already_write;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
use super::File;
|
use super::File;
|
||||||
use crate::drivers::chardev::CharDevice;
|
use crate::drivers::chardev::CharDevice;
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
use crate::drivers::chardev::UART;
|
use crate::drivers::chardev::UART;
|
||||||
use crate::mm::UserBuffer;
|
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 Stdin;
|
||||||
pub struct Stdout;
|
pub struct Stdout;
|
||||||
@ -18,7 +13,6 @@ impl File for Stdin {
|
|||||||
fn writable(&self) -> bool {
|
fn writable(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
fn read(&self, mut user_buf: UserBuffer) -> usize {
|
fn read(&self, mut user_buf: UserBuffer) -> usize {
|
||||||
assert_eq!(user_buf.len(), 1);
|
assert_eq!(user_buf.len(), 1);
|
||||||
//println!("before UART.read() in Stdin::read()");
|
//println!("before UART.read() in Stdin::read()");
|
||||||
@ -28,27 +22,6 @@ impl File for Stdin {
|
|||||||
}
|
}
|
||||||
1
|
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 {
|
fn write(&self, _user_buf: UserBuffer) -> usize {
|
||||||
panic!("Cannot write to stdin!");
|
panic!("Cannot write to stdin!");
|
||||||
}
|
}
|
||||||
|
@ -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<ButtonInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ButtonInner {
|
|
||||||
graphic: Graphics,
|
|
||||||
text: String,
|
|
||||||
parent: Option<Arc<dyn Component>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Button {
|
|
||||||
pub fn new(size: Size, point: Point, parent: Option<Arc<dyn Component>>, text: String) -> Self {
|
|
||||||
let point = match &parent {
|
|
||||||
Some(p) => {
|
|
||||||
let (_, p) = p.bound();
|
|
||||||
Point::new(p.x + point.x, p.y + point.y)
|
|
||||||
}
|
|
||||||
None => point,
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
inner: unsafe {
|
|
||||||
UPIntrFreeCell::new(ButtonInner {
|
|
||||||
graphic: Graphics {
|
|
||||||
size,
|
|
||||||
point,
|
|
||||||
drv: GPU_DEVICE.clone(),
|
|
||||||
},
|
|
||||||
text,
|
|
||||||
parent,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Button {
|
|
||||||
fn paint(&self) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let text = inner.text.clone();
|
|
||||||
Text::with_alignment(
|
|
||||||
text.as_str(),
|
|
||||||
inner.graphic.bounding_box().center(),
|
|
||||||
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
|
|
||||||
Alignment::Center,
|
|
||||||
)
|
|
||||||
.draw(&mut inner.graphic);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bound(
|
|
||||||
&self,
|
|
||||||
) -> (
|
|
||||||
embedded_graphics::prelude::Size,
|
|
||||||
embedded_graphics::prelude::Point,
|
|
||||||
) {
|
|
||||||
let inner = self.inner.exclusive_access();
|
|
||||||
(inner.graphic.size, inner.graphic.point)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<dyn GPUDevice>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
|
||||||
{
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<IconControllerInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct IconControllerInner {
|
|
||||||
files: Vec<String>,
|
|
||||||
graphic: Graphics,
|
|
||||||
parent: Option<Arc<dyn Component>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IconController {
|
|
||||||
pub fn new(files: Vec<String>, parent: Option<Arc<dyn Component>>) -> Self {
|
|
||||||
IconController {
|
|
||||||
inner: unsafe {
|
|
||||||
UPIntrFreeCell::new(IconControllerInner {
|
|
||||||
files,
|
|
||||||
graphic: Graphics {
|
|
||||||
size: Size::new(VIRTGPU_XRES, VIRTGPU_YRES),
|
|
||||||
point: Point::new(0, 0),
|
|
||||||
drv: GPU_DEVICE.clone(),
|
|
||||||
},
|
|
||||||
parent,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for IconController {
|
|
||||||
fn paint(&self) {
|
|
||||||
println!("demo");
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let mut x = 10;
|
|
||||||
let mut y = 10;
|
|
||||||
let v = inner.files.clone();
|
|
||||||
for file in v {
|
|
||||||
println!("file");
|
|
||||||
let bmp = Bmp::<Rgb888>::from_slice(FILEICON).unwrap();
|
|
||||||
Image::new(&bmp, Point::new(x, y)).draw(&mut inner.graphic);
|
|
||||||
let text = Text::new(
|
|
||||||
file.as_str(),
|
|
||||||
Point::new(x + 20, y + 80),
|
|
||||||
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
|
|
||||||
);
|
|
||||||
text.draw(&mut inner.graphic);
|
|
||||||
if y >= 600 {
|
|
||||||
x = x + 70;
|
|
||||||
y = 10;
|
|
||||||
} else {
|
|
||||||
y = y + 90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, comp: Arc<dyn Component>) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bound(&self) -> (Size, Point) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<ImageInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ImageInner {
|
|
||||||
image: &'static [u8],
|
|
||||||
graphic: Graphics,
|
|
||||||
parent: Option<Arc<dyn Component>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageComp {
|
|
||||||
pub fn new(
|
|
||||||
size: Size,
|
|
||||||
point: Point,
|
|
||||||
v: &'static [u8],
|
|
||||||
parent: Option<Arc<dyn Component>>,
|
|
||||||
) -> Self {
|
|
||||||
unsafe {
|
|
||||||
ImageComp {
|
|
||||||
inner: UPIntrFreeCell::new(ImageInner {
|
|
||||||
parent,
|
|
||||||
image: v,
|
|
||||||
graphic: Graphics {
|
|
||||||
size,
|
|
||||||
point,
|
|
||||||
drv: GPU_DEVICE.clone(),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for ImageComp {
|
|
||||||
fn paint(&self) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let b = unsafe {
|
|
||||||
let len = inner.image.len();
|
|
||||||
let ptr = inner.image.as_ptr() as *const u8;
|
|
||||||
core::slice::from_raw_parts(ptr, len)
|
|
||||||
};
|
|
||||||
let bmp = Bmp::<Rgb888>::from_slice(b).unwrap();
|
|
||||||
let point = match &inner.parent {
|
|
||||||
Some(parent) => {
|
|
||||||
let (_, point) = parent.bound();
|
|
||||||
Point::new(
|
|
||||||
point.x + inner.graphic.point.x,
|
|
||||||
point.y + inner.graphic.point.y,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
None => inner.graphic.point,
|
|
||||||
};
|
|
||||||
Image::new(&bmp, point).draw(&mut inner.graphic);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bound(&self) -> (Size, Point) {
|
|
||||||
let inner = self.inner.exclusive_access();
|
|
||||||
(inner.graphic.size, inner.graphic.point)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<dyn Component>);
|
|
||||||
fn bound(&self) -> (Size, Point);
|
|
||||||
}
|
|
@ -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<PanelInner>,
|
|
||||||
}
|
|
||||||
struct PanelInner {
|
|
||||||
graphic: Graphics,
|
|
||||||
comps: VecDeque<Arc<dyn Component>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Panel {
|
|
||||||
pub fn new(size: Size, point: Point) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: unsafe {
|
|
||||||
UPIntrFreeCell::new(PanelInner {
|
|
||||||
graphic: Graphics {
|
|
||||||
size,
|
|
||||||
point,
|
|
||||||
drv: GPU_DEVICE.clone(),
|
|
||||||
},
|
|
||||||
comps: VecDeque::new(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Panel {
|
|
||||||
fn paint(&self) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
|
|
||||||
Rectangle::new(Point::new(0, 0), inner.graphic.size)
|
|
||||||
.into_styled(PrimitiveStyle::with_fill(Rgb888::WHITE))
|
|
||||||
.draw(&mut inner.graphic)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let len = inner.comps.len();
|
|
||||||
drop(inner);
|
|
||||||
for i in 0..len {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let comp = Arc::downgrade(&inner.comps[i]);
|
|
||||||
drop(inner);
|
|
||||||
comp.upgrade().unwrap().paint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, comp: alloc::sync::Arc<dyn Component>) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
inner.comps.push_back(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bound(&self) -> (Size, Point) {
|
|
||||||
let inner = self.inner.exclusive_access();
|
|
||||||
(inner.graphic.size, inner.graphic.point)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<TerminalInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TerminalInner {
|
|
||||||
pub text: String,
|
|
||||||
titel: Option<String>,
|
|
||||||
graphic: Graphics,
|
|
||||||
comps: VecDeque<Arc<dyn Component>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Terminal {
|
|
||||||
pub fn new(
|
|
||||||
size: Size,
|
|
||||||
point: Point,
|
|
||||||
parent: Option<Arc<dyn Component>>,
|
|
||||||
titel: Option<String>,
|
|
||||||
text: String,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: unsafe {
|
|
||||||
UPIntrFreeCell::new(TerminalInner {
|
|
||||||
text,
|
|
||||||
titel,
|
|
||||||
graphic: Graphics {
|
|
||||||
size,
|
|
||||||
point,
|
|
||||||
drv: GPU_DEVICE.clone(),
|
|
||||||
},
|
|
||||||
comps: VecDeque::new(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn repaint(&self, text: String) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
inner.text += text.as_str();
|
|
||||||
Text::with_alignment(
|
|
||||||
inner.text.clone().as_str(),
|
|
||||||
Point::new(20, 50),
|
|
||||||
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
|
|
||||||
Alignment::Left,
|
|
||||||
)
|
|
||||||
.draw(&mut inner.graphic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Terminal {
|
|
||||||
fn paint(&self) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let len = inner.comps.len();
|
|
||||||
drop(inner);
|
|
||||||
for i in 0..len {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let comp = Arc::downgrade(&inner.comps[i]);
|
|
||||||
drop(inner);
|
|
||||||
comp.upgrade().unwrap().paint();
|
|
||||||
}
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
let titel = inner.titel.get_or_insert("No Titel".to_string()).clone();
|
|
||||||
let text = Text::new(
|
|
||||||
titel.as_str(),
|
|
||||||
Point::new(20, 20),
|
|
||||||
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
|
|
||||||
);
|
|
||||||
text.draw(&mut inner.graphic);
|
|
||||||
|
|
||||||
Text::with_alignment(
|
|
||||||
inner.text.clone().as_str(),
|
|
||||||
Point::new(20, 50),
|
|
||||||
MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK),
|
|
||||||
Alignment::Left,
|
|
||||||
)
|
|
||||||
.draw(&mut inner.graphic);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, comp: Arc<dyn Component>) {
|
|
||||||
let mut inner = self.inner.exclusive_access();
|
|
||||||
inner.comps.push_back(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bound(&self) -> (Size, Point) {
|
|
||||||
let inner = self.inner.exclusive_access();
|
|
||||||
(inner.graphic.size, inner.graphic.point)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
OUTPUT_ARCH(riscv)
|
OUTPUT_ARCH(riscv)
|
||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
BASE_ADDRESS = 0x80200000;
|
BASE_ADDRESS = 0x80000000;
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
|
229
os/src/main.rs
229
os/src/main.rs
@ -2,28 +2,24 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
#[cfg(feature = "board_qemu")]
|
#![feature(naked_functions)]
|
||||||
use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE};
|
#![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;
|
extern crate alloc;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
#[cfg(feature = "board_k210")]
|
|
||||||
#[path = "boards/k210.rs"]
|
|
||||||
mod board;
|
|
||||||
#[cfg(not(any(feature = "board_k210")))]
|
|
||||||
#[path = "boards/qemu.rs"]
|
#[path = "boards/qemu.rs"]
|
||||||
mod board;
|
mod board;
|
||||||
|
use board::*;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod console;
|
mod console;
|
||||||
mod config;
|
mod config;
|
||||||
mod drivers;
|
mod drivers;
|
||||||
mod fs;
|
mod fs;
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
mod gui;
|
|
||||||
mod lang_items;
|
mod lang_items;
|
||||||
mod mm;
|
mod mm;
|
||||||
mod sbi;
|
mod sbi;
|
||||||
@ -32,7 +28,16 @@ mod syscall;
|
|||||||
mod task;
|
mod task;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod trap;
|
mod trap;
|
||||||
|
mod net;
|
||||||
|
|
||||||
|
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
|
//use syscall::create_desktop; //for test
|
||||||
|
|
||||||
core::arch::global_asm!(include_str!("entry.asm"));
|
core::arch::global_asm!(include_str!("entry.asm"));
|
||||||
@ -56,26 +61,212 @@ lazy_static! {
|
|||||||
unsafe { UPIntrFreeCell::new(false) };
|
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::<usize>::into(CLINT_MTIME) as *const u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_mtimecmp(value: u64) {
|
||||||
|
let offset = Into::<usize>::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::<usize>::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]
|
#[no_mangle]
|
||||||
pub fn rust_main() -> ! {
|
pub fn rust_main() -> ! {
|
||||||
clear_bss();
|
|
||||||
|
//clear_bss();
|
||||||
mm::init();
|
mm::init();
|
||||||
|
UART.init();
|
||||||
println!("KERN: init gpu");
|
println!("KERN: init gpu");
|
||||||
#[cfg(feature = "board_qemu")]
|
let _gpu = GPU_DEVICE.clone();
|
||||||
GPU_DEVICE.clone();
|
|
||||||
println!("KERN: init keyboard");
|
println!("KERN: init keyboard");
|
||||||
#[cfg(feature = "board_qemu")]
|
let _keyboard = KEYBOARD_DEVICE.clone();
|
||||||
KEYBOARD_DEVICE.clone();
|
|
||||||
println!("KERN: init mouse");
|
println!("KERN: init mouse");
|
||||||
#[cfg(feature = "board_qemu")]
|
let _mouse = MOUSE_DEVICE.clone();
|
||||||
MOUSE_DEVICE.clone();
|
|
||||||
println!("KERN: init trap");
|
println!("KERN: init trap");
|
||||||
trap::init();
|
trap::init();
|
||||||
trap::enable_timer_interrupt();
|
//trap::enable_timer_interrupt();
|
||||||
timer::set_next_trigger();
|
//timer::set_next_trigger();
|
||||||
board::device_init();
|
board::device_init();
|
||||||
fs::list_apps();
|
fs::list_apps();
|
||||||
//syscall::create_desktop(); //for test
|
|
||||||
task::add_initproc();
|
task::add_initproc();
|
||||||
*DEV_NON_BLOCKING_ACCESS.exclusive_access() = true;
|
*DEV_NON_BLOCKING_ACCESS.exclusive_access() = true;
|
||||||
task::run_tasks();
|
task::run_tasks();
|
||||||
|
@ -35,6 +35,7 @@ impl Drop for FrameTracker {
|
|||||||
trait FrameAllocator {
|
trait FrameAllocator {
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
fn alloc(&mut self) -> Option<PhysPageNum>;
|
fn alloc(&mut self) -> Option<PhysPageNum>;
|
||||||
|
fn alloc_more(&mut self, pages: usize) -> Option<Vec<PhysPageNum>>;
|
||||||
fn dealloc(&mut self, ppn: PhysPageNum);
|
fn dealloc(&mut self, ppn: PhysPageNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ impl StackFrameAllocator {
|
|||||||
pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) {
|
pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) {
|
||||||
self.current = l.0;
|
self.current = l.0;
|
||||||
self.end = r.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 {
|
impl FrameAllocator for StackFrameAllocator {
|
||||||
@ -69,6 +70,16 @@ impl FrameAllocator for StackFrameAllocator {
|
|||||||
Some((self.current - 1).into())
|
Some((self.current - 1).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn alloc_more(&mut self, pages: usize) -> Option<Vec<PhysPageNum>> {
|
||||||
|
if self.current + pages >= self.end {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.current += pages;
|
||||||
|
let arr:Vec<usize> = (1..pages + 1).collect();
|
||||||
|
let v = arr.iter().map(|x| (self.current - x).into()).collect();
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
fn dealloc(&mut self, ppn: PhysPageNum) {
|
fn dealloc(&mut self, ppn: PhysPageNum) {
|
||||||
let ppn = ppn.0;
|
let ppn = ppn.0;
|
||||||
// validity check
|
// validity check
|
||||||
@ -104,6 +115,13 @@ pub fn frame_alloc() -> Option<FrameTracker> {
|
|||||||
.map(FrameTracker::new)
|
.map(FrameTracker::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn frame_alloc_more(num: usize) -> Option<Vec<FrameTracker>> {
|
||||||
|
FRAME_ALLOCATOR
|
||||||
|
.exclusive_access()
|
||||||
|
.alloc_more(num)
|
||||||
|
.map(|x| x.iter().map(|&t| FrameTracker::new(t)).collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn frame_dealloc(ppn: PhysPageNum) {
|
pub fn frame_dealloc(ppn: PhysPageNum) {
|
||||||
FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
|
FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
|
||||||
}
|
}
|
||||||
@ -125,3 +143,21 @@ pub fn frame_allocator_test() {
|
|||||||
drop(v);
|
drop(v);
|
||||||
println!("frame_allocator_test passed!");
|
println!("frame_allocator_test passed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn frame_allocator_alloc_more_test() {
|
||||||
|
let mut v: Vec<FrameTracker> = 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!");
|
||||||
|
}
|
||||||
|
@ -71,7 +71,10 @@ impl MemorySet {
|
|||||||
self.areas.remove(idx);
|
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);
|
map_area.map(&mut self.page_table);
|
||||||
if let Some(data) = data {
|
if let Some(data) = data {
|
||||||
map_area.copy_data(&mut self.page_table, data);
|
map_area.copy_data(&mut self.page_table, data);
|
||||||
@ -92,14 +95,14 @@ impl MemorySet {
|
|||||||
// map trampoline
|
// map trampoline
|
||||||
memory_set.map_trampoline();
|
memory_set.map_trampoline();
|
||||||
// map kernel sections
|
// map kernel sections
|
||||||
println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
|
// println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
|
||||||
println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
|
// println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
|
||||||
println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
|
// println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
|
||||||
println!(
|
// println!(
|
||||||
".bss [{:#x}, {:#x})",
|
// ".bss [{:#x}, {:#x})",
|
||||||
sbss_with_stack as usize, ebss as usize
|
// sbss_with_stack as usize, ebss as usize
|
||||||
);
|
// );
|
||||||
println!("mapping .text section");
|
// println!("mapping .text section");
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
(stext as usize).into(),
|
(stext as usize).into(),
|
||||||
@ -109,7 +112,7 @@ impl MemorySet {
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
println!("mapping .rodata section");
|
// println!("mapping .rodata section");
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
(srodata as usize).into(),
|
(srodata as usize).into(),
|
||||||
@ -119,7 +122,7 @@ impl MemorySet {
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
println!("mapping .data section");
|
// println!("mapping .data section");
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
(sdata as usize).into(),
|
(sdata as usize).into(),
|
||||||
@ -129,7 +132,7 @@ impl MemorySet {
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
println!("mapping .bss section");
|
// println!("mapping .bss section");
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
(sbss_with_stack as usize).into(),
|
(sbss_with_stack as usize).into(),
|
||||||
@ -139,7 +142,7 @@ impl MemorySet {
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
println!("mapping physical memory");
|
// println!("mapping physical memory");
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
(ekernel as usize).into(),
|
(ekernel as usize).into(),
|
||||||
@ -149,7 +152,7 @@ impl MemorySet {
|
|||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
println!("mapping memory-mapped registers");
|
//println!("mapping memory-mapped registers");
|
||||||
for pair in MMIO {
|
for pair in MMIO {
|
||||||
memory_set.push(
|
memory_set.push(
|
||||||
MapArea::new(
|
MapArea::new(
|
||||||
@ -286,6 +289,11 @@ impl MapArea {
|
|||||||
ppn = frame.ppn;
|
ppn = frame.ppn;
|
||||||
self.data_frames.insert(vpn, frame);
|
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();
|
let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
|
||||||
page_table.map(vpn, ppn, pte_flags);
|
page_table.map(vpn, ppn, pte_flags);
|
||||||
@ -334,6 +342,8 @@ impl MapArea {
|
|||||||
pub enum MapType {
|
pub enum MapType {
|
||||||
Identical,
|
Identical,
|
||||||
Framed,
|
Framed,
|
||||||
|
/// offset of page num
|
||||||
|
Linear(isize),
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -4,11 +4,11 @@ mod heap_allocator;
|
|||||||
mod memory_set;
|
mod memory_set;
|
||||||
mod page_table;
|
mod page_table;
|
||||||
|
|
||||||
use address::VPNRange;
|
pub use address::VPNRange;
|
||||||
pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum};
|
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::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;
|
use page_table::PTEFlags;
|
||||||
pub use page_table::{
|
pub use page_table::{
|
||||||
translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable,
|
translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable,
|
||||||
|
92
os/src/net/mod.rs
Normal file
92
os/src/net/mod.rs
Normal file
@ -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<LoseStack>);
|
||||||
|
|
||||||
|
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<NetStack> = 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);
|
||||||
|
}
|
93
os/src/net/socket.rs
Normal file
93
os/src/net/socket.rs
Normal file
@ -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<Vec<u8>> // datas
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SOCKET_TABLE:UPIntrFreeCell<Vec<Option<Socket>>> = unsafe {
|
||||||
|
UPIntrFreeCell::new(Vec::new())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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<u8>) {
|
||||||
|
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<Vec<u8>> {
|
||||||
|
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()
|
||||||
|
}
|
94
os/src/net/udp.rs
Normal file
94
os/src/net/udp.rs
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
614
os/src/riscvregs.rs
Normal file
614
os/src/riscvregs.rs
Normal file
@ -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::<usize>() * 8 - 1);
|
||||||
|
self.bits & !bit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trap cause
|
||||||
|
#[inline]
|
||||||
|
pub fn cause(&self) -> Trap {
|
||||||
|
if self.is_interrupt() {
|
||||||
|
Trap::Interrupt(Interrupt::from(self.code()))
|
||||||
|
} else {
|
||||||
|
Trap::Exception(Exception::from(self.code()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is trap cause an interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_interrupt(&self) -> bool {
|
||||||
|
self.bits & (1 << (size_of::<usize>() * 8 - 1)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is trap cause an exception.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_exception(&self) -> bool {
|
||||||
|
!self.is_interrupt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read() -> Scause {
|
||||||
|
let bits: usize;
|
||||||
|
unsafe {
|
||||||
|
asm!("csrr {}, scause", out(reg) bits);
|
||||||
|
}
|
||||||
|
Scause { bits }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supervisor Trap Value
|
||||||
|
pub mod stval {
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read() -> usize {
|
||||||
|
let bits: usize;
|
||||||
|
unsafe { asm!("csrr {}, stval", out(reg) bits) }
|
||||||
|
bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
use registers::*;
|
||||||
|
|
||||||
|
// enable device interrupts
|
||||||
|
#[inline]
|
||||||
|
pub fn intr_on() {
|
||||||
|
unsafe {
|
||||||
|
sstatus::set_sie();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable device interrupts
|
||||||
|
#[inline]
|
||||||
|
pub fn intr_off() {
|
||||||
|
unsafe {
|
||||||
|
sstatus::clear_sie();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// are device interrupts enabled?
|
||||||
|
#[inline]
|
||||||
|
pub fn intr_get() -> bool {
|
||||||
|
sstatus::read().sie()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush the TLB.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn sfence_vma() {
|
||||||
|
// the zero, zero means flush all TLB entries
|
||||||
|
asm!("sfence.vma zero, zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PGSIZE: usize = 4096; // bytes per page
|
||||||
|
pub const PGSHIFT: usize = 12; // bits of offset within a page
|
||||||
|
|
||||||
|
pub const fn pgroundup(sz: usize) -> usize {
|
||||||
|
(sz + PGSIZE - 1) & !(PGSIZE - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn pgrounddown(sz: usize) -> usize {
|
||||||
|
sz & !(PGSIZE - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PTE flags
|
||||||
|
pub mod pteflags {
|
||||||
|
pub const PTE_V: usize = 1 << 0; // valid
|
||||||
|
pub const PTE_R: usize = 1 << 1;
|
||||||
|
pub const PTE_W: usize = 1 << 2;
|
||||||
|
pub const PTE_X: usize = 1 << 3;
|
||||||
|
pub const PTE_U: usize = 1 << 4; // user can access
|
||||||
|
}
|
@ -40,13 +40,7 @@ pub fn console_getchar() -> usize {
|
|||||||
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
|
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
use crate::board::QEMUExit;
|
use crate::board::QEMUExit;
|
||||||
pub fn shutdown(exit_code: usize) -> ! {
|
pub fn shutdown(exit_code: usize) -> ! {
|
||||||
#[cfg(feature = "board_k210")]
|
crate::board::QEMU_EXIT_HANDLE.exit_failure()
|
||||||
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!");
|
|
||||||
}
|
}
|
||||||
|
86
os/src/start.rs
Normal file
86
os/src/start.rs
Normal file
@ -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();
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use crate::sync::{Mutex, UPIntrFreeCell};
|
use crate::sync::{Mutex, UPIntrFreeCell};
|
||||||
use crate::task::{
|
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,
|
TaskControlBlock,
|
||||||
};
|
};
|
||||||
use alloc::{collections::VecDeque, sync::Arc};
|
use alloc::{collections::VecDeque, sync::Arc};
|
||||||
@ -27,7 +27,7 @@ impl Condvar {
|
|||||||
pub fn signal(&self) {
|
pub fn signal(&self) {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
if let Some(task) = inner.wait_queue.pop_front() {
|
if let Some(task) = inner.wait_queue.pop_front() {
|
||||||
add_task(task);
|
wakeup_task(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::UPIntrFreeCell;
|
use super::UPIntrFreeCell;
|
||||||
use crate::task::TaskControlBlock;
|
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 crate::task::{block_current_and_run_next, suspend_current_and_run_next};
|
||||||
use alloc::{collections::VecDeque, sync::Arc};
|
use alloc::{collections::VecDeque, sync::Arc};
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ impl Mutex for MutexBlocking {
|
|||||||
let mut mutex_inner = self.inner.exclusive_access();
|
let mut mutex_inner = self.inner.exclusive_access();
|
||||||
assert!(mutex_inner.locked);
|
assert!(mutex_inner.locked);
|
||||||
if let Some(waking_task) = mutex_inner.wait_queue.pop_front() {
|
if let Some(waking_task) = mutex_inner.wait_queue.pop_front() {
|
||||||
add_task(waking_task);
|
wakeup_task(waking_task);
|
||||||
} else {
|
} else {
|
||||||
mutex_inner.locked = false;
|
mutex_inner.locked = false;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::sync::UPIntrFreeCell;
|
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};
|
use alloc::{collections::VecDeque, sync::Arc};
|
||||||
|
|
||||||
pub struct Semaphore {
|
pub struct Semaphore {
|
||||||
@ -28,7 +28,7 @@ impl Semaphore {
|
|||||||
inner.count += 1;
|
inner.count += 1;
|
||||||
if inner.count <= 0 {
|
if inner.count <= 0 {
|
||||||
if let Some(task) = inner.wait_queue.pop_front() {
|
if let Some(task) = inner.wait_queue.pop_front() {
|
||||||
add_task(task);
|
wakeup_task(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,34 @@
|
|||||||
use alloc::{string::ToString, sync::Arc, vec::Vec};
|
use crate::drivers::GPU_DEVICE;
|
||||||
use embedded_graphics::{
|
use crate::mm::{MapArea, MapPermission, MapType, PhysAddr, VirtAddr};
|
||||||
prelude::{Point, Size},
|
use crate::task::current_process;
|
||||||
primitives::arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
const FB_VADDR: usize = 0x10000000;
|
||||||
fs::ROOT_INODE,
|
|
||||||
gui::{Button, Component, IconController, ImageComp, Panel, Terminal},
|
|
||||||
sync::UPIntrFreeCell,
|
|
||||||
};
|
|
||||||
|
|
||||||
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");
|
let current_process = current_process();
|
||||||
|
let mut inner = current_process.inner_exclusive_access();
|
||||||
lazy_static::lazy_static!(
|
inner.memory_set.push(
|
||||||
pub static ref DESKTOP:UPIntrFreeCell<Arc<dyn Component>> = unsafe {
|
MapArea::new(
|
||||||
UPIntrFreeCell::new(Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0))))
|
(FB_VADDR as usize).into(),
|
||||||
};
|
(FB_VADDR + len as usize).into(),
|
||||||
pub static ref PAD:UPIntrFreeCell<Option<Arc<Terminal>>> = unsafe {
|
MapType::Linear(pn_offset),
|
||||||
UPIntrFreeCell::new(None)
|
MapPermission::R | MapPermission::W | MapPermission::U,
|
||||||
};
|
),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
FB_VADDR as isize
|
||||||
pub fn create_desktop() -> isize {
|
|
||||||
let mut p: Arc<dyn Component + 'static> =
|
|
||||||
Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0)));
|
|
||||||
let image = ImageComp::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0), DT, Some(p.clone()));
|
|
||||||
let icon = IconController::new(ROOT_INODE.ls(), Some(p.clone()));
|
|
||||||
p.add(Arc::new(image));
|
|
||||||
p.add(Arc::new(icon));
|
|
||||||
let mut desktop = DESKTOP.exclusive_access();
|
|
||||||
*desktop = p;
|
|
||||||
desktop.paint();
|
|
||||||
drop(desktop);
|
|
||||||
create_terminal();
|
|
||||||
1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_terminal() {
|
pub fn sys_framebuffer_flush() -> isize {
|
||||||
let desktop = DESKTOP.exclusive_access();
|
GPU_DEVICE.flush();
|
||||||
let arc_t = Arc::new(Terminal::new(
|
0
|
||||||
Size::new(400, 400),
|
|
||||||
Point::new(200, 100),
|
|
||||||
Some(desktop.clone()),
|
|
||||||
Some("demo.txt".to_string()),
|
|
||||||
"".to_string(),
|
|
||||||
));
|
|
||||||
let text = Panel::new(Size::new(400, 400), Point::new(200, 100));
|
|
||||||
let button = Button::new(
|
|
||||||
Size::new(20, 20),
|
|
||||||
Point::new(370, 10),
|
|
||||||
Some(arc_t.clone()),
|
|
||||||
"X".to_string(),
|
|
||||||
);
|
|
||||||
arc_t.add(Arc::new(text));
|
|
||||||
arc_t.add(Arc::new(button));
|
|
||||||
arc_t.paint();
|
|
||||||
desktop.add(arc_t.clone());
|
|
||||||
let mut pad = PAD.exclusive_access();
|
|
||||||
*pad = Some(arc_t);
|
|
||||||
}
|
}
|
||||||
|
28
os/src/syscall/input.rs
Normal file
28
os/src/syscall/input.rs
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
const SYSCALL_DUP: usize = 24;
|
const SYSCALL_DUP: usize = 24;
|
||||||
|
const SYSCALL_CONNECT: usize = 29;
|
||||||
const SYSCALL_OPEN: usize = 56;
|
const SYSCALL_OPEN: usize = 56;
|
||||||
const SYSCALL_CLOSE: usize = 57;
|
const SYSCALL_CLOSE: usize = 57;
|
||||||
const SYSCALL_PIPE: usize = 59;
|
const SYSCALL_PIPE: usize = 59;
|
||||||
@ -25,27 +26,31 @@ const SYSCALL_SEMAPHORE_DOWN: usize = 1022;
|
|||||||
const SYSCALL_CONDVAR_CREATE: usize = 1030;
|
const SYSCALL_CONDVAR_CREATE: usize = 1030;
|
||||||
const SYSCALL_CONDVAR_SIGNAL: usize = 1031;
|
const SYSCALL_CONDVAR_SIGNAL: usize = 1031;
|
||||||
const SYSCALL_CONDVAR_WAIT: usize = 1032;
|
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;
|
mod fs;
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
mod gui;
|
mod gui;
|
||||||
|
mod input;
|
||||||
mod process;
|
mod process;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod thread;
|
mod thread;
|
||||||
#[cfg(feature = "board_qemu")]
|
mod net;
|
||||||
pub use self::gui::create_desktop;
|
|
||||||
use fs::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "board_qemu")]
|
use fs::*;
|
||||||
pub use gui::PAD;
|
use gui::*;
|
||||||
|
use input::*;
|
||||||
use process::*;
|
use process::*;
|
||||||
use sync::*;
|
use sync::*;
|
||||||
use thread::*;
|
use thread::*;
|
||||||
|
use net::*;
|
||||||
|
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
||||||
match syscall_id {
|
match syscall_id {
|
||||||
SYSCALL_DUP => sys_dup(args[0]),
|
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_OPEN => sys_open(args[0] as *const u8, args[1] as u32),
|
||||||
SYSCALL_CLOSE => sys_close(args[0]),
|
SYSCALL_CLOSE => sys_close(args[0]),
|
||||||
SYSCALL_PIPE => sys_pipe(args[0] as *mut usize),
|
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_CREATE => sys_semaphore_create(args[0]),
|
||||||
SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]),
|
SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]),
|
||||||
SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(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_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_SIGNAL => sys_condvar_signal(args[0]),
|
SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]),
|
||||||
SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]),
|
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),
|
_ => panic!("Unsupported syscall_id: {}", syscall_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
os/src/syscall/net.rs
Normal file
14
os/src/syscall/net.rs
Normal file
@ -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
|
||||||
|
}
|
@ -93,7 +93,7 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_condvar_create(_arg: usize) -> isize {
|
pub fn sys_condvar_create() -> isize {
|
||||||
let process = current_process();
|
let process = current_process();
|
||||||
let mut process_inner = process.inner_exclusive_access();
|
let mut process_inner = process.inner_exclusive_access();
|
||||||
let id = if let Some(id) = process_inner
|
let id = if let Some(id) = process_inner
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{ProcessControlBlock, TaskControlBlock};
|
use super::{ProcessControlBlock, TaskControlBlock, TaskStatus};
|
||||||
use crate::sync::UPIntrFreeCell;
|
use crate::sync::UPIntrFreeCell;
|
||||||
use alloc::collections::{BTreeMap, VecDeque};
|
use alloc::collections::{BTreeMap, VecDeque};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
@ -34,6 +34,13 @@ pub fn add_task(task: Arc<TaskControlBlock>) {
|
|||||||
TASK_MANAGER.exclusive_access().add(task);
|
TASK_MANAGER.exclusive_access().add(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wakeup_task(task: Arc<TaskControlBlock>) {
|
||||||
|
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<Arc<TaskControlBlock>> {
|
pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
|
||||||
TASK_MANAGER.exclusive_access().fetch()
|
TASK_MANAGER.exclusive_access().fetch()
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ use switch::__switch;
|
|||||||
|
|
||||||
pub use context::TaskContext;
|
pub use context::TaskContext;
|
||||||
pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle, IDLE_PID};
|
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::{
|
pub use processor::{
|
||||||
current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va,
|
current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va,
|
||||||
current_user_token, run_tasks, schedule, take_current_task,
|
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 {
|
pub fn block_current_task() -> *mut TaskContext {
|
||||||
let task = take_current_task().unwrap();
|
let task = take_current_task().unwrap();
|
||||||
let mut task_inner = task.inner_exclusive_access();
|
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
|
&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();
|
let task_cx_ptr = block_current_task();
|
||||||
schedule(task_cx_ptr);
|
schedule(task_cx_ptr);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
use crate::board::QEMUExit;
|
use crate::board::QEMUExit;
|
||||||
|
|
||||||
pub fn exit_current_and_run_next(exit_code: i32) {
|
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
|
// the process should terminate at once
|
||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
let pid = process.getpid();
|
let pid = process.getpid();
|
||||||
#[cfg(feature = "board_qemu")]
|
|
||||||
if pid == IDLE_PID {
|
if pid == IDLE_PID {
|
||||||
println!(
|
println!(
|
||||||
"[kernel] Idle process exit with exit_code {} ...",
|
"[kernel] Idle process exit with exit_code {} ...",
|
||||||
|
@ -76,5 +76,5 @@ impl TaskControlBlock {
|
|||||||
pub enum TaskStatus {
|
pub enum TaskStatus {
|
||||||
Ready,
|
Ready,
|
||||||
Running,
|
Running,
|
||||||
Blocking,
|
Blocked,
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use core::cmp::Ordering;
|
|||||||
use crate::config::CLOCK_FREQ;
|
use crate::config::CLOCK_FREQ;
|
||||||
use crate::sbi::set_timer;
|
use crate::sbi::set_timer;
|
||||||
use crate::sync::UPIntrFreeCell;
|
use crate::sync::UPIntrFreeCell;
|
||||||
use crate::task::{add_task, TaskControlBlock};
|
use crate::task::{wakeup_task, TaskControlBlock};
|
||||||
use alloc::collections::BinaryHeap;
|
use alloc::collections::BinaryHeap;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use lazy_static::*;
|
use lazy_static::*;
|
||||||
@ -61,13 +61,14 @@ pub fn add_timer(expire_ms: usize, task: Arc<TaskControlBlock>) {
|
|||||||
|
|
||||||
pub fn check_timer() {
|
pub fn check_timer() {
|
||||||
let current_ms = get_time_ms();
|
let current_ms = get_time_ms();
|
||||||
let mut timers = TIMERS.exclusive_access();
|
TIMERS.exclusive_session(|timers| {
|
||||||
while let Some(timer) = timers.peek() {
|
while let Some(timer) = timers.peek() {
|
||||||
if timer.expire_ms <= current_ms {
|
if timer.expire_ms <= current_ms {
|
||||||
add_task(Arc::clone(&timer.task));
|
wakeup_task(Arc::clone(&timer.task));
|
||||||
timers.pop();
|
timers.pop();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use core::arch::{asm, global_asm};
|
|||||||
use riscv::register::{
|
use riscv::register::{
|
||||||
mtvec::TrapMode,
|
mtvec::TrapMode,
|
||||||
scause::{self, Exception, Interrupt, Trap},
|
scause::{self, Exception, Interrupt, Trap},
|
||||||
sie, sscratch, sstatus, stval, stvec,
|
sie, sscratch, sstatus, stval, stvec,sip
|
||||||
};
|
};
|
||||||
|
|
||||||
global_asm!(include_str!("trap.S"));
|
global_asm!(include_str!("trap.S"));
|
||||||
@ -95,10 +95,20 @@ pub fn trap_handler() -> ! {
|
|||||||
Trap::Exception(Exception::IllegalInstruction) => {
|
Trap::Exception(Exception::IllegalInstruction) => {
|
||||||
current_add_signal(SignalFlags::SIGILL);
|
current_add_signal(SignalFlags::SIGILL);
|
||||||
}
|
}
|
||||||
Trap::Interrupt(Interrupt::SupervisorTimer) => {
|
// Trap::Interrupt(Interrupt::SupervisorTimer) => {
|
||||||
set_next_trigger();
|
// 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();
|
check_timer();
|
||||||
suspend_current_and_run_next();
|
// do not schedule now
|
||||||
}
|
}
|
||||||
Trap::Interrupt(Interrupt::SupervisorExternal) => {
|
Trap::Interrupt(Interrupt::SupervisorExternal) => {
|
||||||
crate::board::irq_handler();
|
crate::board::irq_handler();
|
||||||
@ -151,8 +161,18 @@ pub fn trap_from_kernel(_trap_cx: &TrapContext) {
|
|||||||
Trap::Interrupt(Interrupt::SupervisorExternal) => {
|
Trap::Interrupt(Interrupt::SupervisorExternal) => {
|
||||||
crate::board::irq_handler();
|
crate::board::irq_handler();
|
||||||
}
|
}
|
||||||
Trap::Interrupt(Interrupt::SupervisorTimer) => {
|
// Trap::Interrupt(Interrupt::SupervisorTimer) => {
|
||||||
set_next_trigger();
|
// //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();
|
check_timer();
|
||||||
// do not schedule now
|
// do not schedule now
|
||||||
}
|
}
|
||||||
|
18
ping.py
Normal file
18
ping.py
Normal file
@ -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)
|
@ -1,4 +1,5 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
channel = "nightly-2022-07-20"
|
# use the nightly version of the last stable toolchain, see <https://forge.rust-lang.org/>
|
||||||
|
channel = "nightly-2022-08-05"
|
||||||
components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"]
|
components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"]
|
||||||
|
@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf"
|
|||||||
|
|
||||||
[target.riscv64gc-unknown-none-elf]
|
[target.riscv64gc-unknown-none-elf]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-Clink-args=-Tsrc/linker.ld",
|
"-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
|
||||||
]
|
]
|
||||||
|
@ -10,10 +10,10 @@ edition = "2018"
|
|||||||
buddy_system_allocator = "0.6"
|
buddy_system_allocator = "0.6"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
|
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]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[features]
|
|
||||||
board_qemu = []
|
|
||||||
board_k210 = []
|
|
@ -19,7 +19,7 @@ ifeq ($(TEST), 1)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
binary: elf
|
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
|
build: binary
|
||||||
|
|
||||||
|
55
user/src/bin/adder.rs
Normal file
55
user/src/bin/adder.rs
Normal file
@ -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
|
||||||
|
}
|
72
user/src/bin/adder_atomic.rs
Normal file
72
user/src/bin/adder_atomic.rs
Normal file
@ -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
|
||||||
|
}
|
59
user/src/bin/adder_mutex_blocking.rs
Normal file
59
user/src/bin/adder_mutex_blocking.rs
Normal file
@ -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
|
||||||
|
}
|
60
user/src/bin/adder_mutex_spin.rs
Normal file
60
user/src/bin/adder_mutex_spin.rs
Normal file
@ -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
|
||||||
|
}
|
90
user/src/bin/adder_peterson_spin.rs
Normal file
90
user/src/bin/adder_peterson_spin.rs
Normal file
@ -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
|
||||||
|
}
|
89
user/src/bin/adder_peterson_yield.rs
Normal file
89
user/src/bin/adder_peterson_yield.rs
Normal file
@ -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
|
||||||
|
}
|
68
user/src/bin/adder_simple_spin.rs
Normal file
68
user/src/bin/adder_simple_spin.rs
Normal file
@ -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
|
||||||
|
}
|
70
user/src/bin/adder_simple_yield.rs
Normal file
70
user/src/bin/adder_simple_yield.rs
Normal file
@ -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
|
||||||
|
}
|
72
user/src/bin/barrier_condvar.rs
Normal file
72
user/src/bin/barrier_condvar.rs
Normal file
@ -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<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<isize> = 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
|
||||||
|
}
|
33
user/src/bin/barrier_fail.rs
Normal file
33
user/src/bin/barrier_fail.rs
Normal file
@ -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<isize> = 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
|
||||||
|
}
|
@ -35,8 +35,8 @@ unsafe fn second() -> ! {
|
|||||||
println!("Second: A is {}", A);
|
println!("Second: A is {}", A);
|
||||||
condvar_wait(CONDVAR_ID, MUTEX_ID);
|
condvar_wait(CONDVAR_ID, MUTEX_ID);
|
||||||
}
|
}
|
||||||
mutex_unlock(MUTEX_ID);
|
|
||||||
println!("A is {}, Second can work now", A);
|
println!("A is {}, Second can work now", A);
|
||||||
|
mutex_unlock(MUTEX_ID);
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
|
|
64
user/src/bin/condsync_sem.rs
Normal file
64
user/src/bin/condsync_sem.rs
Normal file
@ -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
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
67
user/src/bin/gui_rect.rs
Normal file
67
user/src/bin/gui_rect.rs
Normal file
@ -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
|
||||||
|
}
|
23
user/src/bin/gui_simple.rs
Normal file
23
user/src/bin/gui_simple.rs
Normal file
@ -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
|
||||||
|
}
|
351
user/src/bin/gui_snake.rs
Normal file
351
user/src/bin/gui_snake.rs
Normal file
@ -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<T: PixelColor, const MAX_SIZE: usize> {
|
||||||
|
parts: [Pixel<T>; MAX_SIZE],
|
||||||
|
len: usize,
|
||||||
|
direction: Direction,
|
||||||
|
size_x: u32,
|
||||||
|
size_y: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> {
|
||||||
|
snake: &'a Snake<T, MAX_SIZE>,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake<T, MAX_SIZE> {
|
||||||
|
type Item = Pixel<T>;
|
||||||
|
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<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let cur = self.snake.parts[self.index];
|
||||||
|
if self.index < self.snake.len {
|
||||||
|
self.index += 1;
|
||||||
|
return Some(cur);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PixelColor, const MAX_SIZE: usize> Snake<T, MAX_SIZE> {
|
||||||
|
fn new(color: T, size_x: u32, size_y: u32) -> Snake<T, MAX_SIZE> {
|
||||||
|
Snake {
|
||||||
|
parts: [Pixel::<T>(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<T: PixelColor> {
|
||||||
|
size_x: u32,
|
||||||
|
size_y: u32,
|
||||||
|
place: Pixel<T>,
|
||||||
|
rng: oorandom::Rand32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PixelColor> Food<T> {
|
||||||
|
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<T, MAX_SIZE>) {
|
||||||
|
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::<T> {
|
||||||
|
0: p,
|
||||||
|
1: self.place.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_pixel(&self) -> Pixel<T> {
|
||||||
|
self.place
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SnakeGame<const MAX_SNAKE_SIZE: usize, T: PixelColor> {
|
||||||
|
snake: Snake<T, MAX_SNAKE_SIZE>,
|
||||||
|
food: Food<T>,
|
||||||
|
food_age: u32,
|
||||||
|
food_lifetime: u32,
|
||||||
|
size_x: u32,
|
||||||
|
size_y: u32,
|
||||||
|
scale_x: u32,
|
||||||
|
scale_y: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MAX_SIZE: usize, T: PixelColor> SnakeGame<MAX_SIZE, T> {
|
||||||
|
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::<T, MAX_SIZE>::new(snake_color, size_x / scale_x, size_y / scale_y);
|
||||||
|
let mut food = Food::<T>::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<D>(&mut self, target: &mut D) -> ()
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = T>,
|
||||||
|
{
|
||||||
|
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::<D> {
|
||||||
|
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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
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::<Rgb888, 20>::new(Rgb888::RED, 8, 8);
|
||||||
|
snake.set_direction(crate::Direction::Right);
|
||||||
|
assert_eq!(
|
||||||
|
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
|
||||||
|
snake.into_iter().next().unwrap()
|
||||||
|
);
|
||||||
|
snake.make_step();
|
||||||
|
assert_eq!(
|
||||||
|
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
|
||||||
|
snake.into_iter().nth(0).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Pixel::<Rgb888>(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::<Rgb888>(Point { x: 1, y: 1 }, Rgb888::RED),
|
||||||
|
snake.into_iter().nth(0).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
|
||||||
|
snake.into_iter().nth(1).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Pixel::<Rgb888>(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
|
||||||
|
}
|
125
user/src/bin/gui_uart.rs
Normal file
125
user/src/bin/gui_uart.rs
Normal file
@ -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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
extern crate user_lib;
|
extern crate user_lib;
|
||||||
extern crate alloc;
|
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::{close, get_time, gettid, open, write, OpenFlags};
|
||||||
use user_lib::{exit, thread_create, waittid};
|
use user_lib::{exit, thread_create, waittid};
|
||||||
|
|
||||||
|
25
user/src/bin/inputdev_event.rs
Normal file
25
user/src/bin/inputdev_event.rs
Normal file
@ -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
|
||||||
|
}
|
@ -14,7 +14,7 @@ use user_lib::{thread_create, waittid};
|
|||||||
|
|
||||||
const SEM_MUTEX: usize = 0;
|
const SEM_MUTEX: usize = 0;
|
||||||
const SEM_EMPTY: usize = 1;
|
const SEM_EMPTY: usize = 1;
|
||||||
const SEM_EXISTED: usize = 2;
|
const SEM_AVAIL: usize = 2;
|
||||||
const BUFFER_SIZE: usize = 8;
|
const BUFFER_SIZE: usize = 8;
|
||||||
static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||||
static mut FRONT: usize = 0;
|
static mut FRONT: usize = 0;
|
||||||
@ -27,20 +27,20 @@ unsafe fn producer(id: *const usize) -> ! {
|
|||||||
for _ in 0..NUMBER_PER_PRODUCER {
|
for _ in 0..NUMBER_PER_PRODUCER {
|
||||||
semaphore_down(SEM_EMPTY);
|
semaphore_down(SEM_EMPTY);
|
||||||
semaphore_down(SEM_MUTEX);
|
semaphore_down(SEM_MUTEX);
|
||||||
BUFFER[FRONT] = id;
|
BUFFER[TAIL] = id;
|
||||||
FRONT = (FRONT + 1) % BUFFER_SIZE;
|
TAIL = (TAIL + 1) % BUFFER_SIZE;
|
||||||
semaphore_up(SEM_MUTEX);
|
semaphore_up(SEM_MUTEX);
|
||||||
semaphore_up(SEM_EXISTED);
|
semaphore_up(SEM_AVAIL);
|
||||||
}
|
}
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn consumer() -> ! {
|
unsafe fn consumer() -> ! {
|
||||||
for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER {
|
for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER {
|
||||||
semaphore_down(SEM_EXISTED);
|
semaphore_down(SEM_AVAIL);
|
||||||
semaphore_down(SEM_MUTEX);
|
semaphore_down(SEM_MUTEX);
|
||||||
print!("{} ", BUFFER[TAIL]);
|
print!("{} ", BUFFER[FRONT]);
|
||||||
TAIL = (TAIL + 1) % BUFFER_SIZE;
|
FRONT = (FRONT + 1) % BUFFER_SIZE;
|
||||||
semaphore_up(SEM_MUTEX);
|
semaphore_up(SEM_MUTEX);
|
||||||
semaphore_up(SEM_EMPTY);
|
semaphore_up(SEM_EMPTY);
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ pub fn main() -> i32 {
|
|||||||
// create semaphores
|
// create semaphores
|
||||||
assert_eq!(semaphore_create(1) as usize, SEM_MUTEX);
|
assert_eq!(semaphore_create(1) as usize, SEM_MUTEX);
|
||||||
assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY);
|
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
|
// create threads
|
||||||
let ids: Vec<_> = (0..PRODUCER_COUNT).collect();
|
let ids: Vec<_> = (0..PRODUCER_COUNT).collect();
|
||||||
let mut threads = Vec::new();
|
let mut threads = Vec::new();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
#![feature(asm)]
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate user_lib;
|
extern crate user_lib;
|
||||||
@ -11,7 +10,7 @@ extern crate core;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use user_lib::{exit, sleep, thread_create, waittid};
|
use user_lib::{exit, sleep, thread_create, waittid};
|
||||||
const N: usize = 3;
|
const N: usize = 1000;
|
||||||
|
|
||||||
static mut TURN: usize = 0;
|
static mut TURN: usize = 0;
|
||||||
static mut FLAG: [bool; 2] = [false; 2];
|
static mut FLAG: [bool; 2] = [false; 2];
|
||||||
@ -30,27 +29,30 @@ fn critical_test_exit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn peterson_enter_critical(id: usize, peer_id: usize) {
|
fn peterson_enter_critical(id: usize, peer_id: usize) {
|
||||||
println!("Thread[{}] try enter", id);
|
// println!("Thread[{}] try enter", id);
|
||||||
vstore!(&FLAG[id], true);
|
vstore!(&FLAG[id], true);
|
||||||
vstore!(&TURN, peer_id);
|
vstore!(&TURN, peer_id);
|
||||||
memory_fence!();
|
memory_fence!();
|
||||||
while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id {
|
while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id {
|
||||||
println!("Thread[{}] enter fail", id);
|
// println!("Thread[{}] enter fail", id);
|
||||||
sleep(1);
|
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) {
|
fn peterson_exit_critical(id: usize) {
|
||||||
vstore!(&FLAG[id], false);
|
vstore!(&FLAG[id], false);
|
||||||
println!("Thread[{}] exit", id);
|
// println!("Thread[{}] exit", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_fn(id: usize) -> ! {
|
pub fn thread_fn(id: usize) -> ! {
|
||||||
println!("Thread[{}] init.", id);
|
// println!("Thread[{}] init.", id);
|
||||||
let peer_id: usize = id ^ 1;
|
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);
|
peterson_enter_critical(id, peer_id);
|
||||||
critical_test_enter();
|
critical_test_enter();
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
16
user/src/bin/random_num.rs
Normal file
16
user/src/bin/random_num.rs
Normal file
@ -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
|
||||||
|
}
|
@ -8,7 +8,7 @@ use user_lib::{exec, fork, wait};
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn main() -> i32 {
|
pub fn main() -> i32 {
|
||||||
for i in 0..50 {
|
for i in 0..5 {
|
||||||
if fork() == 0 {
|
if fork() == 0 {
|
||||||
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
|
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate user_lib;
|
extern crate user_lib;
|
||||||
|
|
||||||
fn f(d: usize) {
|
#[allow(unconditional_recursion)]
|
||||||
println!("d = {}", d);
|
fn f(depth: usize) {
|
||||||
f(d + 1);
|
if depth % 10 == 0 {
|
||||||
|
println!("depth = {}", depth);
|
||||||
|
}
|
||||||
|
f(depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
350
user/src/bin/stackful_coroutine.rs
Normal file
350
user/src/bin/stackful_coroutine.rs
Normal file
@ -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<Task>,
|
||||||
|
current: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
enum State {
|
||||||
|
Available,
|
||||||
|
Running,
|
||||||
|
Ready,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
id: usize,
|
||||||
|
stack: Vec<u8>,
|
||||||
|
ctx: TaskContext,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable
|
||||||
|
pub struct TaskContext {
|
||||||
|
// 15 u64
|
||||||
|
x1: u64, //ra: return addres
|
||||||
|
x2: u64, //sp
|
||||||
|
x8: u64, //s0,fp
|
||||||
|
x9: u64, //s1
|
||||||
|
x18: u64, //x18-27: s2-11
|
||||||
|
x19: u64,
|
||||||
|
x20: u64,
|
||||||
|
x21: u64,
|
||||||
|
x22: u64,
|
||||||
|
x23: u64,
|
||||||
|
x24: u64,
|
||||||
|
x25: u64,
|
||||||
|
x26: u64,
|
||||||
|
x27: u64,
|
||||||
|
nx1: u64, //new return addres
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task {
|
||||||
|
fn new(id: usize) -> Self {
|
||||||
|
// We initialize each task here and allocate the stack. This is not neccesary,
|
||||||
|
// we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts
|
||||||
|
// to do it here. The important part is that once allocated it MUST NOT move in memory.
|
||||||
|
Task {
|
||||||
|
id:id,
|
||||||
|
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||||
|
ctx: TaskContext::default(),
|
||||||
|
state: State::Available,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// This will be our base task, which will be initialized in the `running` state
|
||||||
|
let base_task = Task {
|
||||||
|
id: 0,
|
||||||
|
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||||
|
ctx: TaskContext::default(),
|
||||||
|
state: State::Running,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We initialize the rest of our tasks.
|
||||||
|
let mut tasks = vec![base_task];
|
||||||
|
let mut available_tasks: Vec<Task> = (1..MAX_TASKS).map(|i| Task::new(i)).collect();
|
||||||
|
tasks.append(&mut available_tasks);
|
||||||
|
|
||||||
|
Runtime { tasks, current: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if
|
||||||
|
/// we don't have a reference to it.
|
||||||
|
pub fn init(&self) {
|
||||||
|
unsafe {
|
||||||
|
let r_ptr: *const Runtime = self;
|
||||||
|
RUNTIME = r_ptr as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is where we start running our runtime. If it is our base task, we call yield until
|
||||||
|
/// it returns false (which means that there are no tasks scheduled) and we are done.
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
while self.t_yield() {}
|
||||||
|
println!("All tasks finished!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is our return function. The only place we use this is in our `guard` function.
|
||||||
|
/// If the current task is not our base task we set its state to Available. It means
|
||||||
|
/// we're finished with it. Then we yield which will schedule a new task to be run.
|
||||||
|
fn t_return(&mut self) {
|
||||||
|
if self.current != 0 {
|
||||||
|
self.tasks[self.current].state = State::Available;
|
||||||
|
self.t_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state.
|
||||||
|
/// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm.
|
||||||
|
///
|
||||||
|
/// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`.
|
||||||
|
/// Then we call switch which will save the current context (the old context) and load the new context
|
||||||
|
/// into the CPU which then resumes based on the context it was just passed.
|
||||||
|
///
|
||||||
|
/// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result
|
||||||
|
#[inline(never)]
|
||||||
|
fn t_yield(&mut self) -> bool {
|
||||||
|
let mut pos = self.current;
|
||||||
|
while self.tasks[pos].state != State::Ready {
|
||||||
|
pos += 1;
|
||||||
|
if pos == self.tasks.len() {
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
if pos == self.current {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.tasks[self.current].state != State::Available {
|
||||||
|
self.tasks[self.current].state = State::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tasks[pos].state = State::Running;
|
||||||
|
let old_pos = self.current;
|
||||||
|
self.current = pos;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler
|
||||||
|
// is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows
|
||||||
|
// and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that
|
||||||
|
// will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead,
|
||||||
|
// this code will never be run anyways and if it did it would always be `true`.
|
||||||
|
self.tasks.len() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// While `yield` is the logically interesting function I think this the technically most interesting.
|
||||||
|
///
|
||||||
|
/// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state).
|
||||||
|
/// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that.
|
||||||
|
/// We keep things simple for now.
|
||||||
|
///
|
||||||
|
/// When we find an available task we get the stack length and a pointer to our u8 bytearray.
|
||||||
|
///
|
||||||
|
/// The next part we have to use some unsafe functions. First we write an address to our `guard` function
|
||||||
|
/// that will be called if the function we provide returns. Then we set the address to the function we
|
||||||
|
/// pass inn.
|
||||||
|
///
|
||||||
|
/// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start
|
||||||
|
/// executing that first when we are scheuled to run.
|
||||||
|
///
|
||||||
|
/// Lastly we set the state as `Ready` which means we have work to do and is ready to do it.
|
||||||
|
pub fn spawn(&mut self, f: fn()) {
|
||||||
|
let available = self
|
||||||
|
.tasks
|
||||||
|
.iter_mut()
|
||||||
|
.find(|t| t.state == State::Available)
|
||||||
|
.expect("no available task.");
|
||||||
|
|
||||||
|
println!("RUNTIME: spawning task {}\n", available.id);
|
||||||
|
let size = available.stack.len();
|
||||||
|
unsafe {
|
||||||
|
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
|
||||||
|
|
||||||
|
// make sure our stack itself is 8 byte aligned - it will always
|
||||||
|
// offset to a lower memory address. Since we know we're at the "high"
|
||||||
|
// memory address of our allocated space, we know that offsetting to
|
||||||
|
// a lower one will be a valid address (given that we actually allocated)
|
||||||
|
// enough space to actually get an aligned pointer in the first place).
|
||||||
|
let s_ptr = (s_ptr as usize & !7) as *mut u8;
|
||||||
|
|
||||||
|
available.ctx.x1 = guard as u64; //ctx.x1 is old return address
|
||||||
|
available.ctx.nx1 = f as u64; //ctx.nx2 is new return address
|
||||||
|
available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp
|
||||||
|
}
|
||||||
|
available.state = State::Ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is our guard function that we place on top of the stack. All this function does is set the
|
||||||
|
/// state of our current task and then `yield` which will then schedule a new task to be run.
|
||||||
|
fn guard() {
|
||||||
|
unsafe {
|
||||||
|
let rt_ptr = RUNTIME as *mut Runtime;
|
||||||
|
(*rt_ptr).t_return();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We know that Runtime is alive the length of the program and that we only access from one core
|
||||||
|
/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our
|
||||||
|
/// Runtime and then calling `t_yield`
|
||||||
|
pub fn yield_task() {
|
||||||
|
unsafe {
|
||||||
|
let rt_ptr = RUNTIME as *mut Runtime;
|
||||||
|
(*rt_ptr).t_yield();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first
|
||||||
|
/// read out the values of all the registers we need and then sets all the register values to the register values we
|
||||||
|
/// saved when we suspended exceution on the "new" task.
|
||||||
|
///
|
||||||
|
/// This is essentially all we need to do to save and resume execution.
|
||||||
|
///
|
||||||
|
/// Some details about inline assembly.
|
||||||
|
///
|
||||||
|
/// The assembly commands in the string literal is called the assemblt template. It is preceeded by
|
||||||
|
/// zero or up to four segments indicated by ":":
|
||||||
|
///
|
||||||
|
/// - First ":" we have our output parameters, this parameters that this function will return.
|
||||||
|
/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context
|
||||||
|
/// but we modify the "old" context saving our registers there (see volatile option below)
|
||||||
|
/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely
|
||||||
|
/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel"
|
||||||
|
///
|
||||||
|
/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to
|
||||||
|
/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects"
|
||||||
|
/// therefore we should use the `volatile` option. I **think** this is actually set for us by default
|
||||||
|
/// when there are no output parameters given (my own assumption after going through the source code)
|
||||||
|
/// for the `asm` macro, but we should make it explicit anyway.
|
||||||
|
///
|
||||||
|
/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full
|
||||||
|
/// control over the stack layout since normal functions has a prologue-and epilogue added by the
|
||||||
|
/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked".
|
||||||
|
/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else
|
||||||
|
/// the compiler decides to inline this function (curiously this currently only happens on Windows).
|
||||||
|
/// If the function is inlined we get a curious runtime error where it fails when switching back
|
||||||
|
/// to as saved context and in general our assembly will not work as expected.
|
||||||
|
///
|
||||||
|
/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||||
|
/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
|
||||||
|
/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||||
|
#[naked]
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) {
|
||||||
|
// a0: _old, a1: _new
|
||||||
|
asm!(
|
||||||
|
"
|
||||||
|
sd x1, 0x00(a0)
|
||||||
|
sd x2, 0x08(a0)
|
||||||
|
sd x8, 0x10(a0)
|
||||||
|
sd x9, 0x18(a0)
|
||||||
|
sd x18, 0x20(a0)
|
||||||
|
sd x19, 0x28(a0)
|
||||||
|
sd x20, 0x30(a0)
|
||||||
|
sd x21, 0x38(a0)
|
||||||
|
sd x22, 0x40(a0)
|
||||||
|
sd x23, 0x48(a0)
|
||||||
|
sd x24, 0x50(a0)
|
||||||
|
sd x25, 0x58(a0)
|
||||||
|
sd x26, 0x60(a0)
|
||||||
|
sd x27, 0x68(a0)
|
||||||
|
sd x1, 0x70(a0)
|
||||||
|
|
||||||
|
ld x1, 0x00(a1)
|
||||||
|
ld x2, 0x08(a1)
|
||||||
|
ld x8, 0x10(a1)
|
||||||
|
ld x9, 0x18(a1)
|
||||||
|
ld x18, 0x20(a1)
|
||||||
|
ld x19, 0x28(a1)
|
||||||
|
ld x20, 0x30(a1)
|
||||||
|
ld x21, 0x38(a1)
|
||||||
|
ld x22, 0x40(a1)
|
||||||
|
ld x23, 0x48(a1)
|
||||||
|
ld x24, 0x50(a1)
|
||||||
|
ld x25, 0x58(a1)
|
||||||
|
ld x26, 0x60(a1)
|
||||||
|
ld x27, 0x68(a1)
|
||||||
|
ld t0, 0x70(a1)
|
||||||
|
|
||||||
|
jr t0
|
||||||
|
",
|
||||||
|
options(noreturn)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn main() {
|
||||||
|
println!("stackful_coroutine begin...");
|
||||||
|
println!("TASK 0(Runtime) STARTING");
|
||||||
|
let mut runtime = Runtime::new();
|
||||||
|
runtime.init();
|
||||||
|
runtime.spawn(|| {
|
||||||
|
println!("TASK 1 STARTING");
|
||||||
|
let id = 1;
|
||||||
|
for i in 0..4 {
|
||||||
|
println!("task: {} counter: {}", id, i);
|
||||||
|
yield_task();
|
||||||
|
}
|
||||||
|
println!("TASK 1 FINISHED");
|
||||||
|
});
|
||||||
|
runtime.spawn(|| {
|
||||||
|
println!("TASK 2 STARTING");
|
||||||
|
let id = 2;
|
||||||
|
for i in 0..8 {
|
||||||
|
println!("task: {} counter: {}", id, i);
|
||||||
|
yield_task();
|
||||||
|
}
|
||||||
|
println!("TASK 2 FINISHED");
|
||||||
|
});
|
||||||
|
runtime.spawn(|| {
|
||||||
|
println!("TASK 3 STARTING");
|
||||||
|
let id = 3;
|
||||||
|
for i in 0..12 {
|
||||||
|
println!("task: {} counter: {}", id, i);
|
||||||
|
yield_task();
|
||||||
|
}
|
||||||
|
println!("TASK 3 FINISHED");
|
||||||
|
});
|
||||||
|
runtime.spawn(|| {
|
||||||
|
println!("TASK 4 STARTING");
|
||||||
|
let id = 4;
|
||||||
|
for i in 0..16 {
|
||||||
|
println!("task: {} counter: {}", id, i);
|
||||||
|
yield_task();
|
||||||
|
}
|
||||||
|
println!("TASK 4 FINISHED");
|
||||||
|
});
|
||||||
|
runtime.run();
|
||||||
|
println!("stackful_coroutine PASSED");
|
||||||
|
exit(0);
|
||||||
|
}
|
129
user/src/bin/stackless_coroutine.rs
Normal file
129
user/src/bin/stackless_coroutine.rs
Normal file
@ -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<Self::Output> {
|
||||||
|
match self.task.state {
|
||||||
|
State::Halted => {
|
||||||
|
self.task.state = State::Running;
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
State::Running => {
|
||||||
|
self.task.state = State::Halted;
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Executor {
|
||||||
|
tasks: VecDeque<Pin<Box<dyn Future<Output = ()>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Executor {
|
||||||
|
fn new() -> Self {
|
||||||
|
Executor {
|
||||||
|
tasks: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push<C, F>(&mut self, closure: C)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + 'static,
|
||||||
|
C: FnOnce(Task) -> F,
|
||||||
|
{
|
||||||
|
let task = Task {
|
||||||
|
state: State::Running,
|
||||||
|
};
|
||||||
|
self.tasks.push_back(Box::pin(closure(task)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) {
|
||||||
|
let waker = create_waker();
|
||||||
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
while let Some(mut task) = self.tasks.pop_front() {
|
||||||
|
match task.as_mut().poll(&mut context) {
|
||||||
|
Poll::Pending => {
|
||||||
|
self.tasks.push_back(task);
|
||||||
|
}
|
||||||
|
Poll::Ready(()) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_waker() -> Waker {
|
||||||
|
// Safety: The waker points to a vtable with functions that do nothing. Doing
|
||||||
|
// nothing is memory-safe.
|
||||||
|
unsafe { Waker::from_raw(RAW_WAKER) }
|
||||||
|
}
|
||||||
|
|
||||||
|
const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE);
|
||||||
|
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||||
|
|
||||||
|
unsafe fn clone(_: *const ()) -> RawWaker {
|
||||||
|
RAW_WAKER
|
||||||
|
}
|
||||||
|
unsafe fn wake(_: *const ()) {}
|
||||||
|
unsafe fn wake_by_ref(_: *const ()) {}
|
||||||
|
unsafe fn drop(_: *const ()) {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn main() -> i32 {
|
||||||
|
println!("stackless coroutine Begin..");
|
||||||
|
let mut exec = Executor::new();
|
||||||
|
println!(" Create futures");
|
||||||
|
for instance in 1..=3 {
|
||||||
|
exec.push(move |mut task| async move {
|
||||||
|
println!(" Task {}: begin state", instance);
|
||||||
|
task.waiter().await;
|
||||||
|
println!(" Task {}: next state", instance);
|
||||||
|
task.waiter().await;
|
||||||
|
println!(" Task {}: end state", instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(" Running");
|
||||||
|
exec.run();
|
||||||
|
println!(" Done");
|
||||||
|
println!("stackless coroutine PASSED");
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
46
user/src/bin/udp.rs
Normal file
46
user/src/bin/udp.rs
Normal file
@ -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
|
||||||
|
}
|
@ -27,19 +27,22 @@ static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[
|
|||||||
("phil_din_mutex\0", "\0", "\0", "\0", 0),
|
("phil_din_mutex\0", "\0", "\0", "\0", 0),
|
||||||
("pipe_large_test\0", "\0", "\0", "\0", 0),
|
("pipe_large_test\0", "\0", "\0", "\0", 0),
|
||||||
("pipetest\0", "\0", "\0", "\0", 0),
|
("pipetest\0", "\0", "\0", "\0", 0),
|
||||||
("race_adder_arg\0", "3\0", "\0", "\0", 0),
|
("adder_peterson_spin\0", "\0", "\0", "\0", 0),
|
||||||
("race_adder_atomic\0", "\0", "\0", "\0", 0),
|
("adder_peterson_yield\0", "\0", "\0", "\0", 0),
|
||||||
("race_adder_mutex_blocking\0", "\0", "\0", "\0", 0),
|
("adder_mutex_blocking\0", "\0", "\0", "\0", 0),
|
||||||
("race_adder_mutex_spin\0", "\0", "\0", "\0", 0),
|
("adder_mutex_spin\0", "\0", "\0", "\0", 0),
|
||||||
("run_pipe_test\0", "\0", "\0", "\0", 0),
|
("run_pipe_test\0", "\0", "\0", "\0", 0),
|
||||||
("sleep_simple\0", "\0", "\0", "\0", 0),
|
("sleep_simple\0", "\0", "\0", "\0", 0),
|
||||||
("sleep\0", "\0", "\0", "\0", 0),
|
("sleep\0", "\0", "\0", "\0", 0),
|
||||||
("sleep_simple\0", "\0", "\0", "\0", 0),
|
("sleep_simple\0", "\0", "\0", "\0", 0),
|
||||||
("sync_sem\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_arg\0", "\0", "\0", "\0", 0),
|
||||||
("threads\0", "\0", "\0", "\0", 0),
|
("threads\0", "\0", "\0", "\0", 0),
|
||||||
("yield\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)] = &[
|
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),
|
("priv_inst\0", "\0", "\0", "\0", -4),
|
||||||
("store_fault\0", "\0", "\0", "\0", -11),
|
("store_fault\0", "\0", "\0", "\0", -11),
|
||||||
("until_timeout\0", "\0", "\0", "\0", -6),
|
("until_timeout\0", "\0", "\0", "\0", -6),
|
||||||
("race_adder\0", "\0", "\0", "\0", -6),
|
("adder\0", "\0", "\0", "\0", -6),
|
||||||
("huge_write_mt\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};
|
use user_lib::{exec, fork, waitpid};
|
||||||
|
30
user/src/file.rs
Normal file
30
user/src/file.rs
Normal file
@ -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)
|
||||||
|
}
|
118
user/src/io.rs
Normal file
118
user/src/io.rs
Normal file
@ -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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
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<InputEvent> {
|
||||||
|
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<u64> 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<DecodeType> {
|
||||||
|
Decoder::decode(
|
||||||
|
self.event_type as usize,
|
||||||
|
self.code as usize,
|
||||||
|
self.value as usize,
|
||||||
|
).ok()
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user