├── xtask ├── src │ ├── test.rs │ ├── detect.rs │ └── main.rs └── Cargo.toml ├── .gitignore ├── rustsbi-k210 ├── kendryte-k210.dtb ├── .cargo │ └── config.toml ├── Cargo.toml ├── src │ ├── feature.rs │ ├── feature │ │ ├── transfer_trap.rs │ │ ├── emulate_rdtime.rs │ │ ├── sfence_vma.rs │ │ ├── supervisor_interrupt.rs │ │ └── delegate_page_fault.rs │ ├── peripheral.rs │ ├── hart_csr_utils.rs │ ├── execute.rs │ ├── main.rs │ └── runtime.rs ├── build.rs ├── link-k210.ld └── kendryte-k210.dts ├── Cargo.toml ├── .cargo └── config.toml ├── test-kernel ├── .cargo │ └── config.toml ├── src │ ├── feature.rs │ ├── linker.ld │ ├── console.rs │ ├── feature │ │ ├── base_extension.rs │ │ ├── sfence_vma.rs │ │ ├── delegate_trap.rs │ │ └── catch_page_fault.rs │ ├── main.rs │ └── sbi.rs ├── Cargo.toml └── build.rs ├── README.md └── Cargo.lock /xtask/src/test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn run_test_kernel() { 3 | eprintln!("Test!"); 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | xtask/ktool.py 4 | 5 | # RustRover IDE files 6 | .idea 7 | -------------------------------------------------------------------------------- /rustsbi-k210/kendryte-k210.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustsbi/rustsbi-k210/HEAD/rustsbi-k210/kendryte-k210.dtb -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "rustsbi-k210", 5 | "test-kernel", 6 | "xtask" 7 | ] 8 | default-members = ["xtask"] 9 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | make = "xtask make" 4 | k210 = "xtask k210" 5 | asm = "xtask asm" 6 | size = "xtask size" 7 | detect = "xtask detect" 8 | -------------------------------------------------------------------------------- /test-kernel/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64imac-unknown-none-elf" 3 | 4 | [target.riscv64imac-unknown-none-elf] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlinker.ld", 7 | ] 8 | -------------------------------------------------------------------------------- /rustsbi-k210/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64imac-unknown-none-elf" 3 | 4 | [target.riscv64imac-unknown-none-elf] 5 | rustflags = [ 6 | "-C", "link-arg=-Tlink-k210.ld", 7 | ] 8 | -------------------------------------------------------------------------------- /test-kernel/src/feature.rs: -------------------------------------------------------------------------------- 1 | mod base_extension; 2 | mod catch_page_fault; 3 | mod delegate_trap; 4 | mod sfence_vma; 5 | 6 | pub use base_extension::test_base_extension; 7 | pub use catch_page_fault::test_catch_page_fault; 8 | pub use delegate_trap::test_delegate_trap; 9 | pub use sfence_vma::test_sfence_vma; 10 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | authors = ["Luo Jia "] 5 | description = "interactive cargo runner" 6 | edition = "2021" 7 | publish = false 8 | 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | clap = "2" 14 | serialport = "4" 15 | -------------------------------------------------------------------------------- /rustsbi-k210/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustsbi-k210" 3 | version = "0.0.2" 4 | authors = ["luojia65 "] 5 | edition = "2021" 6 | publish = false 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | rustsbi = "0.2.2" 12 | riscv = "0.10.1" 13 | buddy_system_allocator = "0.8" 14 | k210-hal = { git = "https://github.com/riscv-rust/k210-hal", rev = "7e9c8d70" } 15 | r0 = "1.0" 16 | bit_field = "0.10" 17 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature.rs: -------------------------------------------------------------------------------- 1 | mod delegate_page_fault; 2 | mod emulate_rdtime; 3 | mod sfence_vma; 4 | mod supervisor_interrupt; 5 | mod transfer_trap; 6 | 7 | pub use delegate_page_fault::is_page_fault; 8 | pub use emulate_rdtime::emulate_rdtime; 9 | pub use sfence_vma::emulate_sfence_vma; 10 | pub use supervisor_interrupt::{ 11 | call_supervisor_interrupt, emulate_sbi_rustsbi_k210_sext, forward_supervisor_soft, 12 | forward_supervisor_timer, preprocess_supervisor_external, 13 | }; 14 | pub use transfer_trap::{do_transfer_trap, should_transfer_trap}; 15 | -------------------------------------------------------------------------------- /test-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-kernel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | r0 = "1" 10 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 11 | riscv = { git = "https://github.com/rust-embedded/riscv", rev = "cd31989b", features = ["inline-asm"] } 12 | buddy_system_allocator = "0.8" 13 | spin = "0.9" 14 | bitflags = "1.2" 15 | bit_field = "0.10" 16 | 17 | [[bin]] 18 | name = "test-kernel" 19 | test = false 20 | bench = false 21 | -------------------------------------------------------------------------------- /test-kernel/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 8 | 9 | // Put the linker script somewhere the linker can find it 10 | fs::File::create(out_dir.join("linker.ld")) 11 | .unwrap() 12 | .write_all(include_bytes!("src/linker.ld")) 13 | .unwrap(); 14 | println!("cargo:rustc-link-search={}", out_dir.display()); 15 | 16 | println!("cargo:rerun-if-changed=build.rs"); 17 | println!("cargo:rerun-if-changed=src/linker.ld"); 18 | } 19 | -------------------------------------------------------------------------------- /rustsbi-k210/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 8 | 9 | // Put the linker script somewhere the linker can find it 10 | fs::File::create(out_dir.join("link-k210.ld")) 11 | .unwrap() 12 | .write_all(include_bytes!("link-k210.ld")) 13 | .unwrap(); 14 | println!("cargo:rustc-link-search={}", out_dir.display()); 15 | 16 | println!("cargo:rerun-if-changed=build.rs"); 17 | println!("cargo:rerun-if-changed=link-k210.ld"); 18 | } 19 | -------------------------------------------------------------------------------- /test-kernel/src/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | 4 | BASE_ADDRESS = 0x80020000; 5 | 6 | SECTIONS 7 | { 8 | . = BASE_ADDRESS; 9 | start = .; 10 | 11 | .text : ALIGN(4K) { 12 | stext = .; 13 | *(.text.entry) 14 | *(.text .text.*) 15 | etext = .; 16 | } 17 | 18 | .rodata : ALIGN(4K) { 19 | srodata = .; 20 | *(.rodata .rodata.*) 21 | erodata = .; 22 | } 23 | 24 | .data : ALIGN(4K) { 25 | sidata = LOADADDR(.data); 26 | sdata = .; 27 | *(.data .data.*) 28 | edata = .; 29 | } 30 | 31 | .bss (NOLOAD) : ALIGN(4K) { 32 | *(.bss.uninit) 33 | sbss = .; 34 | *(.sbss .bss .bss.*) 35 | ebss = .; 36 | } 37 | 38 | PROVIDE(end = .); 39 | } 40 | -------------------------------------------------------------------------------- /test-kernel/src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi::console_putchar; 2 | use core::fmt::{self, Write}; 3 | 4 | struct Stdout; 5 | 6 | impl Write for Stdout { 7 | fn write_str(&mut self, s: &str) -> fmt::Result { 8 | for c in s.chars() { 9 | console_putchar(c as usize); 10 | } 11 | Ok(()) 12 | } 13 | } 14 | 15 | pub fn print(args: fmt::Arguments) { 16 | Stdout.write_fmt(args).unwrap(); 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! print { 21 | ($fmt: literal $(, $($arg: tt)+)?) => { 22 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 23 | } 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! println { 28 | ($fmt: literal $(, $($arg: tt)+)?) => { 29 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RustSBI K210 平台支持包 2 | 3 | 这个平台支持包包含较多的平台兼容功能,允许在K210上运行1.12版本标准的操作系统内核。 4 | 5 | ## 二进制包下载 6 | 7 | 请参阅发行页面:[这里](https://github.com/rustsbi/rustsbi-k210/releases)。 8 | 9 | ## 使用说明 10 | 11 | 请先下载[ktool.py](https://github.com/loboris/ktool),放置在`xtask`目录下,即文件位置为`xtask/ktool.py`。 12 | 13 | 运行以下指令,来直接在目标开发板运行代码。 14 | 15 | ``` 16 | cargo k210 17 | ``` 18 | 19 | 这个平台支持包会启动位于`0x80020000`的操作系统内核,并在`a1`寄存器提供一个简单的设备树。 20 | 操作系统内核应当使用《RISC-V指令集架构 第二卷:特权级指令》的1.12版本,而非芯片支持的1.9.1版本。 21 | 22 | ## 兼容性使用文档 23 | 24 | 稍后放出。包括`sfence.vma`指令、页异常编号转发等等。 25 | 26 | ## 立即体验 27 | 28 | 先下载代码,然后直接运行内核测试: 29 | 30 | ``` 31 | cargo test 32 | ``` 33 | 34 | ## 版权声明 35 | 36 | 项目的测试框架使用了[KTool](https://github.com/loboris/ktool)。这个项目使用Apache 2.0协议开源,感谢KTool项目和它的维护者们! 37 | 38 | Reference implementaion K210 includes Kendryte K210 DTS file from Western Digital, this file is 39 | (C) Western Digital Corporation or its affiliates under BSD-2-Clause license. 40 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature/transfer_trap.rs: -------------------------------------------------------------------------------- 1 | use crate::runtime::SupervisorContext; 2 | use riscv::register::{ 3 | mstatus::{self, MPP, SPP}, 4 | mtval, scause, sepc, stval, stvec, 5 | }; 6 | 7 | pub unsafe fn should_transfer_trap(ctx: &mut SupervisorContext) -> bool { 8 | ctx.mstatus.mpp() != MPP::Machine 9 | } 10 | 11 | pub unsafe fn do_transfer_trap(ctx: &mut SupervisorContext, cause: scause::Trap) { 12 | // 设置S层异常原因为:非法指令 13 | scause::set(cause); 14 | // 填写异常指令的指令内容 15 | stval::write(mtval::read()); 16 | // 填写S层需要返回到的地址,这里的mepc会被随后的代码覆盖掉 17 | sepc::write(ctx.mepc); 18 | // 设置中断位 19 | mstatus::set_mpp(MPP::Supervisor); 20 | mstatus::set_spp(SPP::Supervisor); 21 | if mstatus::read().sie() { 22 | mstatus::set_spie() 23 | } 24 | mstatus::clear_sie(); 25 | ctx.mstatus = mstatus::read(); 26 | // 设置返回地址,返回到S层 27 | // 注意,无论是Direct还是Vectored模式,所有异常的向量偏移都是0,不需要处理中断向量,跳转到入口地址即可 28 | ctx.mepc = stvec::read().address(); 29 | } 30 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature/emulate_rdtime.rs: -------------------------------------------------------------------------------- 1 | use crate::runtime::SupervisorContext; 2 | 3 | #[inline] 4 | pub fn emulate_rdtime(ctx: &mut SupervisorContext, ins: usize) -> bool { 5 | if ins & 0xFFFFF07F == 0xC0102073 { 6 | // rdtime is actually a csrrw instruction 7 | let rd = ((ins >> 7) & 0b1_1111) as u8; 8 | let mtime = k210_hal::clint::mtime::read(); 9 | let time_usize = mtime as usize; 10 | set_register_xi(ctx, rd, time_usize); 11 | ctx.mepc = ctx.mepc.wrapping_add(4); // skip current instruction 12 | return true; 13 | } else { 14 | return false; // is not a rdtime instruction 15 | } 16 | } 17 | 18 | #[inline] 19 | fn set_register_xi(ctx: &mut SupervisorContext, i: u8, data: usize) { 20 | let registers = unsafe { &mut *(ctx as *mut _ as *mut [usize; 31]) }; 21 | assert!(i <= 31, "i should be valid register target"); 22 | if i == 0 { 23 | // x0, don't modify 24 | return; 25 | } 26 | registers[(i - 1) as usize] = data; 27 | } 28 | -------------------------------------------------------------------------------- /rustsbi-k210/link-k210.ld: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | /* 存储单元的物理地址 */ 3 | SRAM : ORIGIN = 0x80000000, LENGTH = 128K 4 | } 5 | 6 | PROVIDE(stext = 0x80000000); 7 | 8 | REGION_ALIAS("REGION_TEXT", SRAM); 9 | REGION_ALIAS("REGION_RODATA", SRAM); 10 | REGION_ALIAS("REGION_DATA", SRAM); 11 | REGION_ALIAS("REGION_BSS", SRAM); 12 | 13 | OUTPUT_ARCH(riscv) 14 | 15 | ENTRY(_start) 16 | 17 | SECTIONS 18 | { 19 | .text stext : { 20 | stext = .; 21 | *(.text.entry) 22 | *(.text .text.*) 23 | . = ALIGN(4); 24 | etext = .; 25 | } > REGION_TEXT 26 | 27 | .rodata : ALIGN(4) { 28 | srodata = .; 29 | *(.rodata .rodata.*) 30 | *(.srodata .srodata.*) 31 | . = ALIGN(4); 32 | erodata = .; 33 | } > REGION_RODATA 34 | 35 | .data : ALIGN(4) { 36 | sidata = LOADADDR(.data); 37 | sdata = .; 38 | *(.data .data.*) 39 | *(.sdata .sdata.*) 40 | . = ALIGN(4); 41 | edata = .; 42 | } > REGION_DATA 43 | 44 | .bss (NOLOAD) : ALIGN(4) { 45 | *(.bss.uninit) 46 | sbss = .; 47 | *(.bss .bss.*) 48 | *(.sbss .sbss.*) 49 | . = ALIGN(4); 50 | ebss = .; 51 | } > REGION_BSS 52 | 53 | /DISCARD/ : { 54 | *(.eh_frame .eh_frame_hdr) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test-kernel/src/feature/base_extension.rs: -------------------------------------------------------------------------------- 1 | use crate::{println, sbi}; 2 | 3 | pub fn test_base_extension() { 4 | println!(">> Test-kernel: Testing base extension"); 5 | let base_version = sbi::probe_extension(sbi::EXTENSION_BASE); 6 | if base_version == 0 { 7 | println!("!! Test-kernel: no base extension probed; SBI call returned value '0'"); 8 | println!( 9 | "!! Test-kernel: This SBI implementation may only have legacy extension implemented" 10 | ); 11 | println!("!! Test-kernel: SBI test FAILED due to no base extension found"); 12 | sbi::shutdown() 13 | } 14 | println!("<< Test-kernel: Base extension version: {:x}", base_version); 15 | println!( 16 | "<< Test-kernel: SBI specification version: {:x}", 17 | sbi::get_spec_version() 18 | ); 19 | println!( 20 | "<< Test-kernel: SBI implementation Id: {:x}", 21 | sbi::get_sbi_impl_id() 22 | ); 23 | println!( 24 | "<< Test-kernel: SBI implementation version: {:x}", 25 | sbi::get_sbi_impl_version() 26 | ); 27 | println!( 28 | "<< Test-kernel: Device mvendorid: {:x}", 29 | sbi::get_mvendorid() 30 | ); 31 | println!("<< Test-kernel: Device marchid: {:x}", sbi::get_marchid()); 32 | println!("<< Test-kernel: Device mimpid: {:x}", sbi::get_mimpid()); 33 | } 34 | -------------------------------------------------------------------------------- /test-kernel/src/feature/sfence_vma.rs: -------------------------------------------------------------------------------- 1 | use crate::{println, sbi}; 2 | use riscv::{ 3 | asm, 4 | register::satp::{self, Mode}, 5 | }; 6 | 7 | #[repr(align(4096))] 8 | struct PageTable { 9 | #[allow(unused)] // Will be used by RISC-V hardware 10 | entries: [usize; 512], 11 | } 12 | 13 | static TEST_PAGE_TABLE: PageTable = { 14 | let mut entries = [0; 512]; 15 | entries[2] = (0x80000 << 10) | 0xcf; // 0x8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1 16 | entries[508] = (0x00000 << 10) | 0xcf; // 0xffff_ffff_0000_0000 -> 0x0000_0000,0xcf 表示 VRWXAD 均为 1 17 | entries[510] = (0x80000 << 10) | 0xcf; // 0xffff_ffff_8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1 18 | PageTable { entries } 19 | }; 20 | 21 | static VARIABLE: usize = 0x6666233399998888; 22 | 23 | pub fn test_sfence_vma() { 24 | println!(">> Test-kernel: Testing emulated virtual memory unit"); 25 | let pa = &TEST_PAGE_TABLE as *const _ as usize; 26 | let ppn = pa >> 12; 27 | unsafe { satp::set(Mode::Sv39, 0, ppn) }; 28 | unsafe { asm::sfence_vma_all() }; // SBI will emulate this instruction 29 | println!("<< Test-kernel: Code memory page test success"); 30 | let ptr = &VARIABLE as *const _ as usize; 31 | let mapped_ptr = ptr + 0xffff_ffff_0000_0000; 32 | let mapped_variable = unsafe { *(mapped_ptr as *const usize) }; 33 | if mapped_variable != VARIABLE { 34 | println!("!! Test-kernel: Multi mapping page test failed: variable value don't match"); 35 | sbi::shutdown() 36 | } 37 | println!("<< Test-kernel: Multi mapping page test success"); 38 | } 39 | -------------------------------------------------------------------------------- /rustsbi-k210/kendryte-k210.dts: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2019 Western Digital Corporation or its affiliates. 5 | * 6 | * Authors: 7 | * Damien Le Moal 8 | */ 9 | 10 | /dts-v1/; 11 | / { 12 | #address-cells = <2>; 13 | #size-cells = <2>; 14 | compatible = "kendryte,k210"; 15 | 16 | chosen { 17 | bootargs = "console=hvc0 earlycon=sbi"; 18 | }; 19 | 20 | cpus { 21 | #address-cells = <1>; 22 | #size-cells = <0>; 23 | cpu0: cpu@0 { 24 | device_type = "cpu"; 25 | clock-frequency = <390000000>; 26 | i-cache-size = <32768>; 27 | d-cache-size = <32768>; 28 | mmu-type = "none"; 29 | reg = <0>; 30 | riscv,isa = "rv64imafdc"; 31 | status = "okay"; 32 | cpu0_intc: interrupt-controller { 33 | #interrupt-cells = <1>; 34 | compatible = "riscv,cpu-intc"; 35 | interrupt-controller; 36 | }; 37 | }; 38 | cpu1: cpu@1 { 39 | device_type = "cpu"; 40 | clock-frequency = <390000000>; 41 | d-cache-size = <32768>; 42 | i-cache-size = <32768>; 43 | mmu-type = "none"; 44 | reg = <1>; 45 | riscv,isa = "rv64imafdc"; 46 | status = "okay"; 47 | cpu1_intc: interrupt-controller { 48 | #interrupt-cells = <1>; 49 | compatible = "riscv,cpu-intc"; 50 | interrupt-controller; 51 | }; 52 | }; 53 | }; 54 | 55 | memory@80000000 { 56 | /* Bank 0: 4 MB, Bank 1: 2 MB, AI chip SRAM: 2MB */ 57 | device_type = "memory"; 58 | reg = <0x00000000 0x80000000 0x00000000 0x00800000>; 59 | }; 60 | 61 | plic0: interrupt-controller@C000000 { 62 | #interrupt-cells = <1>; 63 | compatible = "riscv,plic0"; 64 | interrupt-controller; 65 | interrupts-extended = 66 | <&cpu0_intc 11 &cpu0_intc 9 67 | &cpu1_intc 11 &cpu1_intc 9>; 68 | reg = <0x0 0xc000000 0x0 0x4000000>; 69 | }; 70 | }; 71 | -------------------------------------------------------------------------------- /rustsbi-k210/src/peripheral.rs: -------------------------------------------------------------------------------- 1 | use k210_hal::{clint::msip, clock::Clocks, fpioa, pac, prelude::*}; 2 | use riscv::register::{mhartid, mip}; 3 | use rustsbi::println; 4 | 5 | pub fn init_peripheral() { 6 | let p = pac::Peripherals::take().unwrap(); 7 | 8 | let mut sysctl = p.SYSCTL.constrain(); 9 | let fpioa = p.FPIOA.split(&mut sysctl.apb0); 10 | let clocks = Clocks::new(); 11 | let _uarths_tx = fpioa.io5.into_function(fpioa::UARTHS_TX); 12 | let _uarths_rx = fpioa.io4.into_function(fpioa::UARTHS_RX); 13 | // Configure UART 14 | let serial = p.UARTHS.configure(115_200.bps(), &clocks); 15 | let (tx, rx) = serial.split(); 16 | 17 | rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal_fuse(tx, rx); 18 | rustsbi::init_timer(Timer); 19 | rustsbi::init_reset(Reset); 20 | rustsbi::init_ipi(Ipi); 21 | } 22 | 23 | struct Ipi; 24 | 25 | impl rustsbi::Ipi for Ipi { 26 | fn max_hart_id(&self) -> usize { 27 | 1 28 | } 29 | fn send_ipi_many(&self, hart_mask: rustsbi::HartMask) -> rustsbi::SbiRet { 30 | for i in 0..=1 { 31 | if hart_mask.has_bit(i) { 32 | msip::set_ipi(i); 33 | msip::clear_ipi(i); 34 | } 35 | } 36 | rustsbi::SbiRet::ok(0) 37 | } 38 | } 39 | 40 | struct Timer; 41 | 42 | impl rustsbi::Timer for Timer { 43 | fn set_timer(&self, stime_value: u64) { 44 | // This function must clear the pending timer interrupt bit as well. 45 | use k210_hal::clint::mtimecmp; 46 | mtimecmp::write(mhartid::read(), stime_value); 47 | unsafe { mip::clear_stimer() }; 48 | } 49 | } 50 | 51 | pub struct Reset; 52 | 53 | impl rustsbi::Reset for Reset { 54 | fn system_reset(&self, reset_type: usize, reset_reason: usize) -> rustsbi::SbiRet { 55 | println!("[rustsbi] reset triggered! todo: shutdown all harts on k210; program halt. Type: {}, reason: {}", reset_type, reset_reason); 56 | loop {} 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature/sfence_vma.rs: -------------------------------------------------------------------------------- 1 | use crate::runtime::SupervisorContext; 2 | use core::arch::asm; 3 | use riscv::register::{mstatus, satp}; 4 | 5 | // There is no `sfence.vma` in 1.9.1 privileged spec; however there is a `sfence.vm`. 6 | // For backward compability, here we emulate the first instruction using the second one. 7 | // sfence.vma: | 31..25 funct7=SFENCE.VMA(0001001) | 24..20 rs2/asid | 19..15 rs1/vaddr | 8 | // 14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) | 9 | // sfence.vm(1.9): | 31..=20 SFENCE.VM(000100000100) | 19..15 rs1/vaddr | 10 | // 14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) | 11 | 12 | #[inline] 13 | pub fn emulate_sfence_vma(ctx: &mut SupervisorContext, ins: usize) -> bool { 14 | if ins & 0xFE007FFF == 0x12000073 { 15 | // sfence.vma instruction 16 | // discard rs2 // let _rs2_asid = ((ins >> 20) & 0b1_1111) as u8; 17 | // let rs1_vaddr = ((ins >> 15) & 0b1_1111) as u8; 18 | // read paging mode from satp (sptbr) 19 | let satp_bits = satp::read().bits(); 20 | // bit 63..20 is not readable and writeable on K210, so we cannot 21 | // decide paging type from the 'satp' register. 22 | // that also means that the asid function is not usable on this chip. 23 | // we have to fix it to be Sv39. 24 | let ppn = satp_bits & 0xFFF_FFFF_FFFF; // 43..0 PPN WARL 25 | // write to sptbr 26 | let sptbr_bits = ppn & 0x3F_FFFF_FFFF; 27 | unsafe { asm!("csrw 0x180, {}", in(reg) sptbr_bits) }; // write to sptbr 28 | // enable paging (in v1.9.1, mstatus: | 28..24 VM[4:0] WARL | ... ) 29 | let mut mstatus_bits: usize; 30 | unsafe { asm!("csrr {}, mstatus", out(reg) mstatus_bits) }; 31 | mstatus_bits &= !0x1F00_0000; 32 | mstatus_bits |= 9 << 24; 33 | unsafe { asm!("csrw mstatus, {}", in(reg) mstatus_bits) }; 34 | ctx.mstatus = mstatus::read(); 35 | // emulate with sfence.vm (declared in privileged spec v1.9) 36 | unsafe { asm!(".word 0x10400073") }; // sfence.vm x0 37 | // ::"r"(rs1_vaddr) 38 | ctx.mepc = ctx.mepc.wrapping_add(4); // skip current instruction 39 | return true; 40 | } else { 41 | return false; // is not a sfence.vma instruction 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test-kernel/src/feature/delegate_trap.rs: -------------------------------------------------------------------------------- 1 | use crate::{println, sbi}; 2 | use core::arch::asm; 3 | use riscv::register::{ 4 | scause::{self, Exception, Trap}, 5 | sepc, 6 | stvec::{self, TrapMode}, 7 | }; 8 | 9 | pub fn test_delegate_trap() { 10 | println!(">> Test-kernel: Trigger illegal exception"); 11 | let stvec_before = stvec::read().address(); 12 | init_trap_vector(); 13 | unsafe { asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction 14 | unsafe { stvec::write(stvec_before, TrapMode::Direct) }; 15 | } 16 | 17 | fn init_trap_vector() { 18 | let mut addr = delegate_test_trap as usize; 19 | if addr & 0x2 != 0 { 20 | addr = addr.wrapping_add(0x2); // 必须对齐到4个字节 21 | } 22 | unsafe { stvec::write(addr, TrapMode::Direct) }; 23 | } 24 | 25 | extern "C" fn rust_test_trap_handler() { 26 | let cause = scause::read().cause(); 27 | println!("<< Test-kernel: Value of scause: {:?}", cause); 28 | if cause != Trap::Exception(Exception::IllegalInstruction) { 29 | println!("!! Test-kernel: Wrong cause associated to illegal instruction"); 30 | sbi::shutdown() 31 | } 32 | println!("<< Test-kernel: Illegal exception delegate success"); 33 | sepc::write(sepc::read().wrapping_add(4)); // skip mcycle write illegal instruction 34 | } 35 | 36 | #[naked] 37 | #[link_section = ".text"] 38 | unsafe extern "C" fn delegate_test_trap() -> ! { 39 | asm!( 40 | ".align 4", // align to 4 bytes 41 | "addi sp, sp, -8*16 42 | sd ra, 8*0(sp) 43 | sd t0, 8*1(sp) 44 | sd t1, 8*2(sp) 45 | sd t2, 8*3(sp) 46 | sd t3, 8*4(sp) 47 | sd t4, 8*5(sp) 48 | sd t5, 8*6(sp) 49 | sd t6, 8*7(sp) 50 | sd a0, 8*8(sp) 51 | sd a1, 8*9(sp) 52 | sd a2, 8*10(sp) 53 | sd a3, 8*11(sp) 54 | sd a4, 8*12(sp) 55 | sd a5, 8*13(sp) 56 | sd a6, 8*14(sp) 57 | sd a7, 8*15(sp)", 58 | "call {rust_test_trap_handler}", 59 | "ld ra, 8*0(sp) 60 | ld t0, 8*1(sp) 61 | ld t1, 8*2(sp) 62 | ld t2, 8*3(sp) 63 | ld t3, 8*4(sp) 64 | ld t4, 8*5(sp) 65 | ld t5, 8*6(sp) 66 | ld t6, 8*7(sp) 67 | ld a0, 8*8(sp) 68 | ld a1, 8*9(sp) 69 | ld a2, 8*10(sp) 70 | ld a3, 8*11(sp) 71 | ld a4, 8*12(sp) 72 | ld a5, 8*13(sp) 73 | ld a6, 8*14(sp) 74 | ld a7, 8*15(sp) 75 | addi sp, sp, 8*16", 76 | "sret", 77 | rust_test_trap_handler = sym rust_test_trap_handler, 78 | options(noreturn) 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /rustsbi-k210/src/hart_csr_utils.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use riscv::register::{ 3 | medeleg, mideleg, 4 | misa::{self, MXL}, 5 | }; 6 | use rustsbi::{print, println}; 7 | 8 | pub fn print_hart_csrs() { 9 | print_misa(); 10 | print_mideleg(); 11 | print_medeleg(); 12 | } 13 | 14 | #[inline] 15 | fn print_misa() { 16 | let isa = misa::read(); 17 | if let Some(isa) = isa { 18 | let mxl_str = match isa.mxl() { 19 | MXL::XLEN32 => "RV32", 20 | MXL::XLEN64 => "RV64", 21 | MXL::XLEN128 => "RV128", 22 | }; 23 | print!("[rustsbi] misa: {}", mxl_str); 24 | for ext in 'A'..='Z' { 25 | if isa.has_extension(ext) { 26 | print!("{}", ext); 27 | } 28 | } 29 | println!(""); 30 | } 31 | } 32 | 33 | #[inline] 34 | fn print_mideleg() { 35 | let mideleg = mideleg::read(); 36 | let mut delegs = Vec::new(); 37 | if mideleg.usoft() { 38 | delegs.push("usoft") 39 | } 40 | if mideleg.utimer() { 41 | delegs.push("utimer") 42 | } 43 | if mideleg.uext() { 44 | delegs.push("uext") 45 | } 46 | if mideleg.ssoft() { 47 | delegs.push("ssoft") 48 | } 49 | if mideleg.stimer() { 50 | delegs.push("stimer") 51 | } 52 | if mideleg.sext() { 53 | delegs.push("sext") 54 | } 55 | println!( 56 | "[rustsbi] mideleg: {} ({:#x})", 57 | delegs.join(", "), 58 | mideleg.bits() 59 | ); 60 | } 61 | 62 | #[inline] 63 | fn print_medeleg() { 64 | let medeleg = medeleg::read(); 65 | let mut delegs = Vec::new(); 66 | if medeleg.instruction_misaligned() { 67 | delegs.push("ima") 68 | } 69 | if medeleg.instruction_fault() { 70 | delegs.push("ia") // instruction access 71 | } 72 | if medeleg.illegal_instruction() { 73 | delegs.push("illinsn") 74 | } 75 | if medeleg.breakpoint() { 76 | delegs.push("bkpt") 77 | } 78 | if medeleg.load_misaligned() { 79 | delegs.push("lma") 80 | } 81 | if medeleg.load_fault() { 82 | delegs.push("la") // load access 83 | } 84 | if medeleg.store_misaligned() { 85 | delegs.push("sma") 86 | } 87 | if medeleg.store_fault() { 88 | delegs.push("sa") // store access 89 | } 90 | if medeleg.user_env_call() { 91 | delegs.push("uecall") 92 | } 93 | if medeleg.supervisor_env_call() { 94 | delegs.push("secall") 95 | } 96 | if medeleg.machine_env_call() { 97 | delegs.push("mecall") 98 | } 99 | if medeleg.instruction_page_fault() { 100 | delegs.push("ipage") 101 | } 102 | if medeleg.load_page_fault() { 103 | delegs.push("lpage") 104 | } 105 | if medeleg.store_page_fault() { 106 | delegs.push("spage") 107 | } 108 | println!( 109 | "[rustsbi] medeleg: {} ({:#x})", 110 | delegs.join(", "), 111 | medeleg.bits() 112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /test-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(naked_functions)] 2 | #![feature(asm_const)] 3 | #![feature(stdsimd)] 4 | #![no_std] 5 | #![no_main] 6 | 7 | use core::arch::asm; 8 | 9 | mod console; 10 | mod feature; 11 | mod sbi; 12 | 13 | const PER_HART_STACK_SIZE: usize = 64 * 1024; // 64KiB 14 | const KERNEL_STACK_SIZE: usize = 2 * PER_HART_STACK_SIZE; 15 | #[link_section = ".bss.uninit"] 16 | static mut KERNEL_STACK: [u8; KERNEL_STACK_SIZE] = [0; KERNEL_STACK_SIZE]; 17 | 18 | const KERNEL_HEAP_SIZE: usize = 128 * 1024; // 128KiB 19 | #[link_section = ".bss.uninit"] 20 | static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 21 | #[global_allocator] 22 | static KERNEL_HEAP: LockedHeap<32> = LockedHeap::empty(); 23 | 24 | use buddy_system_allocator::LockedHeap; 25 | 26 | extern "C" fn rust_main(hartid: usize, opaque: usize) -> ! { 27 | if hartid == 0 { 28 | init_bss(); 29 | init_heap(); 30 | } 31 | println!( 32 | "<< Test-kernel: Hart id = {}, opaque = {:#x}", 33 | hartid, opaque 34 | ); 35 | feature::test_base_extension(); 36 | feature::test_delegate_trap(); 37 | feature::test_sfence_vma(); 38 | test_emulate_rdtime(); 39 | feature::test_catch_page_fault(); 40 | println!("<< Test-kernel: SBI test SUCCESS, shutdown"); 41 | sbi::shutdown() 42 | } 43 | 44 | pub fn test_emulate_rdtime() { 45 | println!(">> Test-kernel: Testing SBI instruction emulation"); 46 | let time = riscv::register::time::read64(); 47 | println!("<< Test-kernel: Current time: {:x}", time); 48 | } 49 | 50 | fn init_bss() { 51 | extern "C" { 52 | static mut ebss: u32; 53 | static mut sbss: u32; 54 | static mut edata: u32; 55 | static mut sdata: u32; 56 | static sidata: u32; 57 | } 58 | unsafe { 59 | r0::zero_bss(&mut sbss, &mut ebss); 60 | r0::init_data(&mut sdata, &mut edata, &sidata); 61 | } 62 | } 63 | 64 | fn init_heap() { 65 | unsafe { 66 | KERNEL_HEAP 67 | .lock() 68 | .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE) 69 | } 70 | } 71 | 72 | use core::panic::PanicInfo; 73 | 74 | #[cfg_attr(not(test), panic_handler)] 75 | #[allow(unused)] 76 | fn panic(info: &PanicInfo) -> ! { 77 | println!("!! Test-kernel: {}", info); 78 | println!("!! Test-kernel: SBI test FAILED due to panic"); 79 | sbi::shutdown() 80 | } 81 | 82 | #[naked] 83 | #[link_section = ".text.entry"] 84 | #[export_name = "_start"] 85 | unsafe extern "C" fn entry() -> ! { 86 | asm!( 87 | // 1. set sp 88 | // sp = bootstack + (hartid + 1) * HART_STACK_SIZE 89 | " 90 | la sp, {stack} 91 | li t0, {per_hart_stack_size} 92 | addi t1, a0, 1 93 | 1: add sp, sp, t0 94 | addi t1, t1, -1 95 | bnez t1, 1b 96 | ", 97 | // 2. jump to rust_main (absolute address) 98 | "j {rust_main}", 99 | per_hart_stack_size = const PER_HART_STACK_SIZE, 100 | stack = sym KERNEL_STACK, 101 | rust_main = sym rust_main, 102 | options(noreturn)) 103 | } 104 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature/supervisor_interrupt.rs: -------------------------------------------------------------------------------- 1 | use crate::runtime::SupervisorContext; 2 | use core::arch::asm; 3 | use riscv::register::{mie, mip, mstatus}; 4 | 5 | static mut DEVINTRENTRY: usize = 0; 6 | 7 | pub unsafe fn call_supervisor_interrupt(ctx: &mut SupervisorContext) { 8 | let mut mstatus: usize; 9 | asm!("csrr {}, mstatus", out(reg) mstatus); 10 | // set mstatus.mprv 11 | mstatus |= 1 << 17; 12 | // it may trap from U/S Mode 13 | // save mpp and set mstatus.mpp to S Mode 14 | let mpp = (mstatus >> 11) & 3; 15 | mstatus = mstatus & !(3 << 11); 16 | mstatus |= 1 << 11; 17 | // drop mstatus.mprv protection 18 | asm!("csrw mstatus, {}", in(reg) mstatus); 19 | // compiler helps us save/restore caller-saved registers 20 | devintr(); 21 | // restore mstatus 22 | mstatus = mstatus & !(3 << 11); 23 | mstatus |= mpp << 11; 24 | mstatus -= 1 << 17; 25 | asm!("csrw mstatus, {}", in(reg) mstatus); 26 | ctx.mstatus = mstatus::read(); 27 | } 28 | 29 | // We use implementation specific sbi_rustsbi_k210_sext function (extension 30 | // id: 0x0A000004, function id: 0x210) to register S-level interrupt handler 31 | // for K210 chip only. This chip uses 1.9.1 version of privileged spec, 32 | // which did not declare any S-level external interrupts. 33 | #[inline] 34 | pub fn emulate_sbi_rustsbi_k210_sext(ctx: &mut SupervisorContext) -> bool { 35 | if ctx.a7 == 0x0A000004 && ctx.a6 == 0x210 { 36 | unsafe { 37 | DEVINTRENTRY = ctx.a0; 38 | } 39 | // enable mext 40 | unsafe { 41 | mie::set_mext(); 42 | } 43 | // return values 44 | ctx.a0 = 0; // SbiRet::error = SBI_SUCCESS 45 | ctx.a1 = 0; // SbiRet::value = 0 46 | ctx.mepc = ctx.mepc.wrapping_add(4); // PC += 4 47 | return true; 48 | } else { 49 | return false; 50 | } 51 | } 52 | 53 | fn devintr() { 54 | #[cfg(target_arch = "riscv")] 55 | unsafe { 56 | // call devintr defined in application 57 | // we have to ask compiler save ra explicitly 58 | asm!("jalr 0({})", in(reg) DEVINTRENTRY, lateout("ra") _); 59 | } 60 | } 61 | 62 | // Due to legacy 1.9.1 version of privileged spec, if we are in S-level 63 | // timer handler (delegated from M mode), and we call SBI's `set_timer`, 64 | // a M-level external interrupt may be triggered. This may try to obtain 65 | // data structures locked previously by S-level interrupt handler, which 66 | // results in a deadlock. 67 | // Ref: https://github.com/luojia65/rustsbi/pull/5 68 | pub fn preprocess_supervisor_external(ctx: &mut SupervisorContext) { 69 | if ctx.a7 == 0x0 { 70 | unsafe { 71 | let mtip = mip::read().mtimer(); 72 | if mtip { 73 | if DEVINTRENTRY != 0 { 74 | mie::set_mext(); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | pub fn forward_supervisor_timer() { 82 | // Forward to S-level timer interrupt 83 | unsafe { 84 | mip::set_stimer(); // set S-timer interrupt flag 85 | mie::clear_mext(); // Ref: rustsbi Pull request #5 86 | mie::clear_mtimer(); // mask M-timer interrupt 87 | } 88 | } 89 | 90 | pub fn forward_supervisor_soft() { 91 | // Forward to S-level software interrupt 92 | unsafe { 93 | mip::set_ssoft(); // set S-soft interrupt flag 94 | mie::clear_msoft(); // mask M-soft interrupt 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /xtask/src/detect.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs, 3 | io::{self, Write}, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use serialport::{SerialPortType, UsbPortInfo}; 8 | 9 | pub fn detect_serial_ports() -> Option<(String, UsbPortInfo)> { 10 | let ports = serialport::available_ports().expect("list available ports"); 11 | let mut ans = Vec::new(); 12 | for p in ports { 13 | if let SerialPortType::UsbPort(info) = p.port_type { 14 | if info.vid == 0x1a86 && info.pid == 0x7523 { 15 | // CH340 16 | ans.push((p.port_name, info)); 17 | } else if info.vid == 0x0403 && info.pid == 0x6010 { 18 | // FT2232 19 | ans.push((p.port_name, info)); 20 | } 21 | } 22 | } 23 | if ans.len() == 0 { 24 | return None; 25 | } else if ans.len() == 1 { 26 | return Some(ans[0].clone()); 27 | } else { 28 | let mut name_list = String::new(); 29 | for (i, (e, _)) in ans.iter().enumerate() { 30 | name_list += e; 31 | if i != ans.len() - 1 { 32 | name_list += ", "; 33 | } 34 | } // fixme: rewrite using Join trait 35 | let mut input = String::new(); 36 | println!("xtask: multiple serial ports detected."); 37 | for (port_name, info) in ans.iter() { 38 | dump_port(port_name, info); 39 | } 40 | let stdin = io::stdin(); 41 | let mut stdout = io::stdout(); 42 | let (port_name, info) = 'outer: loop { 43 | print!("xtask: please select one port [{}]: ", name_list); 44 | stdout.flush().unwrap(); 45 | stdin.read_line(&mut input).expect("read line"); 46 | for (port_name, info) in ans.iter() { 47 | if input.trim().eq_ignore_ascii_case(port_name) { 48 | break 'outer (port_name, info); 49 | } 50 | } 51 | eprintln!( 52 | "Input '{}' does not match to any ports! Please input again.", 53 | input.trim() 54 | ); 55 | input.clear(); 56 | }; 57 | return Some((port_name.clone(), info.clone())); 58 | } 59 | } 60 | 61 | pub fn dump_port(port_name: &str, info: &UsbPortInfo) { 62 | print!( 63 | "Port {}: vid: {:x}, pid: {:x}", 64 | port_name, info.vid, info.pid 65 | ); 66 | if let Some(serial_number) = &info.serial_number { 67 | print!(", serial number: {}", serial_number) 68 | } 69 | if let Some(manufacturer) = &info.manufacturer { 70 | print!(", manufacturer: {}", manufacturer) 71 | } 72 | if let Some(product) = &info.product { 73 | print!(", product: {}", product) 74 | } 75 | println!() 76 | } 77 | 78 | pub fn save_to_file(port_name: &str) { 79 | fs::create_dir_all(project_root().join("target").join("xtask")).expect("create folder"); 80 | let mut file = fs::OpenOptions::new() 81 | .read(true) 82 | .write(true) 83 | .create(true) 84 | .open( 85 | project_root() 86 | .join("target") 87 | .join("xtask") 88 | .join("serial-port.txt"), 89 | ) 90 | .expect("create and open file"); 91 | file.write(port_name.as_bytes()).expect("write file"); 92 | } 93 | 94 | pub fn read_serial_port_choose_file() -> io::Result { 95 | fs::read_to_string( 96 | project_root() 97 | .join("target") 98 | .join("xtask") 99 | .join("serial-port.txt"), 100 | ) 101 | } 102 | 103 | fn project_root() -> PathBuf { 104 | Path::new(&env!("CARGO_MANIFEST_DIR")) 105 | .ancestors() 106 | .nth(1) 107 | .unwrap() 108 | .to_path_buf() 109 | } 110 | -------------------------------------------------------------------------------- /test-kernel/src/sbi.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use core::arch::asm; 3 | 4 | pub const EXTENSION_BASE: usize = 0x10; 5 | pub const EXTENSION_TIMER: usize = 0x54494D45; 6 | pub const EXTENSION_IPI: usize = 0x735049; 7 | pub const EXTENSION_RFENCE: usize = 0x52464E43; 8 | pub const EXTENSION_HSM: usize = 0x48534D; 9 | pub const EXTENSION_SRST: usize = 0x53525354; 10 | 11 | const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0; 12 | const FUNCTION_BASE_GET_SBI_IMPL_ID: usize = 0x1; 13 | const FUNCTION_BASE_GET_SBI_IMPL_VERSION: usize = 0x2; 14 | const FUNCTION_BASE_PROBE_EXTENSION: usize = 0x3; 15 | const FUNCTION_BASE_GET_MVENDORID: usize = 0x4; 16 | const FUNCTION_BASE_GET_MARCHID: usize = 0x5; 17 | const FUNCTION_BASE_GET_MIMPID: usize = 0x6; 18 | 19 | #[repr(C)] 20 | pub struct SbiRet { 21 | /// Error number 22 | pub error: usize, 23 | /// Result value 24 | pub value: usize, 25 | } 26 | 27 | #[inline(always)] 28 | fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet { 29 | let (error, value); 30 | match () { 31 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 32 | () => unsafe { 33 | asm!( 34 | "ecall", 35 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 36 | in("a6") function, in("a7") extension, 37 | lateout("a0") error, lateout("a1") value, 38 | ) 39 | }, 40 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 41 | () => { 42 | drop((extension, function, arg0, arg1, arg2)); 43 | unimplemented!("not RISC-V instruction set architecture") 44 | } 45 | }; 46 | SbiRet { error, value } 47 | } 48 | 49 | #[inline] 50 | pub fn get_spec_version() -> usize { 51 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0, 0).value 52 | } 53 | 54 | #[inline] 55 | pub fn get_sbi_impl_id() -> usize { 56 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_ID, 0, 0, 0).value 57 | } 58 | 59 | #[inline] 60 | pub fn get_sbi_impl_version() -> usize { 61 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_VERSION, 0, 0, 0).value 62 | } 63 | 64 | #[inline] 65 | pub fn probe_extension(extension_id: usize) -> usize { 66 | sbi_call( 67 | EXTENSION_BASE, 68 | FUNCTION_BASE_PROBE_EXTENSION, 69 | extension_id, 70 | 0, 71 | 0, 72 | ) 73 | .value 74 | } 75 | 76 | #[inline] 77 | pub fn get_mvendorid() -> usize { 78 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MVENDORID, 0, 0, 0).value 79 | } 80 | 81 | #[inline] 82 | pub fn get_marchid() -> usize { 83 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MARCHID, 0, 0, 0).value 84 | } 85 | 86 | #[inline] 87 | pub fn get_mimpid() -> usize { 88 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MIMPID, 0, 0, 0).value 89 | } 90 | 91 | #[inline(always)] 92 | fn sbi_call_legacy(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 93 | let ret; 94 | match () { 95 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 96 | () => unsafe { 97 | asm!( 98 | "ecall", 99 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 100 | in("a7") which, 101 | lateout("a0") ret, 102 | ) 103 | }, 104 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 105 | () => { 106 | drop((which, arg0, arg1, arg2)); 107 | unimplemented!("not RISC-V instruction set architecture") 108 | } 109 | }; 110 | ret 111 | } 112 | 113 | const SBI_SET_TIMER: usize = 0; 114 | const SBI_CONSOLE_PUTCHAR: usize = 1; 115 | const SBI_CONSOLE_GETCHAR: usize = 2; 116 | const SBI_CLEAR_IPI: usize = 3; 117 | const SBI_SEND_IPI: usize = 4; 118 | const SBI_REMOTE_FENCE_I: usize = 5; 119 | const SBI_REMOTE_SFENCE_VMA: usize = 6; 120 | const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; 121 | const SBI_SHUTDOWN: usize = 8; 122 | 123 | pub fn console_putchar(c: usize) { 124 | sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c, 0, 0); 125 | } 126 | 127 | pub fn console_getchar() -> usize { 128 | sbi_call_legacy(SBI_CONSOLE_GETCHAR, 0, 0, 0) 129 | } 130 | 131 | pub fn shutdown() -> ! { 132 | sbi_call_legacy(SBI_SHUTDOWN, 0, 0, 0); 133 | unreachable!() 134 | } 135 | 136 | pub fn set_timer(time: usize) { 137 | sbi_call_legacy(SBI_SET_TIMER, time, 0, 0); 138 | } 139 | -------------------------------------------------------------------------------- /rustsbi-k210/src/execute.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use riscv::register::scause::{Exception, Trap}; 3 | 4 | use crate::feature; 5 | use crate::runtime::{MachineTrap, Runtime, SupervisorContext}; 6 | 7 | pub fn execute_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> ! { 8 | let mut rt = Runtime::new_sbi_supervisor(supervisor_mepc, a0, a1); 9 | loop { 10 | match rt.next() { 11 | Some(MachineTrap::SbiCall()) => { 12 | let ctx = rt.context_mut(); 13 | if emulate_sbi_call(ctx) { 14 | continue; 15 | } 16 | feature::preprocess_supervisor_external(ctx); // specific for 1.9.1; see document for details 17 | let param = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5]; 18 | let ans = rustsbi::ecall(ctx.a7, ctx.a6, param); 19 | ctx.a0 = ans.error; 20 | ctx.a1 = ans.value; 21 | ctx.mepc = ctx.mepc.wrapping_add(4); 22 | } 23 | Some(MachineTrap::IllegalInstruction()) => { 24 | let ctx = rt.context_mut(); 25 | // FIXME: get_vaddr_u32这个过程可能出错。 26 | let ins = unsafe { get_vaddr_u32(ctx.mepc) } as usize; 27 | if !emulate_illegal_instruction(ctx, ins) { 28 | unsafe { 29 | if feature::should_transfer_trap(ctx) { 30 | feature::do_transfer_trap( 31 | ctx, 32 | Trap::Exception(Exception::IllegalInstruction), 33 | ) 34 | } else { 35 | fail_illegal_instruction(ctx, ins) 36 | } 37 | } 38 | } 39 | } 40 | Some(MachineTrap::ExternalInterrupt()) => unsafe { 41 | let ctx = rt.context_mut(); 42 | feature::call_supervisor_interrupt(ctx) 43 | }, 44 | Some(MachineTrap::MachineTimer()) => feature::forward_supervisor_timer(), 45 | Some(MachineTrap::MachineSoft()) => feature::forward_supervisor_soft(), 46 | // todo:编写样例,验证store page fault和instruction page fault 47 | Some(MachineTrap::InstructionFault(addr)) => { 48 | let ctx = rt.context_mut(); 49 | if feature::is_page_fault(addr) { 50 | unsafe { 51 | feature::do_transfer_trap( 52 | ctx, 53 | Trap::Exception(Exception::InstructionPageFault), 54 | ) 55 | } 56 | } else { 57 | unsafe { 58 | feature::do_transfer_trap(ctx, Trap::Exception(Exception::InstructionFault)) 59 | } 60 | } 61 | } 62 | Some(MachineTrap::LoadFault(addr)) => { 63 | let ctx = rt.context_mut(); 64 | if feature::is_page_fault(addr) { 65 | unsafe { 66 | feature::do_transfer_trap(ctx, Trap::Exception(Exception::LoadPageFault)) 67 | } 68 | } else { 69 | unsafe { feature::do_transfer_trap(ctx, Trap::Exception(Exception::LoadFault)) } 70 | } 71 | } 72 | Some(MachineTrap::StoreFault(addr)) => { 73 | let ctx = rt.context_mut(); 74 | if feature::is_page_fault(addr) { 75 | unsafe { 76 | feature::do_transfer_trap(ctx, Trap::Exception(Exception::StorePageFault)) 77 | } 78 | } else { 79 | unsafe { 80 | feature::do_transfer_trap(ctx, Trap::Exception(Exception::StoreFault)) 81 | } 82 | } 83 | } 84 | None => unreachable!(), 85 | } 86 | } 87 | } 88 | 89 | #[inline] 90 | unsafe fn get_vaddr_u32(vaddr: usize) -> u32 { 91 | get_vaddr_u16(vaddr) as u32 | ((get_vaddr_u16(vaddr.wrapping_add(2)) as u32) << 16) 92 | } 93 | 94 | #[inline] 95 | unsafe fn get_vaddr_u16(vaddr: usize) -> u16 { 96 | let mut ans: u16; 97 | asm!(" 98 | li {2}, (1 << 17) 99 | csrrs {2}, mstatus, {2} 100 | lhu {0}, 0({1}) 101 | csrw mstatus, {2} 102 | ", out(reg) ans, in(reg) vaddr, out(reg) _); 103 | ans 104 | } 105 | 106 | fn emulate_sbi_call(ctx: &mut SupervisorContext) -> bool { 107 | if feature::emulate_sbi_rustsbi_k210_sext(ctx) { 108 | return true; 109 | } 110 | false 111 | } 112 | 113 | fn emulate_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> bool { 114 | if feature::emulate_rdtime(ctx, ins) { 115 | return true; 116 | } 117 | if feature::emulate_sfence_vma(ctx, ins) { 118 | return true; 119 | } 120 | false 121 | } 122 | 123 | // 真·非法指令异常,是M层出现的 124 | fn fail_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> ! { 125 | panic!("invalid instruction from machine level, mepc: {:016x?}, instruction: {:016x?}, context: {:016x?}", ctx.mepc, ins, ctx); 126 | } 127 | -------------------------------------------------------------------------------- /rustsbi-k210/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions)] 4 | #![feature(asm_const)] 5 | #![feature(riscv_ext_intrinsics)] 6 | 7 | mod execute; 8 | mod feature; 9 | mod hart_csr_utils; 10 | mod peripheral; 11 | mod runtime; 12 | 13 | extern crate alloc; 14 | 15 | use buddy_system_allocator::LockedHeap; 16 | use core::arch::asm; 17 | use core::panic::PanicInfo; 18 | 19 | use rustsbi::println; 20 | 21 | const PER_HART_STACK_SIZE: usize = 8 * 1024; // 8KiB 22 | const SBI_STACK_SIZE: usize = 2 * PER_HART_STACK_SIZE; 23 | #[link_section = ".bss.uninit"] 24 | static mut SBI_STACK: [u8; SBI_STACK_SIZE] = [0; SBI_STACK_SIZE]; 25 | 26 | const SBI_HEAP_SIZE: usize = 8 * 1024; // 8KiB 27 | #[link_section = ".bss.uninit"] 28 | static mut HEAP_SPACE: [u8; SBI_HEAP_SIZE] = [0; SBI_HEAP_SIZE]; 29 | #[global_allocator] 30 | static SBI_HEAP: LockedHeap<32> = LockedHeap::empty(); 31 | 32 | static DEVICE_TREE_BINARY: &[u8] = include_bytes!("../kendryte-k210.dtb"); 33 | 34 | #[cfg_attr(not(test), panic_handler)] 35 | #[allow(unused)] 36 | fn panic(info: &PanicInfo) -> ! { 37 | let hart_id = riscv::register::mhartid::read(); 38 | // 输出的信息大概是“[rustsbi-panic] hart 0 panicked at ...” 39 | println!("[rustsbi-panic] hart {} {}", hart_id, info); 40 | println!("[rustsbi-panic] system shutdown scheduled due to RustSBI panic"); 41 | use rustsbi::Reset; 42 | peripheral::Reset.system_reset( 43 | rustsbi::reset::RESET_TYPE_SHUTDOWN, 44 | rustsbi::reset::RESET_REASON_SYSTEM_FAILURE, 45 | ); 46 | loop {} 47 | } 48 | 49 | extern "C" fn rust_main() -> ! { 50 | let hartid = riscv::register::mhartid::read(); 51 | if hartid == 0 { 52 | init_bss(); 53 | } 54 | pause_if_not_start_hart(); 55 | runtime::init(); 56 | if hartid == 0 { 57 | init_heap(); 58 | peripheral::init_peripheral(); 59 | println!("[rustsbi] RustSBI version {}", rustsbi::VERSION); 60 | println!("{}", rustsbi::LOGO); 61 | println!( 62 | "[rustsbi] Implementation: RustSBI-K210 Version {}", 63 | env!("CARGO_PKG_VERSION") 64 | ); 65 | } 66 | delegate_interrupt_exception(); 67 | if hartid == 0 { 68 | hart_csr_utils::print_hart_csrs(); 69 | println!("[rustsbi] enter supervisor 0x80020000"); 70 | } 71 | execute::execute_supervisor(0x80020000, hartid, DEVICE_TREE_BINARY.as_ptr() as usize) 72 | } 73 | 74 | fn pause_if_not_start_hart() { 75 | use k210_hal::clint::msip; 76 | use riscv::asm::wfi; 77 | use riscv::register::{mhartid, mie, mip}; 78 | 79 | let hartid = mhartid::read(); 80 | if hartid != 0 { 81 | unsafe { 82 | // Clear IPI 83 | msip::clear_ipi(hartid); 84 | // Start listening for software interrupts 85 | mie::set_msoft(); 86 | 87 | loop { 88 | wfi(); 89 | if mip::read().msoft() { 90 | break; 91 | } 92 | } 93 | 94 | // Stop listening for software interrupts 95 | mie::clear_msoft(); 96 | // Clear IPI 97 | msip::clear_ipi(hartid); 98 | } 99 | } 100 | } 101 | 102 | fn init_bss() { 103 | extern "C" { 104 | static mut ebss: u32; 105 | static mut sbss: u32; 106 | static mut edata: u32; 107 | static mut sdata: u32; 108 | static sidata: u32; 109 | } 110 | unsafe { 111 | r0::zero_bss(&mut sbss, &mut ebss); 112 | r0::init_data(&mut sdata, &mut edata, &sidata); 113 | } 114 | } 115 | 116 | fn init_heap() { 117 | unsafe { 118 | SBI_HEAP 119 | .lock() 120 | .init(HEAP_SPACE.as_ptr() as usize, SBI_HEAP_SIZE) 121 | } 122 | } 123 | 124 | // 委托终端;把S的中断全部委托给S层 125 | fn delegate_interrupt_exception() { 126 | use riscv::register::{medeleg, mideleg, mie}; 127 | unsafe { 128 | //mideleg::set_sext(); 129 | mideleg::set_stimer(); 130 | mideleg::set_ssoft(); 131 | medeleg::set_instruction_misaligned(); 132 | medeleg::set_breakpoint(); 133 | medeleg::set_user_env_call(); 134 | /* MMU Exception Delegation 135 | /* Page Faults are *Reserved* in 1.9.1 version */ 136 | - medeleg::set_instruction_page_fault(); 137 | - medeleg::set_load_page_fault(); 138 | - medeleg::set_store_page_fault(); 139 | /* Actually, in 1.9.1 they are merged into more general exceptions */ 140 | + medeleg::set_instruction_fault(); 141 | + medeleg::set_load_fault(); 142 | + medeleg::set_store_fault(); */ 143 | // medeleg::set_instruction_fault(); 144 | // medeleg::set_load_fault(); 145 | // medeleg::set_store_fault(); 146 | // 默认不打开mie::set_mext 147 | // 不打开mie::set_mtimer 148 | mie::set_msoft(); 149 | } 150 | } 151 | 152 | #[naked] 153 | #[link_section = ".text.entry"] 154 | #[export_name = "_start"] 155 | unsafe extern "C" fn entry() -> ! { 156 | asm!( 157 | // 1. set sp 158 | // sp = bootstack + (hartid + 1) * HART_STACK_SIZE 159 | " 160 | la sp, {stack} 161 | li t0, {per_hart_stack_size} 162 | csrr a0, mhartid 163 | addi t1, a0, 1 164 | 1: add sp, sp, t0 165 | addi t1, t1, -1 166 | bnez t1, 1b 167 | ", 168 | // 2. jump to rust_main (absolute address) 169 | "j {rust_main}", 170 | per_hart_stack_size = const PER_HART_STACK_SIZE, 171 | stack = sym SBI_STACK, 172 | rust_main = sym rust_main, 173 | options(noreturn)) 174 | } 175 | -------------------------------------------------------------------------------- /test-kernel/src/feature/catch_page_fault.rs: -------------------------------------------------------------------------------- 1 | use crate::{println, sbi}; 2 | use core::arch::{asm, riscv64::sfence_vma_all}; 3 | use riscv::register::{ 4 | satp::{self, Mode}, 5 | scause::{self, Exception, Trap}, 6 | sepc, 7 | stvec::{self, TrapMode}, 8 | }; 9 | 10 | #[repr(align(4096))] 11 | struct PageTable { 12 | #[allow(unused)] // Will be used by RISC-V hardware 13 | entries: [usize; 512], 14 | } 15 | 16 | static mut TEST_PAGE_TABLE_0: PageTable = PageTable { entries: [0; 512] }; 17 | static mut TEST_PAGE_TABLE_1: PageTable = PageTable { entries: [0; 512] }; 18 | static mut TEST_PAGE_TABLE_2: PageTable = PageTable { entries: [0; 512] }; 19 | 20 | pub fn test_catch_page_fault() { 21 | println!(">> Test-kernel: Testing catch page fault"); 22 | init_trap_vector(); 23 | let ppn = init_page_table(); 24 | unsafe { satp::set(Mode::Sv39, 0, ppn) }; 25 | unsafe { sfence_vma_all() }; 26 | unsafe { 27 | println!(">> Test-kernel: Wrong sign extension"); 28 | assert!(is_read_page_fault(0xfeff_ff80_0000_0000 as *const usize)); 29 | assert!(is_read_page_fault(0x0100_0000_0000_0000 as *const usize)); 30 | } 31 | unsafe { 32 | println!(">> Test-kernel: Read from invalid entry"); 33 | assert!(is_read_page_fault(0x1_0000_0000 as *const usize)); 34 | assert!(is_read_page_fault(0x0_c040_0000 as *const usize)); 35 | assert!(is_read_page_fault(0x0_c020_2000 as *const usize)); 36 | }; 37 | // unsafe { 38 | println!(">> Test-kernel: Unaligned huge page"); 39 | // assert!(is_read_page_fault(0x1_4000_0000 as *const usize)); 40 | // assert!(is_read_page_fault(0x0_c060_0000 as *const usize)); 41 | // }; 42 | // unsafe { 43 | println!(">> Test-kernel: Non existing page"); 44 | // assert!(is_read_page_fault(0x1_8000_0000 as *const usize)); 45 | // assert!(is_read_page_fault(0x0_c080_0000 as *const usize)); 46 | // assert!(is_read_page_fault(0x0_c020_3000 as *const usize)); 47 | // }; 48 | unsafe { 49 | println!(">> Test-kernel: Level zero page cannot have leaves"); 50 | assert!(is_read_page_fault(0x0_c020_1000 as *const usize)); 51 | }; 52 | } 53 | 54 | fn init_page_table() -> usize { 55 | let ppn1 = (unsafe { &TEST_PAGE_TABLE_1 } as *const _ as usize) >> 12; 56 | let ppn2 = (unsafe { &TEST_PAGE_TABLE_2 } as *const _ as usize) >> 12; 57 | unsafe { 58 | TEST_PAGE_TABLE_0.entries[2] = (0x80000 << 10) | 0xf; // RWX, V 59 | TEST_PAGE_TABLE_0.entries[3] = (ppn1 << 10) | 0x1; // 叶子, V 60 | TEST_PAGE_TABLE_0.entries[4] = 0; // 无效 61 | TEST_PAGE_TABLE_0.entries[5] = (0x80200 << 10) | 0xf; // RWX, V 62 | TEST_PAGE_TABLE_0.entries[6] = (0x7ffff << 10) | 0xf; // RWX, V 63 | TEST_PAGE_TABLE_0.entries[7] = (0x80000 << 10) | 0x7; // RW, V 64 | } 65 | unsafe { 66 | TEST_PAGE_TABLE_1.entries[1] = (ppn2 << 10) | 0x1; // 叶子, V 67 | TEST_PAGE_TABLE_1.entries[2] = 0; // 无效 68 | TEST_PAGE_TABLE_1.entries[3] = (0x80201 << 10) | 0xf; // RWX, V 69 | TEST_PAGE_TABLE_1.entries[4] = (0x7ffff << 10) | 0xf; // RWX, V 70 | TEST_PAGE_TABLE_1.entries[5] = (0x80200 << 10) | 0x3; // R, V 71 | } 72 | unsafe { 73 | TEST_PAGE_TABLE_2.entries[1] = (0x80200 << 10) | 0x1; // 叶子, V 74 | TEST_PAGE_TABLE_2.entries[2] = 0; // 无效 75 | TEST_PAGE_TABLE_2.entries[3] = (0x7ffff << 10) | 0xf; // RWX, V 76 | TEST_PAGE_TABLE_2.entries[4] = (0x80200 << 10) | 0x9; // X, V 77 | } 78 | let pa = unsafe { &TEST_PAGE_TABLE_0 } as *const _ as usize; 79 | let ppn = pa >> 12; 80 | ppn 81 | } 82 | 83 | #[derive(Debug)] 84 | struct NotPageFault; 85 | 86 | unsafe fn is_read_page_fault(ptr: *const usize) -> bool { 87 | let saved_stvec_address = init_trap_vector(); 88 | let _ans: usize; 89 | asm!("li tp, 0"); 90 | asm!("ld {}, 0({})", out(reg) _ans, in(reg) ptr); 91 | let is_page_fault: usize; 92 | asm!("mv {}, tp", out(reg) is_page_fault); 93 | let ans = is_page_fault == 1; 94 | recover_trap_vector(saved_stvec_address); 95 | return ans; 96 | } 97 | 98 | fn init_trap_vector() -> usize { 99 | let mut addr = delegate_test_trap as usize; 100 | if addr & 0x2 != 0 { 101 | addr = addr.wrapping_add(0x2); // 必须对齐到4个字节 102 | } 103 | let saved_stvec_address = stvec::read().address(); 104 | unsafe { stvec::write(addr, TrapMode::Direct) }; 105 | saved_stvec_address 106 | } 107 | 108 | fn recover_trap_vector(saved_stvec_address: usize) { 109 | unsafe { stvec::write(saved_stvec_address, TrapMode::Direct) } 110 | } 111 | 112 | extern "C" fn rust_test_trap_handler() { 113 | let cause = scause::read().cause(); 114 | if cause != Trap::Exception(Exception::LoadPageFault) { 115 | println!( 116 | "!! Test-kernel: Wrong cause associated to page fault, sepc: {:#x}, stval: {:#x}", 117 | riscv::register::sepc::read(), 118 | riscv::register::stval::read() 119 | ); 120 | sbi::shutdown() 121 | } 122 | unsafe { asm!("li tp, 1") }; // tp = 1 说明是缺页异常 123 | let bad_ins_addr = sepc::read(); 124 | let ins_16 = unsafe { core::ptr::read_volatile(bad_ins_addr as *const u16) }; 125 | let bytes = if ins_16 & 0b11 != 0b11 { 2 } else { 4 }; 126 | sepc::write(sepc::read().wrapping_add(bytes)); // skip current instruction 127 | } 128 | 129 | #[naked] 130 | #[link_section = ".text"] 131 | unsafe extern "C" fn delegate_test_trap() -> ! { 132 | asm!( 133 | ".align 4", // align to 4 bytes 134 | "addi sp, sp, -8*16 135 | sd ra, 8*0(sp) 136 | sd t0, 8*1(sp) 137 | sd t1, 8*2(sp) 138 | sd t2, 8*3(sp) 139 | sd t3, 8*4(sp) 140 | sd t4, 8*5(sp) 141 | sd t5, 8*6(sp) 142 | sd t6, 8*7(sp) 143 | sd a0, 8*8(sp) 144 | sd a1, 8*9(sp) 145 | sd a2, 8*10(sp) 146 | sd a3, 8*11(sp) 147 | sd a4, 8*12(sp) 148 | sd a5, 8*13(sp) 149 | sd a6, 8*14(sp) 150 | sd a7, 8*15(sp)", 151 | "call {rust_test_trap_handler}", 152 | "ld ra, 8*0(sp) 153 | ld t0, 8*1(sp) 154 | ld t1, 8*2(sp) 155 | ld t2, 8*3(sp) 156 | ld t3, 8*4(sp) 157 | ld t4, 8*5(sp) 158 | ld t5, 8*6(sp) 159 | ld t6, 8*7(sp) 160 | ld a0, 8*8(sp) 161 | ld a1, 8*9(sp) 162 | ld a2, 8*10(sp) 163 | ld a3, 8*11(sp) 164 | ld a4, 8*12(sp) 165 | ld a5, 8*13(sp) 166 | ld a6, 8*14(sp) 167 | ld a7, 8*15(sp) 168 | addi sp, sp, 8*16", 169 | "sret", 170 | rust_test_trap_handler = sym rust_test_trap_handler, 171 | options(noreturn) 172 | ) 173 | } 174 | -------------------------------------------------------------------------------- /rustsbi-k210/src/feature/delegate_page_fault.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use riscv::register::{ 3 | mcause::{self, Exception, Trap}, 4 | mepc, 5 | mtvec::{self, TrapMode}, 6 | }; 7 | 8 | // This function will lookup virtual memory module and page table system 9 | // if memory fault from address `addr` is a page fault, return true 10 | // otherwise when not a page fault, or when paging is disabled, return false 11 | pub fn is_page_fault(addr: usize) -> bool { 12 | if !is_s1p9_mstatus_sv39_mode() { 13 | return false; 14 | } 15 | if !check_sext_sv39(addr) { 16 | return true; 17 | } 18 | let base_ppn = read_sptbr_ppn(); 19 | let level_2_ppn = unsafe { 20 | let vpn2 = (addr >> 30) & 0x1FF; 21 | let ptr = ((base_ppn << 12) as *const usize).add(vpn2); 22 | let level_2_pte = if let Ok(ans) = try_read_address(ptr) { 23 | ans 24 | } else { 25 | // level 2 ppn read failed 26 | return true; 27 | }; 28 | if (level_2_pte & 0b1) == 0 { 29 | // level 2 pte is not valid 30 | return true; 31 | } 32 | if (level_2_pte & 0b1110) != 0b0000 && (level_2_pte >> 10) & 0x3FFFF != 0 { 33 | // 大页对齐出错,返回页异常 34 | // level 2 huge page align not satisfied 35 | return true; 36 | } 37 | (level_2_pte >> 10) & 0x3F_FFFF_FFFF 38 | }; 39 | let level_1_ppn = unsafe { 40 | let vpn1 = (addr >> 21) & 0x1FF; 41 | let ptr = ((level_2_ppn << 12) as *const usize).add(vpn1); 42 | let level_1_pte = if let Ok(ans) = try_read_address(ptr) { 43 | ans 44 | } else { 45 | // level 1 ppn read failed 46 | return true; 47 | }; 48 | if (level_1_pte & 0b1) == 0 { 49 | // level 1 pte is not valid 50 | return true; 51 | } 52 | if (level_1_pte & 0b1110) != 0b0000 && (level_1_pte >> 10) & 0x1FF != 0 { 53 | // 大页对齐出错,返回页异常 54 | // level 1 huge page align not satisfied 55 | return true; 56 | } 57 | (level_1_pte >> 10) & 0x3F_FFFF_FFFF 58 | }; 59 | let _ppn = unsafe { 60 | let vpn0 = (addr >> 12) & 0x1FF; 61 | let ptr = ((level_1_ppn << 12) as *const usize).add(vpn0); 62 | let final_pte = if let Ok(ans) = try_read_address(ptr) { 63 | ans 64 | } else { 65 | // level 0 ppn read failed 66 | return true; 67 | }; 68 | if (final_pte & 0b1) == 0 { 69 | // level 0 pte is not valid 70 | return true; 71 | } 72 | if (final_pte & 0b1110) == 0b0000 { 73 | // level 0 page cannot have leaves 74 | return true; 75 | } 76 | (final_pte >> 10) & 0x3F_FFFF_FFFF 77 | }; 78 | // 到这一步都没有错误,说明查找是成功的,并非页异常 79 | false 80 | } 81 | 82 | // read Privileged Spec v1.9 defined mstatus to decide virtual memory mode 83 | // 9 -> Sv39 84 | fn is_s1p9_mstatus_sv39_mode() -> bool { 85 | let mut mstatus_bits: usize; 86 | unsafe { asm!("csrr {}, mstatus", out(reg) mstatus_bits) }; 87 | let mode = (mstatus_bits >> 24) & 0b1_1111; 88 | mode == 9 89 | } 90 | 91 | // if sext is not valid, raise a page fault 92 | fn check_sext_sv39(addr: usize) -> bool { 93 | let addr_b38 = (addr >> 38) & 0b1 == 1; 94 | let sext = addr >> 39; 95 | if addr_b38 && sext == 0x1FFFFFF { 96 | return true; 97 | } 98 | if !addr_b38 && sext == 0 { 99 | return true; 100 | } 101 | false 102 | } 103 | 104 | // get Privileged Spec v1.9 defined sptbr root page table base 105 | fn read_sptbr_ppn() -> usize { 106 | let sptbr_bits: usize; 107 | unsafe { asm!("csrr {}, 0x180", out(reg) sptbr_bits) }; 108 | sptbr_bits & 0xFFF_FFFF_FFFF 109 | } 110 | 111 | #[derive(Debug)] 112 | struct LoadAccessFault; 113 | 114 | unsafe fn try_read_address(ptr: *const usize) -> Result { 115 | let saved_mtvec_address = init_trap_vector(); 116 | let ans: usize; 117 | asm!("li tp, 0"); 118 | asm!("ld {}, 0({})", out(reg) ans, in(reg) ptr); 119 | let has_error: usize; 120 | asm!("mv {}, tp", out(reg) has_error); 121 | let ans = if has_error == 1 { 122 | Err(LoadAccessFault) 123 | } else { 124 | Ok(ans) 125 | }; 126 | recover_trap_vector(saved_mtvec_address); 127 | return ans; 128 | } 129 | 130 | extern "C" fn memory_fault_catch_handler() { 131 | let cause = mcause::read().cause(); 132 | if cause == Trap::Exception(Exception::LoadFault) { 133 | unsafe { asm!("li tp, 1") }; // tp = 1 说明发生了错误 134 | } 135 | let bad_ins_addr = mepc::read(); 136 | let ins_16 = unsafe { core::ptr::read_volatile(bad_ins_addr as *const u16) }; 137 | let bytes = if ins_16 & 0b11 != 0b11 { 2 } else { 4 }; 138 | mepc::write(mepc::read().wrapping_add(bytes)); // skip current load instruction 139 | } 140 | 141 | fn init_trap_vector() -> usize { 142 | let mut addr = delegate_catch_trap as usize; 143 | if addr & 0x2 != 0 { 144 | addr = addr.wrapping_add(0x2); // 必须对齐到4个字节 145 | } 146 | let saved_mtvec_address = mtvec::read().address(); 147 | unsafe { mtvec::write(addr, TrapMode::Direct) }; 148 | saved_mtvec_address 149 | } 150 | 151 | fn recover_trap_vector(saved_mtvec_address: usize) { 152 | unsafe { mtvec::write(saved_mtvec_address, TrapMode::Direct) } 153 | } 154 | 155 | #[naked] 156 | #[link_section = ".text"] 157 | unsafe extern "C" fn delegate_catch_trap() -> ! { 158 | asm!( 159 | ".align 4", // align to 4 bytes 160 | "addi sp, sp, -8*16 161 | sd ra, 8*0(sp) 162 | sd t0, 8*1(sp) 163 | sd t1, 8*2(sp) 164 | sd t2, 8*3(sp) 165 | sd t3, 8*4(sp) 166 | sd t4, 8*5(sp) 167 | sd t5, 8*6(sp) 168 | sd t6, 8*7(sp) 169 | sd a0, 8*8(sp) 170 | sd a1, 8*9(sp) 171 | sd a2, 8*10(sp) 172 | sd a3, 8*11(sp) 173 | sd a4, 8*12(sp) 174 | sd a5, 8*13(sp) 175 | sd a6, 8*14(sp) 176 | sd a7, 8*15(sp)", 177 | "call {memory_fault_catch_handler}", 178 | "ld ra, 8*0(sp) 179 | ld t0, 8*1(sp) 180 | ld t1, 8*2(sp) 181 | ld t2, 8*3(sp) 182 | ld t3, 8*4(sp) 183 | ld t4, 8*5(sp) 184 | ld t5, 8*6(sp) 185 | ld t6, 8*7(sp) 186 | ld a0, 8*8(sp) 187 | ld a1, 8*9(sp) 188 | ld a2, 8*10(sp) 189 | ld a3, 8*11(sp) 190 | ld a4, 8*12(sp) 191 | ld a5, 8*13(sp) 192 | ld a6, 8*14(sp) 193 | ld a7, 8*15(sp) 194 | addi sp, sp, 8*16", 195 | "mret", 196 | memory_fault_catch_handler = sym memory_fault_catch_handler, 197 | options(noreturn) 198 | ) 199 | } 200 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | mod detect; 2 | mod test; 3 | 4 | use clap::{clap_app, crate_authors, crate_description, crate_version}; 5 | use std::{ 6 | env, fs, 7 | io::{Seek, SeekFrom, Write}, 8 | path::{Path, PathBuf}, 9 | process::{self, Command}, 10 | }; 11 | 12 | #[derive(Debug)] 13 | struct XtaskEnv { 14 | compile_mode: CompileMode, 15 | } 16 | 17 | #[derive(Debug)] 18 | enum CompileMode { 19 | Debug, 20 | Release, 21 | } 22 | 23 | const DEFAULT_TARGET: &'static str = "riscv64imac-unknown-none-elf"; 24 | 25 | fn main() { 26 | let matches = clap_app!(xtask => 27 | (version: crate_version!()) 28 | (author: crate_authors!()) 29 | (about: crate_description!()) 30 | (@subcommand make => 31 | (about: "Build project") 32 | (@arg release: --release "Build artifacts in release mode, with optimizations") 33 | ) 34 | (@subcommand k210 => 35 | (about: "Run project on actual board") 36 | (@arg release: --release "Build artifacts in release mode, with optimizations") 37 | ) 38 | (@subcommand detect => 39 | (about: "Detect target serial port") 40 | ) 41 | (@subcommand asm => 42 | (about: "View asm code for project") 43 | ) 44 | (@subcommand size => 45 | (about: "View size for project") 46 | ) 47 | ) 48 | .get_matches(); 49 | let mut xtask_env = XtaskEnv { 50 | compile_mode: CompileMode::Debug, 51 | }; 52 | // Read: python xtask/ktool.py -p COM11 -a 0x80000000 -R -L 0x20000 ./target/xtask/flash_dump.bin 53 | if let Some(matches) = matches.subcommand_matches("k210") { 54 | let port = match detect::read_serial_port_choose_file() { 55 | Ok(string) => { 56 | println!("xtask: using previously selected serial port {}.", string); 57 | string 58 | } 59 | Err(_e) => detect_save_port_or_exit(), 60 | }; 61 | let ktool_exists = fs::metadata(project_root().join("xtask").join("ktool.py")).is_ok(); 62 | if !ktool_exists { 63 | eprintln!( 64 | "xtask: ktool.py file not found 65 | To install ktool.py, download from https://github.com/loboris/ktool, 66 | then copy ktool.py file into path xtask/ktool.py." 67 | ); 68 | process::exit(1); 69 | } 70 | if matches.is_present("release") { 71 | xtask_env.compile_mode = CompileMode::Release; 72 | } 73 | println!("xtask: mode: {:?}", xtask_env.compile_mode); 74 | xtask_build_sbi(&xtask_env); 75 | xtask_binary_sbi(&xtask_env); 76 | xtask_build_test_kernel(&xtask_env); 77 | xtask_binary_test_kernel(&xtask_env); 78 | xtask_fuse_binary(&xtask_env); 79 | xtask_run_k210(&xtask_env, &port); 80 | } else if let Some(matches) = matches.subcommand_matches("make") { 81 | if matches.is_present("release") { 82 | xtask_env.compile_mode = CompileMode::Release; 83 | } 84 | println!("xtask: mode: {:?}", xtask_env.compile_mode); 85 | xtask_build_sbi(&xtask_env); 86 | xtask_binary_sbi(&xtask_env); 87 | } else if let Some(_matches) = matches.subcommand_matches("detect") { 88 | let ans = detect::detect_serial_ports(); 89 | if let Some((port_name, info)) = ans { 90 | detect::dump_port(&port_name, &info); 91 | detect::save_to_file(&port_name); 92 | } else { 93 | println!("xtask: no CH340 serial port found."); 94 | } 95 | } else { 96 | println!("Use `cargo k210` to run, `cargo xtask --help` for help") 97 | } 98 | } 99 | 100 | fn detect_save_port_or_exit() -> String { 101 | if let Some((port_name, info)) = detect::detect_serial_ports() { 102 | println!("xtask: port detected"); 103 | detect::dump_port(&port_name, &info); 104 | detect::save_to_file(&port_name); 105 | port_name 106 | } else { 107 | println!("xtask: no serial port found; program exit"); 108 | std::process::exit(1); 109 | } 110 | } 111 | 112 | // @python ./ktool.py --port {{k210-serialport}} -b 1500000 --terminal {{fused-bin}} 113 | fn xtask_run_k210(xtask_env: &XtaskEnv, port: &str) { 114 | let status = Command::new("python") 115 | .current_dir(project_root().join("xtask")) 116 | .arg("ktool.py") 117 | .args(&["--port", port]) 118 | .args(&["--baudrate", "1500000"]) // todo: configurate baudrate 119 | .arg("--terminal") 120 | .arg(dist_dir(xtask_env).join("k210-fused.bin")) 121 | .status() 122 | .unwrap(); 123 | if !status.success() { 124 | eprintln!( 125 | "xtask: run ktool.py failed with code {}", 126 | status.code().unwrap() 127 | ); 128 | process::exit(status.code().unwrap()) 129 | } 130 | } 131 | 132 | fn xtask_build_sbi(xtask_env: &XtaskEnv) { 133 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 134 | let mut command = Command::new(cargo); 135 | command.current_dir(project_root().join("rustsbi-k210")); 136 | command.arg("build"); 137 | match xtask_env.compile_mode { 138 | CompileMode::Debug => {} 139 | CompileMode::Release => { 140 | command.arg("--release"); 141 | } 142 | } 143 | command.args(&["--package", "rustsbi-k210"]); 144 | command.args(&["--target", DEFAULT_TARGET]); 145 | let status = command.status().unwrap(); 146 | if !status.success() { 147 | println!("cargo build failed"); 148 | process::exit(1); 149 | } 150 | } 151 | 152 | fn xtask_binary_sbi(xtask_env: &XtaskEnv) { 153 | let objcopy = "rust-objcopy"; 154 | let status = Command::new(objcopy) 155 | .current_dir(dist_dir(xtask_env)) 156 | .arg("rustsbi-k210") 157 | .arg("--binary-architecture=riscv64") 158 | .arg("--strip-all") 159 | .args(&["-O", "binary", "rustsbi-k210.bin"]) 160 | .status() 161 | .unwrap(); 162 | 163 | if !status.success() { 164 | println!("objcopy binary failed"); 165 | process::exit(1); 166 | } 167 | } 168 | 169 | fn xtask_build_test_kernel(xtask_env: &XtaskEnv) { 170 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 171 | let mut command = Command::new(cargo); 172 | command.current_dir(project_root().join("test-kernel")); 173 | command.arg("build"); 174 | match xtask_env.compile_mode { 175 | CompileMode::Debug => {} 176 | CompileMode::Release => { 177 | command.arg("--release"); 178 | } 179 | } 180 | command.args(&["--package", "test-kernel"]); 181 | command.args(&["--target", DEFAULT_TARGET]); 182 | let status = command.status().unwrap(); 183 | if !status.success() { 184 | println!("cargo build failed"); 185 | process::exit(1); 186 | } 187 | } 188 | 189 | fn xtask_binary_test_kernel(xtask_env: &XtaskEnv) { 190 | let objcopy = "rust-objcopy"; 191 | let status = Command::new(objcopy) 192 | .current_dir(dist_dir(xtask_env)) 193 | .arg("test-kernel") 194 | .arg("--binary-architecture=riscv64") 195 | .arg("--strip-all") 196 | .args(&["-O", "binary", "test-kernel.bin"]) 197 | .status() 198 | .unwrap(); 199 | 200 | if !status.success() { 201 | println!("objcopy binary failed"); 202 | process::exit(1); 203 | } 204 | } 205 | 206 | fn xtask_fuse_binary(xtask_env: &XtaskEnv) { 207 | let sbi_binary_path = dist_dir(xtask_env).join("rustsbi-k210.bin"); 208 | let test_kernel_binary_path = dist_dir(xtask_env).join("test-kernel.bin"); 209 | let output_path = dist_dir(xtask_env).join("k210-fused.bin"); 210 | let offset = 0x20000; 211 | fs::copy(sbi_binary_path, &output_path).expect("copy sbi base"); 212 | let mut output = fs::OpenOptions::new() 213 | .read(true) 214 | .write(true) 215 | .open(output_path) 216 | .expect("open output file"); 217 | let buf = fs::read(test_kernel_binary_path).expect("read kernel binary"); 218 | output 219 | .seek(SeekFrom::Start(offset)) 220 | .expect("seek to offset"); 221 | output.write(&buf).expect("write output"); 222 | } 223 | 224 | fn dist_dir(xtask_env: &XtaskEnv) -> PathBuf { 225 | let mut path_buf = project_root().join("target").join(DEFAULT_TARGET); 226 | path_buf = match xtask_env.compile_mode { 227 | CompileMode::Debug => path_buf.join("debug"), 228 | CompileMode::Release => path_buf.join("release"), 229 | }; 230 | path_buf 231 | } 232 | 233 | fn project_root() -> PathBuf { 234 | Path::new(&env!("CARGO_MANIFEST_DIR")) 235 | .ancestors() 236 | .nth(1) 237 | .unwrap() 238 | .to_path_buf() 239 | } 240 | -------------------------------------------------------------------------------- /rustsbi-k210/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use riscv::register::{ 3 | mcause::{self, Exception, Interrupt, Trap}, 4 | mstatus::{self, Mstatus, MPP}, 5 | mtval, 6 | mtvec::{self, TrapMode}, 7 | }; 8 | 9 | pub fn init() { 10 | let mut addr = from_supervisor_save as usize; 11 | if addr & 0x2 != 0 { 12 | addr += 0x2; // 必须对齐到4个字节 13 | } 14 | unsafe { mtvec::write(addr, TrapMode::Direct) }; 15 | } 16 | 17 | pub struct Runtime { 18 | context: SupervisorContext, 19 | } 20 | 21 | impl Runtime { 22 | pub fn new_sbi_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> Self { 23 | let context: SupervisorContext = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; 24 | let mut ans = Runtime { context }; 25 | ans.prepare_supervisor(supervisor_mepc); 26 | ans.context.a0 = a0; 27 | ans.context.a1 = a1; 28 | ans 29 | } 30 | 31 | fn reset(&mut self) { 32 | unsafe { mstatus::set_mpp(MPP::Supervisor) }; 33 | self.context.mstatus = mstatus::read(); 34 | self.context.machine_stack = 0x2333333366666666; // 将会被resume函数覆盖 35 | } 36 | 37 | // 在处理异常的时候,使用context_mut得到运行时当前用户的上下文,可以改变上下文的内容 38 | pub fn context_mut(&mut self) -> &mut SupervisorContext { 39 | &mut self.context 40 | } 41 | 42 | pub fn prepare_supervisor(&mut self, new_mepc: usize) { 43 | self.reset(); 44 | self.context.mepc = new_mepc; 45 | } 46 | } 47 | 48 | impl Iterator for Runtime { 49 | type Item = MachineTrap; 50 | 51 | fn next(&mut self) -> Option { 52 | unsafe { do_resume(&mut self.context as *mut _) }; 53 | let mtval = mtval::read(); 54 | let trap = match mcause::read().cause() { 55 | Trap::Exception(Exception::SupervisorEnvCall) => MachineTrap::SbiCall(), 56 | Trap::Exception(Exception::IllegalInstruction) => MachineTrap::IllegalInstruction(), 57 | Trap::Exception(Exception::InstructionFault) => MachineTrap::InstructionFault(mtval), 58 | Trap::Exception(Exception::LoadFault) => MachineTrap::LoadFault(mtval), 59 | Trap::Exception(Exception::StoreFault) => MachineTrap::StoreFault(mtval), 60 | Trap::Interrupt(Interrupt::MachineExternal) => MachineTrap::ExternalInterrupt(), 61 | Trap::Interrupt(Interrupt::MachineTimer) => MachineTrap::MachineTimer(), 62 | Trap::Interrupt(Interrupt::MachineSoft) => MachineTrap::MachineSoft(), 63 | e => panic!( 64 | "unhandled exception: {:?}! mtval: {:#x?}, ctx: {:#x?}", 65 | e, mtval, self.context 66 | ), 67 | }; 68 | Some(trap) 69 | } 70 | } 71 | 72 | #[repr(C)] 73 | #[derive(Debug)] 74 | pub enum MachineTrap { 75 | SbiCall(), 76 | IllegalInstruction(), 77 | ExternalInterrupt(), 78 | MachineTimer(), 79 | MachineSoft(), 80 | InstructionFault(usize), 81 | LoadFault(usize), 82 | StoreFault(usize), 83 | } 84 | 85 | #[derive(Debug)] 86 | #[repr(C)] 87 | pub struct SupervisorContext { 88 | pub ra: usize, // 0 89 | pub sp: usize, 90 | pub gp: usize, 91 | pub tp: usize, 92 | pub t0: usize, 93 | pub t1: usize, 94 | pub t2: usize, 95 | pub s0: usize, 96 | pub s1: usize, 97 | pub a0: usize, 98 | pub a1: usize, 99 | pub a2: usize, 100 | pub a3: usize, 101 | pub a4: usize, 102 | pub a5: usize, 103 | pub a6: usize, 104 | pub a7: usize, 105 | pub s2: usize, 106 | pub s3: usize, 107 | pub s4: usize, 108 | pub s5: usize, 109 | pub s6: usize, 110 | pub s7: usize, 111 | pub s8: usize, 112 | pub s9: usize, 113 | pub s10: usize, 114 | pub s11: usize, 115 | pub t3: usize, 116 | pub t4: usize, 117 | pub t5: usize, 118 | pub t6: usize, // 30 119 | pub mstatus: Mstatus, // 31 120 | pub mepc: usize, // 32 121 | pub machine_stack: usize, // 33 122 | } 123 | 124 | #[naked] 125 | #[link_section = ".text"] 126 | unsafe extern "C" fn do_resume(_supervisor_context: *mut SupervisorContext) { 127 | asm!("j {from_machine_save}", from_machine_save = sym from_machine_save, options(noreturn)) 128 | } 129 | 130 | #[naked] 131 | #[link_section = ".text"] 132 | unsafe extern "C" fn from_machine_save(_supervisor_context: *mut SupervisorContext) -> ! { 133 | asm!( // sp:机器栈顶 134 | "addi sp, sp, -15*8", // sp:机器栈顶 135 | // 进入函数之前,已经保存了调用者寄存器,应当保存被调用者寄存器 136 | "sd ra, 0*8(sp) 137 | sd gp, 1*8(sp) 138 | sd tp, 2*8(sp) 139 | sd s0, 3*8(sp) 140 | sd s1, 4*8(sp) 141 | sd s2, 5*8(sp) 142 | sd s3, 6*8(sp) 143 | sd s4, 7*8(sp) 144 | sd s5, 8*8(sp) 145 | sd s6, 9*8(sp) 146 | sd s7, 10*8(sp) 147 | sd s8, 11*8(sp) 148 | sd s9, 12*8(sp) 149 | sd s10, 13*8(sp) 150 | sd s11, 14*8(sp)", 151 | // a0:特权级上下文 152 | "j {to_supervisor_restore}", 153 | to_supervisor_restore = sym to_supervisor_restore, 154 | options(noreturn) 155 | ) 156 | } 157 | 158 | #[naked] 159 | #[link_section = ".text"] 160 | pub unsafe extern "C" fn to_supervisor_restore(_supervisor_context: *mut SupervisorContext) -> ! { 161 | asm!( 162 | // a0:特权级上下文 163 | "sd sp, 33*8(a0)", // 机器栈顶放进特权级上下文 164 | "csrw mscratch, a0", // 新mscratch:特权级上下文 165 | // mscratch:特权级上下文 166 | "mv sp, a0", // 新sp:特权级上下文 167 | "ld t0, 31*8(sp) 168 | ld t1, 32*8(sp) 169 | csrw mstatus, t0 170 | csrw mepc, t1", 171 | "ld ra, 0*8(sp) 172 | ld gp, 2*8(sp) 173 | ld tp, 3*8(sp) 174 | ld t0, 4*8(sp) 175 | ld t1, 5*8(sp) 176 | ld t2, 6*8(sp) 177 | ld s0, 7*8(sp) 178 | ld s1, 8*8(sp) 179 | ld a0, 9*8(sp) 180 | ld a1, 10*8(sp) 181 | ld a2, 11*8(sp) 182 | ld a3, 12*8(sp) 183 | ld a4, 13*8(sp) 184 | ld a5, 14*8(sp) 185 | ld a6, 15*8(sp) 186 | ld a7, 16*8(sp) 187 | ld s2, 17*8(sp) 188 | ld s3, 18*8(sp) 189 | ld s4, 19*8(sp) 190 | ld s5, 20*8(sp) 191 | ld s6, 21*8(sp) 192 | ld s7, 22*8(sp) 193 | ld s8, 23*8(sp) 194 | ld s9, 24*8(sp) 195 | ld s10, 25*8(sp) 196 | ld s11, 26*8(sp) 197 | ld t3, 27*8(sp) 198 | ld t4, 28*8(sp) 199 | ld t5, 29*8(sp) 200 | ld t6, 30*8(sp)", 201 | "ld sp, 1*8(sp)", // 新sp:特权级栈 202 | // sp:特权级栈, mscratch:特权级上下文 203 | "mret", 204 | options(noreturn) 205 | ) 206 | } 207 | 208 | // 中断开始 209 | 210 | #[naked] 211 | #[link_section = ".text"] 212 | pub unsafe extern "C" fn from_supervisor_save() -> ! { 213 | asm!( // sp:特权级栈,mscratch:特权级上下文 214 | ".p2align 2", 215 | "csrrw sp, mscratch, sp", // 新mscratch:特权级栈, 新sp:特权级上下文 216 | "sd ra, 0*8(sp) 217 | sd gp, 2*8(sp) 218 | sd tp, 3*8(sp) 219 | sd t0, 4*8(sp) 220 | sd t1, 5*8(sp) 221 | sd t2, 6*8(sp) 222 | sd s0, 7*8(sp) 223 | sd s1, 8*8(sp) 224 | sd a0, 9*8(sp) 225 | sd a1, 10*8(sp) 226 | sd a2, 11*8(sp) 227 | sd a3, 12*8(sp) 228 | sd a4, 13*8(sp) 229 | sd a5, 14*8(sp) 230 | sd a6, 15*8(sp) 231 | sd a7, 16*8(sp) 232 | sd s2, 17*8(sp) 233 | sd s3, 18*8(sp) 234 | sd s4, 19*8(sp) 235 | sd s5, 20*8(sp) 236 | sd s6, 21*8(sp) 237 | sd s7, 22*8(sp) 238 | sd s8, 23*8(sp) 239 | sd s9, 24*8(sp) 240 | sd s10, 25*8(sp) 241 | sd s11, 26*8(sp) 242 | sd t3, 27*8(sp) 243 | sd t4, 28*8(sp) 244 | sd t5, 29*8(sp) 245 | sd t6, 30*8(sp)", 246 | "csrr t0, mstatus 247 | sd t0, 31*8(sp)", 248 | "csrr t1, mepc 249 | sd t1, 32*8(sp)", 250 | // mscratch:特权级栈,sp:特权级上下文 251 | "csrrw t2, mscratch, sp", // 新mscratch:特权级上下文,t2:特权级栈 252 | "sd t2, 1*8(sp)", // 保存特权级栈 253 | "j {to_machine_restore}", 254 | to_machine_restore = sym to_machine_restore, 255 | options(noreturn) 256 | ) 257 | } 258 | 259 | #[naked] 260 | #[link_section = ".text"] 261 | unsafe extern "C" fn to_machine_restore() -> ! { 262 | asm!( 263 | // mscratch:特权级上下文 264 | "csrr sp, mscratch", // sp:特权级上下文 265 | "ld sp, 33*8(sp)", // sp:机器栈 266 | "ld ra, 0*8(sp) 267 | ld gp, 1*8(sp) 268 | ld tp, 2*8(sp) 269 | ld s0, 3*8(sp) 270 | ld s1, 4*8(sp) 271 | ld s2, 5*8(sp) 272 | ld s3, 6*8(sp) 273 | ld s4, 7*8(sp) 274 | ld s5, 8*8(sp) 275 | ld s6, 9*8(sp) 276 | ld s7, 10*8(sp) 277 | ld s8, 11*8(sp) 278 | ld s9, 12*8(sp) 279 | ld s10, 13*8(sp) 280 | ld s11, 14*8(sp)", 281 | "addi sp, sp, 15*8", // sp:机器栈顶 282 | "jr ra", // 其实就是ret 283 | options(noreturn) 284 | ) 285 | } 286 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.12.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "atty" 25 | version = "0.2.14" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 28 | dependencies = [ 29 | "hermit-abi", 30 | "libc", 31 | "winapi", 32 | ] 33 | 34 | [[package]] 35 | name = "autocfg" 36 | version = "1.1.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 39 | 40 | [[package]] 41 | name = "bare-metal" 42 | version = "0.2.5" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 45 | dependencies = [ 46 | "rustc_version", 47 | ] 48 | 49 | [[package]] 50 | name = "bare-metal" 51 | version = "1.0.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 54 | 55 | [[package]] 56 | name = "bit_field" 57 | version = "0.9.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" 60 | 61 | [[package]] 62 | name = "bit_field" 63 | version = "0.10.2" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 66 | 67 | [[package]] 68 | name = "bitflags" 69 | version = "1.3.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 72 | 73 | [[package]] 74 | name = "bitflags" 75 | version = "2.4.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 78 | 79 | [[package]] 80 | name = "buddy_system_allocator" 81 | version = "0.8.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "55703ac5f02c246ce6158eff6ae2dd9e9069917969682b6831f8a5123abb8a48" 84 | dependencies = [ 85 | "spin 0.7.1", 86 | ] 87 | 88 | [[package]] 89 | name = "cfg-if" 90 | version = "1.0.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 93 | 94 | [[package]] 95 | name = "clap" 96 | version = "2.34.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 99 | dependencies = [ 100 | "ansi_term", 101 | "atty", 102 | "bitflags 1.3.2", 103 | "strsim", 104 | "textwrap", 105 | "unicode-width", 106 | "vec_map", 107 | ] 108 | 109 | [[package]] 110 | name = "core-foundation-sys" 111 | version = "0.8.6" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 114 | 115 | [[package]] 116 | name = "critical-section" 117 | version = "1.1.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 120 | 121 | [[package]] 122 | name = "embedded-hal" 123 | version = "0.2.7" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 126 | dependencies = [ 127 | "nb 0.1.3", 128 | "void", 129 | ] 130 | 131 | [[package]] 132 | name = "hermit-abi" 133 | version = "0.1.19" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 136 | dependencies = [ 137 | "libc", 138 | ] 139 | 140 | [[package]] 141 | name = "io-kit-sys" 142 | version = "0.4.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" 145 | dependencies = [ 146 | "core-foundation-sys", 147 | "mach2", 148 | ] 149 | 150 | [[package]] 151 | name = "k210-hal" 152 | version = "0.2.0" 153 | source = "git+https://github.com/riscv-rust/k210-hal?rev=7e9c8d70#7e9c8d70224c6a7f9502e6463b2b586d4a8bc494" 154 | dependencies = [ 155 | "bitflags 1.3.2", 156 | "embedded-hal", 157 | "k210-pac", 158 | "nb 1.1.0", 159 | ] 160 | 161 | [[package]] 162 | name = "k210-pac" 163 | version = "0.2.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "69b1cda20e843558e892373b1fd01a900232103f65fa4be8f18edcd130dde30e" 166 | dependencies = [ 167 | "bare-metal 0.2.5", 168 | "riscv 0.5.4", 169 | "vcell", 170 | ] 171 | 172 | [[package]] 173 | name = "lazy_static" 174 | version = "1.4.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 177 | dependencies = [ 178 | "spin 0.5.2", 179 | ] 180 | 181 | [[package]] 182 | name = "libc" 183 | version = "0.2.151" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" 186 | 187 | [[package]] 188 | name = "libudev" 189 | version = "0.3.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" 192 | dependencies = [ 193 | "libc", 194 | "libudev-sys", 195 | ] 196 | 197 | [[package]] 198 | name = "libudev-sys" 199 | version = "0.1.4" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 202 | dependencies = [ 203 | "libc", 204 | "pkg-config", 205 | ] 206 | 207 | [[package]] 208 | name = "lock_api" 209 | version = "0.4.11" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 212 | dependencies = [ 213 | "autocfg", 214 | "scopeguard", 215 | ] 216 | 217 | [[package]] 218 | name = "mach2" 219 | version = "0.4.2" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 222 | dependencies = [ 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "memchr" 228 | version = "2.6.4" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 231 | 232 | [[package]] 233 | name = "nb" 234 | version = "0.1.3" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 237 | dependencies = [ 238 | "nb 1.1.0", 239 | ] 240 | 241 | [[package]] 242 | name = "nb" 243 | version = "1.1.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 246 | 247 | [[package]] 248 | name = "nix" 249 | version = "0.26.4" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 252 | dependencies = [ 253 | "bitflags 1.3.2", 254 | "cfg-if", 255 | "libc", 256 | ] 257 | 258 | [[package]] 259 | name = "pkg-config" 260 | version = "0.3.28" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" 263 | 264 | [[package]] 265 | name = "proc-macro2" 266 | version = "1.0.71" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" 269 | dependencies = [ 270 | "unicode-ident", 271 | ] 272 | 273 | [[package]] 274 | name = "quote" 275 | version = "1.0.33" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 278 | dependencies = [ 279 | "proc-macro2", 280 | ] 281 | 282 | [[package]] 283 | name = "r0" 284 | version = "1.0.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" 287 | 288 | [[package]] 289 | name = "regex" 290 | version = "1.10.2" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 293 | dependencies = [ 294 | "aho-corasick", 295 | "memchr", 296 | "regex-automata", 297 | "regex-syntax", 298 | ] 299 | 300 | [[package]] 301 | name = "regex-automata" 302 | version = "0.4.3" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 305 | dependencies = [ 306 | "aho-corasick", 307 | "memchr", 308 | "regex-syntax", 309 | ] 310 | 311 | [[package]] 312 | name = "regex-syntax" 313 | version = "0.8.2" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 316 | 317 | [[package]] 318 | name = "riscv" 319 | version = "0.5.4" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "6bb785ce81e0bd87b8d1d357266eeb03f081d9d5871a31e7f95b7e6fd67002eb" 322 | dependencies = [ 323 | "bare-metal 0.2.5", 324 | "bit_field 0.9.0", 325 | ] 326 | 327 | [[package]] 328 | name = "riscv" 329 | version = "0.7.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 332 | dependencies = [ 333 | "bare-metal 1.0.0", 334 | "bit_field 0.10.2", 335 | "riscv-target", 336 | ] 337 | 338 | [[package]] 339 | name = "riscv" 340 | version = "0.7.0" 341 | source = "git+https://github.com/rust-embedded/riscv?rev=cd31989b#cd31989ba11d5d64e1addd8aab98bfb00dd927d5" 342 | dependencies = [ 343 | "bare-metal 1.0.0", 344 | "bit_field 0.10.2", 345 | "embedded-hal", 346 | "riscv-target", 347 | ] 348 | 349 | [[package]] 350 | name = "riscv" 351 | version = "0.10.1" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "aa3145d2fae3778b1e31ec2e827b228bdc6abd9b74bb5705ba46dcb82069bc4f" 354 | dependencies = [ 355 | "bit_field 0.10.2", 356 | "critical-section", 357 | "embedded-hal", 358 | ] 359 | 360 | [[package]] 361 | name = "riscv-target" 362 | version = "0.1.2" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 365 | dependencies = [ 366 | "lazy_static", 367 | "regex", 368 | ] 369 | 370 | [[package]] 371 | name = "rustc_version" 372 | version = "0.2.3" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 375 | dependencies = [ 376 | "semver", 377 | ] 378 | 379 | [[package]] 380 | name = "rustsbi" 381 | version = "0.2.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "039ff1f03eaf3ead3f6804b4e7abb7c5abd4f9e71b0483ebd554d78ce1298b5d" 384 | dependencies = [ 385 | "embedded-hal", 386 | "nb 1.1.0", 387 | "riscv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 388 | ] 389 | 390 | [[package]] 391 | name = "rustsbi-k210" 392 | version = "0.0.2" 393 | dependencies = [ 394 | "bit_field 0.10.2", 395 | "buddy_system_allocator", 396 | "k210-hal", 397 | "r0", 398 | "riscv 0.10.1", 399 | "rustsbi", 400 | ] 401 | 402 | [[package]] 403 | name = "scopeguard" 404 | version = "1.2.0" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 407 | 408 | [[package]] 409 | name = "semver" 410 | version = "0.9.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 413 | dependencies = [ 414 | "semver-parser", 415 | ] 416 | 417 | [[package]] 418 | name = "semver-parser" 419 | version = "0.7.0" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 422 | 423 | [[package]] 424 | name = "serialport" 425 | version = "4.3.0" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828" 428 | dependencies = [ 429 | "bitflags 2.4.1", 430 | "cfg-if", 431 | "core-foundation-sys", 432 | "io-kit-sys", 433 | "libudev", 434 | "mach2", 435 | "nix", 436 | "regex", 437 | "scopeguard", 438 | "unescaper", 439 | "winapi", 440 | ] 441 | 442 | [[package]] 443 | name = "spin" 444 | version = "0.5.2" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 447 | 448 | [[package]] 449 | name = "spin" 450 | version = "0.7.1" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 453 | 454 | [[package]] 455 | name = "spin" 456 | version = "0.9.8" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 459 | dependencies = [ 460 | "lock_api", 461 | ] 462 | 463 | [[package]] 464 | name = "strsim" 465 | version = "0.8.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 468 | 469 | [[package]] 470 | name = "syn" 471 | version = "2.0.43" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" 474 | dependencies = [ 475 | "proc-macro2", 476 | "quote", 477 | "unicode-ident", 478 | ] 479 | 480 | [[package]] 481 | name = "test-kernel" 482 | version = "0.1.0" 483 | dependencies = [ 484 | "bit_field 0.10.2", 485 | "bitflags 1.3.2", 486 | "buddy_system_allocator", 487 | "lazy_static", 488 | "r0", 489 | "riscv 0.7.0 (git+https://github.com/rust-embedded/riscv?rev=cd31989b)", 490 | "spin 0.9.8", 491 | ] 492 | 493 | [[package]] 494 | name = "textwrap" 495 | version = "0.11.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 498 | dependencies = [ 499 | "unicode-width", 500 | ] 501 | 502 | [[package]] 503 | name = "thiserror" 504 | version = "1.0.52" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" 507 | dependencies = [ 508 | "thiserror-impl", 509 | ] 510 | 511 | [[package]] 512 | name = "thiserror-impl" 513 | version = "1.0.52" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" 516 | dependencies = [ 517 | "proc-macro2", 518 | "quote", 519 | "syn", 520 | ] 521 | 522 | [[package]] 523 | name = "unescaper" 524 | version = "0.1.3" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "d8f0f68e58d297ba8b22b8b5a96a87b863ba6bb46aaf51e19a4b02c5a6dd5b7f" 527 | dependencies = [ 528 | "thiserror", 529 | ] 530 | 531 | [[package]] 532 | name = "unicode-ident" 533 | version = "1.0.12" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 536 | 537 | [[package]] 538 | name = "unicode-width" 539 | version = "0.1.11" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 542 | 543 | [[package]] 544 | name = "vcell" 545 | version = "0.1.3" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 548 | 549 | [[package]] 550 | name = "vec_map" 551 | version = "0.8.2" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 554 | 555 | [[package]] 556 | name = "void" 557 | version = "1.0.2" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 560 | 561 | [[package]] 562 | name = "winapi" 563 | version = "0.3.9" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 566 | dependencies = [ 567 | "winapi-i686-pc-windows-gnu", 568 | "winapi-x86_64-pc-windows-gnu", 569 | ] 570 | 571 | [[package]] 572 | name = "winapi-i686-pc-windows-gnu" 573 | version = "0.4.0" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 576 | 577 | [[package]] 578 | name = "winapi-x86_64-pc-windows-gnu" 579 | version = "0.4.0" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 582 | 583 | [[package]] 584 | name = "xtask" 585 | version = "0.1.0" 586 | dependencies = [ 587 | "clap", 588 | "serialport", 589 | ] 590 | --------------------------------------------------------------------------------