Commit 2411f989 authored by k1p123456's avatar k1p123456
Browse files

initial

parents
No related merge requests found
Showing with 1556 additions and 0 deletions
+1556 -0
.gitignore 0 → 100644
tags
fs.img
xv6-user/_*
xv6-user/*.d
xv6-user/*.o
xv6-user/*.asm
xv6-user/*.sym
xv6-user/initcode
xv6-user/initcode.out
xv6-user/usys.S
kernel/*.back.c
kernel/*.example.c
kernel/*.d
kernel/*.o
kernel/include/*.back.h
kernel/include/*.example.h
target/*
LICENSE 0 → 100644
MIT License
Copyright (c) 2020 SKTT1Ryze/hustccc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
Makefile 0 → 100644
platform := k210
#platform := qemu
# mode := debug
mode := release
K=kernel
U=xv6-user
T=target
OBJS =
ifeq ($(platform), k210)
OBJS += $K/entry_k210.o
else
OBJS += $K/entry_qemu.o
endif
OBJS += \
$K/printf.o \
$K/kalloc.o \
$K/intr.o \
$K/spinlock.o \
$K/string.o \
$K/main.o \
$K/vm.o \
$K/proc.o \
$K/swtch.o \
$K/trampoline.o \
$K/trap.o \
$K/syscall.o \
$K/sysproc.o \
$K/bio.o \
$K/sleeplock.o \
$K/file.o \
$K/pipe.o \
$K/exec.o \
$K/sysfile.o \
$K/kernelvec.o \
$K/timer.o \
$K/disk.o \
$K/fat32.o \
$K/plic.o \
$K/console.o
ifeq ($(platform), k210)
OBJS += \
$K/spi.o \
$K/gpiohs.o \
$K/fpioa.o \
$K/utils.o \
$K/sdcard.o \
$K/dmac.o \
$K/sysctl.o \
else
OBJS += \
$K/virtio_disk.o \
#$K/uart.o \
endif
QEMU = qemu-system-riscv64
ifeq ($(platform), k210)
RUSTSBI = ./bootloader/SBI/sbi-k210
else
RUSTSBI = ./bootloader/SBI/sbi-qemu
endif
# TOOLPREFIX := riscv64-unknown-elf-
TOOLPREFIX := riscv64-linux-gnu-
CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -g
CFLAGS += -MD
CFLAGS += -mcmodel=medany
CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax
CFLAGS += -I.
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ifeq ($(mode), debug)
CFLAGS += -DDEBUG
endif
ifeq ($(platform), qemu)
CFLAGS += -D QEMU
endif
LDFLAGS = -z max-page-size=4096
ifeq ($(platform), k210)
linker = ./linker/k210.ld
endif
ifeq ($(platform), qemu)
linker = ./linker/qemu.ld
endif
# Compile Kernel
$T/kernel: $(OBJS) $(linker) $U/initcode
@if [ ! -d "./target" ]; then mkdir target; fi
@$(LD) $(LDFLAGS) -T $(linker) -o $T/kernel $(OBJS)
@$(OBJDUMP) -S $T/kernel > $T/kernel.asm
@$(OBJDUMP) -t $T/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $T/kernel.sym
build: $T/kernel userprogs
# Compile RustSBI
RUSTSBI:
ifeq ($(platform), k210)
@cd ./bootloader/SBI/rustsbi-k210 && cargo build && cp ./target/riscv64gc-unknown-none-elf/debug/rustsbi-k210 ../sbi-k210
@$(OBJDUMP) -S ./bootloader/SBI/sbi-k210 > $T/rustsbi-k210.asm
else
@cd ./bootloader/SBI/rustsbi-qemu && cargo build && cp ./target/riscv64gc-unknown-none-elf/debug/rustsbi-qemu ../sbi-qemu
@$(OBJDUMP) -S ./bootloader/SBI/sbi-qemu > $T/rustsbi-qemu.asm
endif
rustsbi-clean:
@cd ./bootloader/SBI/rustsbi-k210 && cargo clean
@cd ./bootloader/SBI/rustsbi-qemu && cargo clean
image = $T/kernel.bin
k210 = $T/k210.bin
k210-serialport := /dev/ttyUSB0
ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -machine virt -kernel $T/kernel -m 8M -nographic
# use multi-core
QEMUOPTS += -smp $(CPUS)
QEMUOPTS += -bios $(RUSTSBI)
# import virtual disk image
QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
run: build
ifeq ($(platform), k210)
@$(OBJCOPY) $T/kernel --strip-all -O binary $(image)
@$(OBJCOPY) $(RUSTSBI) --strip-all -O binary $(k210)
@dd if=$(image) of=$(k210) bs=128k seek=1
@$(OBJDUMP) -D -b binary -m riscv $(k210) > $T/k210.asm
@sudo chmod 777 $(k210-serialport)
@python3 ./tools/kflash.py -p $(k210-serialport) -b 1500000 -t $(k210)
else
@$(QEMU) $(QEMUOPTS)
endif
$U/initcode: $U/initcode.S
$(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o
$(OBJCOPY) -S -O binary $U/initcode.out $U/initcode
$(OBJDUMP) -S $U/initcode.o > $U/initcode.asm
tags: $(OBJS) _init
@etags *.S *.c
ULIB = $U/ulib.o $U/usys.o $U/printf.o $U/umalloc.o
_%: %.o $(ULIB)
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
$(OBJDUMP) -S $@ > $*.asm
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
$U/usys.S : $U/usys.pl
@perl $U/usys.pl > $U/usys.S
$U/usys.o : $U/usys.S
$(CC) $(CFLAGS) -c -o $U/usys.o $U/usys.S
$U/_forktest: $U/forktest.o $(ULIB)
# forktest has less library code linked in - needs to be small
# in order to be able to max out the proc table.
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o
$(OBJDUMP) -S $U/_forktest > $U/forktest.asm
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
# that disk image changes after first build are persistent until clean. More
# details:
# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
.PRECIOUS: %.o
UPROGS=\
$U/_init\
$U/_sh\
$U/_cat\
$U/_echo\
$U/_grep\
$U/_ls\
$U/_kill\
$U/_mkdir\
$U/_xargs\
$U/_sleep\
$U/_find\
$U/_rm\
$U/_wc\
$U/_test\
$U/_usertests\
$U/_strace\
$U/_mv\
# $U/_forktest\
# $U/_ln\
# $U/_stressfs\
# $U/_grind\
# $U/_zombie\
userprogs: $(UPROGS)
dst=/mnt
# @sudo cp $U/_init $(dst)/init
# @sudo cp $U/_sh $(dst)/sh
# Make fs image
fs: $(UPROGS)
@if [ ! -f "fs.img" ]; then \
echo "making fs image..."; \
dd if=/dev/zero of=fs.img bs=512k count=512; \
mkfs.vfat -F 32 fs.img; fi
@sudo mount fs.img $(dst)
@if [ ! -d "$(dst)/bin" ]; then sudo mkdir $(dst)/bin; fi
@sudo cp README $(dst)/README
@for file in $$( ls $U/_* ); do \
sudo cp $$file $(dst)/$${file#$U/_};\
sudo cp $$file $(dst)/bin/$${file#$U/_}; done
@sudo umount $(dst)
# Write mounted sdcard
sdcard: userprogs
@if [ ! -d "$(dst)/bin" ]; then sudo mkdir $(dst)/bin; fi
@for file in $$( ls $U/_* ); do \
sudo cp $$file $(dst)/bin/$${file#$U/_}; done
@sudo cp $U/_init $(dst)/init
@sudo cp $U/_sh $(dst)/sh
@sudo cp README $(dst)/README
clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
*/*.o */*.d */*.asm */*.sym \
$T/* \
$U/initcode $U/initcode.out \
$K/kernel \
.gdbinit \
$U/usys.S \
$(UPROGS)
README 0 → 100644
xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
Version 6 (v6). xv6 loosely follows the structure and style of v6,
but is implemented for a modern RISC-V multiprocessor using ANSI C.
ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.828/, which
provides pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching,
locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements.
We are also grateful for the bug reports and patches contributed by
Silas Boyd-Wickizer, Anton Burtsev, Dan Cross, Cody Cutler, Mike CAT,
Tej Chajed, Asami Doi, eyalz800, , Nelson Elhage, Saar Ettinger, Alice
Ferrazzi, Nathaniel Filardo, Peter Froehlich, Yakir Goaron,Shivam
Handa, Bryan Henry, jaichenhengjie, Jim Huang, Alexander Kapshuk,
Anders Kaseorg, kehao95, Wolfgang Keller, Jonathan Kimmitt, Eddie
Kohler, Austin Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay,
Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider,
Greg Price, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya
Shigemitsu, Takahiro, Cam Tenny, tyfkda, Rafael Ubal, Warren Toomey,
Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas
Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang
Wei.
The code in the files that constitute xv6 is
Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.S081, so we are more interested in
simplifications and clarifications than new features.
BUILDING AND RUNNING XV6
You will need a RISC-V "newlib" tool chain from
https://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for
riscv64-softmmu. Once they are installed, and in your shell
search path, you can run "make qemu".
README.md 0 → 100644
# XV6-RISCV On K210
Run xv6-riscv on k210 board
[English](./README.md) | [中文](./README_cn.md)
```
(`-') (`-') <-.(`-')
(OO )_.-> _(OO ) __( OO)
(_| \_)--.,--.(_/,-.\ ,--. (`-') '-'. ,--. .----. .--. .----.
\ `.' / \ \ / (_/ / .' ( OO).-> | .' / \_,-. | /_ | / .. \
\ .') \ / / . / -. (,------. | /) .' .' | | | / \ .
.' \ _ \ /_)' .-. \ `------' | . ' .' /_ | | ' \ / '
/ .'. \ \-'\ / \ `-' / | |\ \ | | | | \ `' /
`--' '--' `-' `----' `--' '--' `------' `--' `---''
```
![run-k210](./img/xv6-k210_run.gif)
## Dependencies
+ `k210 board` or `qemu-system-riscv64`
+ RISC-V Toolchain: [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain.git)
## Installation
```bash
git clone https://github.com/HUST-OS/xv6-k210
```
## Run on k210 board
First you need to connect your k210 board to your PC.
And check the `USB serial port` (In my situation it will be `ttyUSB0`):
```bash
ls /dev/ | grep USB
```
Build the kernel and user program:
```bash
cd xv6-k210
make build
```
Instead of the original file system, xv6-k210 runs with FAT32. You might need an SD card with FAT32 format.
Your SD card should NOT keep a partition table. To start `shell` and other user programs, you need to copy them into your SD card.
First, connect and mount your SD card (SD card reader required).
```bash
ls /dev/ # To check your SD device
mount <your SD device name> <mount point>
make sdcard dst="SD card mount point"
umount <mount point>
```
Then, insert the SD card to your k210 board and run:
```bash
make run
```
Sometimes you should change the `USB serial port`:
```bash
make run k210-serialport=`Your-USB-port`(default by ttyUSB0)
```
Ps: Most of the k210-port in Linux is ttyUSB0, if you use Windows or Mac OS, this doc
may help you: [maixpy-doc](https://maixpy.sipeed.com/zh/get_started/env_install_driver.html#)
## Run on qemu-system-riscv64
First, make sure `qemu-system-riscv64` is installed on your system.
Second, make a disk image file with FAT32 file system.
```bash
make fs
```
It will generate a disk image file `fs.img`, and compile some user programs like `shell` then copy them into the `fs.img`.
As long as the `fs.img` exists, you don't need to do this every time before running, unless you want to update it.
Finally, start running.
```bash
make run platform=qemu
```
Ps: Press Ctrl + A then X to quit qemu.
## About shell
The shell commands are user programs, too. Those program should be put in a "/bin" directory in your SD card or the `fs.img`.
Now we support a few useful commands, such as `cd`, `ls`, `cat` and so on.
In addition, `shell` supports some shortcut keys as below:
- Ctrl-H -- backspace
- Ctrl-U -- kill a line
- Ctrl-D -- end of file (EOF)
- Ctrl-P -- print process list
## Add my programs on xv6-k210
1. Make a new C source file in `xv6-user/` like `myprog.c`, and put your codes;
2. You can include `user.h` to use the functions declared in it, such as `open`, `gets` and `printf`;
3. Add a line "`$U/_myprog\`" in `Makefile` as below:
```Makefile
UPROGS=\
$U/_init\
$U/_sh\
$U/_cat\
...
$U/_myprog\ # Don't ignore the leading '_'
```
4. Then make:
```bash
make userprogs
```
Now you might see `_myprog` in `xv6-user/` if no error detected. Finally you need to copy it into your SD (see [here](#run-on-k210-board))
or FS image (see [here](#run-on-qemu-system-riscv64)).
## Progress
- [x] Multicore boot
- [x] Bare-metal printf
- [x] Memory alloc
- [x] Page Table
- [x] Timer interrupt
- [x] S mode extern interrupt
- [x] Receive uarths message
- [x] SD card driver
- [x] Process management
- [x] File system
- [x] User program
- [X] Steady keyboard input(k210)
## TODO
Fix the bugs of U-mode exception on k210.
README_cn.md 0 → 100644
# XV6-RISCV On K210
`K210` 开发板上运行 `xv6-riscv` 操作系统
[English](./README.md) | [中文](./README_cn.md)
```
(`-') (`-') <-.(`-')
(OO )_.-> _(OO ) __( OO)
(_| \_)--.,--.(_/,-.\ ,--. (`-') '-'. ,--. .----. .--. .----.
\ `.' / \ \ / (_/ / .' ( OO).-> | .' / \_,-. | /_ | / .. \
\ .') \ / / . / -. (,------. | /) .' .' | | | / \ .
.' \ _ \ /_)' .-. \ `------' | . ' .' /_ | | ' \ / '
/ .'. \ \-'\ / \ `-' / | |\ \ | | | | \ `' /
`--' '--' `-' `----' `--' '--' `------' `--' `---''
```
<!-- ![run-k210](./img/xv6-k210_on_k210.gif) -->
## 依赖
+ `k210` 开发板或者 `qemu-system-riscv64`
+ RISC-V GCC 编译链: [riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain.git)
## 下载
```bash
git clone https://github.com/HUST-OS/xv6-k210
```
## <a id="title_k210">在 k210 开发板上运行</a>
首先您需要连接 `k210` 开发板到电脑,然后检查 USB 端口:
```bash
ls /dev/ | grep USB
```
在我的机器上的情况是将会显示 `ttyUSB0`,这就是 USB 端口。
然后运行以下命令,以编译内核和用户程序:
```bash
cd xv6-k210
make build
```
Xv6-k210 采用 FAT32 文件系统,而不是其原本的文件系统。您需要一张 FAT32 格式的 SD 卡才能运行。并且 SD 卡上不能有分区表。
为了能启动 `shell` 和其他用户程序,您需要将它们拷贝至 SD 卡中。
首先,需要将 SD 卡连至主机(需要读卡器)并进行挂载。
```bash
ls /dev/ # 确认您的 SD 卡设备名
mount <SD 卡设备名> <挂载点>
make sdcard dst="挂载点"
umount <挂载点>
```
然后,将 SD 卡接入 `k210` 并运行:
```bash
make run
```
某些情况下您需要修改 `USB 端口`,端口名称可以通过前面说的步骤得到,然后运行以下命令:
```bash
make run k210-serialport=`USB 端口`(默认是 ttyUSB0)
```
Ps: 在 `Linux` 上这个端口大部分情况是 `ttyUSB0`, 如果您使用 `Windows` 或者 `MacOS`,这个文档可以帮助到您:[maixpy-doc](https://maixpy.sipeed.com/zh/get_started/env_install_driver.html#)
## <a id="title_qemu">在 qemu-system-riscv64 模拟器上运行</a>
首先,确保 `qemu-system-riscv64` 已经下载到您的机器上并且加到了环境变量中;
其次,需要一个 FAT32 磁盘镜像文件;
```bash
make fs
```
这会生成一个镜像文件 `fs.img` ,编译一些用户程序(如 `shell`)并拷贝至镜像中。只要 `fs.img` 存在并且不需要修改,您不必每次运行前都执行这个命令。
最后,开始运行:
```bash
make run platform=qemu
```
Ps: 按 `Ctrl + A` 然后 `X` 退出 `qemu`
## 关于 Shell
Shell 命令其实也是用户程序。这些程序应当放置在 SD 卡或 `fs.img` 文件镜像的 "/bin" 目录下。
目前已经支持几个常用命令,如 `cd``ls``cat` 等。
此外,`shell` 支持下列快捷键:
- Ctrl-H -- 退格
- Ctrl-U -- 删除行
- Ctrl-D -- 文件尾(EOF)
- Ctrl-P -- 打印进程列表
## 添加用户程序
1.`xv6-user/` 目录下新建一个 C 文件,如 `myprog.c`,然后写入您的代码;
2. 您可以引入 `user.h` 头文件,以使用其中提供的函数,如 `open``gets``printf`等;
3.`Makefile` 中添加一行 “`$U/_myprog\`”,具体如下:
```Makefile
UPROGS=\
$U/_init\
$U/_sh\
$U/_cat\
...
$U/_myprog\ # 请不要忽略开头的 '_'
```
4. 然后执行:
```bash
make userprogs
```
如果没有出错,您应该可以在 `xv6-user/` 中看到 `_myprog` 文件。最后,您需要将它拷贝到SD卡(参考<a href="#title_k210">此处</a>)
或磁盘镜像(参考<a href="#title_qemu">此处</a>)中。
## 进度
- [x] 多核启动
- [x] 裸机 printf
- [x] 内存分配
- [x] 页表
- [x] 时钟中断
- [x] S 态外部中断
- [x] 接收 `UARTHS` 串口数据
- [x] SD card 驱动
- [x] 进程管理
- [x] 文件系统
- [x] 用户程序
- [X] 稳定的键盘输入(k210)
## TODO
解决用户态由于未知原因导致 panic 的 bug
[build]
target = "riscv64gc-unknown-none-elf"
[target.riscv64gc-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tlink-k210.ld",
]
[package]
name = "rustsbi-k210"
version = "0.1.0"
authors = ["hustccc <1276675421@qq.com>"]
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rustsbi = "0.1.1"
riscv = { git = "https://github.com/rust-embedded/riscv", features = ["inline-asm"] }
linked_list_allocator = "0.8"
k210-hal = { git = "https://github.com/riscv-rust/k210-hal" }
embedded-hal = "1.0.0-alpha.1"
lazy_static = {version = "1.1.0", features = ["spin_no_std"]}
spin = "0.7.1"
r0 = "1.0"
## RustSBI on Kendryte K210
Ref: https://github.com/luojia65/rustsbi/tree/master/platform/k210
This crate is the implementation of `RustSBI` on `kendryte K210` platform based on RustSBI `0.1.1` version.
More details in [RustSBI](https://github.com/luojia65/rustsbi).
// Ref: https://github.com/luojia65/rustsbi/blob/master/platform/k210/build.rs
use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
// Put the linker script somewhere the linker can find it
fs::File::create(out_dir.join("link-k210.ld"))
.unwrap()
.write_all(include_bytes!("link-k210.ld"))
.unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=link-k210.ld");
}
target := "riscv64gc-unknown-none-elf"
mode := "debug"
build-path := "./target/" + target + "/" + mode + "/"
m-firmware-file := build-path + "rustsbi-k210"
m-bin-file := build-path + "rustsbi-k210.bin"
objdump := "rust-objdump"
objcopy := "rust-objcopy --binary-architecture=riscv64"
build: firmware
@{{objcopy}} {{m-firmware-file}} --strip-all -O binary {{m-bin-file}}
firmware:
@cargo build --target={{target}}
asm: build
@{{objdump}} -D {{m-firmware-file}} | less
// Source: https://github.com/torvalds/linux/blob/master/arch/riscv/boot/dts/kendryte/k210.dtsi
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
* Copyright (C) 2020 Western Digital Corporation or its affiliates.
*/
/*
#define K210_CLK_PLL0 0
#define K210_CLK_PLL1 0
#define K210_CLK_ACLK 0
#define K210_CLK_CPU 0
*/
// todo: generate clock speed in sbi binary
/ {
/*
* Although the K210 is a 64-bit CPU, the address bus is only 32-bits
* wide, and the upper half of all addresses is ignored.
*/
#address-cells = <1>;
#size-cells = <1>;
compatible = "kendryte,k210";
aliases {
serial0 = &uarths0;
};
/*
* The K210 has an sv39 MMU following the priviledge specification v1.9.
* Since this is a non-ratified draft specification, the kernel does not
* support it and the K210 support enabled only for the !MMU case.
* Be consistent with this by setting the CPUs MMU type to "none".
*/
/* No, I want to use mmu in my application -- luojia65 */
cpus {
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <7800000>;
cpu0: cpu@0 {
device_type = "cpu";
reg = <0>;
compatible = "kendryte,k210", "sifive,rocket0", "riscv";
riscv,isa = "rv64imafdc";
mmu-type = "none";
i-cache-size = <0x8000>;
i-cache-block-size = <64>;
d-cache-size = <0x8000>;
d-cache-block-size = <64>;
clocks = <&sysctl K210_CLK_CPU>;
clock-frequency = <390000000>;
cpu0_intc: interrupt-controller {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "riscv,cpu-intc";
};
};
cpu1: cpu@1 {
device_type = "cpu";
reg = <1>;
compatible = "kendryte,k210", "sifive,rocket0", "riscv";
riscv,isa = "rv64imafdc";
mmu-type = "none";
i-cache-size = <0x8000>;
i-cache-block-size = <64>;
d-cache-size = <0x8000>;
d-cache-block-size = <64>;
clocks = <&sysctl K210_CLK_CPU>;
clock-frequency = <390000000>;
cpu1_intc: interrupt-controller {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "riscv,cpu-intc";
};
};
};
sram: memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x400000>,
<0x80400000 0x200000>,
<0x80600000 0x200000>;
reg-names = "sram0", "sram1", "aisram";
};
clocks {
in0: oscillator {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <26000000>;
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "kendryte,k210-soc", "simple-bus";
ranges;
interrupt-parent = <&plic0>;
sysctl: sysctl@50440000 {
compatible = "kendryte,k210-sysctl", "simple-mfd";
reg = <0x50440000 0x1000>;
#clock-cells = <1>;
};
clint0: interrupt-controller@2000000 {
compatible = "riscv,clint0";
reg = <0x2000000 0xC000>;
interrupts-extended = <&cpu0_intc 3>, <&cpu1_intc 3>;
clocks = <&sysctl K210_CLK_ACLK>;
};
plic0: interrupt-controller@c000000 {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "kendryte,k210-plic0", "riscv,plic0";
reg = <0xC000000 0x4000000>;
interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 0xffffffff>,
<&cpu1_intc 11>, <&cpu1_intc 0xffffffff>;
riscv,ndev = <65>;
riscv,max-priority = <7>;
};
uarths0: serial@38000000 {
compatible = "kendryte,k210-uarths", "sifive,uart0";
reg = <0x38000000 0x1000>;
interrupts = <33>;
clocks = <&sysctl K210_CLK_CPU>;
};
// todo: other peripherals -- luojia65
};
};
/* Ref: https://github.com/luojia65/rustsbi/blob/master/platform/k210/link-k210.ld */
MEMORY {
/* 存储单元的物理地址 */
SRAM : ORIGIN = 0x80000000, LENGTH = 128K
}
_max_hart_id = 1;
PROVIDE(_stext = 0x80000000);
PROVIDE(_heap_size = 32K);
PROVIDE(_hart_stack_size = 16K);
REGION_ALIAS("REGION_TEXT", SRAM);
REGION_ALIAS("REGION_RODATA", SRAM);
REGION_ALIAS("REGION_DATA", SRAM);
REGION_ALIAS("REGION_BSS", SRAM);
REGION_ALIAS("REGION_HEAP", SRAM);
REGION_ALIAS("REGION_STACK", SRAM);
OUTPUT_ARCH(riscv)
ENTRY(_start)
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
SECTIONS
{
/* .text 字段 */
.text _stext : {
/* 把 entry 函数放在最前面 */
*(.text.entry)
/* 要链接的文件的 .text 字段集中放在这里 */
*(.text .text.*)
_etext = .;
} > REGION_TEXT
/* .rodata 字段 */
.rodata : ALIGN(4) {
_srodata = .;
/* 要链接的文件的 .rodata 字段集中放在这里 */
*(.rodata .rodata.*)
. = ALIGN(4);
_erodata = .;
} > REGION_RODATA
/* .data 字段 */
.data : ALIGN(4) {
_sidata = LOADADDR(.data);
_sdata = .;
/* Must be called __global_pointer$ for linker relaxations to work. */
PROVIDE(__global_pointer$ = . + 0x800);
/* 要链接的文件的 .data 字段集中放在这里 */
*(.sdata .sdata.* .sdata2 .sdata2.*);
*(.data .data.*)
. = ALIGN(4);
_edata = .;
} > REGION_DATA
/* .bss 字段 */
.bss (NOLOAD) : {
_sbss = .;
/* 要链接的文件的 .bss 字段集中放在这里 */
*(.sbss .bss .bss.*)
. = ALIGN(4);
_ebss = .;
} > REGION_BSS
.heap (NOLOAD) : {
_sheap = .;
. += _heap_size;
. = ALIGN(4);
_eheap = .;
} > REGION_HEAP
/* fictitious region that represents the memory available for the stack */
.stack (NOLOAD) : {
_estack = .;
. = _stack_start;
. = ALIGN(4);
_sstack = .;
} > REGION_STACK
/* Discard .eh_frame, we are not doing unwind on panic so it is not needed */
/DISCARD/ :
{
*(.eh_frame .eh_frame_hdr);
}
}
nightly-2020-08-01
\ No newline at end of file
// The implementation of stdin
// Ref: https://github.com/luojia65/rustsbi/blob/master/platform/k210/src/main.rs
#![no_std]
#![no_main]
#![feature(alloc_error_handler)]
#![feature(global_asm)]
#![feature(llvm_asm)]
#[cfg(not(test))]
use core::alloc::Layout;
#[cfg(not(test))]
use core::panic::PanicInfo;
use k210_hal::{clock::Clocks, fpioa, pac, prelude::*};
use linked_list_allocator::LockedHeap;
use rustsbi::{enter_privileged, print, println};
use riscv::register::{
mcause::{self, Exception, Interrupt, Trap},
medeleg, mepc, mhartid, mideleg, mie, mip, misa::{self, MXL},
mstatus::{self, MPP},
mtval,
mtvec::{self, TrapMode},
satp,
};
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
static mut DEVINTRENTRY: usize = 0;
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("[rustsbi] {}", info);
loop {}
}
#[cfg(not(test))]
#[alloc_error_handler]
fn oom(layout: Layout) -> ! {
println!("[rustsbi] out of memory for layout {:?}", layout);
loop {}
}
fn mp_hook() -> bool {
use riscv::asm::wfi;
use k210_hal::clint::msip;
let hartid = mhartid::read();
if hartid == 0 {
true
} else {
unsafe {
// Clear IPI
msip::clear_ipi(hartid);
// Start listening for software interrupts
mie::set_msoft();
loop {
wfi();
if mip::read().msoft() {
break;
}
}
// Stop listening for software interrupts
mie::clear_msoft();
// Clear IPI
msip::clear_ipi(hartid);
}
false
}
}
#[export_name = "_start"]
#[link_section = ".text.entry"] // this is stable
fn main() -> ! {
unsafe {
llvm_asm!(
"
csrr a2, mhartid
lui t0, %hi(_max_hart_id)
add t0, t0, %lo(_max_hart_id)
bgtu a2, t0, _start_abort
la sp, _stack_start
lui t0, %hi(_hart_stack_size)
add t0, t0, %lo(_hart_stack_size)
.ifdef __riscv_mul
mul t0, a2, t0
.else
beqz a2, 2f // Jump if single-hart
mv t1, a2
mv t2, t0
1:
add t0, t0, t2
addi t1, t1, -1
bnez t1, 1b
2:
.endif
sub sp, sp, t0
csrw mscratch, zero
j _start_success
_start_abort:
wfi
j _start_abort
_start_success:
"
)
};
if mp_hook() {
extern "C" {
static mut _ebss: u32;
static mut _sbss: u32;
static mut _edata: u32;
static mut _sdata: u32;
static _sidata: u32;
}
unsafe {
r0::zero_bss(&mut _sbss, &mut _ebss);
r0::init_data(&mut _sdata, &mut _edata, &_sidata);
}
}
extern "C" {
fn _start_trap();
}
unsafe {
mtvec::write(_start_trap as usize, TrapMode::Direct);
}
if mhartid::read() == 0 {
extern "C" {
fn _sheap();
fn _heap_size();
}
let sheap = &mut _sheap as *mut _ as usize;
let heap_size = &_heap_size as *const _ as usize;
unsafe {
ALLOCATOR.lock().init(sheap, heap_size);
}
let p = pac::Peripherals::take().unwrap();
let mut sysctl = p.SYSCTL.constrain();
let fpioa = p.FPIOA.split(&mut sysctl.apb0);
let clocks = Clocks::new();
let _uarths_tx = fpioa.io5.into_function(fpioa::UARTHS_TX);
let _uarths_rx = fpioa.io4.into_function(fpioa::UARTHS_RX);
// Configure UART
let serial = p.UARTHS.configure(115_200.bps(), &clocks);
let (tx, rx) = serial.split();
use rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal_fuse;
init_legacy_stdio_embedded_hal_fuse(tx, rx);
struct Ipi;
impl rustsbi::Ipi for Ipi {
fn max_hart_id(&self) -> usize {
1
}
fn send_ipi_many(&mut self, hart_mask: rustsbi::HartMask) {
use k210_hal::clint::msip;
for i in 0..=1 {
if hart_mask.has_bit(i) {
msip::set_ipi(i);
msip::clear_ipi(i);
}
}
}
}
use rustsbi::init_ipi;
init_ipi(Ipi);
struct Timer;
impl rustsbi::Timer for Timer {
fn set_timer(&mut self, stime_value: u64) {
// This function must clear the pending timer interrupt bit as well.
use k210_hal::clint::mtimecmp;
mtimecmp::write(mhartid::read(), stime_value);
unsafe { mip::clear_mtimer() };
}
}
use rustsbi::init_timer;
init_timer(Timer);
struct Reset;
impl rustsbi::Reset for Reset {
fn system_reset(&self, reset_type: usize, reset_reason: usize) -> rustsbi::SbiRet {
println!("[rustsbi] reset triggered! todo: shutdown all harts on k210; program halt. Type: {}, reason: {}", reset_type, reset_reason);
loop {}
}
}
use rustsbi::init_reset;
init_reset(Reset);
use k210_hal::plic::Priority;
use k210_hal::pac::Interrupt;
use k210_hal::gpiohs::Edge;
unsafe {
pac::PLIC::set_threshold(mhartid::read(), Priority::P0);
}
let gpiohs = p.GPIOHS.split();
fpioa.io16.into_function(fpioa::GPIOHS0);
let mut boot = gpiohs.gpiohs0.into_pull_up_input();
boot.trigger_on_edge(Edge::RISING | Edge::FALLING);
unsafe {
pac::PLIC::set_priority(Interrupt::GPIOHS0, Priority::P1);
pac::PLIC::unmask(mhartid::read(), Interrupt::GPIOHS0);
}
boot.clear_interrupt_pending_bits();
}
unsafe {
//mideleg::set_sext();
mideleg::set_stimer();
mideleg::set_ssoft();
medeleg::set_instruction_misaligned();
medeleg::set_breakpoint();
medeleg::set_user_env_call();
/* MMU Exception Delegation
/* Page Faults are *Reserved* in 1.9.1 version */
- medeleg::set_instruction_page_fault();
- medeleg::set_load_page_fault();
- medeleg::set_store_page_fault();
/* Actually, in 1.9.1 they are merged into more general exceptions */
+ medeleg::set_instruction_fault();
+ medeleg::set_load_fault();
+ medeleg::set_store_fault(); */
medeleg::set_instruction_fault();
medeleg::set_load_fault();
medeleg::set_store_fault();
// 默认不打开mie::set_mext
// 不打开mie::set_mtimer
mie::set_msoft();
}
if mhartid::read() == 0 {
println!("[rustsbi] RustSBI version {}", rustsbi::VERSION);
println!("{}", rustsbi::LOGO);
println!("[rustsbi] Platform: K210 (Version {})", env!("CARGO_PKG_VERSION"));
let isa = misa::read();
if let Some(isa) = isa {
let mxl_str = match isa.mxl() {
MXL::XLEN32 => "RV32",
MXL::XLEN64 => "RV64",
MXL::XLEN128 => "RV128",
};
print!("[rustsbi] misa: {}", mxl_str);
for ext in 'A'..='Z' {
if isa.has_extension(ext) {
print!("{}", ext);
}
}
println!("");
}
println!("[rustsbi] mideleg: {:#x}", mideleg::read().bits());
println!("[rustsbi] medeleg: {:#x}", medeleg::read().bits());
println!("[rustsbi] Kernel entry: 0x80020000");
}
extern "C" {
fn _s_mode_start();
}
unsafe {
mepc::write(_s_mode_start as usize);
mstatus::set_mpp(MPP::Supervisor);
enter_privileged(mhartid::read(), 0x2333333366666666);
}
}
global_asm!(
"
.section .text
.globl _s_mode_start
_s_mode_start:
1: auipc ra, %pcrel_hi(1f)
ld ra, %pcrel_lo(1b)(ra)
jr ra
.align 3
1: .dword 0x80020000
"
);
// todo: configurable target address
global_asm!(
"
.equ REGBYTES, 8
.macro STORE reg, offset
sd \\reg, \\offset*REGBYTES(sp)
.endm
.macro LOAD reg, offset
ld \\reg, \\offset*REGBYTES(sp)
.endm
.section .text
.global _start_trap
.p2align 2
_start_trap:
csrrw sp, mscratch, sp
bnez sp, 1f
/* from M level, load sp */
csrrw sp, mscratch, zero
1:
addi sp, sp, -16 * REGBYTES
STORE ra, 0
STORE t0, 1
STORE t1, 2
STORE t2, 3
STORE t3, 4
STORE t4, 5
STORE t5, 6
STORE t6, 7
STORE a0, 8
STORE a1, 9
STORE a2, 10
STORE a3, 11
STORE a4, 12
STORE a5, 13
STORE a6, 14
STORE a7, 15
mv a0, sp
call _start_trap_rust
LOAD ra, 0
LOAD t0, 1
LOAD t1, 2
LOAD t2, 3
LOAD t3, 4
LOAD t4, 5
LOAD t5, 6
LOAD t6, 7
LOAD a0, 8
LOAD a1, 9
LOAD a2, 10
LOAD a3, 11
LOAD a4, 12
LOAD a5, 13
LOAD a6, 14
LOAD a7, 15
addi sp, sp, 16 * REGBYTES
csrrw sp, mscratch, sp
mret
"
);
#[allow(unused)]
struct TrapFrame {
ra: usize,
t0: usize,
t1: usize,
t2: usize,
t3: usize,
t4: usize,
t5: usize,
t6: usize,
a0: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
}
#[export_name = "_start_trap_rust"]
extern "C" fn start_trap_rust(trap_frame: &mut TrapFrame) {
let cause = mcause::read().cause();
match cause {
Trap::Exception(Exception::SupervisorEnvCall) => {
if trap_frame.a7 == 0x0A000004 && trap_frame.a6 == 0x210 {
// We use implementation specific sbi_rustsbi_k210_sext function (extension
// id: 0x0A000004, function id: 0x210) to register S-level interrupt handler
// for K210 chip only. This chip uses 1.9.1 version of privileged spec,
// which did not declare any S-level external interrupts.
unsafe { DEVINTRENTRY = trap_frame.a0; }
// enable mext
unsafe { mie::set_mext(); }
// return values
trap_frame.a0 = 0; // SbiRet::error = SBI_SUCCESS
trap_frame.a1 = 0; // SbiRet::value = 0
} else {
// Due to legacy 1.9.1 version of privileged spec, if we are in S-level
// timer handler (delegated from M mode), and we call SBI's `set_timer`,
// a M-level external interrupt may be triggered. This may try to obtain
// data structures locked previously by S-level interrupt handler, which
// results in a deadlock.
// Ref: https://github.com/luojia65/rustsbi/pull/5
if trap_frame.a7 == 0x0 {
unsafe {
let mtip = mip::read().mtimer();
if mtip {
if DEVINTRENTRY != 0 {
mie::set_mext();
}
}
}
}
// Actual ecall handler which is common for all RustSBI platforms
let params = [trap_frame.a0, trap_frame.a1, trap_frame.a2, trap_frame.a3];
let ans = rustsbi::ecall(trap_frame.a7, trap_frame.a6, params);
trap_frame.a0 = ans.error;
trap_frame.a1 = ans.value;
}
mepc::write(mepc::read().wrapping_add(4));
}
Trap::Interrupt(Interrupt::MachineSoft) => {
// Forward to S-level software interrupt
unsafe {
mip::set_ssoft(); // set S-soft interrupt flag
mie::clear_msoft(); // mask M-soft interrupt
}
}
Trap::Interrupt(Interrupt::MachineTimer) => {
// Forward to S-level timer interrupt
unsafe {
mip::set_stimer(); // set S-timer interrupt flag
mie::clear_mext(); // Ref: Pull request #5
mie::clear_mtimer(); // mask M-timer interrupt
}
}
Trap::Interrupt(Interrupt::MachineExternal) => {
/* legacy software delegation
// to make UARTHS interrupt soft delegation work; ref: pull request #1
// PLIC target0(Always Hart0-M-Interrupt) acquire
let irq_id = unsafe { (0x0c20_0004 as *const u32).read_volatile() };
// read from UARTHS RXFIFO
let ch: u8 = unsafe { (0x3800_0004 as *const u32).read_volatile() & 0xFF } as u8;
// black magic @_@, soft delegation won't success without it!
print!("{}", 0 as char);
// PLIC complete
unsafe { (0x0c20_0004 as *mut u32).write_volatile(irq_id); }
// communicate with delegated interrupt with stval CSR
unsafe { llvm_asm!("csrw stval, $0" :: "r"(ch as usize) :: "volatile"); }
// soft delegate to S Mode soft interrupt
unsafe { mip::set_ssoft(); }
*/
unsafe {
let mut mstatus: usize;
llvm_asm!("csrr $0, mstatus" : "=r"(mstatus) ::: "volatile");
// set mstatus.mprv
mstatus |= 1 << 17;
// it may trap from U/S Mode
// save mpp and set mstatus.mpp to S Mode
let mpp = (mstatus >> 11) & 3;
mstatus = mstatus & !(3 << 11);
mstatus |= 1 << 11;
// drop mstatus.mprv protection
llvm_asm!("csrw mstatus, $0" :: "r"(mstatus) :: "volatile");
fn devintr() {
unsafe {
// call devintr defined in application
// we have to ask compiler save ra explicitly
llvm_asm!("jalr 0($0)" :: "r"(DEVINTRENTRY) : "ra" : "volatile");
}
}
// compiler helps us save/restore caller-saved registers
devintr();
// restore mstatus
mstatus = mstatus &!(3 << 11);
mstatus |= mpp << 11;
mstatus -= 1 << 17;
llvm_asm!("csrw mstatus, $0" :: "r"(mstatus) :: "volatile");
}
}
Trap::Exception(Exception::IllegalInstruction) => {
let vaddr = mepc::read();
let ins = unsafe { get_vaddr_u32(vaddr) };
if ins & 0xFFFFF07F == 0xC0102073 { // rdtime instruction
// rdtime is actually a csrrw instruction
let rd = ((ins >> 7) & 0b1_1111) as u8;
let mtime = k210_hal::clint::mtime::read();
let time_usize = mtime as usize;
set_rd(trap_frame, rd, time_usize);
mepc::write(mepc::read().wrapping_add(4)); // skip current instruction
} else if ins & 0xFE007FFF == 0x12000073 { // sfence.vma instruction
// There is no `sfence.vma` in 1.9.1 privileged spec; however there is a `sfence.vm`.
// For backward compability, here we emulate the first instruction using the second one.
// sfence.vma: | 31..25 funct7=SFENCE.VMA(0001001) | 24..20 rs2/asid | 19..15 rs1/vaddr |
// 14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) |
// sfence.vm(1.9): | 31..=20 SFENCE.VM(000100000100) | 19..15 rs1/vaddr |
// 14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) |
// discard rs2 // let _rs2_asid = ((ins >> 20) & 0b1_1111) as u8;
// let rs1_vaddr = ((ins >> 15) & 0b1_1111) as u8;
// read paging mode from satp (sptbr)
let satp_bits = satp::read().bits();
// bit 63..20 is not readable and writeable on K210, so we cannot
// decide paging type from the 'satp' register.
// that also means that the asid function is not usable on this chip.
// we have to fix it to be Sv39.
let ppn = satp_bits & 0xFFF_FFFF_FFFF; // 43..0 PPN WARL
// write to sptbr
let sptbr_bits = ppn & 0x3F_FFFF_FFFF;
unsafe { llvm_asm!("csrw 0x180, $0"::"r"(sptbr_bits)) }; // write to sptbr
// enable paging (in v1.9.1, mstatus: | 28..24 VM[4:0] WARL | ... )
let mut mstatus_bits: usize;
unsafe { llvm_asm!("csrr $0, mstatus":"=r"(mstatus_bits)) };
mstatus_bits &= !0x1F00_0000;
mstatus_bits |= 9 << 24;
unsafe { llvm_asm!("csrw mstatus, $0"::"r"(mstatus_bits)) };
// emulate with sfence.vm (declared in privileged spec v1.9)
unsafe { llvm_asm!(".word 0x10400073") }; // sfence.vm x0
// ::"r"(rs1_vaddr)
mepc::write(mepc::read().wrapping_add(4)); // skip current instruction
} else {
panic!("invalid instruction! mepc: {:016x?}, instruction: {:08x?}", mepc::read(), ins);
}
}
cause => panic!(
"unhandled trap! mcause: {:?}, mepc: {:016x?}, mtval: {:016x?}",
cause,
mepc::read(),
mtval::read(),
),
}
}
#[inline]
unsafe fn get_vaddr_u32(vaddr: usize) -> u32 {
// todo: comment
get_vaddr_u16(vaddr) as u32 |
((get_vaddr_u16(vaddr.wrapping_add(2)) as u32) << 16)
}
#[inline]
unsafe fn get_vaddr_u16(vaddr: usize) -> u16 {
let mut ans: u16;
llvm_asm!("
li t0, (1 << 17)
csrrs t0, mstatus, t0
lhu $0, 0($1)
csrw mstatus, t0
"
:"=r"(ans)
:"r"(vaddr)
:"t0", "t1");
ans
}
#[inline]
fn set_rd(trap_frame: &mut TrapFrame, rd: u8, value: usize) {
match rd {
10 => trap_frame.a0 = value,
11 => trap_frame.a1 = value,
12 => trap_frame.a2 = value,
13 => trap_frame.a3 = value,
14 => trap_frame.a4 = value,
15 => trap_frame.a5 = value,
16 => trap_frame.a6 = value,
17 => trap_frame.a7 = value,
5 => trap_frame.t0 = value,
6 => trap_frame.t1 = value,
7 => trap_frame.t2 = value,
28 => trap_frame.t3 = value,
29 => trap_frame.t4 = value,
30 => trap_frame.t5 = value,
31 => trap_frame.t6 = value,
_ => panic!("invalid target `rd`"),
}
}
This diff is collapsed.
// An interface for UART communication
trait SerialPair: core::fmt::Write {
fn getchar(&mut self) ->Option<u8>;
fn putchar(&mut self, c: u8);
}
use embedded_hal::serial::{
Write, Read,
};
use core::convert::Infallible;
struct Serial<T, R>(T, R);
impl<T, R> SerialPair for Serial<T, R>
where
T: Write<u8, Error = Infallible> + Send + 'static,
R: Read<u8, Error = Infallible> + Send + 'static,
{
fn getchar(&mut self) ->Option<u8> {
match self.1.try_read() {
Ok(c) => Some(c),
Err(_) => None,
}
}
fn putchar(&mut self, c: u8) {
// try_write() may fail or have to wait for last char
// to finish transmitting. so loop here until
// transmitting succeeds.
while let Err(_) = self.0.try_write(c) {}
}
}
impl<T, R> core::fmt::Write for Serial<T, R>
where
T: Write<u8, Error = Infallible> + Send + 'static,
R: Read<u8, Error = Infallible> + Send + 'static,
Serial<T, R>: SerialPair,
{
fn write_str(&mut self, s: &str) ->core::fmt::Result {
for c in s.bytes() {
self.putchar(c as u8);
}
Ok(())
}
}
use lazy_static::lazy_static;
use spin::Mutex;
extern crate alloc;
use alloc::boxed::Box;
lazy_static! {
static ref UARTHS: Mutex<Option<Box<dyn SerialPair + Send>>> =
Mutex::new(None);
}
pub fn init(ser: k210_hal::serial::Serial<k210_hal::pac::UARTHS>) {
let (tx, rx) = ser.split();
*UARTHS.lock() = Some(Box::new(Serial(tx, rx)));
crate::println!("serial init");
}
pub fn getchar() ->Option<u8> {
let mut _uarths = UARTHS.lock(); // acquire lock
match _uarths.as_mut() {
Some(ser) => ser.getchar(),
_ => None,
}
}
pub fn putchar(c: u8) {
let mut _uarths = UARTHS.lock(); // acquire lock
if let Some(ser) = _uarths.as_mut() {
ser.putchar(c);
}
}
pub fn print(args: core::fmt::Arguments) {
// write_fmt() is provided by `Write`, so there's
// no need that we implement it.
let mut _uarths = UARTHS.lock(); // acquire lock
if let Some(ser) = _uarths.as_mut() {
ser.write_fmt(args).unwrap();
}
}
#[macro_export]
macro_rules! print {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::serial::print(format_args!($fmt $(, $($arg)+)?));
}
}
#[macro_export]
macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::serial::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
}
}
[build]
target = "riscv64gc-unknown-none-elf"
[target.riscv64gc-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tlink-qemu.ld",
]
[package]
name = "rustsbi-qemu"
version = "0.1.0"
authors = ["hustccc <1276675421@qq.com>"]
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rustsbi = "0.1.1"
linked_list_allocator = "0.8"
lazy_static = { version = "1", features = ["spin_no_std"] }
spin = "0.7"
riscv = { git = "https://github.com/rust-embedded/riscv", features = ["inline-asm"] }
device_tree = { git = "https://github.com/rcore-os/device_tree-rs/" }
embedded-hal = "1.0.0-alpha.1"
nb = "1"
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment