├── .gitignore ├── rustsbi-hifive-unmatched ├── .cargo │ └── config.toml ├── src │ ├── peripheral │ │ ├── mod.rs │ │ ├── clint.rs │ │ └── uart.rs │ ├── hifive-unmatched-a00.dtb │ ├── feature │ │ ├── mod.rs │ │ ├── transfer_trap.rs │ │ └── emulate_rdtime.rs │ ├── device_tree.rs │ ├── u740.ld │ ├── console.rs │ ├── util.rs │ ├── execute.rs │ ├── early_trap.rs │ ├── main.rs │ ├── runtime.rs │ └── hart_csr_utils.rs ├── build.rs └── Cargo.toml ├── .cargo └── config.toml ├── Cargo.toml ├── test-kernel ├── build.rs ├── Cargo.toml ├── src │ ├── mm.rs │ ├── linker64.ld │ ├── console.rs │ ├── util.rs │ ├── sbi.rs │ └── main.rs └── sd-image-debug.its ├── xtask ├── Cargo.toml └── src │ └── main.rs ├── .vscode └── settings.json ├── sd-image-release.its ├── openocd.cfg ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64imac-unknown-none-elf" 3 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/peripheral/mod.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | pub(crate) mod uart; 3 | pub use uart::Uart; 4 | mod clint; 5 | pub use clint::Clint; 6 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | make = "xtask make" 4 | asm = "xtask asm" 5 | gdb = "xtask gdb" 6 | image = "xtask image" 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rustsbi-hifive-unmatched", 4 | "test-kernel", 5 | "xtask" 6 | ] 7 | default-members = ["xtask"] 8 | -------------------------------------------------------------------------------- /test-kernel/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=build.rs"); 3 | println!("cargo:rustc-link-arg=-Ttest-kernel/src/linker64.ld"); 4 | } 5 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/hifive-unmatched-a00.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustsbi/rustsbi-hifive-unmatched/HEAD/rustsbi-hifive-unmatched/src/hifive-unmatched-a00.dtb -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/build.rs: -------------------------------------------------------------------------------- 1 | // 添加链接器脚本 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=build.rs"); 5 | println!("cargo:rustc-link-arg=-Trustsbi-hifive-unmatched/src/u740.ld"); 6 | } 7 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/feature/mod.rs: -------------------------------------------------------------------------------- 1 | mod emulate_rdtime; 2 | mod transfer_trap; 3 | 4 | pub use emulate_rdtime::emulate_rdtime; 5 | pub use transfer_trap::{do_transfer_trap, should_transfer_trap}; 6 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 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 | clap = "2.33" 10 | ctrlc = "3.2" 11 | -------------------------------------------------------------------------------- /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 | buddy_system_allocator = "0.8" 10 | riscv = "0.7" 11 | -------------------------------------------------------------------------------- /test-kernel/src/mm.rs: -------------------------------------------------------------------------------- 1 | use buddy_system_allocator::LockedHeap; 2 | 3 | const HEAP_SIZE: usize = 64 * 1024; // 64KiB 4 | #[link_section = ".bss.uninit"] 5 | static mut HEAP_SPACE: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 6 | #[global_allocator] 7 | static HEAP: LockedHeap<32> = LockedHeap::empty(); 8 | 9 | pub fn init_heap() { 10 | unsafe { HEAP.lock().init(HEAP_SPACE.as_ptr() as usize, HEAP_SIZE) } 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // 防止在no_std下出现“can't find crate for `test`” 3 | // 参考:https://github.com/rust-lang/vscode-rust/issues/729 4 | // 针对“vscode-rust”插件用户: 5 | "rust.target": "riscv64imac-unknown-none-elf", 6 | "rust.all_targets": false, 7 | // 针对Rust Analyzer插件用户: 8 | "rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf", 9 | "rust-analyzer.checkOnSave.allTargets": false 10 | } 11 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustsbi-hifive-unmatched" 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 | riscv = "0.7" 10 | fu740-hal = { git = "https://github.com/riscv-rust/fu740-hal" } 11 | rustsbi = "0.2.0-alpha.9" 12 | buddy_system_allocator = "0.8" 13 | embedded-hal = "0.2.6" 14 | nb = "1" 15 | r0 = "1" 16 | bit_field = "0.10" 17 | serde-device-tree = { version = "0.0.1", default-features = false, features = ["alloc"] } 18 | serde_derive = "1.0" 19 | serde = { version = "1.0", default-features = false, features = ["alloc"] } 20 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/device_tree.rs: -------------------------------------------------------------------------------- 1 | // use alloc::collections::BTreeMap; 2 | use serde_derive::Deserialize; 3 | use serde_device_tree::{self, error::Result}; 4 | 5 | #[derive(Debug, Deserialize)] 6 | struct Tree<'a> { 7 | // #[serde(borrow)] 8 | // aliases: BTreeMap<&'a str, &'a str>, 9 | #[serde(borrow)] 10 | chosen: Option>, 11 | } 12 | 13 | #[derive(Debug, Deserialize)] 14 | #[serde(rename_all = "kebab-case")] 15 | struct Chosen<'a> { 16 | stdout_path: Option<&'a str>, 17 | } 18 | 19 | pub unsafe fn parse_device_tree(dtb_pa: usize) -> Result<()> { 20 | let tree: Tree = serde_device_tree::from_raw(dtb_pa as *const u8)?; 21 | use crate::console::println; 22 | if let Some(chosen) = tree.chosen { 23 | if let Some(stdout_path) = chosen.stdout_path { 24 | println!("[rustsbi] stdout path: {}", stdout_path); 25 | } 26 | } 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/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 | #[inline] 8 | pub unsafe fn should_transfer_trap(ctx: &mut SupervisorContext) -> bool { 9 | ctx.mstatus.mpp() != MPP::Machine 10 | } 11 | 12 | #[inline] 13 | pub unsafe fn do_transfer_trap(ctx: &mut SupervisorContext, cause: scause::Trap) { 14 | // 设置S层异常原因 15 | scause::set(cause); 16 | // 填写异常指令的指令内容 17 | stval::write(mtval::read()); 18 | // 填写S层需要返回到的地址,这里的mepc会被随后的代码覆盖掉 19 | sepc::write(ctx.mepc); 20 | // 设置中断位 21 | mstatus::set_mpp(MPP::Supervisor); 22 | mstatus::set_spp(SPP::Supervisor); 23 | if mstatus::read().sie() { 24 | mstatus::set_spie() 25 | } 26 | mstatus::clear_sie(); 27 | ctx.mstatus = mstatus::read(); 28 | // 设置返回地址,返回到S层 29 | // 注意,无论是Direct还是Vectored模式,所有异常的向量偏移都是0,不需要处理中断向量,跳转到入口地址即可 30 | ctx.mepc = stvec::read().address(); 31 | } 32 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/feature/emulate_rdtime.rs: -------------------------------------------------------------------------------- 1 | use crate::peripheral::Clint; 2 | use crate::runtime::SupervisorContext; 3 | 4 | #[inline] 5 | pub fn emulate_rdtime(ctx: &mut SupervisorContext, ins: usize) -> bool { 6 | if ins & 0xFFFFF07F == 0xC0102073 { 7 | let rd = ((ins >> 7) & 0b1_1111) as u8; 8 | let clint = Clint::new(0x2000000 as *mut u8); 9 | let time_usize = clint.get_mtime() as usize; 10 | set_register_xi(ctx, rd, time_usize); 11 | ctx.mepc = ctx.mepc.wrapping_add(4); // skip rdtime 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 | -------------------------------------------------------------------------------- /test-kernel/src/linker64.ld: -------------------------------------------------------------------------------- 1 | /* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore */ 2 | 3 | /* Simple linker script for the ucore kernel. 4 | See the GNU ld 'info' manual ("info ld") to learn the syntax. */ 5 | 6 | OUTPUT_ARCH(riscv) 7 | ENTRY(_start) 8 | 9 | BASE_ADDRESS = 0x80200000; 10 | 11 | SECTIONS 12 | { 13 | /* Load the kernel at this address: "." means the current address */ 14 | . = BASE_ADDRESS; 15 | start = .; 16 | 17 | .text : ALIGN(4K) { 18 | _stext = .; 19 | *(.text.entry) 20 | *(.text .text.*) 21 | _etext = .; 22 | } 23 | 24 | .rodata : ALIGN(4K) { 25 | _srodata = .; 26 | *(.rodata .rodata.*) 27 | _erodata = .; 28 | } 29 | 30 | .data : ALIGN(4K) { 31 | _sdata = .; 32 | *(.data .data.*) 33 | _edata = .; 34 | } 35 | 36 | .bss (NOLOAD) : ALIGN(4K) { 37 | _sbss = .; 38 | *(.sbss .bss .bss.*) 39 | _ebss = .; 40 | } 41 | 42 | PROVIDE(end = .); 43 | } 44 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/u740.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | 4 | /* RustSBI will be executed at DDR and remains resident on this location */ 5 | PROVIDE(stext = 0x80000000); 6 | 7 | SECTIONS 8 | { 9 | .text stext : { 10 | stext = .; 11 | *(.text.entry) 12 | *(.text .text.*) 13 | . = ALIGN(4); 14 | etext = .; 15 | } 16 | 17 | .rodata : ALIGN(4) { 18 | srodata = .; 19 | *(.rodata .rodata.*) 20 | *(.srodata .srodata.*) 21 | . = ALIGN(4); 22 | erodata = .; 23 | } 24 | 25 | .data : ALIGN(4) { 26 | sidata = LOADADDR(.data); 27 | sdata = .; 28 | *(.data .data.*) 29 | *(.sdata .sdata.*) 30 | . = ALIGN(4); 31 | edata = .; 32 | } 33 | 34 | .bss (NOLOAD) : ALIGN(4) { 35 | *(.bss.uninit) 36 | sbss = .; 37 | *(.bss .bss.*) 38 | *(.sbss .sbss.*) 39 | . = ALIGN(4); 40 | ebss = .; 41 | } 42 | 43 | /DISCARD/ : { 44 | *(.eh_frame .eh_frame_hdr) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test-kernel/src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi; 2 | use crate::util::AmoMutex; 3 | use core::fmt::{self, Write}; 4 | 5 | struct Stdout; 6 | 7 | impl Write for Stdout { 8 | fn write_str(&mut self, s: &str) -> fmt::Result { 9 | let mut buffer = [0u8; 4]; 10 | for c in s.chars() { 11 | for code_point in c.encode_utf8(&mut buffer).as_bytes().iter() { 12 | sbi::console_putchar(*code_point as usize); 13 | } 14 | } 15 | Ok(()) 16 | } 17 | } 18 | 19 | #[allow(unused)] 20 | pub fn print(args: fmt::Arguments) { 21 | STDOUT.lock().write_fmt(args).unwrap(); 22 | } 23 | 24 | static STDOUT: AmoMutex = AmoMutex::new(Stdout); 25 | 26 | #[macro_export] 27 | macro_rules! print { 28 | ($fmt: literal $(, $($arg: tt)+)?) => { 29 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 30 | } 31 | } 32 | 33 | #[macro_export] 34 | macro_rules! println { 35 | ($fmt: literal $(, $($arg: tt)+)?) => { 36 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sd-image-release.its: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | description = "RustSBI Test Image"; 5 | #address-cells = <1>; 6 | 7 | images { 8 | rustsbi { 9 | data = /incbin/("target/riscv64imac-unknown-none-elf/release/rustsbi-hifive-unmatched.bin"); 10 | description = "RustSBI Firmware (Release)"; 11 | type = "firmware"; 12 | os = "rustsbi"; 13 | arch = "riscv"; 14 | compression = "none"; 15 | load = <0x80000000>; 16 | entry = <0x80000000>; 17 | }; 18 | fdt-1 { 19 | description = "hifive-unmatched-a00"; 20 | type = "flat_dt"; 21 | compression = "none"; 22 | data = /incbin/("rustsbi-hifive-unmatched/src/hifive-unmatched-a00.dtb"); 23 | }; 24 | payload { 25 | data = <0x0, 0x0, 0x0, 0x0>; 26 | description = "U-Boot"; 27 | type = "standalone"; 28 | os = "U-Boot"; 29 | arch = "riscv"; 30 | compression = "none"; 31 | load = <0x80200000>; 32 | }; 33 | }; 34 | 35 | configurations { 36 | default = "unmatched-sdcard"; 37 | 38 | unmatched-sdcard { 39 | description = "hifive-unmatched-a00"; 40 | firmware = "rustsbi"; 41 | loadables = "payload"; 42 | fdt = "fdt-1"; 43 | }; 44 | }; 45 | }; -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # HiFive Unmatched OpenOCD调试脚本 2 | # 注意:在Windows下使用OpenOCD调试之前,请更换驱动到WinUSB驱动(可以使用Zadig软件完成操作) 3 | # 需要将“Dual RS232-HS (Interface 0)”更换驱动,不需要更换Interface 1的驱动 4 | # 5 | # 脚本参考:https://gist.github.com/a4lg/df51da397b72299042182ccc19f75371 6 | 7 | # 调试适配器设置 8 | # HiFive Unmatched板载FTDI调试器 9 | adapter speed 10000 10 | adapter driver ftdi 11 | 12 | ftdi_device_desc "Dual RS232-HS" 13 | ftdi_vid_pid 0x0403 0x6010 14 | ftdi_layout_init 0x0008 0x001b 15 | ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020 16 | 17 | # 调试接入点和传输设置 18 | # 使用JTAG传输协议 19 | transport select jtag 20 | 21 | # Freedom U740只有一个调试接入点(TAP),这个接入点指令寄存器的位宽最多为5 22 | jtag newtap freedom-u740 cpu -irlen 5 23 | 24 | # 接入所有的核心,包括一个S7(hart 0)和四个U74(hart 1-4) 25 | target create freedom-u740.cpu0 riscv -chain-position freedom-u740.cpu -coreid 0 -rtos hwthread 26 | target create freedom-u740.cpu1 riscv -chain-position freedom-u740.cpu -coreid 1 27 | target create freedom-u740.cpu2 riscv -chain-position freedom-u740.cpu -coreid 2 28 | target create freedom-u740.cpu3 riscv -chain-position freedom-u740.cpu -coreid 3 29 | target create freedom-u740.cpu4 riscv -chain-position freedom-u740.cpu -coreid 4 30 | target smp freedom-u740.cpu0 freedom-u740.cpu1 freedom-u740.cpu2 freedom-u740.cpu3 freedom-u740.cpu4 31 | 32 | # 开始调试 33 | init 34 | halt 35 | -------------------------------------------------------------------------------- /test-kernel/sd-image-debug.its: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | description = "RustSBI Test Image"; 5 | #address-cells = <1>; 6 | 7 | images { 8 | rustsbi { 9 | data = /incbin/("../target/riscv64imac-unknown-none-elf/debug/rustsbi-hifive-unmatched.bin"); 10 | description = "RustSBI Firmware (Debug)"; 11 | type = "firmware"; 12 | os = "rustsbi"; 13 | arch = "riscv"; 14 | compression = "none"; 15 | load = <0x80000000>; 16 | entry = <0x80000000>; 17 | }; 18 | fdt-1 { 19 | description = "hifive-unmatched-a00"; 20 | type = "flat_dt"; 21 | compression = "none"; 22 | data = /incbin/("../rustsbi-hifive-unmatched/src/hifive-unmatched-a00.dtb"); 23 | }; 24 | test-kernel { 25 | description = "RustSBI Unit Test Kernel"; 26 | type = "kernel"; 27 | arch = "riscv"; 28 | compression = "none"; 29 | load = <0x80200000>; 30 | entry = <0x80200000>; 31 | data = /incbin/("../target/riscv64imac-unknown-none-elf/debug/test-kernel.bin"); 32 | }; 33 | }; 34 | 35 | configurations { 36 | default = "unmatched-sdcard"; 37 | 38 | unmatched-sdcard { 39 | description = "hifive-unmatched-a00"; 40 | firmware = "rustsbi"; 41 | fdt = "fdt-1"; 42 | kernel = "test-kernel"; 43 | }; 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/peripheral/clint.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub struct Clint { 3 | base: *mut u8, 4 | } 5 | 6 | unsafe impl Send for Clint {} 7 | unsafe impl Sync for Clint {} 8 | 9 | #[allow(unused)] 10 | impl Clint { 11 | pub fn new(base: *mut u8) -> Clint { 12 | Clint { base } 13 | } 14 | 15 | pub fn get_mtime(&self) -> u64 { 16 | unsafe { core::ptr::read_volatile(self.base.offset(0xbff8) as *mut u64) } 17 | } 18 | 19 | pub fn set_timer(&self, hart_id: usize, instant: u64) { 20 | unsafe { 21 | core::ptr::write_volatile((self.base.offset(0x4000) as *mut u64).add(hart_id), instant); 22 | } 23 | } 24 | 25 | pub fn send_soft(&self, hart_id: usize) { 26 | unsafe { 27 | core::ptr::write_volatile((self.base as *mut u32).add(hart_id), 1); 28 | } 29 | } 30 | 31 | pub fn clear_soft(&self, hart_id: usize) { 32 | unsafe { 33 | core::ptr::write_volatile((self.base as *mut u32).add(hart_id), 0); 34 | } 35 | } 36 | } 37 | 38 | impl rustsbi::Ipi for Clint { 39 | fn max_hart_id(&self) -> usize { 40 | 4 41 | } 42 | 43 | fn send_ipi_many(&self, hart_mask: rustsbi::HartMask) -> rustsbi::SbiRet { 44 | for i in 0..=self.max_hart_id() { 45 | if hart_mask.has_bit(i) { 46 | self.send_soft(i); 47 | } 48 | } 49 | rustsbi::SbiRet::ok(0) 50 | } 51 | } 52 | 53 | impl rustsbi::Timer for Clint { 54 | fn set_timer(&self, time_value: u64) { 55 | let this_mhartid = riscv::register::mhartid::read(); 56 | self.set_timer(this_mhartid, time_value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RustSBI 在 HiFive Unmatched 主板的支持软件 2 | 3 | 这个项目的目的是在SiFive [HiFive Unmatched](https://www.sifive.com/boards/hifive-unmatched)主板上支持RustSBI。 4 | RustSBI是一个引导程序环境;主板上电时,RustSBI将会先行启动,而后,它将会找到一个可引导的操作系统,引导启动这个操作系统。 5 | 在启动后,RustSBI仍然常驻后台,提供操作系统需要的功能。 6 | RustSBI的设计完全符合RISC-V SBI规范标准,只要支持此标准的操作系统,都可以使用RustSBI引导启动。 7 | 8 | ## 编译和运行 9 | 10 | 这个项目使用xtask框架,可以使用以下指令来编译: 11 | 12 | ```shell 13 | cargo image 14 | ``` 15 | 16 | (如果增加--release参数,说明编译的是不带调试符号的release版本) 17 | 18 | 这时候编译产生一个elf文件和一个img镜像。注意,产生的中间数据bin文件不可以直接用于烧录。 19 | 20 | 使用以下操作来烧录img格式的镜像到sd卡分区。(危险!必须先备份数据) 21 | 22 | ```shell 23 | sudo dd if=target/sd-card-partition-2.img of=\\?\Device\Harddisk????\Partition2 --progress 24 | ``` 25 | 26 | 烧录完成后,就可以使用RustSBI引导启动了。 27 | 28 | ## Rust版本 29 | 30 | 编译这个项目至少需要`rustc 1.59.0-nightly (c5ecc1570 2021-12-15)`的Rust版本。 31 | 32 | ## 文档 33 | 34 | 请参考[项目Wiki](https://github.com/rustsbi/rustsbi-hifive-unmatched/wiki)来获取完整的文档。 35 | 36 | 项目中仍然需要完善的部分也被记录在文档中,详见[这里](https://github.com/rustsbi/rustsbi-hifive-unmatched/wiki/接下来要做……)。 37 | 38 | ## 对大小核设计的支持方案 39 | 40 | HiFive Unmatched主板板载SiFive Freedom U740处理器。FU740是异构的多核处理器,它总共有五个核。 41 | 它的五个核分别为四个U74应用处理器内核,以及一个S7嵌入式处理器内核。 42 | 43 | 作为RustSBI的软件实现开发者,我们注意到S7管理小核将有广泛的用途。 44 | 因此,RustSBI在HiFive Unmatched上不屏蔽任何的核,以供操作系统选择和使用。 45 | 46 | ## 有用的链接 47 | 48 | - HiFive Unmatched 入门指南(中文)1.4版 [PDF](https://sifive.cdn.prismic.io/sifive/b9376339-5d60-45c9-8280-58fd0557c2f0_hifive-unmatched-gsg-v1p4_ZH.pdf) 49 | - SiFive FU740-000 Manual v1p3 [PDF](https://sifive.cdn.prismic.io/sifive/de1491e5-077c-461d-9605-e8a0ce57337d_fu740-c000-manual-v1p3.pdf) 50 | 51 | ## 命令行 52 | 53 | 查看汇编代码 54 | 55 | ``` 56 | cargo asm 57 | ``` 58 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::peripheral::Uart; 2 | use crate::util::AmoMutex; 3 | use core::fmt; 4 | use embedded_hal::serial::Write; 5 | 6 | static STDOUT: AmoMutex> = AmoMutex::new(None); 7 | 8 | pub fn init_stdout(uart: Uart) { 9 | let mut lock = STDOUT.lock(); 10 | *lock = Some(uart); 11 | drop(lock); 12 | } 13 | 14 | impl fmt::Write for Uart { 15 | fn write_str(&mut self, s: &str) -> fmt::Result { 16 | for byte in s.as_bytes() { 17 | nb::block!(self.write(*byte)).ok(); // todo: 为了极致性能,未来添加水标设置 18 | } 19 | nb::block!(self.flush()).ok(); // todo: 这行会影响输出 20 | Ok(()) 21 | } 22 | } 23 | 24 | #[doc(hidden)] 25 | pub fn _print(args: fmt::Arguments) { 26 | use fmt::Write; 27 | let lock = STDOUT.lock(); 28 | if let Some(mut stdout) = *lock { 29 | stdout.write_fmt(args).unwrap(); 30 | } 31 | drop(lock); 32 | } 33 | 34 | #[doc(hidden)] 35 | pub fn _eprint(args: fmt::Arguments) { 36 | use fmt::Write; 37 | let lock = STDOUT.lock(); 38 | if let Some(mut stdout) = *lock { 39 | stdout.write_fmt(args).unwrap(); 40 | } 41 | drop(lock); 42 | } 43 | 44 | #[allow(unused)] 45 | macro_rules! print { 46 | ($($arg:tt)*) => ({ 47 | $crate::console::_print(core::format_args!($($arg)*)) 48 | }); 49 | } 50 | 51 | #[allow(unused)] 52 | macro_rules! println { 53 | ($fmt: literal $(, $($arg: tt)+)?) => { 54 | $crate::console::_print(core::format_args!(core::concat!($fmt, "\r\n") $(, $($arg)+)?)) 55 | } 56 | } 57 | 58 | #[allow(unused)] 59 | macro_rules! eprintln { 60 | ($fmt: literal $(, $($arg: tt)+)?) => { 61 | $crate::console::_eprint(core::format_args!(core::concat!($fmt, "\r\n") $(, $($arg)+)?)) 62 | } 63 | } 64 | 65 | #[allow(unused)] 66 | pub(crate) use {eprintln, print, println}; 67 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/peripheral/uart.rs: -------------------------------------------------------------------------------- 1 | use core::convert::Infallible; 2 | use embedded_hal::serial::{Read, Write}; 3 | use fu740_hal::pac; 4 | 5 | // UART that is initialized by prior steps of bootloading 6 | #[derive(Clone, Copy)] 7 | pub struct Uart { 8 | inner: *const pac::uart0::RegisterBlock, 9 | } 10 | 11 | // UART外设是可以跨上下文共享的 12 | unsafe impl Send for Uart {} 13 | unsafe impl Sync for Uart {} 14 | 15 | impl Uart { 16 | #[inline] 17 | pub unsafe fn preloaded_uart0() -> Self { 18 | let inner = pac::UART0::ptr(); 19 | Self { inner } 20 | } 21 | } 22 | 23 | // Ref: fu740-hal 24 | 25 | impl Read for Uart { 26 | type Error = Infallible; 27 | 28 | #[inline] 29 | fn read(&mut self) -> nb::Result { 30 | let rxdata = unsafe { &*self.inner }.rxdata.read(); 31 | 32 | if rxdata.empty().bit_is_set() { 33 | Err(nb::Error::WouldBlock) 34 | } else { 35 | Ok(rxdata.data().bits() as u8) 36 | } 37 | } 38 | } 39 | 40 | impl Write for Uart { 41 | type Error = Infallible; 42 | 43 | #[inline] 44 | fn write(&mut self, byte: u8) -> nb::Result<(), Infallible> { 45 | let txdata = unsafe { &*self.inner }.txdata.read(); 46 | 47 | if txdata.full().bit_is_set() { 48 | Err(nb::Error::WouldBlock) 49 | } else { 50 | unsafe { 51 | (&*self.inner) 52 | .txdata 53 | .write_with_zero(|w| w.data().bits(byte)); 54 | } 55 | Ok(()) 56 | } 57 | } 58 | 59 | #[inline] 60 | fn flush(&mut self) -> nb::Result<(), Infallible> { 61 | Ok(()) // todo: 观察水标 62 | // if unsafe { &*self.inner }.ip.read().txwm().bit_is_set() { 63 | // // FIFO count is below the receive watermark (1) 64 | // Ok(()) 65 | // } else { 66 | // Err(nb::Error::WouldBlock) 67 | // } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test-kernel/src/util.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::UnsafeCell, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | /// Use only amo instructions on mutex; no lr/sc instruction is used 7 | pub struct AmoMutex { 8 | lock: UnsafeCell, 9 | data: UnsafeCell, 10 | } 11 | 12 | pub struct AmoMutexGuard<'a, T: ?Sized> { 13 | lock: *mut u8, 14 | data: &'a mut T, 15 | } 16 | 17 | impl AmoMutex { 18 | /// Create a new AmoMutex 19 | pub const fn new(data: T) -> Self { 20 | AmoMutex { 21 | data: UnsafeCell::new(data), 22 | lock: UnsafeCell::new(0), 23 | } 24 | } 25 | /// Locks the mutex and returns a guard that permits access to the inner data. 26 | pub fn lock(&self) -> AmoMutexGuard { 27 | unsafe { 28 | core::arch::asm!( 29 | "li {one}, 1", 30 | "1: lw {tmp}, ({lock})", // check if lock is held 31 | // "call {relax}", // spin loop hint 32 | "bnez {tmp}, 1b", // retry if held 33 | "amoswap.w.aq {tmp}, {one}, ({lock})", // attempt to acquire lock 34 | "bnez {tmp}, 1b", // retry if held 35 | lock = in(reg) self.lock.get(), 36 | tmp = out(reg) _, 37 | one = out(reg) _, 38 | // relax = sym pause, 39 | options(nostack) 40 | ); 41 | } 42 | AmoMutexGuard { 43 | lock: self.lock.get(), 44 | data: unsafe { &mut *self.data.get() }, 45 | } 46 | } 47 | // pub unsafe fn force_unlock(&self) { 48 | // *self.lock.get() = 0 49 | // } 50 | } 51 | 52 | unsafe impl Sync for AmoMutex {} 53 | unsafe impl Send for AmoMutex {} 54 | 55 | impl<'a, T: ?Sized> Deref for AmoMutexGuard<'a, T> { 56 | type Target = T; 57 | fn deref(&self) -> &T { 58 | self.data 59 | } 60 | } 61 | 62 | impl<'a, T: ?Sized> DerefMut for AmoMutexGuard<'a, T> { 63 | fn deref_mut(&mut self) -> &mut T { 64 | self.data 65 | } 66 | } 67 | 68 | impl<'a, T: ?Sized> Drop for AmoMutexGuard<'a, T> { 69 | /// The dropping of the mutex guard will release the lock it was created from. 70 | fn drop(&mut self) { 71 | unsafe { 72 | core::arch::asm!( 73 | "amoswap.w.rl x0, x0, ({lock})", // release lock by storing 0 74 | lock = in(reg) self.lock, 75 | ); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/util.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::UnsafeCell, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | /// Use only amo instructions on mutex; no lr/sc instruction is used 7 | pub struct AmoMutex { 8 | lock: UnsafeCell, 9 | data: UnsafeCell, 10 | } 11 | 12 | pub struct AmoMutexGuard<'a, T: ?Sized> { 13 | lock: *mut u8, 14 | data: &'a mut T, 15 | } 16 | 17 | impl AmoMutex { 18 | /// Create a new AmoMutex 19 | pub const fn new(data: T) -> Self { 20 | AmoMutex { 21 | data: UnsafeCell::new(data), 22 | lock: UnsafeCell::new(0), 23 | } 24 | } 25 | /// Locks the mutex and returns a guard that permits access to the inner data. 26 | pub fn lock(&self) -> AmoMutexGuard { 27 | unsafe { 28 | core::arch::asm!( 29 | "li {one}, 1", 30 | "1: lw {tmp}, ({lock})", // check if lock is held 31 | // "call {relax}", // spin loop hint 32 | "bnez {tmp}, 1b", // retry if held 33 | "amoswap.w.aq {tmp}, {one}, ({lock})", // attempt to acquire lock 34 | "bnez {tmp}, 1b", // retry if held 35 | lock = in(reg) self.lock.get(), 36 | tmp = out(reg) _, 37 | one = out(reg) _, 38 | // relax = sym pause, 39 | options(nostack) 40 | ); 41 | } 42 | AmoMutexGuard { 43 | lock: self.lock.get(), 44 | data: unsafe { &mut *self.data.get() }, 45 | } 46 | } 47 | // pub unsafe fn force_unlock(&self) { 48 | // *self.lock.get() = 0 49 | // } 50 | } 51 | 52 | unsafe impl Sync for AmoMutex {} 53 | unsafe impl Send for AmoMutex {} 54 | 55 | impl<'a, T: ?Sized> Deref for AmoMutexGuard<'a, T> { 56 | type Target = T; 57 | fn deref(&self) -> &T { 58 | self.data 59 | } 60 | } 61 | 62 | impl<'a, T: ?Sized> DerefMut for AmoMutexGuard<'a, T> { 63 | fn deref_mut(&mut self) -> &mut T { 64 | self.data 65 | } 66 | } 67 | 68 | impl<'a, T: ?Sized> Drop for AmoMutexGuard<'a, T> { 69 | /// The dropping of the mutex guard will release the lock it was created from. 70 | fn drop(&mut self) { 71 | unsafe { 72 | core::arch::asm!( 73 | "amoswap.w.rl x0, x0, ({lock})", // release lock by storing 0 74 | lock = in(reg) self.lock, 75 | ); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/execute.rs: -------------------------------------------------------------------------------- 1 | use crate::feature; 2 | use crate::runtime::{MachineTrap, Runtime, SupervisorContext}; 3 | use core::{ 4 | ops::{Generator, GeneratorState}, 5 | pin::Pin, 6 | }; 7 | use riscv::register::scause::{Exception, Trap}; 8 | use riscv::register::{mie, mip}; 9 | 10 | pub fn execute_supervisor(supervisor_mepc: usize, hart_id: usize, opaque: usize) { 11 | let mut rt = Runtime::new_sbi_supervisor(supervisor_mepc, hart_id, opaque); 12 | loop { 13 | match Pin::new(&mut rt).resume(()) { 14 | GeneratorState::Yielded(MachineTrap::SbiCall()) => { 15 | let ctx = rt.context_mut(); 16 | let param = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5]; 17 | let ans = rustsbi::ecall(ctx.a7, ctx.a6, param); 18 | ctx.a0 = ans.error; 19 | ctx.a1 = ans.value; 20 | ctx.mepc = ctx.mepc.wrapping_add(4); 21 | } 22 | GeneratorState::Yielded(MachineTrap::IllegalInstruction()) => { 23 | let ctx = rt.context_mut(); 24 | // FIXME: get_vaddr_u32这个过程可能出错。 25 | let ins = unsafe { get_vaddr_u32(ctx.mepc) } as usize; 26 | if !emulate_illegal_instruction(ctx, ins) { 27 | unsafe { 28 | if feature::should_transfer_trap(ctx) { 29 | feature::do_transfer_trap( 30 | ctx, 31 | Trap::Exception(Exception::IllegalInstruction), 32 | ) 33 | } else { 34 | fail_illegal_instruction(ctx, ins) 35 | } 36 | } 37 | } 38 | } 39 | GeneratorState::Yielded(MachineTrap::MachineTimer()) => unsafe { 40 | mip::set_stimer(); 41 | mie::clear_mtimer(); 42 | }, 43 | GeneratorState::Yielded(MachineTrap::MachineSoft()) => todo!(), 44 | GeneratorState::Complete(()) => break, 45 | } 46 | } 47 | } 48 | 49 | #[inline] 50 | unsafe fn get_vaddr_u32(vaddr: usize) -> u32 { 51 | let mut ans: u32; 52 | core::arch::asm!(" 53 | li {tmp}, (1 << 17) 54 | csrrs {tmp}, mstatus, {tmp} 55 | lwu {ans}, 0({vaddr}) 56 | csrw mstatus, {tmp} 57 | ", 58 | tmp = out(reg) _, 59 | vaddr = in(reg) vaddr, 60 | ans = lateout(reg) ans 61 | ); 62 | ans 63 | } 64 | 65 | fn emulate_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> bool { 66 | if feature::emulate_rdtime(ctx, ins) { 67 | return true; 68 | } 69 | false 70 | } 71 | 72 | // 真·非法指令异常,是M层出现的 73 | fn fail_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> ! { 74 | #[cfg(target_pointer_width = "64")] 75 | panic!("invalid instruction from machine level, mepc: {:016x?}, instruction: {:016x?}, context: {:016x?}", ctx.mepc, ins, ctx); 76 | #[cfg(target_pointer_width = "32")] 77 | panic!("invalid instruction from machine level, mepc: {:08x?}, instruction: {:08x?}, context: {:08x?}", ctx.mepc, ins, ctx); 78 | } 79 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/early_trap.rs: -------------------------------------------------------------------------------- 1 | use riscv::register::{ 2 | mcause, mscratch, 3 | mstatus::Mstatus, 4 | mtval, 5 | mtvec::{self, TrapMode}, 6 | }; 7 | 8 | #[inline] 9 | pub fn init(hart_id: usize) { 10 | let stack_base = unsafe { &super::SBI_STACK } as *const _ as usize; 11 | let stack = stack_base + (hart_id + 1) * super::PER_HART_STACK_SIZE; 12 | mscratch::write(stack); 13 | let mut addr = early_trap_fail as usize; 14 | if addr & 0x2 != 0 { 15 | addr += 0x2; // 中断入口地址必须对齐到4个字节 16 | } 17 | unsafe { mtvec::write(addr, TrapMode::Direct) }; 18 | } 19 | 20 | extern "C" fn rust_fail(ctx: &SupervisorContext) -> ! { 21 | crate::console::eprintln!( 22 | "rustsbi: early init stage fail, context: {:x?}, mcause: {:?}, mtval: {:x}", 23 | ctx, 24 | mcause::read().cause(), 25 | mtval::read() 26 | ); 27 | loop {} 28 | } 29 | 30 | #[derive(Debug)] 31 | #[repr(C)] 32 | pub struct SupervisorContext { 33 | pub ra: usize, // 0 34 | pub sp: usize, 35 | pub gp: usize, 36 | pub tp: usize, 37 | pub t0: usize, 38 | pub t1: usize, 39 | pub t2: usize, 40 | pub s0: usize, 41 | pub s1: usize, 42 | pub a0: usize, 43 | pub a1: usize, 44 | pub a2: usize, 45 | pub a3: usize, 46 | pub a4: usize, 47 | pub a5: usize, 48 | pub a6: usize, 49 | pub a7: usize, 50 | pub s2: usize, 51 | pub s3: usize, 52 | pub s4: usize, 53 | pub s5: usize, 54 | pub s6: usize, 55 | pub s7: usize, 56 | pub s8: usize, 57 | pub s9: usize, 58 | pub s10: usize, 59 | pub s11: usize, 60 | pub t3: usize, 61 | pub t4: usize, 62 | pub t5: usize, 63 | pub t6: usize, // 30 64 | pub mstatus: Mstatus, // 31 65 | pub mepc: usize, // 32 66 | } 67 | 68 | #[naked] 69 | #[link_section = ".text"] 70 | pub unsafe extern "C" fn early_trap_fail() -> ! { 71 | core::arch::asm!( // sp:特权级栈,mscratch:特权级上下文 72 | ".p2align 2", 73 | "csrrw sp, mscratch, sp", // 新mscratch:特权级栈, 新sp:特权级上下文 74 | "addi sp, sp, -33*8", 75 | "sd ra, 0*8(sp) 76 | sd gp, 2*8(sp) 77 | sd tp, 3*8(sp) 78 | sd t0, 4*8(sp) 79 | sd t1, 5*8(sp) 80 | sd t2, 6*8(sp) 81 | sd s0, 7*8(sp) 82 | sd s1, 8*8(sp) 83 | sd a0, 9*8(sp) 84 | sd a1, 10*8(sp) 85 | sd a2, 11*8(sp) 86 | sd a3, 12*8(sp) 87 | sd a4, 13*8(sp) 88 | sd a5, 14*8(sp) 89 | sd a6, 15*8(sp) 90 | sd a7, 16*8(sp) 91 | sd s2, 17*8(sp) 92 | sd s3, 18*8(sp) 93 | sd s4, 19*8(sp) 94 | sd s5, 20*8(sp) 95 | sd s6, 21*8(sp) 96 | sd s7, 22*8(sp) 97 | sd s8, 23*8(sp) 98 | sd s9, 24*8(sp) 99 | sd s10, 25*8(sp) 100 | sd s11, 26*8(sp) 101 | sd t3, 27*8(sp) 102 | sd t4, 28*8(sp) 103 | sd t5, 29*8(sp) 104 | sd t6, 30*8(sp)", 105 | "csrr t0, mstatus 106 | sd t0, 31*8(sp)", 107 | "csrr t1, mepc 108 | sd t1, 32*8(sp)", 109 | "csrr t2, mscratch 110 | sd t2, 1*8(sp)", 111 | "mv a0, sp", 112 | "j {fail}", 113 | fail = sym rust_fail, 114 | options(noreturn) 115 | ) 116 | } 117 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const, asm_sym)] 4 | #![feature(generator_trait)] 5 | #![feature(default_alloc_error_handler)] 6 | #![feature(ptr_metadata)] 7 | 8 | extern crate alloc; 9 | 10 | mod console; 11 | mod device_tree; 12 | mod early_trap; 13 | mod execute; 14 | mod feature; 15 | mod hart_csr_utils; 16 | mod peripheral; 17 | mod runtime; 18 | mod util; 19 | 20 | use console::{eprintln, println}; 21 | use core::panic::PanicInfo; 22 | 23 | #[panic_handler] 24 | fn on_panic(info: &PanicInfo) -> ! { 25 | let hart_id = riscv::register::mhartid::read(); 26 | eprintln!("[rustsbi-panic] hart {} {}", hart_id, info); // [rustsbi-panic] hart 0 panicked at xxx 27 | loop {} 28 | } 29 | 30 | static DEVICE_TREE: &'static [u8] = include_bytes!("hifive-unmatched-a00.dtb"); 31 | 32 | fn rust_main(hart_id: usize, opaque: usize) { 33 | let clint = peripheral::Clint::new(0x2000000 as *mut u8); 34 | if hart_id == 0 { 35 | init_bss(); 36 | let uart = unsafe { peripheral::Uart::preloaded_uart0() }; 37 | crate::console::init_stdout(uart); 38 | for target_hart_id in 0..=4 { 39 | if target_hart_id != 0 { 40 | clint.send_soft(target_hart_id); 41 | } 42 | } 43 | } else { 44 | pause(clint); 45 | } 46 | let opaque = if opaque == 0 { 47 | // 如果上一级没有填写设备树文件,这一级填写 48 | DEVICE_TREE.as_ptr() as usize 49 | } else { 50 | opaque 51 | }; 52 | early_trap::init(hart_id); 53 | if hart_id == 0 { 54 | init_heap(); // 必须先加载堆内存,才能使用rustsbi框架 55 | let uart = unsafe { peripheral::Uart::preloaded_uart0() }; 56 | init_rustsbi_stdio(uart); 57 | init_rustsbi_clint(clint); 58 | println!("[rustsbi] RustSBI version {}", rustsbi::VERSION); 59 | println!("{}", rustsbi::LOGO); 60 | println!( 61 | "[rustsbi] Implementation: RustSBI-HiFive-Unleashed Version {}", 62 | env!("CARGO_PKG_VERSION") 63 | ); 64 | if let Err(e) = unsafe { device_tree::parse_device_tree(opaque) } { 65 | println!("[rustsbi] warning: choose from device tree error, {}", e); 66 | } 67 | println!( 68 | "[rustsbi] enter supervisor 0x80200000, opaque register {:#x}", 69 | opaque 70 | ); 71 | hart_csr_utils::print_hart0_csrs(); 72 | for target_hart_id in 0..=4 { 73 | if target_hart_id != 0 { 74 | clint.send_soft(target_hart_id); 75 | } 76 | } 77 | } else { 78 | // 不是初始化核,先暂停 79 | delegate_interrupt_exception(); // 第0个核不能委托中断(@dram) 80 | if hart_id == 1 { 81 | hart_csr_utils::print_hartn_csrs(); 82 | } 83 | pause(clint); 84 | } 85 | runtime::init(); 86 | execute::execute_supervisor(0x80200000, hart_id, opaque); 87 | } 88 | 89 | fn init_bss() { 90 | extern "C" { 91 | static mut ebss: u32; 92 | static mut sbss: u32; 93 | static mut edata: u32; 94 | static mut sdata: u32; 95 | static sidata: u32; 96 | } 97 | unsafe { 98 | r0::zero_bss(&mut sbss, &mut ebss); 99 | r0::init_data(&mut sdata, &mut edata, &sidata); 100 | } 101 | } 102 | 103 | fn init_rustsbi_stdio(uart: peripheral::Uart) { 104 | use rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal; 105 | init_legacy_stdio_embedded_hal(uart); 106 | } 107 | 108 | fn init_rustsbi_clint(clint: peripheral::Clint) { 109 | rustsbi::init_ipi(clint); 110 | rustsbi::init_timer(clint); 111 | } 112 | 113 | fn delegate_interrupt_exception() { 114 | use riscv::register::{medeleg, mideleg, mie}; 115 | unsafe { 116 | mideleg::set_sext(); 117 | mideleg::set_stimer(); 118 | mideleg::set_ssoft(); 119 | mideleg::set_uext(); 120 | mideleg::set_utimer(); 121 | mideleg::set_usoft(); 122 | medeleg::set_instruction_misaligned(); 123 | medeleg::set_breakpoint(); 124 | medeleg::set_user_env_call(); 125 | medeleg::set_instruction_page_fault(); 126 | medeleg::set_load_page_fault(); 127 | medeleg::set_store_page_fault(); 128 | medeleg::set_instruction_fault(); 129 | medeleg::set_load_fault(); 130 | medeleg::set_store_fault(); 131 | mie::set_mext(); 132 | // 不打开mie::set_mtimer 133 | mie::set_msoft(); 134 | } 135 | } 136 | 137 | pub fn pause(clint: peripheral::Clint) { 138 | use riscv::asm::wfi; 139 | use riscv::register::{mhartid, mie, mip}; 140 | unsafe { 141 | let hartid = mhartid::read(); 142 | clint.clear_soft(hartid); // Clear IPI 143 | mip::clear_msoft(); // clear machine software interrupt flag 144 | let prev_msoft = mie::read().msoft(); 145 | mie::set_msoft(); // Start listening for software interrupts 146 | loop { 147 | wfi(); 148 | if mip::read().msoft() { 149 | break; 150 | } 151 | } 152 | if !prev_msoft { 153 | mie::clear_msoft(); // Stop listening for software interrupts 154 | } 155 | clint.clear_soft(hartid); // Clear IPI 156 | } 157 | } 158 | 159 | const SBI_HEAP_SIZE: usize = 64 * 1024; // 64KiB 160 | #[link_section = ".bss.uninit"] 161 | static mut HEAP_SPACE: [u8; SBI_HEAP_SIZE] = [0; SBI_HEAP_SIZE]; 162 | 163 | use buddy_system_allocator::LockedHeap; 164 | 165 | #[global_allocator] 166 | static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty(); 167 | 168 | #[inline] 169 | fn init_heap() { 170 | unsafe { 171 | HEAP_ALLOCATOR 172 | .lock() 173 | .init(HEAP_SPACE.as_ptr() as usize, SBI_HEAP_SIZE); 174 | } 175 | } 176 | 177 | const PER_HART_STACK_SIZE: usize = 4 * 4096; // 16KiB 178 | const SBI_STACK_SIZE: usize = 5 * PER_HART_STACK_SIZE; // 5 harts 179 | #[link_section = ".bss.uninit"] 180 | static mut SBI_STACK: [u8; SBI_STACK_SIZE] = [0; SBI_STACK_SIZE]; 181 | 182 | #[naked] 183 | #[link_section = ".text.entry"] 184 | #[export_name = "_start"] 185 | unsafe extern "C" fn entry() -> ! { 186 | core::arch::asm!( 187 | // 1. clear all registers 188 | "li x1, 0 189 | li x2, 0 190 | li x3, 0 191 | li x4, 0 192 | li x5, 0 193 | li x6, 0 194 | li x7, 0 195 | li x8, 0 196 | li x9, 0", 197 | // no x10 and x11: x10 is a0 and x11 is a1, they are passed to 198 | // main function as arguments 199 | "li x12, 0 200 | li x13, 0 201 | li x14, 0 202 | li x15, 0 203 | li x16, 0 204 | li x17, 0 205 | li x18, 0 206 | li x19, 0 207 | li x20, 0 208 | li x21, 0 209 | li x22, 0 210 | li x23, 0 211 | li x24, 0 212 | li x25, 0 213 | li x26, 0 214 | li x27, 0 215 | li x28, 0 216 | li x29, 0 217 | li x30, 0 218 | li x31, 0", 219 | // 2. set sp 220 | // sp = bootstack + (hart_id + 1) * HART_STACK_SIZE 221 | " 222 | la sp, {stack} 223 | li t0, {per_hart_stack_size} 224 | csrr t1, mhartid 225 | addi t2, t1, 1 226 | 1: add sp, sp, t0 227 | addi t2, t2, -1 228 | bnez t2, 1b 229 | ", 230 | // 3. jump to main function (absolute address) 231 | "call {rust_main}", 232 | // 4. after main function return, invoke CEASE instruction 233 | ".word 0x30500073", // cease 234 | per_hart_stack_size = const PER_HART_STACK_SIZE, 235 | stack = sym SBI_STACK, 236 | rust_main = sym rust_main, 237 | options(noreturn)) 238 | } 239 | -------------------------------------------------------------------------------- /test-kernel/src/sbi.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use core::arch::asm; 3 | use core::fmt; 4 | 5 | pub const EXTENSION_BASE: usize = 0x10; 6 | pub const EXTENSION_TIMER: usize = 0x54494D45; 7 | pub const EXTENSION_IPI: usize = 0x735049; 8 | pub const EXTENSION_RFENCE: usize = 0x52464E43; 9 | pub const EXTENSION_HSM: usize = 0x48534D; 10 | pub const EXTENSION_SRST: usize = 0x53525354; 11 | 12 | const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0; 13 | const FUNCTION_BASE_GET_SBI_IMPL_ID: usize = 0x1; 14 | const FUNCTION_BASE_GET_SBI_IMPL_VERSION: usize = 0x2; 15 | const FUNCTION_BASE_PROBE_EXTENSION: usize = 0x3; 16 | const FUNCTION_BASE_GET_MVENDORID: usize = 0x4; 17 | const FUNCTION_BASE_GET_MARCHID: usize = 0x5; 18 | const FUNCTION_BASE_GET_MIMPID: usize = 0x6; 19 | 20 | #[repr(C)] 21 | pub struct SbiRet { 22 | /// Error number 23 | pub error: usize, 24 | /// Result value 25 | pub value: usize, 26 | } 27 | 28 | const SBI_SUCCESS: usize = 0; 29 | const SBI_ERR_FAILED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-1)); 30 | const SBI_ERR_NOT_SUPPORTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-2)); 31 | const SBI_ERR_INVALID_PARAM: usize = usize::from_ne_bytes(isize::to_ne_bytes(-3)); 32 | const SBI_ERR_DENIED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-4)); 33 | const SBI_ERR_INVALID_ADDRESS: usize = usize::from_ne_bytes(isize::to_ne_bytes(-5)); 34 | const SBI_ERR_ALREADY_AVAILABLE: usize = usize::from_ne_bytes(isize::to_ne_bytes(-6)); 35 | const SBI_ERR_ALREADY_STARTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-7)); 36 | const SBI_ERR_ALREADY_STOPPED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-8)); 37 | 38 | impl fmt::Debug for SbiRet { 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 | match self.error { 41 | SBI_SUCCESS => write!(f, "{:?}", self.value), 42 | SBI_ERR_FAILED => write!(f, ""), 43 | SBI_ERR_NOT_SUPPORTED => write!(f, ""), 44 | SBI_ERR_INVALID_PARAM => write!(f, ""), 45 | SBI_ERR_DENIED => write!(f, ""), 46 | SBI_ERR_INVALID_ADDRESS => write!(f, ""), 47 | SBI_ERR_ALREADY_AVAILABLE => write!(f, ""), 48 | SBI_ERR_ALREADY_STARTED => write!(f, ""), 49 | SBI_ERR_ALREADY_STOPPED => write!(f, ""), 50 | unknown => write!(f, "[SBI Unknown error: {}]", unknown), 51 | } 52 | } 53 | } 54 | 55 | #[inline] 56 | pub fn get_spec_version() -> usize { 57 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION).value 58 | } 59 | 60 | #[inline] 61 | pub fn get_sbi_impl_id() -> usize { 62 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_ID).value 63 | } 64 | 65 | #[inline] 66 | pub fn get_sbi_impl_version() -> usize { 67 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_VERSION).value 68 | } 69 | 70 | #[inline] 71 | pub fn probe_extension(extension_id: usize) -> usize { 72 | sbi_call_1(EXTENSION_BASE, FUNCTION_BASE_PROBE_EXTENSION, extension_id).value 73 | } 74 | 75 | #[inline] 76 | pub fn get_mvendorid() -> usize { 77 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MVENDORID).value 78 | } 79 | 80 | #[inline] 81 | pub fn get_marchid() -> usize { 82 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MARCHID).value 83 | } 84 | 85 | #[inline] 86 | pub fn get_mimpid() -> usize { 87 | sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MIMPID).value 88 | } 89 | 90 | const FUNCTION_SYSTEM_RESET: usize = 0x0; 91 | 92 | pub const RESET_TYPE_SHUTDOWN: usize = 0x0000_0000; 93 | pub const RESET_TYPE_COLD_REBOOT: usize = 0x0000_0001; 94 | pub const RESET_TYPE_WARM_REBOOT: usize = 0x0000_0002; 95 | pub const RESET_REASON_NO_REASON: usize = 0x0000_0000; 96 | pub const RESET_REASON_SYSTEM_FAILURE: usize = 0x0000_0001; 97 | 98 | #[inline] 99 | pub fn reset(reset_type: usize, reset_reason: usize) -> SbiRet { 100 | sbi_call_2( 101 | EXTENSION_SRST, 102 | FUNCTION_SYSTEM_RESET, 103 | reset_type, 104 | reset_reason, 105 | ) 106 | } 107 | 108 | pub fn shutdown() -> ! { 109 | sbi_call_2( 110 | EXTENSION_SRST, 111 | FUNCTION_SYSTEM_RESET, 112 | RESET_TYPE_SHUTDOWN, 113 | RESET_REASON_NO_REASON, 114 | ); 115 | unreachable!() 116 | } 117 | 118 | #[inline(always)] 119 | fn sbi_call_legacy(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 120 | let ret; 121 | match () { 122 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 123 | () => unsafe { 124 | asm!( 125 | "ecall", 126 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 127 | in("a7") which, 128 | lateout("a0") ret, 129 | ) 130 | }, 131 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 132 | () => { 133 | drop((which, arg0, arg1, arg2)); 134 | unimplemented!("not RISC-V instruction set architecture") 135 | } 136 | }; 137 | ret 138 | } 139 | 140 | const SBI_SET_TIMER: usize = 0; 141 | const SBI_CONSOLE_PUTCHAR: usize = 1; 142 | const SBI_CONSOLE_GETCHAR: usize = 2; 143 | const SBI_CLEAR_IPI: usize = 3; 144 | const SBI_SEND_IPI: usize = 4; 145 | const SBI_REMOTE_FENCE_I: usize = 5; 146 | const SBI_REMOTE_SFENCE_VMA: usize = 6; 147 | const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; 148 | const SBI_SHUTDOWN: usize = 8; 149 | 150 | pub fn console_putchar(c: usize) { 151 | sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c, 0, 0); 152 | } 153 | 154 | pub fn console_getchar() -> usize { 155 | sbi_call_legacy(SBI_CONSOLE_GETCHAR, 0, 0, 0) 156 | } 157 | 158 | pub fn set_timer(time: usize) { 159 | sbi_call_legacy(SBI_SET_TIMER, time, 0, 0); 160 | } 161 | 162 | const FUNCTION_IPI_SEND_IPI: usize = 0x0; 163 | 164 | pub fn send_ipi(hart_mask: usize, hart_mask_base: usize) -> SbiRet { 165 | sbi_call_2( 166 | EXTENSION_IPI, 167 | FUNCTION_IPI_SEND_IPI, 168 | hart_mask, 169 | hart_mask_base, 170 | ) 171 | } 172 | 173 | const FUNCTION_HSM_HART_START: usize = 0x0; 174 | const FUNCTION_HSM_HART_STOP: usize = 0x1; 175 | const FUNCTION_HSM_HART_GET_STATUS: usize = 0x2; 176 | const FUNCTION_HSM_HART_SUSPEND: usize = 0x3; 177 | 178 | pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet { 179 | sbi_call_3( 180 | EXTENSION_HSM, 181 | FUNCTION_HSM_HART_START, 182 | hartid, 183 | start_addr, 184 | opaque, 185 | ) 186 | } 187 | 188 | pub fn hart_stop(hartid: usize) -> SbiRet { 189 | sbi_call_1(EXTENSION_HSM, FUNCTION_HSM_HART_STOP, hartid) 190 | } 191 | 192 | pub fn hart_get_status(hartid: usize) -> SbiRet { 193 | sbi_call_1(EXTENSION_HSM, FUNCTION_HSM_HART_GET_STATUS, hartid) 194 | } 195 | 196 | pub fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet { 197 | sbi_call_3( 198 | EXTENSION_HSM, 199 | FUNCTION_HSM_HART_SUSPEND, 200 | suspend_type as usize, 201 | resume_addr, 202 | opaque, 203 | ) 204 | } 205 | 206 | #[inline(always)] 207 | fn sbi_call_0(extension: usize, function: usize) -> SbiRet { 208 | let (error, value); 209 | unsafe { 210 | asm!( 211 | "ecall", 212 | in("a6") function, in("a7") extension, 213 | lateout("a0") error, lateout("a1") value, 214 | ) 215 | }; 216 | SbiRet { error, value } 217 | } 218 | 219 | #[inline(always)] 220 | fn sbi_call_1(extension: usize, function: usize, arg0: usize) -> SbiRet { 221 | let (error, value); 222 | unsafe { 223 | asm!( 224 | "ecall", 225 | in("a0") arg0, 226 | in("a6") function, in("a7") extension, 227 | lateout("a0") error, lateout("a1") value, 228 | ) 229 | }; 230 | SbiRet { error, value } 231 | } 232 | 233 | #[inline(always)] 234 | fn sbi_call_2(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet { 235 | let (error, value); 236 | unsafe { 237 | asm!( 238 | "ecall", 239 | in("a0") arg0, in("a1") arg1, 240 | in("a6") function, in("a7") extension, 241 | lateout("a0") error, lateout("a1") value, 242 | ) 243 | }; 244 | SbiRet { error, value } 245 | } 246 | 247 | #[inline(always)] 248 | fn sbi_call_3(extension: usize, function: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet { 249 | let (error, value); 250 | unsafe { 251 | asm!( 252 | "ecall", 253 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 254 | in("a6") function, in("a7") extension, 255 | lateout("a0") error, lateout("a1") value, 256 | ) 257 | }; 258 | SbiRet { error, value } 259 | } 260 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | arch::asm, 3 | ops::{Generator, GeneratorState}, 4 | pin::Pin, 5 | }; 6 | use riscv::register::{ 7 | mcause::{self, Exception, Interrupt, Trap}, 8 | mstatus::{self, Mstatus, MPP}, 9 | mtval, 10 | mtvec::{self, TrapMode}, 11 | }; 12 | 13 | #[inline] 14 | pub fn init() { 15 | let mut addr = from_supervisor_save as usize; 16 | if addr & 0x2 != 0 { 17 | addr += 0x2; // 中断入口地址必须对齐到4个字节 18 | } 19 | unsafe { mtvec::write(addr, TrapMode::Direct) }; 20 | } 21 | 22 | pub struct Runtime { 23 | context: SupervisorContext, 24 | } 25 | 26 | impl Runtime { 27 | pub fn new_sbi_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> Self { 28 | let context: SupervisorContext = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; 29 | let mut ans = Runtime { context }; 30 | ans.prepare_supervisor(supervisor_mepc); 31 | ans.context.a0 = a0; 32 | ans.context.a1 = a1; 33 | ans 34 | } 35 | 36 | fn reset(&mut self) { 37 | unsafe { mstatus::set_mpp(MPP::Supervisor) }; 38 | self.context.mstatus = mstatus::read(); 39 | self.context.machine_stack = 0x2333333366666666; // 将会被resume函数覆盖 40 | } 41 | 42 | // 在处理异常的时候,使用context_mut得到运行时当前用户的上下文,可以改变上下文的内容 43 | pub fn context_mut(&mut self) -> &mut SupervisorContext { 44 | &mut self.context 45 | } 46 | 47 | pub fn prepare_supervisor(&mut self, new_mepc: usize) { 48 | self.reset(); 49 | self.context.mepc = new_mepc; 50 | } 51 | } 52 | 53 | impl Generator for Runtime { 54 | type Yield = MachineTrap; 55 | type Return = (); 56 | fn resume(mut self: Pin<&mut Self>, _arg: ()) -> GeneratorState { 57 | unsafe { do_resume(&mut self.context as *mut _) }; 58 | let mtval = mtval::read(); 59 | let trap = match mcause::read().cause() { 60 | Trap::Exception(Exception::SupervisorEnvCall) => MachineTrap::SbiCall(), 61 | Trap::Exception(Exception::IllegalInstruction) => MachineTrap::IllegalInstruction(), 62 | Trap::Interrupt(Interrupt::MachineTimer) => MachineTrap::MachineTimer(), 63 | Trap::Interrupt(Interrupt::MachineSoft) => MachineTrap::MachineSoft(), 64 | e => panic!( 65 | "unhandled exception: {:?}! mtval: {:x?}, ctx: {:x?}", 66 | e, mtval, self.context 67 | ), 68 | }; 69 | GeneratorState::Yielded(trap) 70 | } 71 | } 72 | 73 | #[repr(C)] 74 | pub enum MachineTrap { 75 | SbiCall(), 76 | IllegalInstruction(), 77 | MachineTimer(), 78 | MachineSoft(), 79 | } 80 | 81 | #[derive(Debug)] 82 | #[repr(C)] 83 | pub struct SupervisorContext { 84 | pub ra: usize, // 0 85 | pub sp: usize, 86 | pub gp: usize, 87 | pub tp: usize, 88 | pub t0: usize, 89 | pub t1: usize, 90 | pub t2: usize, 91 | pub s0: usize, 92 | pub s1: usize, 93 | pub a0: usize, 94 | pub a1: usize, 95 | pub a2: usize, 96 | pub a3: usize, 97 | pub a4: usize, 98 | pub a5: usize, 99 | pub a6: usize, 100 | pub a7: usize, 101 | pub s2: usize, 102 | pub s3: usize, 103 | pub s4: usize, 104 | pub s5: usize, 105 | pub s6: usize, 106 | pub s7: usize, 107 | pub s8: usize, 108 | pub s9: usize, 109 | pub s10: usize, 110 | pub s11: usize, 111 | pub t3: usize, 112 | pub t4: usize, 113 | pub t5: usize, 114 | pub t6: usize, // 30 115 | pub mstatus: Mstatus, // 31 116 | pub mepc: usize, // 32 117 | pub machine_stack: usize, // 33 118 | } 119 | 120 | #[naked] 121 | #[link_section = ".text"] 122 | unsafe extern "C" fn do_resume(_supervisor_context: *mut SupervisorContext) { 123 | asm!("j {from_machine_save}", from_machine_save = sym from_machine_save, options(noreturn)) 124 | } 125 | 126 | #[naked] 127 | #[link_section = ".text"] 128 | unsafe extern "C" fn from_machine_save(_supervisor_context: *mut SupervisorContext) -> ! { 129 | asm!( // sp:机器栈顶 130 | "addi sp, sp, -15*8", // sp:机器栈顶 131 | // 进入函数之前,已经保存了调用者寄存器,应当保存被调用者寄存器 132 | "sd ra, 0*8(sp) 133 | sd gp, 1*8(sp) 134 | sd tp, 2*8(sp) 135 | sd s0, 3*8(sp) 136 | sd s1, 4*8(sp) 137 | sd s2, 5*8(sp) 138 | sd s3, 6*8(sp) 139 | sd s4, 7*8(sp) 140 | sd s5, 8*8(sp) 141 | sd s6, 9*8(sp) 142 | sd s7, 10*8(sp) 143 | sd s8, 11*8(sp) 144 | sd s9, 12*8(sp) 145 | sd s10, 13*8(sp) 146 | sd s11, 14*8(sp)", 147 | // a0:特权级上下文 148 | "j {to_supervisor_restore}", 149 | to_supervisor_restore = sym to_supervisor_restore, 150 | options(noreturn) 151 | ) 152 | } 153 | 154 | #[naked] 155 | #[link_section = ".text"] 156 | pub unsafe extern "C" fn to_supervisor_restore(_supervisor_context: *mut SupervisorContext) -> ! { 157 | asm!( 158 | // a0:特权级上下文 159 | "sd sp, 33*8(a0)", // 机器栈顶放进特权级上下文 160 | "csrw mscratch, a0", // 新mscratch:特权级上下文 161 | // mscratch:特权级上下文 162 | "mv sp, a0", // 新sp:特权级上下文 163 | "ld t0, 31*8(sp) 164 | ld t1, 32*8(sp) 165 | csrw mstatus, t0 166 | csrw mepc, t1", 167 | "ld ra, 0*8(sp) 168 | ld gp, 2*8(sp) 169 | ld tp, 3*8(sp) 170 | ld t0, 4*8(sp) 171 | ld t1, 5*8(sp) 172 | ld t2, 6*8(sp) 173 | ld s0, 7*8(sp) 174 | ld s1, 8*8(sp) 175 | ld a0, 9*8(sp) 176 | ld a1, 10*8(sp) 177 | ld a2, 11*8(sp) 178 | ld a3, 12*8(sp) 179 | ld a4, 13*8(sp) 180 | ld a5, 14*8(sp) 181 | ld a6, 15*8(sp) 182 | ld a7, 16*8(sp) 183 | ld s2, 17*8(sp) 184 | ld s3, 18*8(sp) 185 | ld s4, 19*8(sp) 186 | ld s5, 20*8(sp) 187 | ld s6, 21*8(sp) 188 | ld s7, 22*8(sp) 189 | ld s8, 23*8(sp) 190 | ld s9, 24*8(sp) 191 | ld s10, 25*8(sp) 192 | ld s11, 26*8(sp) 193 | ld t3, 27*8(sp) 194 | ld t4, 28*8(sp) 195 | ld t5, 29*8(sp) 196 | ld t6, 30*8(sp)", 197 | "ld sp, 1*8(sp)", // 新sp:特权级栈 198 | // sp:特权级栈, mscratch:特权级上下文 199 | "mret", 200 | options(noreturn) 201 | ) 202 | } 203 | 204 | // 中断开始 205 | 206 | #[naked] 207 | #[link_section = ".text"] 208 | pub unsafe extern "C" fn from_supervisor_save() -> ! { 209 | asm!( // sp:特权级栈,mscratch:特权级上下文 210 | ".p2align 2", 211 | "csrrw sp, mscratch, sp", // 新mscratch:特权级栈, 新sp:特权级上下文 212 | "sd ra, 0*8(sp) 213 | sd gp, 2*8(sp) 214 | sd tp, 3*8(sp) 215 | sd t0, 4*8(sp) 216 | sd t1, 5*8(sp) 217 | sd t2, 6*8(sp) 218 | sd s0, 7*8(sp) 219 | sd s1, 8*8(sp) 220 | sd a0, 9*8(sp) 221 | sd a1, 10*8(sp) 222 | sd a2, 11*8(sp) 223 | sd a3, 12*8(sp) 224 | sd a4, 13*8(sp) 225 | sd a5, 14*8(sp) 226 | sd a6, 15*8(sp) 227 | sd a7, 16*8(sp) 228 | sd s2, 17*8(sp) 229 | sd s3, 18*8(sp) 230 | sd s4, 19*8(sp) 231 | sd s5, 20*8(sp) 232 | sd s6, 21*8(sp) 233 | sd s7, 22*8(sp) 234 | sd s8, 23*8(sp) 235 | sd s9, 24*8(sp) 236 | sd s10, 25*8(sp) 237 | sd s11, 26*8(sp) 238 | sd t3, 27*8(sp) 239 | sd t4, 28*8(sp) 240 | sd t5, 29*8(sp) 241 | sd t6, 30*8(sp)", 242 | "csrr t0, mstatus 243 | sd t0, 31*8(sp)", 244 | "csrr t1, mepc 245 | sd t1, 32*8(sp)", 246 | // mscratch:特权级栈,sp:特权级上下文 247 | "csrrw t2, mscratch, sp", // 新mscratch:特权级上下文,t2:特权级栈 248 | "sd t2, 1*8(sp)", // 保存特权级栈 249 | "j {to_machine_restore}", 250 | to_machine_restore = sym to_machine_restore, 251 | options(noreturn) 252 | ) 253 | } 254 | 255 | #[naked] 256 | #[link_section = ".text"] 257 | unsafe extern "C" fn to_machine_restore() -> ! { 258 | asm!( 259 | // mscratch:特权级上下文 260 | "csrr sp, mscratch", // sp:特权级上下文 261 | "ld sp, 33*8(sp)", // sp:机器栈 262 | "ld ra, 0*8(sp) 263 | ld gp, 1*8(sp) 264 | ld tp, 2*8(sp) 265 | ld s0, 3*8(sp) 266 | ld s1, 4*8(sp) 267 | ld s2, 5*8(sp) 268 | ld s3, 6*8(sp) 269 | ld s4, 7*8(sp) 270 | ld s5, 8*8(sp) 271 | ld s6, 9*8(sp) 272 | ld s7, 10*8(sp) 273 | ld s8, 11*8(sp) 274 | ld s9, 12*8(sp) 275 | ld s10, 13*8(sp) 276 | ld s11, 14*8(sp)", 277 | "addi sp, sp, 15*8", // sp:机器栈顶 278 | "jr ra", // 其实就是ret 279 | options(noreturn) 280 | ) 281 | } 282 | -------------------------------------------------------------------------------- /test-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(naked_functions, asm_sym, asm_const)] 2 | #![feature(default_alloc_error_handler)] 3 | #![no_std] 4 | #![no_main] 5 | 6 | mod console; 7 | mod mm; 8 | mod sbi; 9 | mod util; 10 | 11 | use riscv::register::{ 12 | scause::{self, Exception, Trap}, 13 | sepc, 14 | stvec::{self, TrapMode}, 15 | }; 16 | 17 | pub extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! { 18 | if hartid == 0 { 19 | // initialization 20 | mm::init_heap(); 21 | } 22 | if hartid == 0 { 23 | println!( 24 | "<< Test-kernel: Hart id = {}, DTB physical address = {:#x}", 25 | hartid, dtb_pa 26 | ); 27 | test_base_extension(); 28 | test_sbi_ins_emulation(); 29 | unsafe { stvec::write(start_trap as usize, TrapMode::Direct) }; 30 | println!(">> Test-kernel: Trigger illegal exception"); 31 | unsafe { core::arch::asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction 32 | } 33 | if hartid == 0 { 34 | let sbi_ret = sbi::hart_stop(3); 35 | println!(">> Stop hart 3, return value {:?}", sbi_ret); 36 | for i in 0..4 { 37 | let sbi_ret = sbi::hart_get_status(i); 38 | println!(">> Hart {} state return value: {:?}", i, sbi_ret); 39 | } 40 | } else if hartid == 1 { 41 | let sbi_ret = sbi::hart_suspend(0x00000000, 0, 0); 42 | println!( 43 | ">> Start test for hart {}, retentive suspend return value {:?}", 44 | hartid, sbi_ret 45 | ); 46 | } else if hartid == 2 { 47 | /* resume_addr should be physical address, and here pa == va */ 48 | let sbi_ret = sbi::hart_suspend(0x80000000, hart_2_resume as usize, 0x4567890a); 49 | println!(">> Error for non-retentive suspend: {:?}", sbi_ret); 50 | loop {} 51 | } else { 52 | // hartid == 3 53 | loop {} 54 | } 55 | if hartid == 0 { 56 | println!( 57 | "<< Test-kernel: test for hart {} success, wake another hart", 58 | hartid 59 | ); 60 | let bv: usize = 0b10; 61 | let sbi_ret = sbi::send_ipi(&bv as *const _ as usize, hartid); // wake hartid + 1 62 | println!(">> Wake hart 1, sbi return value {:?}", sbi_ret); 63 | loop {} // wait for machine shutdown 64 | } else if hartid == 1 { 65 | // send software IPI to activate hart 2 66 | let bv: usize = 0b10; 67 | let sbi_ret = sbi::send_ipi(&bv as *const _ as usize, hartid); // wake hartid + 1 68 | println!(">> Wake hart 2, sbi return value {:?}", sbi_ret); 69 | loop {} 70 | } else { 71 | // hartid == 2 || hartid == 3 72 | unreachable!() 73 | } 74 | } 75 | 76 | extern "C" fn hart_2_resume(hart_id: usize, param: usize) { 77 | println!( 78 | "<< The parameter passed to hart {} resume is: {:#x}", 79 | hart_id, param 80 | ); 81 | let param = 0x12345678; 82 | println!(">> Start hart 3 with parameter {:#x}", param); 83 | /* start_addr should be physical address, and here pa == va */ 84 | let sbi_ret = sbi::hart_start(3, hart_3_start as usize, param); 85 | println!(">> SBI return value: {:?}", sbi_ret); 86 | loop {} // wait for machine shutdown 87 | } 88 | 89 | extern "C" fn hart_3_start(hart_id: usize, param: usize) { 90 | println!( 91 | "<< The parameter passed to hart {} start is: {:#x}", 92 | hart_id, param 93 | ); 94 | println!("<< Test-kernel: All hart SBI test SUCCESS, shutdown"); 95 | sbi::shutdown() 96 | } 97 | 98 | fn test_base_extension() { 99 | println!(">> Test-kernel: Testing base extension"); 100 | let base_version = sbi::probe_extension(sbi::EXTENSION_BASE); 101 | if base_version == 0 { 102 | println!("!! Test-kernel: no base extension probed; SBI call returned value '0'"); 103 | println!( 104 | "!! Test-kernel: This SBI implementation may only have legacy extension implemented" 105 | ); 106 | println!("!! Test-kernel: SBI test FAILED due to no base extension found"); 107 | sbi::shutdown() 108 | } 109 | println!("<< Test-kernel: Base extension version: {:x}", base_version); 110 | println!( 111 | "<< Test-kernel: SBI specification version: {:x}", 112 | sbi::get_spec_version() 113 | ); 114 | println!( 115 | "<< Test-kernel: SBI implementation Id: {:x}", 116 | sbi::get_sbi_impl_id() 117 | ); 118 | println!( 119 | "<< Test-kernel: SBI implementation version: {:x}", 120 | sbi::get_sbi_impl_version() 121 | ); 122 | println!( 123 | "<< Test-kernel: Device mvendorid: {:x}", 124 | sbi::get_mvendorid() 125 | ); 126 | println!("<< Test-kernel: Device marchid: {:x}", sbi::get_marchid()); 127 | println!("<< Test-kernel: Device mimpid: {:x}", sbi::get_mimpid()); 128 | } 129 | 130 | fn test_sbi_ins_emulation() { 131 | println!(">> Test-kernel: Testing SBI instruction emulation"); 132 | let time_start = riscv::register::time::read64(); 133 | println!("<< Test-kernel: Current time: {:x}", time_start); 134 | let time_end = riscv::register::time::read64(); 135 | if time_end > time_start { 136 | println!("<< Test-kernel: Time after operation: {:x}", time_end); 137 | } else { 138 | println!("!! Test-kernel: SBI test FAILED due to incorrect time counter"); 139 | sbi::shutdown() 140 | } 141 | } 142 | 143 | pub extern "C" fn rust_trap_exception() { 144 | let cause = scause::read().cause(); 145 | println!("<< Test-kernel: Value of scause: {:?}", cause); 146 | if cause != Trap::Exception(Exception::IllegalInstruction) { 147 | println!("!! Test-kernel: Wrong cause associated to illegal instruction"); 148 | sbi::shutdown() 149 | } 150 | println!("<< Test-kernel: Illegal exception delegate success"); 151 | sepc::write(sepc::read().wrapping_add(4)); 152 | } 153 | 154 | use core::panic::PanicInfo; 155 | 156 | #[cfg_attr(not(test), panic_handler)] 157 | #[allow(unused)] 158 | fn panic(info: &PanicInfo) -> ! { 159 | println!("!! Test-kernel: {}", info); 160 | println!("!! Test-kernel: SBI test FAILED due to panic"); 161 | sbi::reset(sbi::RESET_TYPE_SHUTDOWN, sbi::RESET_REASON_SYSTEM_FAILURE); 162 | loop {} 163 | } 164 | 165 | const BOOT_STACK_SIZE: usize = 0x10000 * 5; 166 | 167 | static mut BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0; BOOT_STACK_SIZE]; 168 | 169 | #[naked] 170 | #[link_section = ".text.entry"] 171 | #[export_name = "_start"] 172 | unsafe extern "C" fn entry() -> ! { 173 | core::arch::asm!(" 174 | # 1. set sp 175 | # sp = bootstack + (hartid + 1) * 0x10000 176 | add t0, a0, 1 177 | slli t0, t0, 14 178 | 1: auipc sp, %pcrel_hi({boot_stack}) 179 | addi sp, sp, %pcrel_lo(1b) 180 | add sp, sp, t0 181 | # 2. jump to rust_main (absolute address) 182 | 1: auipc t0, %pcrel_hi({rust_main}) 183 | addi t0, t0, %pcrel_lo(1b) 184 | jr t0 185 | ", 186 | boot_stack = sym BOOT_STACK, 187 | rust_main = sym rust_main, 188 | options(noreturn)) 189 | } 190 | 191 | #[cfg(target_pointer_width = "128")] 192 | macro_rules! define_store_load { 193 | () => { 194 | ".altmacro 195 | .macro STORE reg, offset 196 | sq \\reg, \\offset* {REGBYTES} (sp) 197 | .endm 198 | .macro LOAD reg, offset 199 | lq \\reg, \\offset* {REGBYTES} (sp) 200 | .endm" 201 | }; 202 | } 203 | 204 | #[cfg(target_pointer_width = "64")] 205 | macro_rules! define_store_load { 206 | () => { 207 | ".altmacro 208 | .macro STORE reg, offset 209 | sd \\reg, \\offset* {REGBYTES} (sp) 210 | .endm 211 | .macro LOAD reg, offset 212 | ld \\reg, \\offset* {REGBYTES} (sp) 213 | .endm" 214 | }; 215 | } 216 | 217 | #[cfg(target_pointer_width = "32")] 218 | macro_rules! define_store_load { 219 | () => { 220 | ".altmacro 221 | .macro STORE reg, offset 222 | sw \\reg, \\offset* {REGBYTES} (sp) 223 | .endm 224 | .macro LOAD reg, offset 225 | lw \\reg, \\offset* {REGBYTES} (sp) 226 | .endm" 227 | }; 228 | } 229 | 230 | #[naked] 231 | #[link_section = ".text"] 232 | unsafe extern "C" fn start_trap() { 233 | core::arch::asm!(define_store_load!(), " 234 | .p2align 2 235 | addi sp, sp, -16 * {REGBYTES} 236 | STORE ra, 0 237 | STORE t0, 1 238 | STORE t1, 2 239 | STORE t2, 3 240 | STORE t3, 4 241 | STORE t4, 5 242 | STORE t5, 6 243 | STORE t6, 7 244 | STORE a0, 8 245 | STORE a1, 9 246 | STORE a2, 10 247 | STORE a3, 11 248 | STORE a4, 12 249 | STORE a5, 13 250 | STORE a6, 14 251 | STORE a7, 15 252 | mv a0, sp 253 | call {rust_trap_exception} 254 | LOAD ra, 0 255 | LOAD t0, 1 256 | LOAD t1, 2 257 | LOAD t2, 3 258 | LOAD t3, 4 259 | LOAD t4, 5 260 | LOAD t5, 6 261 | LOAD t6, 7 262 | LOAD a0, 8 263 | LOAD a1, 9 264 | LOAD a2, 10 265 | LOAD a3, 11 266 | LOAD a4, 12 267 | LOAD a5, 13 268 | LOAD a6, 14 269 | LOAD a7, 15 270 | addi sp, sp, 16 * {REGBYTES} 271 | sret 272 | ", 273 | REGBYTES = const core::mem::size_of::(), 274 | rust_trap_exception = sym rust_trap_exception, 275 | options(noreturn)) 276 | } 277 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::{ 3 | env, 4 | path::{Path, PathBuf}, 5 | process::{self, Command}, 6 | }; 7 | 8 | use clap::{clap_app, crate_authors, crate_description, crate_version}; 9 | 10 | #[derive(Debug)] 11 | struct XtaskEnv { 12 | compile_mode: CompileMode, 13 | } 14 | 15 | #[derive(Debug)] 16 | enum CompileMode { 17 | Debug, 18 | Release, 19 | } 20 | 21 | impl fmt::Display for CompileMode { 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | match *self { 24 | CompileMode::Debug => write!(f, "debug"), 25 | CompileMode::Release => write!(f, "release"), 26 | } 27 | } 28 | } 29 | 30 | const DEFAULT_TARGET: &'static str = "riscv64imac-unknown-none-elf"; 31 | 32 | fn main() { 33 | let matches = clap_app!(xtask => 34 | (version: crate_version!()) 35 | (author: crate_authors!()) 36 | (about: crate_description!()) 37 | (@subcommand make => 38 | (about: "Build project") 39 | (@arg release: --release "Build artifacts in release mode, with optimizations") 40 | ) 41 | (@subcommand asm => 42 | (about: "View asm code for project") 43 | (@arg release: --release "Build artifacts in release mode, with optimizations") 44 | ) 45 | (@subcommand image => 46 | (about: "Build SD card partition image") 47 | (@arg PAYLOAD: "Set the build payload, may be 'test-kernel'") 48 | (@arg release: --release "Build artifacts in release mode, with optimizations") 49 | ) 50 | (@subcommand gdb => 51 | (about: "Run GDB debugger") 52 | ) 53 | ) 54 | .get_matches(); 55 | let mut xtask_env = XtaskEnv { 56 | compile_mode: CompileMode::Debug, 57 | }; 58 | if let Some(matches) = matches.subcommand_matches("make") { 59 | if matches.is_present("release") { 60 | xtask_env.compile_mode = CompileMode::Release; 61 | } 62 | eprintln!("xtask make: mode: {:?}", xtask_env.compile_mode); 63 | xtask_build_sbi(&xtask_env); 64 | xtask_binary_sbi(&xtask_env); 65 | } else if let Some(matches) = matches.subcommand_matches("asm") { 66 | if matches.is_present("release") { 67 | xtask_env.compile_mode = CompileMode::Release; 68 | } 69 | eprintln!("xtask asm: mode: {:?}", xtask_env.compile_mode); 70 | xtask_build_sbi(&xtask_env); 71 | xtask_asm_sbi(&xtask_env); 72 | } else if let Some(matches) = matches.subcommand_matches("image") { 73 | if matches.is_present("release") { 74 | xtask_env.compile_mode = CompileMode::Release; 75 | } 76 | eprintln!("xtask image: mode: {:?}", xtask_env.compile_mode); 77 | xtask_build_sbi(&xtask_env); 78 | xtask_binary_sbi(&xtask_env); 79 | if matches.value_of("PAYLOAD") == Some("test-kernel") { 80 | xtask_build_test_kernel(&xtask_env); 81 | xtask_binary_test_kernel(&xtask_env); 82 | xtask_sd_image_test_kernel(&xtask_env); 83 | } else { 84 | xtask_sd_image(&xtask_env); 85 | } 86 | } else if let Some(_matches) = matches.subcommand_matches("gdb") { 87 | eprintln!("xtask gdb: mode: {:?}", xtask_env.compile_mode); 88 | xtask_build_sbi(&xtask_env); 89 | xtask_binary_sbi(&xtask_env); 90 | xtask_unmatched_gdb(&xtask_env); 91 | } else { 92 | eprintln!("Use `cargo make` to build, `cargo xtask --help` for help") 93 | } 94 | } 95 | 96 | fn xtask_build_sbi(xtask_env: &XtaskEnv) { 97 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 98 | let mut command = Command::new(cargo); 99 | command.current_dir(project_root().join("rustsbi-hifive-unmatched")); 100 | command.arg("build"); 101 | match xtask_env.compile_mode { 102 | CompileMode::Debug => {} 103 | CompileMode::Release => { 104 | command.arg("--release"); 105 | } 106 | } 107 | command.args(&["--package", "rustsbi-hifive-unmatched"]); 108 | command.args(&["--target", DEFAULT_TARGET]); 109 | let status = command.status().unwrap(); 110 | if !status.success() { 111 | eprintln!("cargo build failed"); 112 | process::exit(1); 113 | } 114 | } 115 | 116 | fn xtask_binary_sbi(xtask_env: &XtaskEnv) { 117 | let objcopy = "rust-objcopy"; 118 | let status = Command::new(objcopy) 119 | .current_dir(dist_dir(xtask_env)) 120 | .arg("rustsbi-hifive-unmatched") 121 | .arg("--binary-architecture=riscv64") 122 | .arg("--strip-all") 123 | .args(&["-O", "binary", "rustsbi-hifive-unmatched.bin"]) 124 | .status() 125 | .unwrap(); 126 | 127 | if !status.success() { 128 | eprintln!("objcopy binary failed"); 129 | process::exit(1); 130 | } 131 | } 132 | 133 | fn xtask_asm_sbi(xtask_env: &XtaskEnv) { 134 | // @{{objdump}} -D {{test-kernel-elf}} | less 135 | Command::new("riscv-none-embed-objdump") 136 | .current_dir(dist_dir(xtask_env)) 137 | .arg("--disassemble") 138 | .arg("--demangle") 139 | .arg("rustsbi-hifive-unmatched") 140 | .status() 141 | .unwrap(); 142 | } 143 | 144 | fn xtask_unmatched_gdb(xtask_env: &XtaskEnv) { 145 | let mut command = Command::new("riscv-none-embed-gdb"); 146 | command.current_dir(dist_dir(xtask_env)); 147 | command.args(&["--eval-command", "file rustsbi-hifive-unmatched"]); 148 | command.args(&["--eval-command", "target extended-remote localhost:3333"]); 149 | command.arg("--quiet"); 150 | 151 | ctrlc::set_handler(move || { 152 | // when ctrl-c, don't exit gdb 153 | }) 154 | .expect("disable Ctrl-C exit"); 155 | 156 | let status = command.status().expect("run program"); 157 | 158 | if !status.success() { 159 | eprintln!("gdb failed with status {}", status); 160 | process::exit(status.code().unwrap_or(1)); 161 | } 162 | } 163 | 164 | fn xtask_sd_image(xtask_env: &XtaskEnv) { 165 | let status = find_mkimage() 166 | .expect("find mkimage tool") 167 | .current_dir(project_root()) 168 | .arg("-f") 169 | .arg(&format!("sd-image-{}.its", xtask_env.compile_mode)) 170 | .arg("target/sd-card-partition-2.img") 171 | .status() 172 | .expect("create sd card image"); 173 | 174 | if !status.success() { 175 | eprintln!("mkimage failed with status {}", status); 176 | process::exit(status.code().unwrap_or(1)); 177 | } 178 | } 179 | 180 | fn xtask_build_test_kernel(xtask_env: &XtaskEnv) { 181 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 182 | let mut command = Command::new(cargo); 183 | command.current_dir(project_root().join("test-kernel")); 184 | command.arg("build"); 185 | match xtask_env.compile_mode { 186 | CompileMode::Debug => {} 187 | CompileMode::Release => { 188 | command.arg("--release"); 189 | } 190 | } 191 | command.args(&["--package", "test-kernel"]); 192 | command.args(&["--target", DEFAULT_TARGET]); 193 | let status = command.status().unwrap(); 194 | if !status.success() { 195 | eprintln!("cargo build failed"); 196 | process::exit(1); 197 | } 198 | } 199 | 200 | fn xtask_binary_test_kernel(xtask_env: &XtaskEnv) { 201 | let objcopy = "rust-objcopy"; 202 | let status = Command::new(objcopy) 203 | .current_dir(dist_dir(xtask_env)) 204 | .arg("test-kernel") 205 | .arg("--binary-architecture=riscv64") 206 | .arg("--strip-all") 207 | .args(&["-O", "binary", "test-kernel.bin"]) 208 | .status() 209 | .unwrap(); 210 | 211 | if !status.success() { 212 | eprintln!("objcopy binary failed"); 213 | process::exit(1); 214 | } 215 | } 216 | 217 | fn xtask_sd_image_test_kernel(xtask_env: &XtaskEnv) { 218 | let status = find_mkimage() 219 | .expect("find mkimage tool") 220 | .current_dir(project_root()) 221 | .arg("-f") 222 | .arg(&format!( 223 | "test-kernel/sd-image-{}.its", 224 | xtask_env.compile_mode 225 | )) 226 | .arg("target/rustsbi-with-test-kernel.img") 227 | .status() 228 | .expect("create sd card image"); 229 | 230 | if !status.success() { 231 | eprintln!("mkimage failed with status {}", status); 232 | process::exit(status.code().unwrap_or(1)); 233 | } 234 | } 235 | 236 | fn dist_dir(xtask_env: &XtaskEnv) -> PathBuf { 237 | let mut path_buf = project_root().join("target").join(DEFAULT_TARGET); 238 | path_buf = match xtask_env.compile_mode { 239 | CompileMode::Debug => path_buf.join("debug"), 240 | CompileMode::Release => path_buf.join("release"), 241 | }; 242 | path_buf 243 | } 244 | 245 | fn project_root() -> PathBuf { 246 | Path::new(&env!("CARGO_MANIFEST_DIR")) 247 | .ancestors() 248 | .nth(1) 249 | .unwrap() 250 | .to_path_buf() 251 | } 252 | 253 | fn find_mkimage() -> std::io::Result { 254 | let mkimage = Command::new("mkimage").arg("-V").status(); 255 | if mkimage.is_ok() { 256 | return Ok(Command::new("mkimage")); 257 | } 258 | #[cfg(windows)] 259 | { 260 | let wsl_mkimage = Command::new("wsl").arg("mkimage").arg("-V").status(); 261 | if wsl_mkimage.is_ok() { 262 | let mut cmd = Command::new("wsl"); 263 | cmd.arg("mkimage"); 264 | return Ok(cmd); 265 | } 266 | } 267 | return Err(mkimage.unwrap_err()); 268 | } 269 | -------------------------------------------------------------------------------- /rustsbi-hifive-unmatched/src/hart_csr_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::console::println; 2 | use alloc::format; 3 | use alloc::string::String; 4 | use alloc::vec::Vec; 5 | use bit_field::BitField; 6 | use riscv::register::{ 7 | medeleg, mideleg, 8 | misa::{self, MXL}, 9 | }; 10 | 11 | pub fn print_hart0_csrs() { 12 | print_misa(); 13 | // 第0个核没有S态,不能委托中断 14 | print_pmp(); 15 | } 16 | 17 | pub fn print_hartn_csrs() { 18 | print_misa(); 19 | print_mideleg(); 20 | print_medeleg(); 21 | print_pmp(); 22 | } 23 | 24 | #[inline] 25 | fn print_misa() { 26 | let isa = misa::read(); 27 | if let Some(isa) = isa { 28 | let mxl_str = match isa.mxl() { 29 | MXL::XLEN32 => "RV32", 30 | MXL::XLEN64 => "RV64", 31 | MXL::XLEN128 => "RV128", 32 | }; 33 | let mut misa_string = String::new(); 34 | misa_string.push_str(mxl_str); 35 | for ext in 'A'..='Z' { 36 | if isa.has_extension(ext) { 37 | misa_string.push(ext); 38 | } 39 | } 40 | println!("[rustsbi] misa: {}", misa_string); 41 | } 42 | } 43 | 44 | #[inline] 45 | fn print_mideleg() { 46 | let mideleg = mideleg::read(); 47 | let mut delegs = Vec::new(); 48 | if mideleg.usoft() { 49 | delegs.push("usoft") 50 | } 51 | if mideleg.utimer() { 52 | delegs.push("utimer") 53 | } 54 | if mideleg.uext() { 55 | delegs.push("uext") 56 | } 57 | if mideleg.ssoft() { 58 | delegs.push("ssoft") 59 | } 60 | if mideleg.stimer() { 61 | delegs.push("stimer") 62 | } 63 | if mideleg.sext() { 64 | delegs.push("sext") 65 | } 66 | println!( 67 | "[rustsbi] mideleg: {} ({:#x})", 68 | delegs.join(", "), 69 | mideleg.bits() 70 | ); 71 | } 72 | 73 | #[inline] 74 | fn print_medeleg() { 75 | let medeleg = medeleg::read(); 76 | let mut delegs = Vec::new(); 77 | if medeleg.instruction_misaligned() { 78 | delegs.push("ima") 79 | } 80 | if medeleg.instruction_fault() { 81 | delegs.push("ia") // instruction access 82 | } 83 | if medeleg.illegal_instruction() { 84 | delegs.push("illinsn") 85 | } 86 | if medeleg.breakpoint() { 87 | delegs.push("bkpt") 88 | } 89 | if medeleg.load_misaligned() { 90 | delegs.push("lma") 91 | } 92 | if medeleg.load_fault() { 93 | delegs.push("la") // load access 94 | } 95 | if medeleg.store_misaligned() { 96 | delegs.push("sma") 97 | } 98 | if medeleg.store_fault() { 99 | delegs.push("sa") // store access 100 | } 101 | if medeleg.user_env_call() { 102 | delegs.push("uecall") 103 | } 104 | if medeleg.supervisor_env_call() { 105 | delegs.push("secall") 106 | } 107 | if medeleg.machine_env_call() { 108 | delegs.push("mecall") 109 | } 110 | if medeleg.instruction_page_fault() { 111 | delegs.push("ipage") 112 | } 113 | if medeleg.load_page_fault() { 114 | delegs.push("lpage") 115 | } 116 | if medeleg.store_page_fault() { 117 | delegs.push("spage") 118 | } 119 | println!( 120 | "[rustsbi] medeleg: {} ({:#x})", 121 | delegs.join(", "), 122 | medeleg.bits() 123 | ); 124 | } 125 | 126 | #[cfg(target_pointer_width = "64")] 127 | #[inline] 128 | fn print_pmp() { 129 | let pmps = unsafe { pmps::<16>() }; 130 | for (i, (pmpicfg, pmpiaddr)) in pmps.iter().enumerate() { 131 | let pmpicfg = PmpCfg::from(*pmpicfg); 132 | let range = match pmpicfg.a() { 133 | AddressMatching::Off => continue, 134 | AddressMatching::Tor => (0, (1 << (55 + 1)) - 1), // max pmp bits = 55 135 | AddressMatching::Na4 => ((*pmpiaddr as u128) << 2, ((*pmpiaddr as u128) << 2) + 4), 136 | AddressMatching::Napot => napot_pmpaddr_cfg(*pmpiaddr as u128), 137 | }; 138 | let range = format!("{:#x} ..= {:#x}", range.0, range.1); 139 | let privilege = format!( 140 | "{}{}{}", 141 | if pmpicfg.r() { "r" } else { "-" }, 142 | if pmpicfg.w() { "w" } else { "-" }, 143 | if pmpicfg.x() { "x" } else { "-" }, 144 | ); 145 | let l = if pmpicfg.l() { "l, " } else { "" }; 146 | println!("[rustsbi] pmp{}: {} ({}{})", i, range, privilege, l); 147 | } 148 | } 149 | 150 | fn napot_pmpaddr_cfg(input: u128) -> (u128, u128) { 151 | let trailing_ones = input.trailing_ones(); 152 | if trailing_ones == 0 { 153 | return (input, input); 154 | } 155 | let mask = (1 << trailing_ones) - 1; 156 | ((input - mask) << 2, ((input + 1) << 2) - 1) 157 | } 158 | 159 | struct PmpCfg { 160 | bits: u8, 161 | } 162 | 163 | impl From for PmpCfg { 164 | fn from(bits: u8) -> PmpCfg { 165 | PmpCfg { bits } 166 | } 167 | } 168 | 169 | impl PmpCfg { 170 | #[inline] 171 | pub fn r(&self) -> bool { 172 | self.bits.get_bit(0) 173 | } 174 | #[inline] 175 | pub fn w(&self) -> bool { 176 | self.bits.get_bit(1) 177 | } 178 | #[inline] 179 | pub fn x(&self) -> bool { 180 | self.bits.get_bit(2) 181 | } 182 | #[inline] 183 | pub fn a(&self) -> AddressMatching { 184 | match self.bits.get_bits(3..5) { 185 | 0 => AddressMatching::Off, 186 | 1 => AddressMatching::Tor, 187 | 2 => AddressMatching::Na4, 188 | 3 => AddressMatching::Napot, 189 | _ => unreachable!(), 190 | } 191 | } 192 | #[inline] 193 | pub fn l(&self) -> bool { 194 | self.bits.get_bit(7) 195 | } 196 | } 197 | 198 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 199 | enum AddressMatching { 200 | Off, 201 | Tor, 202 | Na4, 203 | Napot, 204 | } 205 | 206 | // 1.12中,L=64;1.11中,L=16。 207 | // 0..16 => pmpcfg[0, 2] 208 | // 0..64 => pmpcfg[0, 2, 4, 6, .., 14] 209 | #[inline] 210 | unsafe fn pmps() -> [(u8, usize); L] { 211 | assert!(L < 64, "in pmpxcfg, x should be in [0, 64)"); 212 | let xlen: usize = core::mem::size_of::() * 8; 213 | let cfgs_in_pmpcfg: usize = xlen / 8; 214 | let pmpcfg_max_id: usize = L / cfgs_in_pmpcfg; 215 | let mut ans = [(0, 0); L]; 216 | for i in (0..pmpcfg_max_id).step_by(xlen / 32) { 217 | let pmpcfgi = pmpcfg_r(i).to_le_bytes(); 218 | for j in 0..cfgs_in_pmpcfg { 219 | let pmpaddr_id = i * 4 + j; 220 | let pmpaddri = pmpaddr_r(pmpaddr_id); 221 | ans[pmpaddr_id] = (pmpcfgi[j], pmpaddri); 222 | } 223 | } 224 | ans 225 | } 226 | 227 | // 1.12版本中,pmpcfg总共有16个,其中64位下只能访问偶数个,32位下可以访问所有寄存器 228 | // 1.11版本中,pmpcfg只有4个。有些模拟器最多只支持4个pmp寄存器,大于4的编号会出错。 229 | #[inline] 230 | unsafe fn pmpcfg_r(pmpcfg_id: usize) -> usize { 231 | assert!(pmpcfg_id <= 15, "pmpcfg id should be in [0, 15]"); 232 | let ans: usize; 233 | core::arch::asm!( 234 | // tmp <- 1的地址;len <- csrr和j指令的长度和 235 | "la {tmp}, 1f 236 | la {len}, 2f 237 | sub {len}, {len}, {tmp}", 238 | // tmp <- tmp + id * len(csrr + j) 239 | "mul {id}, {id}, {len} 240 | add {tmp}, {tmp}, {id} 241 | jr {tmp}", 242 | "1: csrr {ans}, 0x3A0", "j 1f", 243 | "2: csrr {ans}, 0x3A1", "j 1f", 244 | "csrr {ans}, 0x3A2", "j 1f", 245 | "csrr {ans}, 0x3A3", "j 1f", 246 | "csrr {ans}, 0x3A4", "j 1f", 247 | "csrr {ans}, 0x3A5", "j 1f", 248 | "csrr {ans}, 0x3A6", "j 1f", 249 | "csrr {ans}, 0x3A7", "j 1f", 250 | "csrr {ans}, 0x3A8", "j 1f", 251 | "csrr {ans}, 0x3A9", "j 1f", 252 | "csrr {ans}, 0x3AA", "j 1f", 253 | "csrr {ans}, 0x3AB", "j 1f", 254 | "csrr {ans}, 0x3AC", "j 1f", 255 | "csrr {ans}, 0x3AD", "j 1f", 256 | "csrr {ans}, 0x3AE", "j 1f", 257 | "csrr {ans}, 0x3AF", "j 1f", 258 | "1:", 259 | id = in(reg) pmpcfg_id, tmp = out(reg) _, len = out(reg) _, ans = out(reg) ans); 260 | ans 261 | } 262 | 263 | // 1.12中有63个,但1.11中只有15个。个别模拟器需要注意,详见上文 264 | #[inline] 265 | unsafe fn pmpaddr_r(pmpaddr_id: usize) -> usize { 266 | assert!(pmpaddr_id <= 63, "pmpcfg id should be in [0, 63]"); 267 | let ans: usize; 268 | core::arch::asm!( 269 | // tmp <- 1的地址;len <- csrr和j指令的长度和 270 | "la {tmp}, 1f 271 | la {len}, 2f 272 | sub {len}, {len}, {tmp}", 273 | // tmp <- tmp + id * len(csrr + j) 274 | "mul {id}, {id}, {len} 275 | add {tmp}, {tmp}, {id} 276 | jr {tmp}", 277 | "1: csrr {ans}, 0x3B0", "j 1f", 278 | "2: csrr {ans}, 0x3B1", "j 1f", 279 | "csrr {ans}, 0x3B2", "j 1f", "csrr {ans}, 0x3B3", "j 1f", 280 | "csrr {ans}, 0x3B4", "j 1f", "csrr {ans}, 0x3B5", "j 1f", 281 | "csrr {ans}, 0x3B6", "j 1f", "csrr {ans}, 0x3B7", "j 1f", 282 | "csrr {ans}, 0x3B8", "j 1f", "csrr {ans}, 0x3B9", "j 1f", 283 | "csrr {ans}, 0x3BA", "j 1f", "csrr {ans}, 0x3BB", "j 1f", 284 | "csrr {ans}, 0x3BC", "j 1f", "csrr {ans}, 0x3BD", "j 1f", 285 | "csrr {ans}, 0x3BE", "j 1f", "csrr {ans}, 0x3BF", "j 1f", 286 | "csrr {ans}, 0x3C0", "j 1f", "csrr {ans}, 0x3C1", "j 1f", 287 | "csrr {ans}, 0x3C2", "j 1f", "csrr {ans}, 0x3C3", "j 1f", 288 | "csrr {ans}, 0x3C4", "j 1f", "csrr {ans}, 0x3C5", "j 1f", 289 | "csrr {ans}, 0x3C6", "j 1f", "csrr {ans}, 0x3C7", "j 1f", 290 | "csrr {ans}, 0x3C8", "j 1f", "csrr {ans}, 0x3C9", "j 1f", 291 | "csrr {ans}, 0x3CA", "j 1f", "csrr {ans}, 0x3CB", "j 1f", 292 | "csrr {ans}, 0x3CC", "j 1f", "csrr {ans}, 0x3CD", "j 1f", 293 | "csrr {ans}, 0x3CE", "j 1f", "csrr {ans}, 0x3CF", "j 1f", 294 | "csrr {ans}, 0x3D0", "j 1f", "csrr {ans}, 0x3D1", "j 1f", 295 | "csrr {ans}, 0x3D2", "j 1f", "csrr {ans}, 0x3D3", "j 1f", 296 | "csrr {ans}, 0x3D4", "j 1f", "csrr {ans}, 0x3D5", "j 1f", 297 | "csrr {ans}, 0x3D6", "j 1f", "csrr {ans}, 0x3D7", "j 1f", 298 | "csrr {ans}, 0x3D8", "j 1f", "csrr {ans}, 0x3D9", "j 1f", 299 | "csrr {ans}, 0x3DA", "j 1f", "csrr {ans}, 0x3DB", "j 1f", 300 | "csrr {ans}, 0x3DC", "j 1f", "csrr {ans}, 0x3DD", "j 1f", 301 | "csrr {ans}, 0x3DE", "j 1f", "csrr {ans}, 0x3DF", "j 1f", 302 | "csrr {ans}, 0x3E0", "j 1f", "csrr {ans}, 0x3E1", "j 1f", 303 | "csrr {ans}, 0x3E2", "j 1f", "csrr {ans}, 0x3E3", "j 1f", 304 | "csrr {ans}, 0x3E4", "j 1f", "csrr {ans}, 0x3E5", "j 1f", 305 | "csrr {ans}, 0x3E6", "j 1f", "csrr {ans}, 0x3E7", "j 1f", 306 | "csrr {ans}, 0x3E8", "j 1f", "csrr {ans}, 0x3E9", "j 1f", 307 | "csrr {ans}, 0x3EA", "j 1f", "csrr {ans}, 0x3EB", "j 1f", 308 | "csrr {ans}, 0x3EC", "j 1f", "csrr {ans}, 0x3ED", "j 1f", 309 | "csrr {ans}, 0x3EE", "j 1f", "csrr {ans}, 0x3EF", "j 1f", 310 | "1:", 311 | id = in(reg) pmpaddr_id, tmp = out(reg) _, len = out(reg) _, ans = out(reg) ans); 312 | ans 313 | } 314 | -------------------------------------------------------------------------------- /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 = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.11.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 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.0.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 39 | 40 | [[package]] 41 | name = "bare-metal" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 45 | 46 | [[package]] 47 | name = "bit_field" 48 | version = "0.10.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.3.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 57 | 58 | [[package]] 59 | name = "buddy_system_allocator" 60 | version = "0.8.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "55703ac5f02c246ce6158eff6ae2dd9e9069917969682b6831f8a5123abb8a48" 63 | dependencies = [ 64 | "spin", 65 | ] 66 | 67 | [[package]] 68 | name = "cc" 69 | version = "1.0.72" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 72 | 73 | [[package]] 74 | name = "cfg-if" 75 | version = "1.0.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 78 | 79 | [[package]] 80 | name = "clap" 81 | version = "2.33.3" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 84 | dependencies = [ 85 | "ansi_term", 86 | "atty", 87 | "bitflags", 88 | "strsim", 89 | "textwrap", 90 | "unicode-width", 91 | "vec_map", 92 | ] 93 | 94 | [[package]] 95 | name = "ctrlc" 96 | version = "3.2.1" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" 99 | dependencies = [ 100 | "nix", 101 | "winapi", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.6" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "fu740-hal" 116 | version = "0.1.0" 117 | source = "git+https://github.com/riscv-rust/fu740-hal#7a243e2f3e975e8bd936c7ef294113b7acad6d9e" 118 | dependencies = [ 119 | "embedded-hal", 120 | "fu740-pac", 121 | "nb 0.1.3", 122 | "riscv", 123 | ] 124 | 125 | [[package]] 126 | name = "fu740-pac" 127 | version = "0.1.0" 128 | source = "git+https://github.com/riscv-rust/fu740-pac.git?rev=2ea5c43365fef48cc5bcdeaa884ec6b9a5d91b0b#2ea5c43365fef48cc5bcdeaa884ec6b9a5d91b0b" 129 | dependencies = [ 130 | "riscv", 131 | "vcell", 132 | ] 133 | 134 | [[package]] 135 | name = "hermit-abi" 136 | version = "0.1.19" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 139 | dependencies = [ 140 | "libc", 141 | ] 142 | 143 | [[package]] 144 | name = "lazy_static" 145 | version = "1.4.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 148 | 149 | [[package]] 150 | name = "libc" 151 | version = "0.2.108" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" 154 | 155 | [[package]] 156 | name = "memchr" 157 | version = "2.4.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 160 | 161 | [[package]] 162 | name = "memoffset" 163 | version = "0.6.4" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 166 | dependencies = [ 167 | "autocfg", 168 | ] 169 | 170 | [[package]] 171 | name = "nb" 172 | version = "0.1.3" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 175 | dependencies = [ 176 | "nb 1.0.0", 177 | ] 178 | 179 | [[package]] 180 | name = "nb" 181 | version = "1.0.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 184 | 185 | [[package]] 186 | name = "nix" 187 | version = "0.23.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" 190 | dependencies = [ 191 | "bitflags", 192 | "cc", 193 | "cfg-if", 194 | "libc", 195 | "memoffset", 196 | ] 197 | 198 | [[package]] 199 | name = "proc-macro2" 200 | version = "1.0.34" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" 203 | dependencies = [ 204 | "unicode-xid", 205 | ] 206 | 207 | [[package]] 208 | name = "quote" 209 | version = "1.0.10" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 212 | dependencies = [ 213 | "proc-macro2", 214 | ] 215 | 216 | [[package]] 217 | name = "r0" 218 | version = "1.0.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" 221 | 222 | [[package]] 223 | name = "regex" 224 | version = "1.5.4" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 227 | dependencies = [ 228 | "aho-corasick", 229 | "memchr", 230 | "regex-syntax", 231 | ] 232 | 233 | [[package]] 234 | name = "regex-syntax" 235 | version = "0.6.25" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 238 | 239 | [[package]] 240 | name = "riscv" 241 | version = "0.7.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 244 | dependencies = [ 245 | "bare-metal", 246 | "bit_field", 247 | "riscv-target", 248 | ] 249 | 250 | [[package]] 251 | name = "riscv-target" 252 | version = "0.1.2" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 255 | dependencies = [ 256 | "lazy_static", 257 | "regex", 258 | ] 259 | 260 | [[package]] 261 | name = "rustsbi" 262 | version = "0.2.0-alpha.9" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "3f6313c372248339465f956b8b7a0251a0534cb3ea0380e7dbaec77a85457379" 265 | dependencies = [ 266 | "embedded-hal", 267 | "nb 1.0.0", 268 | "riscv", 269 | ] 270 | 271 | [[package]] 272 | name = "rustsbi-hifive-unmatched" 273 | version = "0.1.0" 274 | dependencies = [ 275 | "bit_field", 276 | "buddy_system_allocator", 277 | "embedded-hal", 278 | "fu740-hal", 279 | "nb 1.0.0", 280 | "r0", 281 | "riscv", 282 | "rustsbi", 283 | "serde", 284 | "serde-device-tree", 285 | "serde_derive", 286 | ] 287 | 288 | [[package]] 289 | name = "serde" 290 | version = "1.0.131" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" 293 | 294 | [[package]] 295 | name = "serde-device-tree" 296 | version = "0.0.1" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "a2154721b4ea244ec9f48785f11bf3c9d192a44c06f02da2fe522ad25a8f6995" 299 | dependencies = [ 300 | "serde", 301 | ] 302 | 303 | [[package]] 304 | name = "serde_derive" 305 | version = "1.0.131" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" 308 | dependencies = [ 309 | "proc-macro2", 310 | "quote", 311 | "syn", 312 | ] 313 | 314 | [[package]] 315 | name = "spin" 316 | version = "0.7.1" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 319 | 320 | [[package]] 321 | name = "strsim" 322 | version = "0.8.0" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 325 | 326 | [[package]] 327 | name = "syn" 328 | version = "1.0.82" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" 331 | dependencies = [ 332 | "proc-macro2", 333 | "quote", 334 | "unicode-xid", 335 | ] 336 | 337 | [[package]] 338 | name = "test-kernel" 339 | version = "0.1.0" 340 | dependencies = [ 341 | "buddy_system_allocator", 342 | "riscv", 343 | ] 344 | 345 | [[package]] 346 | name = "textwrap" 347 | version = "0.11.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 350 | dependencies = [ 351 | "unicode-width", 352 | ] 353 | 354 | [[package]] 355 | name = "unicode-width" 356 | version = "0.1.9" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 359 | 360 | [[package]] 361 | name = "unicode-xid" 362 | version = "0.2.2" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 365 | 366 | [[package]] 367 | name = "vcell" 368 | version = "0.1.3" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 371 | 372 | [[package]] 373 | name = "vec_map" 374 | version = "0.8.2" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 377 | 378 | [[package]] 379 | name = "void" 380 | version = "1.0.2" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 383 | 384 | [[package]] 385 | name = "winapi" 386 | version = "0.3.9" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 389 | dependencies = [ 390 | "winapi-i686-pc-windows-gnu", 391 | "winapi-x86_64-pc-windows-gnu", 392 | ] 393 | 394 | [[package]] 395 | name = "winapi-i686-pc-windows-gnu" 396 | version = "0.4.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 399 | 400 | [[package]] 401 | name = "winapi-x86_64-pc-windows-gnu" 402 | version = "0.4.0" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 405 | 406 | [[package]] 407 | name = "xtask" 408 | version = "0.1.0" 409 | dependencies = [ 410 | "clap", 411 | "ctrlc", 412 | ] 413 | --------------------------------------------------------------------------------