mirror of
https://github.com/rcore-os/rCore-Tutorial-v3.git
synced 2024-11-22 09:26:26 +04:00
Merge branch 'ch9' into ch9-dev
This commit is contained in:
commit
683a4fdc55
9
.github/workflows/doc-and-test.yml
vendored
9
.github/workflows/doc-and-test.yml
vendored
@ -4,6 +4,7 @@ on: [push]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
rust_toolchain: nightly-2022-08-05
|
||||
|
||||
jobs:
|
||||
build-doc:
|
||||
@ -13,11 +14,11 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-04-11
|
||||
toolchain: ${{ env.rust_toolchain }}
|
||||
components: rust-src, llvm-tools-preview
|
||||
target: riscv64gc-unknown-none-elf
|
||||
- name: Build doc
|
||||
run: cd os && cargo doc --no-deps --verbose --features "board_qemu"
|
||||
run: cd os && cargo doc --no-deps --verbose
|
||||
- name: Deploy to Github Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
@ -32,7 +33,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-04-11
|
||||
toolchain: ${{ env.rust_toolchain }}
|
||||
components: rust-src, llvm-tools-preview
|
||||
target: riscv64gc-unknown-none-elf
|
||||
- uses: actions-rs/install@v0.1
|
||||
@ -65,5 +66,3 @@ jobs:
|
||||
run: cd os && make run TEST=1
|
||||
timeout-minutes: 10
|
||||
|
||||
- name: Build for k210
|
||||
run: cd os && make build BOARD=k210
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,5 +9,7 @@ os/src/link_app.S
|
||||
os/src/linker.ld
|
||||
os/last-*
|
||||
os/.gdb_history
|
||||
os/virt.out
|
||||
tools/
|
||||
pushall.sh
|
||||
.vscode/*.log
|
||||
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -6,5 +6,8 @@
|
||||
"rust.all_targets": false,
|
||||
// For Rust Analyzer plugin users:
|
||||
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
|
||||
"rust-analyzer.checkOnSave.allTargets": false
|
||||
}
|
||||
"rust-analyzer.checkOnSave.allTargets": false,
|
||||
// "rust-analyzer.cargo.features": [
|
||||
// "board_qemu"
|
||||
// ]
|
||||
}
|
117
Dockerfile
117
Dockerfile
@ -1,40 +1,85 @@
|
||||
FROM ubuntu:18.04
|
||||
LABEL maintainer="dinghao188" \
|
||||
version="1.1" \
|
||||
description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3"
|
||||
# syntax=docker/dockerfile:1
|
||||
# This Dockerfile is adapted from https://github.com/LearningOS/rCore-Tutorial-v3/blob/main/Dockerfile
|
||||
# with the following major updates:
|
||||
# - ubuntu 18.04 -> 20.04
|
||||
# - qemu 5.0.0 -> 7.0.0
|
||||
# - Extensive comments linking to relevant documentation
|
||||
FROM ubuntu:20.04
|
||||
|
||||
#install some deps
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y curl wget autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
|
||||
gawk build-essential bison flex texinfo gperf libtool patchutils bc xz-utils \
|
||||
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3
|
||||
ARG QEMU_VERSION=7.0.0
|
||||
ARG HOME=/root
|
||||
|
||||
#install rust and qemu
|
||||
RUN set -x; \
|
||||
RUSTUP='/root/rustup.sh' \
|
||||
&& cd $HOME \
|
||||
#install rust
|
||||
&& curl https://sh.rustup.rs -sSf > $RUSTUP && chmod +x $RUSTUP \
|
||||
&& $RUSTUP -y --default-toolchain nightly --profile minimal \
|
||||
# 0. Install general tools
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
curl \
|
||||
git \
|
||||
python3 \
|
||||
wget
|
||||
|
||||
#compile qemu
|
||||
&& wget https://ftp.osuosl.org/pub/blfs/conglomeration/qemu/qemu-5.0.0.tar.xz \
|
||||
&& tar xvJf qemu-5.0.0.tar.xz \
|
||||
&& cd qemu-5.0.0 \
|
||||
&& ./configure --target-list=riscv64-softmmu,riscv64-linux-user \
|
||||
&& make -j$(nproc) install \
|
||||
&& cd $HOME && rm -rf qemu-5.0.0 qemu-5.0.0.tar.xz
|
||||
# 1. Set up QEMU RISC-V
|
||||
# - https://learningos.github.io/rust-based-os-comp2022/0setup-devel-env.html#qemu
|
||||
# - https://www.qemu.org/download/
|
||||
# - https://wiki.qemu.org/Documentation/Platforms/RISCV
|
||||
# - https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html
|
||||
|
||||
#for chinese network
|
||||
RUN set -x; \
|
||||
APT_CONF='/etc/apt/sources.list'; \
|
||||
CARGO_CONF='/root/.cargo/config'; \
|
||||
BASHRC='/root/.bashrc' \
|
||||
&& echo 'export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static' >> $BASHRC \
|
||||
&& echo 'export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup' >> $BASHRC \
|
||||
&& touch $CARGO_CONF \
|
||||
&& echo '[source.crates-io]' > $CARGO_CONF \
|
||||
&& echo "replace-with = 'ustc'" >> $CARGO_CONF \
|
||||
&& echo '[source.ustc]' >> $CARGO_CONF \
|
||||
&& echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_CONF
|
||||
# 1.1. Download source
|
||||
WORKDIR ${HOME}
|
||||
RUN wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz && \
|
||||
tar xvJf qemu-${QEMU_VERSION}.tar.xz
|
||||
|
||||
# 1.2. Install dependencies
|
||||
# - https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html#prerequisites
|
||||
RUN apt-get install -y \
|
||||
autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
|
||||
gawk build-essential bison flex texinfo gperf libtool patchutils bc \
|
||||
zlib1g-dev libexpat-dev git \
|
||||
ninja-build pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev
|
||||
|
||||
# 1.3. Build and install from source
|
||||
WORKDIR ${HOME}/qemu-${QEMU_VERSION}
|
||||
RUN ./configure --target-list=riscv64-softmmu,riscv64-linux-user && \
|
||||
make -j$(nproc) && \
|
||||
make install
|
||||
|
||||
# 1.4. Clean up
|
||||
WORKDIR ${HOME}
|
||||
RUN rm -rf qemu-${QEMU_VERSION} qemu-${QEMU_VERSION}.tar.xz
|
||||
|
||||
# 1.5. Sanity checking
|
||||
RUN qemu-system-riscv64 --version && \
|
||||
qemu-riscv64 --version
|
||||
|
||||
# 2. Set up Rust
|
||||
# - https://learningos.github.io/rust-based-os-comp2022/0setup-devel-env.html#qemu
|
||||
# - https://www.rust-lang.org/tools/install
|
||||
# - https://github.com/rust-lang/docker-rust/blob/master/Dockerfile-debian.template
|
||||
|
||||
# 2.1. Install
|
||||
ENV RUSTUP_HOME=/usr/local/rustup \
|
||||
CARGO_HOME=/usr/local/cargo \
|
||||
PATH=/usr/local/cargo/bin:$PATH \
|
||||
RUST_VERSION=nightly
|
||||
RUN set -eux; \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup-init; \
|
||||
chmod +x rustup-init; \
|
||||
./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION; \
|
||||
rm rustup-init; \
|
||||
chmod -R a+w $RUSTUP_HOME $CARGO_HOME;
|
||||
|
||||
# 2.2. Sanity checking
|
||||
RUN rustup --version && \
|
||||
cargo --version && \
|
||||
rustc --version
|
||||
|
||||
# 3. Build env for labs
|
||||
# See os1/Makefile `env:` for example.
|
||||
# This avoids having to wait for these steps each time using a new container.
|
||||
RUN rustup target add riscv64gc-unknown-none-elf && \
|
||||
cargo install cargo-binutils --vers ~0.2 && \
|
||||
rustup component add rust-src && \
|
||||
rustup component add llvm-tools-preview
|
||||
|
||||
# Ready to go
|
||||
WORKDIR ${HOME}
|
||||
|
10
Makefile
10
Makefile
@ -1,10 +1,12 @@
|
||||
DOCKER_NAME ?= dinghao188/rcore-tutorial
|
||||
DOCKER_NAME ?= rcore-tutorial-v3
|
||||
.PHONY: docker build_docker
|
||||
|
||||
|
||||
docker:
|
||||
docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME}
|
||||
docker run --rm -it -v ${PWD}:/mnt -w /mnt ${DOCKER_NAME} bash
|
||||
|
||||
build_docker:
|
||||
docker build -t ${DOCKER_NAME} .
|
||||
|
||||
fmt:
|
||||
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..
|
||||
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -9,4 +9,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
clap = "2.33.3"
|
||||
easy-fs = { path = "../easy-fs" }
|
||||
rand = "0.8.0"
|
||||
rand = "0.8.0"
|
||||
|
||||
# [features]
|
||||
# board_qemu = []
|
||||
# board_k210 = []
|
@ -12,3 +12,7 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[features]
|
||||
board_qemu = []
|
||||
board_k210 = []
|
@ -13,18 +13,11 @@ buddy_system_allocator = "0.6"
|
||||
bitflags = "1.2.1"
|
||||
xmas-elf = "0.7.0"
|
||||
volatile = "0.3"
|
||||
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" }
|
||||
k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
|
||||
k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
|
||||
k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
|
||||
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" }
|
||||
easy-fs = { path = "../easy-fs" }
|
||||
virtio-input-decoder = "0.1.4"
|
||||
embedded-graphics = "0.7.1"
|
||||
tinybmp = "0.3.1"
|
||||
|
||||
[features]
|
||||
board_qemu = []
|
||||
board_k210 = []
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
71
os/Makefile
71
os/Makefile
@ -5,14 +5,18 @@ KERNEL_ELF := target/$(TARGET)/$(MODE)/os
|
||||
KERNEL_BIN := $(KERNEL_ELF).bin
|
||||
DISASM_TMP := target/$(TARGET)/$(MODE)/asm
|
||||
FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img
|
||||
SDCARD := /dev/sdb
|
||||
APPS := ../user/src/bin/*
|
||||
|
||||
# BOARD
|
||||
BOARD ?= qemu
|
||||
BOARD := qemu
|
||||
SBI ?= rustsbi
|
||||
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
|
||||
K210_BOOTLOADER_SIZE := 131072
|
||||
|
||||
# GUI
|
||||
GUI ?= off
|
||||
ifeq ($(GUI), off)
|
||||
GUI_OPTION := -display none
|
||||
endif
|
||||
|
||||
# Building mode argument
|
||||
ifeq ($(MODE), release)
|
||||
@ -20,15 +24,7 @@ ifeq ($(MODE), release)
|
||||
endif
|
||||
|
||||
# KERNEL ENTRY
|
||||
ifeq ($(BOARD), qemu)
|
||||
KERNEL_ENTRY_PA := 0x80200000
|
||||
else ifeq ($(BOARD), k210)
|
||||
KERNEL_ENTRY_PA := 0x80020000
|
||||
endif
|
||||
|
||||
# Run K210
|
||||
K210-SERIALPORT = /dev/ttyUSB0
|
||||
K210-BURNER = ../tools/kflash.py
|
||||
KERNEL_ENTRY_PA := 0x80200000
|
||||
|
||||
# Binutils
|
||||
OBJDUMP := rust-objdump --arch-name=riscv64
|
||||
@ -40,26 +36,14 @@ DISASM ?= -x
|
||||
# Run usertests or usershell
|
||||
TEST ?=
|
||||
|
||||
build: env switch-check $(KERNEL_BIN) fs-img
|
||||
|
||||
switch-check:
|
||||
ifeq ($(BOARD), qemu)
|
||||
(which last-qemu) || (rm -f last-k210 && touch last-qemu && make clean)
|
||||
else ifeq ($(BOARD), k210)
|
||||
(which last-k210) || (rm -f last-qemu && touch last-k210 && make clean)
|
||||
endif
|
||||
build: env $(KERNEL_BIN) fs-img
|
||||
|
||||
env:
|
||||
(rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET)
|
||||
cargo install cargo-binutils --vers =0.3.3
|
||||
cargo install cargo-binutils
|
||||
rustup component add rust-src
|
||||
rustup component add llvm-tools-preview
|
||||
|
||||
sdcard: fs-img
|
||||
@echo "Are you sure write to $(SDCARD) ? [y/N] " && read ans && [ $${ans:-N} = y ]
|
||||
@sudo dd if=/dev/zero of=$(SDCARD) bs=1048576 count=32
|
||||
@sudo dd if=$(FS_IMG) of=$(SDCARD)
|
||||
|
||||
$(KERNEL_BIN): kernel
|
||||
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
|
||||
|
||||
@ -73,7 +57,7 @@ $(APPS):
|
||||
kernel:
|
||||
@echo Platform: $(BOARD)
|
||||
@cp src/linker-$(BOARD).ld src/linker.ld
|
||||
@cargo build --release --features "board_$(BOARD)"
|
||||
@cargo build --release
|
||||
@rm src/linker.ld
|
||||
|
||||
clean:
|
||||
@ -89,28 +73,12 @@ disasm-vim: kernel
|
||||
|
||||
run: run-inner
|
||||
|
||||
gui: build
|
||||
ifeq ($(BOARD),qemu)
|
||||
@qemu-system-riscv64 \
|
||||
-M 128m \
|
||||
-machine virt \
|
||||
-bios $(BOOTLOADER) \
|
||||
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
|
||||
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
||||
-device virtio-blk-device,drive=x0 \
|
||||
-device virtio-gpu-device \
|
||||
-device virtio-keyboard-device \
|
||||
-device virtio-mouse-device \
|
||||
-serial stdio
|
||||
endif
|
||||
|
||||
run-inner: build
|
||||
ifeq ($(BOARD),qemu)
|
||||
@qemu-system-riscv64 \
|
||||
-M 128m \
|
||||
-machine virt \
|
||||
-bios $(BOOTLOADER) \
|
||||
-display none \
|
||||
$(GUI_OPTION) \
|
||||
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
|
||||
-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
|
||||
-device virtio-blk-device,drive=x0 \
|
||||
@ -118,15 +86,10 @@ ifeq ($(BOARD),qemu)
|
||||
-device virtio-keyboard-device \
|
||||
-device virtio-mouse-device \
|
||||
-serial stdio
|
||||
else
|
||||
(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
|
||||
@cp $(BOOTLOADER) $(BOOTLOADER).copy
|
||||
@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1
|
||||
@mv $(BOOTLOADER).copy $(KERNEL_BIN)
|
||||
@sudo chmod 777 $(K210-SERIALPORT)
|
||||
python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN)
|
||||
python3 -m serial.tools.miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200
|
||||
endif
|
||||
|
||||
fdt:
|
||||
@qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out
|
||||
fdtdump virt.out
|
||||
|
||||
debug: build
|
||||
@tmux new-session -d \
|
||||
@ -141,4 +104,4 @@ gdbserver: build
|
||||
gdbclient:
|
||||
@riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'
|
||||
|
||||
.PHONY: build env kernel clean disasm disasm-vim run-inner switch-check fs-img gdbserver gdbclient
|
||||
.PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient fdt
|
||||
|
@ -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!();
|
||||
}
|
@ -14,6 +14,7 @@ pub const VIRT_PLIC: usize = 0xC00_0000;
|
||||
pub const VIRT_UART: usize = 0x1000_0000;
|
||||
|
||||
pub const VIRTGPU_XRES: u32 = 1280;
|
||||
#[allow(unused)]
|
||||
pub const VIRTGPU_YRES: u32 = 800;
|
||||
|
||||
use crate::drivers::block::BLOCK_DEVICE;
|
||||
@ -30,7 +31,7 @@ pub fn device_init() {
|
||||
plic.set_threshold(hart_id, supervisor, 0);
|
||||
plic.set_threshold(hart_id, machine, 1);
|
||||
//irq nums: 5 keyboard, 6 mouse, 8 block, 10 uart
|
||||
for intr_src_id in [5usize, 6, 8 , 10] {
|
||||
for intr_src_id in [5usize, 6, 8, 10] {
|
||||
plic.enable(hart_id, supervisor, intr_src_id);
|
||||
plic.set_priority(intr_src_id, 1);
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
use crate::drivers::chardev::CharDevice;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
use crate::drivers::chardev::UART;
|
||||
#[cfg(feature = "board_k210")]
|
||||
use crate::sbi::console_putchar;
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
struct Stdout;
|
||||
@ -10,10 +7,7 @@ struct Stdout;
|
||||
impl Write for Stdout {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for c in s.chars() {
|
||||
#[cfg(feature = "board_qemu")]
|
||||
UART.write(c as u8);
|
||||
#[cfg(feature = "board_k210")]
|
||||
console_putchar(c as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
mod sdcard;
|
||||
mod virtio_blk;
|
||||
|
||||
pub use sdcard::SDCardWrapper;
|
||||
pub use virtio_blk::VirtIOBlock;
|
||||
|
||||
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,29 +1,19 @@
|
||||
use super::BlockDevice;
|
||||
use crate::mm::{
|
||||
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
|
||||
StepByOne, VirtAddr,
|
||||
};
|
||||
use crate::drivers::bus::virtio::VirtioHal;
|
||||
use crate::sync::{Condvar, UPIntrFreeCell};
|
||||
use crate::task::schedule;
|
||||
use crate::DEV_NON_BLOCKING_ACCESS;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::*;
|
||||
use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader};
|
||||
|
||||
#[allow(unused)]
|
||||
const VIRTIO0: usize = 0x10008000;
|
||||
|
||||
pub struct VirtIOBlock {
|
||||
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static>>,
|
||||
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static, VirtioHal>>,
|
||||
condvars: BTreeMap<u16, Condvar>,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref QUEUE_FRAMES: UPIntrFreeCell<Vec<FrameTracker>> =
|
||||
unsafe { UPIntrFreeCell::new(Vec::new()) };
|
||||
}
|
||||
|
||||
impl BlockDevice for VirtIOBlock {
|
||||
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
|
||||
let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access();
|
||||
@ -79,7 +69,9 @@ impl BlockDevice for VirtIOBlock {
|
||||
impl VirtIOBlock {
|
||||
pub fn new() -> Self {
|
||||
let virtio_blk = unsafe {
|
||||
UPIntrFreeCell::new(VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap())
|
||||
UPIntrFreeCell::new(
|
||||
VirtIOBlk::<VirtioHal>::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(),
|
||||
)
|
||||
};
|
||||
let mut condvars = BTreeMap::new();
|
||||
let channels = virtio_blk.exclusive_access().virt_queue_size();
|
||||
@ -93,39 +85,3 @@ impl VirtIOBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
|
||||
let mut ppn_base = PhysPageNum(0);
|
||||
for i in 0..pages {
|
||||
let frame = frame_alloc().unwrap();
|
||||
if i == 0 {
|
||||
ppn_base = frame.ppn;
|
||||
}
|
||||
assert_eq!(frame.ppn.0, ppn_base.0 + i);
|
||||
QUEUE_FRAMES.exclusive_access().push(frame);
|
||||
}
|
||||
ppn_base.into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn virtio_dma_dealloc(pa: PhysAddr, pages: usize) -> i32 {
|
||||
let mut ppn_base: PhysPageNum = pa.into();
|
||||
for _ in 0..pages {
|
||||
frame_dealloc(ppn_base);
|
||||
ppn_base.step();
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
|
||||
VirtAddr(paddr.0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
|
||||
PageTable::from_token(kernel_token())
|
||||
.translate_va(vaddr)
|
||||
.unwrap()
|
||||
}
|
||||
|
1
os/src/drivers/bus/mod.rs
Normal file
1
os/src/drivers/bus/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod virtio;
|
52
os/src/drivers/bus/virtio.rs
Normal file
52
os/src/drivers/bus/virtio.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::mm::{
|
||||
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
|
||||
StepByOne, VirtAddr,
|
||||
};
|
||||
use crate::sync::UPIntrFreeCell;
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::*;
|
||||
use virtio_drivers::Hal;
|
||||
|
||||
lazy_static! {
|
||||
static ref QUEUE_FRAMES: UPIntrFreeCell<Vec<FrameTracker>> =
|
||||
unsafe { UPIntrFreeCell::new(Vec::new()) };
|
||||
}
|
||||
|
||||
pub struct VirtioHal;
|
||||
|
||||
impl Hal for VirtioHal {
|
||||
fn dma_alloc(pages: usize) -> usize {
|
||||
let mut ppn_base = PhysPageNum(0);
|
||||
for i in 0..pages {
|
||||
let frame = frame_alloc().unwrap();
|
||||
if i == 0 {
|
||||
ppn_base = frame.ppn;
|
||||
}
|
||||
assert_eq!(frame.ppn.0, ppn_base.0 + i);
|
||||
QUEUE_FRAMES.exclusive_access().push(frame);
|
||||
}
|
||||
let pa: PhysAddr = ppn_base.into();
|
||||
pa.0
|
||||
}
|
||||
|
||||
fn dma_dealloc(pa: usize, pages: usize) -> i32 {
|
||||
let pa = PhysAddr::from(pa);
|
||||
let mut ppn_base: PhysPageNum = pa.into();
|
||||
for _ in 0..pages {
|
||||
frame_dealloc(ppn_base);
|
||||
ppn_base.step();
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn phys_to_virt(addr: usize) -> usize {
|
||||
addr
|
||||
}
|
||||
|
||||
fn virt_to_phys(vaddr: usize) -> usize {
|
||||
PageTable::from_token(kernel_token())
|
||||
.translate_va(VirtAddr::from(vaddr))
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
mod ns16550a;
|
||||
|
||||
#[cfg(feature = "board_qemu")]
|
||||
use crate::board::CharDeviceImpl;
|
||||
use alloc::sync::Arc;
|
||||
use lazy_static::*;
|
||||
@ -11,7 +10,7 @@ pub trait CharDevice {
|
||||
fn write(&self, ch: u8);
|
||||
fn handle_irq(&self);
|
||||
}
|
||||
#[cfg(feature = "board_qemu")]
|
||||
|
||||
lazy_static! {
|
||||
pub static ref UART: Arc<CharDeviceImpl> = Arc::new(CharDeviceImpl::new());
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::drivers::bus::virtio::VirtioHal;
|
||||
use crate::sync::UPIntrFreeCell;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use core::any::Any;
|
||||
@ -5,25 +6,26 @@ use embedded_graphics::pixelcolor::Rgb888;
|
||||
use tinybmp::Bmp;
|
||||
use virtio_drivers::{VirtIOGpu, VirtIOHeader};
|
||||
const VIRTIO7: usize = 0x10007000;
|
||||
pub trait GPUDevice: Send + Sync + Any {
|
||||
pub trait GpuDevice: Send + Sync + Any {
|
||||
fn update_cursor(&self);
|
||||
fn getfreambuffer(&self) -> &mut [u8];
|
||||
fn get_framebuffer(&self) -> &mut [u8];
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
lazy_static::lazy_static!(
|
||||
pub static ref GPU_DEVICE: Arc<dyn GPUDevice> = Arc::new(VirtIOGPU::new());
|
||||
pub static ref GPU_DEVICE: Arc<dyn GpuDevice> = Arc::new(VirtIOGpuWrapper::new());
|
||||
);
|
||||
|
||||
pub struct VirtIOGPU {
|
||||
gpu: UPIntrFreeCell<VirtIOGpu<'static>>,
|
||||
pub struct VirtIOGpuWrapper {
|
||||
gpu: UPIntrFreeCell<VirtIOGpu<'static, VirtioHal>>,
|
||||
fb: &'static [u8],
|
||||
}
|
||||
static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp");
|
||||
impl VirtIOGPU {
|
||||
impl VirtIOGpuWrapper {
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
let mut virtio = VirtIOGpu::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
|
||||
let mut virtio =
|
||||
VirtIOGpu::<VirtioHal>::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap();
|
||||
|
||||
let fbuffer = virtio.setup_framebuffer().unwrap();
|
||||
let len = fbuffer.len();
|
||||
@ -52,11 +54,11 @@ impl VirtIOGPU {
|
||||
}
|
||||
}
|
||||
|
||||
impl GPUDevice for VirtIOGPU {
|
||||
impl GpuDevice for VirtIOGpuWrapper {
|
||||
fn flush(&self) {
|
||||
self.gpu.exclusive_access().flush().unwrap();
|
||||
}
|
||||
fn getfreambuffer(&self) -> &mut [u8] {
|
||||
fn get_framebuffer(&self) -> &mut [u8] {
|
||||
unsafe {
|
||||
let ptr = self.fb.as_ptr() as *const _ as *mut u8;
|
||||
core::slice::from_raw_parts_mut(ptr, self.fb.len())
|
||||
|
@ -1,74 +1,75 @@
|
||||
use crate::drivers::bus::virtio::VirtioHal;
|
||||
use crate::{
|
||||
gui::{Button, Component},
|
||||
gui::{move_rect, reset},
|
||||
sync::UPIntrFreeCell,
|
||||
syscall::PAD,
|
||||
};
|
||||
use alloc::{string::ToString, sync::Arc};
|
||||
use alloc::sync::Arc;
|
||||
use core::any::Any;
|
||||
use embedded_graphics::{
|
||||
prelude::{Point, Size},
|
||||
text::Text,
|
||||
};
|
||||
use k210_hal::cache::Uncache;
|
||||
use virtio_drivers::{VirtIOHeader, VirtIOInput};
|
||||
use virtio_input_decoder::{Decoder, Key, KeyType};
|
||||
|
||||
use super::GPU_DEVICE;
|
||||
|
||||
const VIRTIO5: usize = 0x10005000;
|
||||
const VIRTIO6: usize = 0x10006000;
|
||||
|
||||
struct VirtIOINPUT(UPIntrFreeCell<VirtIOInput<'static>>);
|
||||
struct VirtIOInputWrapper(UPIntrFreeCell<VirtIOInput<'static, VirtioHal>>);
|
||||
|
||||
pub trait INPUTDevice: Send + Sync + Any {
|
||||
pub trait InputDevice: Send + Sync + Any {
|
||||
fn handle_irq(&self);
|
||||
}
|
||||
|
||||
lazy_static::lazy_static!(
|
||||
pub static ref KEYBOARD_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO5));
|
||||
pub static ref MOUSE_DEVICE: Arc<dyn INPUTDevice> = Arc::new(VirtIOINPUT::new(VIRTIO6));
|
||||
pub static ref KEYBOARD_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO5));
|
||||
pub static ref MOUSE_DEVICE: Arc<dyn InputDevice> = Arc::new(VirtIOInputWrapper::new(VIRTIO6));
|
||||
);
|
||||
|
||||
impl VirtIOINPUT {
|
||||
impl VirtIOInputWrapper {
|
||||
pub fn new(addr: usize) -> Self {
|
||||
Self(unsafe {
|
||||
UPIntrFreeCell::new(VirtIOInput::new(&mut *(addr as *mut VirtIOHeader)).unwrap())
|
||||
UPIntrFreeCell::new(
|
||||
VirtIOInput::<VirtioHal>::new(&mut *(addr as *mut VirtIOHeader)).unwrap(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl INPUTDevice for VirtIOINPUT {
|
||||
impl InputDevice for VirtIOInputWrapper {
|
||||
fn handle_irq(&self) {
|
||||
let mut input = self.0.exclusive_access();
|
||||
input.ack_interrupt();
|
||||
let event = input.pop_pending_event().unwrap();
|
||||
let dtype = match Decoder::decode(
|
||||
event.event_type as usize,
|
||||
event.code as usize,
|
||||
event.value as usize,
|
||||
) {
|
||||
Ok(dtype) => dtype,
|
||||
Err(_) => return,
|
||||
};
|
||||
match dtype {
|
||||
virtio_input_decoder::DecodeType::Key(key, r#type) => {
|
||||
println!("{:?} {:?}", key, r#type);
|
||||
if r#type == KeyType::Press {
|
||||
let mut inner = PAD.exclusive_access();
|
||||
let a = inner.as_ref().unwrap();
|
||||
match key.to_char() {
|
||||
Ok(mut k) => {
|
||||
if k == '\r' {
|
||||
a.repaint(k.to_string() + "\n")
|
||||
} else {
|
||||
a.repaint(k.to_string())
|
||||
while let Some(event) = input.pop_pending_event() {
|
||||
let dtype = match Decoder::decode(
|
||||
event.event_type as usize,
|
||||
event.code as usize,
|
||||
event.value as usize,
|
||||
) {
|
||||
Ok(dtype) => dtype,
|
||||
Err(_) => break,
|
||||
};
|
||||
match dtype {
|
||||
virtio_input_decoder::DecodeType::Key(key, r#type) => {
|
||||
if r#type == KeyType::Press {
|
||||
match key {
|
||||
Key::C | Key::MouseLeft => {
|
||||
reset();
|
||||
}
|
||||
Key::W => {
|
||||
move_rect(0, -10);
|
||||
}
|
||||
Key::S => {
|
||||
move_rect(0, 10);
|
||||
}
|
||||
Key::A => {
|
||||
move_rect(-10, 0);
|
||||
}
|
||||
Key::D => {
|
||||
move_rect(10, 0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
virtio_input_decoder::DecodeType::Mouse(mouse) => println!("{:?}", mouse),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
pub mod block;
|
||||
pub mod bus;
|
||||
pub mod chardev;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub mod gpu;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub mod input;
|
||||
pub mod plic;
|
||||
|
||||
pub use block::BLOCK_DEVICE;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub use bus::*;
|
||||
pub use chardev::UART;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub use gpu::*;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub use input::*;
|
||||
|
@ -5,8 +5,8 @@ _start:
|
||||
call rust_main
|
||||
|
||||
.section .bss.stack
|
||||
.globl boot_stack
|
||||
boot_stack:
|
||||
.globl boot_stack_lower_bound
|
||||
boot_stack_lower_bound:
|
||||
.space 4096 * 16
|
||||
.globl boot_stack_top
|
||||
boot_stack_top:
|
||||
|
@ -114,36 +114,40 @@ impl File for Pipe {
|
||||
}
|
||||
fn read(&self, buf: UserBuffer) -> usize {
|
||||
assert!(self.readable());
|
||||
let want_to_read = buf.len();
|
||||
let mut buf_iter = buf.into_iter();
|
||||
let mut read_size = 0usize;
|
||||
let mut already_read = 0usize;
|
||||
loop {
|
||||
let mut ring_buffer = self.buffer.exclusive_access();
|
||||
let loop_read = ring_buffer.available_read();
|
||||
if loop_read == 0 {
|
||||
if ring_buffer.all_write_ends_closed() {
|
||||
return read_size;
|
||||
return already_read;
|
||||
}
|
||||
drop(ring_buffer);
|
||||
suspend_current_and_run_next();
|
||||
continue;
|
||||
}
|
||||
// read at most loop_read bytes
|
||||
for _ in 0..loop_read {
|
||||
if let Some(byte_ref) = buf_iter.next() {
|
||||
unsafe {
|
||||
*byte_ref = ring_buffer.read_byte();
|
||||
}
|
||||
read_size += 1;
|
||||
already_read += 1;
|
||||
if already_read == want_to_read {
|
||||
return want_to_read;
|
||||
}
|
||||
} else {
|
||||
return read_size;
|
||||
return already_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write(&self, buf: UserBuffer) -> usize {
|
||||
assert!(self.writable());
|
||||
let want_to_write = buf.len();
|
||||
let mut buf_iter = buf.into_iter();
|
||||
let mut write_size = 0usize;
|
||||
let mut already_write = 0usize;
|
||||
loop {
|
||||
let mut ring_buffer = self.buffer.exclusive_access();
|
||||
let loop_write = ring_buffer.available_write();
|
||||
@ -156,11 +160,14 @@ impl File for Pipe {
|
||||
for _ in 0..loop_write {
|
||||
if let Some(byte_ref) = buf_iter.next() {
|
||||
ring_buffer.write_byte(unsafe { *byte_ref });
|
||||
write_size += 1;
|
||||
already_write += 1;
|
||||
if already_write == want_to_write {
|
||||
return want_to_write;
|
||||
}
|
||||
} else {
|
||||
return write_size;
|
||||
return already_write;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
use super::File;
|
||||
use crate::drivers::chardev::CharDevice;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
use crate::drivers::chardev::UART;
|
||||
use crate::mm::UserBuffer;
|
||||
#[cfg(feature = "board_k210")]
|
||||
use crate::sbi::console_getchar;
|
||||
#[cfg(feature = "board_k210")]
|
||||
use crate::task::suspend_current_and_run_next;
|
||||
|
||||
pub struct Stdin;
|
||||
pub struct Stdout;
|
||||
@ -18,7 +13,6 @@ impl File for Stdin {
|
||||
fn writable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
#[cfg(feature = "board_qemu")]
|
||||
fn read(&self, mut user_buf: UserBuffer) -> usize {
|
||||
assert_eq!(user_buf.len(), 1);
|
||||
//println!("before UART.read() in Stdin::read()");
|
||||
@ -28,27 +22,6 @@ impl File for Stdin {
|
||||
}
|
||||
1
|
||||
}
|
||||
#[cfg(feature = "board_k210")]
|
||||
fn read(&self, mut user_buf: UserBuffer) -> usize {
|
||||
assert_eq!(user_buf.len(), 1);
|
||||
// busy loop
|
||||
let mut c: usize;
|
||||
loop {
|
||||
c = console_getchar();
|
||||
if c == 0 {
|
||||
suspend_current_and_run_next();
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let ch = c as u8;
|
||||
unsafe {
|
||||
user_buf.buffers[0].as_mut_ptr().write_volatile(ch);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn write(&self, _user_buf: UserBuffer) -> usize {
|
||||
panic!("Cannot write to stdin!");
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -5,14 +5,14 @@ use embedded_graphics::{
|
||||
prelude::{OriginDimensions, Point, RgbColor, Size},
|
||||
};
|
||||
|
||||
use crate::drivers::{GPUDevice, GPU_DEVICE,};
|
||||
use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES};
|
||||
use crate::board::VIRTGPU_XRES;
|
||||
use crate::drivers::{GpuDevice, GPU_DEVICE};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Graphics {
|
||||
pub size: Size,
|
||||
pub point: Point,
|
||||
pub drv: Arc<dyn GPUDevice>,
|
||||
pub drv: Arc<dyn GpuDevice>,
|
||||
}
|
||||
|
||||
impl Graphics {
|
||||
@ -23,6 +23,10 @@ impl Graphics {
|
||||
drv: GPU_DEVICE.clone(),
|
||||
}
|
||||
}
|
||||
pub fn reset(&self) {
|
||||
let fb = self.drv.get_framebuffer();
|
||||
fb.fill(0u8);
|
||||
}
|
||||
}
|
||||
|
||||
impl OriginDimensions for Graphics {
|
||||
@ -40,10 +44,12 @@ impl DrawTarget for Graphics {
|
||||
where
|
||||
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
||||
{
|
||||
let fb = self.drv.getfreambuffer();
|
||||
let fb = self.drv.get_framebuffer();
|
||||
|
||||
pixels.into_iter().for_each(|px| {
|
||||
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x) as usize * 4;
|
||||
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x)
|
||||
as usize
|
||||
* 4;
|
||||
if idx + 2 >= fb.len() {
|
||||
return;
|
||||
}
|
||||
|
@ -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 +1,5 @@
|
||||
mod button;
|
||||
mod graphic;
|
||||
mod icon;
|
||||
mod image;
|
||||
mod panel;
|
||||
mod terminal;
|
||||
use alloc::sync::Arc;
|
||||
pub use button::*;
|
||||
use core::any::Any;
|
||||
use embedded_graphics::prelude::{Point, Size};
|
||||
pub use graphic::*;
|
||||
pub use icon::*;
|
||||
pub use image::*;
|
||||
pub use panel::*;
|
||||
pub use terminal::*;
|
||||
mod paint;
|
||||
|
||||
pub trait Component: Send + Sync + Any {
|
||||
fn paint(&self);
|
||||
fn add(&self, comp: Arc<dyn Component>);
|
||||
fn bound(&self) -> (Size, Point);
|
||||
}
|
||||
use graphic::Graphics;
|
||||
pub use paint::{init_paint, move_rect, reset};
|
||||
|
62
os/src/gui/paint.rs
Normal file
62
os/src/gui/paint.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use super::Graphics;
|
||||
use crate::sync::UPIntrFreeCell;
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size};
|
||||
use embedded_graphics::primitives::Primitive;
|
||||
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
|
||||
use lazy_static::*;
|
||||
|
||||
const INIT_X: i32 = 640;
|
||||
const INIT_Y: i32 = 400;
|
||||
const RECT_SIZE: u32 = 40;
|
||||
|
||||
pub struct DrawingBoard {
|
||||
graphics: Graphics,
|
||||
latest_pos: Point,
|
||||
}
|
||||
|
||||
impl DrawingBoard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
graphics: Graphics::new(Size::new(1280, 800), Point::new(0, 0)),
|
||||
latest_pos: Point::new(INIT_X, INIT_Y),
|
||||
}
|
||||
}
|
||||
fn paint(&mut self) {
|
||||
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1))
|
||||
.draw(&mut self.graphics)
|
||||
.ok();
|
||||
}
|
||||
pub fn move_rect(&mut self, dx: i32, dy: i32) {
|
||||
self.latest_pos.x += dx;
|
||||
self.latest_pos.y += dy;
|
||||
self.paint();
|
||||
}
|
||||
pub fn reset(&mut self) {
|
||||
self.latest_pos = Point::new(INIT_X, INIT_Y);
|
||||
self.graphics.reset();
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DRAWING_BOARD: UPIntrFreeCell<DrawingBoard> = unsafe { UPIntrFreeCell::new(DrawingBoard::new()) };
|
||||
}
|
||||
|
||||
pub fn init_paint() {
|
||||
DRAWING_BOARD.exclusive_session(|ripple| {
|
||||
ripple.paint();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn move_rect(dx: i32, dy: i32) {
|
||||
DRAWING_BOARD.exclusive_session(|ripple| {
|
||||
ripple.move_rect(dx, dy);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn reset() {
|
||||
DRAWING_BOARD.exclusive_session(|ripple| {
|
||||
ripple.reset();
|
||||
});
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#![no_main]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(alloc_error_handler)]
|
||||
#[cfg(feature = "board_qemu")]
|
||||
|
||||
use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE};
|
||||
|
||||
extern crate alloc;
|
||||
@ -10,10 +10,6 @@ extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
#[cfg(feature = "board_k210")]
|
||||
#[path = "boards/k210.rs"]
|
||||
mod board;
|
||||
#[cfg(not(any(feature = "board_k210")))]
|
||||
#[path = "boards/qemu.rs"]
|
||||
mod board;
|
||||
|
||||
@ -22,7 +18,6 @@ mod console;
|
||||
mod config;
|
||||
mod drivers;
|
||||
mod fs;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
mod gui;
|
||||
mod lang_items;
|
||||
mod mm;
|
||||
@ -33,7 +28,7 @@ mod task;
|
||||
mod timer;
|
||||
mod trap;
|
||||
|
||||
// use syscall::create_desktop; //for test
|
||||
//use syscall::create_desktop; //for test
|
||||
|
||||
core::arch::global_asm!(include_str!("entry.asm"));
|
||||
|
||||
@ -61,21 +56,18 @@ pub fn rust_main() -> ! {
|
||||
clear_bss();
|
||||
mm::init();
|
||||
println!("KERN: init gpu");
|
||||
#[cfg(feature = "board_qemu")]
|
||||
GPU_DEVICE.clone();
|
||||
let _gpu = GPU_DEVICE.clone();
|
||||
println!("KERN: init keyboard");
|
||||
#[cfg(feature = "board_qemu")]
|
||||
KEYBOARD_DEVICE.clone();
|
||||
let _keyboard = KEYBOARD_DEVICE.clone();
|
||||
println!("KERN: init mouse");
|
||||
#[cfg(feature = "board_qemu")]
|
||||
MOUSE_DEVICE.clone();
|
||||
let _mouse = MOUSE_DEVICE.clone();
|
||||
println!("KERN: init trap");
|
||||
trap::init();
|
||||
trap::enable_timer_interrupt();
|
||||
timer::set_next_trigger();
|
||||
board::device_init();
|
||||
fs::list_apps();
|
||||
//syscall::create_desktop(); //for test
|
||||
gui::init_paint();
|
||||
task::add_initproc();
|
||||
*DEV_NON_BLOCKING_ACCESS.exclusive_access() = true;
|
||||
task::run_tasks();
|
||||
|
@ -48,7 +48,7 @@ impl StackFrameAllocator {
|
||||
pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) {
|
||||
self.current = l.0;
|
||||
self.end = r.0;
|
||||
println!("last {} Physical Frames.", self.end - self.current);
|
||||
// println!("last {} Physical Frames.", self.end - self.current);
|
||||
}
|
||||
}
|
||||
impl FrameAllocator for StackFrameAllocator {
|
||||
|
@ -92,14 +92,14 @@ impl MemorySet {
|
||||
// map trampoline
|
||||
memory_set.map_trampoline();
|
||||
// map kernel sections
|
||||
println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
|
||||
println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
|
||||
println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
|
||||
println!(
|
||||
".bss [{:#x}, {:#x})",
|
||||
sbss_with_stack as usize, ebss as usize
|
||||
);
|
||||
println!("mapping .text section");
|
||||
// println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
|
||||
// println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
|
||||
// println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
|
||||
// println!(
|
||||
// ".bss [{:#x}, {:#x})",
|
||||
// sbss_with_stack as usize, ebss as usize
|
||||
// );
|
||||
// println!("mapping .text section");
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
(stext as usize).into(),
|
||||
@ -109,7 +109,7 @@ impl MemorySet {
|
||||
),
|
||||
None,
|
||||
);
|
||||
println!("mapping .rodata section");
|
||||
// println!("mapping .rodata section");
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
(srodata as usize).into(),
|
||||
@ -119,7 +119,7 @@ impl MemorySet {
|
||||
),
|
||||
None,
|
||||
);
|
||||
println!("mapping .data section");
|
||||
// println!("mapping .data section");
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
(sdata as usize).into(),
|
||||
@ -129,7 +129,7 @@ impl MemorySet {
|
||||
),
|
||||
None,
|
||||
);
|
||||
println!("mapping .bss section");
|
||||
// println!("mapping .bss section");
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
(sbss_with_stack as usize).into(),
|
||||
@ -139,7 +139,7 @@ impl MemorySet {
|
||||
),
|
||||
None,
|
||||
);
|
||||
println!("mapping physical memory");
|
||||
// println!("mapping physical memory");
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
(ekernel as usize).into(),
|
||||
@ -149,7 +149,7 @@ impl MemorySet {
|
||||
),
|
||||
None,
|
||||
);
|
||||
println!("mapping memory-mapped registers");
|
||||
//println!("mapping memory-mapped registers");
|
||||
for pair in MMIO {
|
||||
memory_set.push(
|
||||
MapArea::new(
|
||||
|
@ -40,13 +40,7 @@ pub fn console_getchar() -> usize {
|
||||
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "board_qemu")]
|
||||
use crate::board::QEMUExit;
|
||||
pub fn shutdown(exit_code: usize) -> ! {
|
||||
#[cfg(feature = "board_k210")]
|
||||
sbi_call(SBI_SHUTDOWN, exit_code, 0, 0);
|
||||
#[cfg(feature = "board_qemu")]
|
||||
crate::board::QEMU_EXIT_HANDLE.exit_failure();
|
||||
#[cfg(feature = "board_k210")]
|
||||
panic!("It should shutdown!");
|
||||
crate::board::QEMU_EXIT_HANDLE.exit_failure()
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
use alloc::{string::ToString, sync::Arc, vec::Vec};
|
||||
use embedded_graphics::{
|
||||
prelude::{Point, Size},
|
||||
primitives::arc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fs::ROOT_INODE,
|
||||
gui::{Button, Component, IconController, ImageComp, Panel, Terminal},
|
||||
sync::UPIntrFreeCell,
|
||||
};
|
||||
|
||||
use crate::board::{VIRTGPU_XRES, VIRTGPU_YRES};
|
||||
|
||||
static DT: &[u8] = include_bytes!("../assert/desktop.bmp");
|
||||
|
||||
lazy_static::lazy_static!(
|
||||
pub static ref DESKTOP:UPIntrFreeCell<Arc<dyn Component>> = unsafe {
|
||||
UPIntrFreeCell::new(Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0))))
|
||||
};
|
||||
pub static ref PAD:UPIntrFreeCell<Option<Arc<Terminal>>> = unsafe {
|
||||
UPIntrFreeCell::new(None)
|
||||
};
|
||||
);
|
||||
|
||||
pub fn create_desktop() -> isize {
|
||||
let mut p: Arc<dyn Component + 'static> =
|
||||
Arc::new(Panel::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0)));
|
||||
let image = ImageComp::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES), Point::new(0, 0), DT, Some(p.clone()));
|
||||
let icon = IconController::new(ROOT_INODE.ls(), Some(p.clone()));
|
||||
p.add(Arc::new(image));
|
||||
p.add(Arc::new(icon));
|
||||
let mut desktop = DESKTOP.exclusive_access();
|
||||
*desktop = p;
|
||||
desktop.paint();
|
||||
drop(desktop);
|
||||
create_terminal();
|
||||
1
|
||||
}
|
||||
|
||||
pub fn create_terminal() {
|
||||
let desktop = DESKTOP.exclusive_access();
|
||||
let arc_t = Arc::new(Terminal::new(
|
||||
Size::new(400, 400),
|
||||
Point::new(200, 100),
|
||||
Some(desktop.clone()),
|
||||
Some("demo.txt".to_string()),
|
||||
"".to_string(),
|
||||
));
|
||||
let text = Panel::new(Size::new(400, 400), Point::new(200, 100));
|
||||
let button = Button::new(
|
||||
Size::new(20, 20),
|
||||
Point::new(370, 10),
|
||||
Some(arc_t.clone()),
|
||||
"X".to_string(),
|
||||
);
|
||||
arc_t.add(Arc::new(text));
|
||||
arc_t.add(Arc::new(button));
|
||||
arc_t.paint();
|
||||
desktop.add(arc_t.clone());
|
||||
let mut pad = PAD.exclusive_access();
|
||||
*pad = Some(arc_t);
|
||||
}
|
@ -25,59 +25,17 @@ const SYSCALL_SEMAPHORE_DOWN: usize = 1022;
|
||||
const SYSCALL_CONDVAR_CREATE: usize = 1030;
|
||||
const SYSCALL_CONDVAR_SIGNAL: usize = 1031;
|
||||
const SYSCALL_CONDVAR_WAIT: usize = 1032;
|
||||
const SYSCALL_CREATE_DESKTOP: usize = 2000;
|
||||
|
||||
mod fs;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
mod gui;
|
||||
mod process;
|
||||
mod sync;
|
||||
mod thread;
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub use self::gui::create_desktop;
|
||||
use fs::*;
|
||||
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub use gui::PAD;
|
||||
use fs::*;
|
||||
use process::*;
|
||||
use sync::*;
|
||||
use thread::*;
|
||||
|
||||
#[cfg(feature = "board_qemu")]
|
||||
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
||||
match syscall_id {
|
||||
SYSCALL_DUP => sys_dup(args[0]),
|
||||
SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32),
|
||||
SYSCALL_CLOSE => sys_close(args[0]),
|
||||
SYSCALL_PIPE => sys_pipe(args[0] as *mut usize),
|
||||
SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]),
|
||||
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
|
||||
SYSCALL_EXIT => sys_exit(args[0] as i32),
|
||||
SYSCALL_SLEEP => sys_sleep(args[0]),
|
||||
SYSCALL_YIELD => sys_yield(),
|
||||
SYSCALL_KILL => sys_kill(args[0], args[1] as u32),
|
||||
SYSCALL_GET_TIME => sys_get_time(),
|
||||
SYSCALL_GETPID => sys_getpid(),
|
||||
SYSCALL_FORK => sys_fork(),
|
||||
SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize),
|
||||
SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32),
|
||||
SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]),
|
||||
SYSCALL_GETTID => sys_gettid(),
|
||||
SYSCALL_WAITTID => sys_waittid(args[0]) as isize,
|
||||
SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1),
|
||||
SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]),
|
||||
SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]),
|
||||
SYSCALL_SEMAPHORE_CREATE => sys_semaphore_create(args[0]),
|
||||
SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]),
|
||||
SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]),
|
||||
SYSCALL_CONDVAR_CREATE => sys_condvar_create(args[0]),
|
||||
SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]),
|
||||
SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]),
|
||||
SYSCALL_CREATE_DESKTOP => create_desktop(),
|
||||
_ => panic!("Unsupported syscall_id: {}", syscall_id),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "board_k210")]
|
||||
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
||||
match syscall_id {
|
||||
SYSCALL_DUP => sys_dup(args[0]),
|
||||
|
@ -56,7 +56,6 @@ pub fn block_current_and_run_next() {
|
||||
let task_cx_ptr = block_current_task();
|
||||
schedule(task_cx_ptr);
|
||||
}
|
||||
#[cfg(feature = "board_qemu")]
|
||||
use crate::board::QEMUExit;
|
||||
|
||||
pub fn exit_current_and_run_next(exit_code: i32) {
|
||||
@ -75,7 +74,6 @@ pub fn exit_current_and_run_next(exit_code: i32) {
|
||||
// the process should terminate at once
|
||||
if tid == 0 {
|
||||
let pid = process.getpid();
|
||||
#[cfg(feature = "board_qemu")]
|
||||
if pid == IDLE_PID {
|
||||
println!(
|
||||
"[kernel] Idle process exit with exit_code {} ...",
|
||||
|
@ -61,13 +61,14 @@ pub fn add_timer(expire_ms: usize, task: Arc<TaskControlBlock>) {
|
||||
|
||||
pub fn check_timer() {
|
||||
let current_ms = get_time_ms();
|
||||
let mut timers = TIMERS.exclusive_access();
|
||||
while let Some(timer) = timers.peek() {
|
||||
if timer.expire_ms <= current_ms {
|
||||
add_task(Arc::clone(&timer.task));
|
||||
timers.pop();
|
||||
} else {
|
||||
break;
|
||||
TIMERS.exclusive_session(|timers| {
|
||||
while let Some(timer) = timers.peek() {
|
||||
if timer.expire_ms <= current_ms {
|
||||
add_task(Arc::clone(&timer.task));
|
||||
timers.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ pub fn trap_handler() -> ! {
|
||||
set_kernel_trap_entry();
|
||||
let scause = scause::read();
|
||||
let stval = stval::read();
|
||||
//println!("into {:?}", scause.cause());
|
||||
// println!("into {:?}", scause.cause());
|
||||
match scause.cause() {
|
||||
Trap::Exception(Exception::UserEnvCall) => {
|
||||
// jump to next instruction anyway
|
||||
|
@ -1 +0,0 @@
|
||||
nightly-2022-04-11
|
5
rust-toolchain.toml
Normal file
5
rust-toolchain.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[toolchain]
|
||||
profile = "minimal"
|
||||
# use the nightly version of the last stable toolchain, see <https://forge.rust-lang.org/>
|
||||
channel = "nightly-2022-08-05"
|
||||
components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"]
|
@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf"
|
||||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
"-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
|
||||
]
|
||||
|
@ -13,3 +13,7 @@ riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
# [features]
|
||||
# board_qemu = []
|
||||
# board_k210 = []
|
@ -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
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{fmt::format, string::String, vec::Vec};
|
||||
use alloc::{fmt::format, vec::Vec};
|
||||
use user_lib::{close, get_time, gettid, open, write, OpenFlags};
|
||||
use user_lib::{exit, thread_create, waittid};
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(asm)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
@ -8,7 +8,7 @@ use user_lib::{exec, fork, wait};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for i in 0..50 {
|
||||
for i in 0..5 {
|
||||
if fork() == 0 {
|
||||
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
|
||||
} else {
|
||||
|
@ -4,9 +4,12 @@
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
fn f(d: usize) {
|
||||
println!("d = {}", d);
|
||||
f(d + 1);
|
||||
#[allow(unconditional_recursion)]
|
||||
fn f(depth: usize) {
|
||||
if depth % 10 == 0 {
|
||||
println!("depth = {}", depth);
|
||||
}
|
||||
f(depth + 1);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
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
|
||||
}
|
@ -198,9 +198,7 @@ pub fn condvar_signal(condvar_id: usize) {
|
||||
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
|
||||
sys_condvar_wait(condvar_id, mutex_id);
|
||||
}
|
||||
pub fn create_desktop() {
|
||||
sys_create_desktop();
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vstore {
|
||||
($var_ref: expr, $value: expr) => {
|
||||
|
@ -154,6 +154,3 @@ pub fn sys_condvar_signal(condvar_id: usize) -> isize {
|
||||
pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize {
|
||||
syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0])
|
||||
}
|
||||
pub fn sys_create_desktop() -> isize {
|
||||
syscall(2000, [0, 0, 0])
|
||||
}
|
Loading…
Reference in New Issue
Block a user