├── .cargo └── config.toml ├── .gitignore ├── Cargo.toml ├── README.md ├── apps └── hello-world │ ├── .cargo │ └── config.toml │ ├── Cargo.toml │ └── src │ └── main.rs ├── bootloader ├── rustsbi-qemu └── rustsbi-qemu.bin ├── kernels └── 01-virt-addr-kernel │ ├── .cargo │ └── config.toml │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── console.rs │ ├── executor.rs │ ├── linker64.ld │ ├── main.rs │ ├── mm.rs │ ├── sbi.rs │ └── syscall.rs ├── library └── tornado-std │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── console.rs │ ├── lib.rs │ ├── linker64.ld │ └── syscall.rs └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | make = "xtask make" 4 | qemu = "xtask qemu" 5 | asm = "xtask asm" 6 | size = "xtask size" 7 | debug = "xtask debug" 8 | gdb = "xtask gdb" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "apps/*", 4 | "library/*", 5 | "kernels/*", 6 | "xtask", 7 | ] 8 | default-members = ["xtask"] 9 | 10 | [workspace.metadata.xtask] 11 | default-kernel-path = "01-virt-addr-kernel" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 洛佳的异步内核实验室 2 | 3 | ## 运行内核 4 | 5 | 使用以下指令来运行内核: 6 | 7 | ```bash 8 | cargo qemu 9 | ``` 10 | 11 | 编译并打包多个程序,再运行内核。以hello-world为例: 12 | 13 | ```bash 14 | cargo qemu hello-world 15 | ``` 16 | 17 | 指令`cargo qemu`可以添加`--release`参数。 18 | 19 | ## 内核程序联合调试 20 | 21 | 使用以下指令: 22 | 23 | ```bash 24 | # 打开一个窗口 25 | cargo debug hello-world 26 | # 打开另一个窗口 27 | cargo gdb 28 | ``` 29 | 30 | 调试指令不能添加`--release`参数。 31 | -------------------------------------------------------------------------------- /apps/hello-world/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64imac-unknown-none-elf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tlinker64.ld", 4 | ] 5 | 6 | [target.riscv32imac-unknown-none-elf] 7 | rustflags = [ 8 | "-C", "link-arg=-Tlinker32.ld", 9 | ] 10 | -------------------------------------------------------------------------------- /apps/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tornado-std = { path = "../../library/tornado-std" } 10 | -------------------------------------------------------------------------------- /apps/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #[macro_use] 4 | extern crate tornado_std; 5 | 6 | #[no_mangle] 7 | fn main() { 8 | println!("你们可能不知道只用20万赢到578万是什么概念,我们一般只会用两个字来形容这种人:赌怪"); 9 | println!("Hello, World!"); 10 | } 11 | -------------------------------------------------------------------------------- /bootloader/rustsbi-qemu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HUST-OS/luojia-os-labs-v2/bc5276cba63df2e3bd3bd0f2b65d128e4e7595c2/bootloader/rustsbi-qemu -------------------------------------------------------------------------------- /bootloader/rustsbi-qemu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HUST-OS/luojia-os-labs-v2/bc5276cba63df2e3bd3bd0f2b65d128e4e7595c2/bootloader/rustsbi-qemu.bin -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64imac-unknown-none-elf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tlinker64.ld", 4 | ] 5 | 6 | [target.riscv32imac-unknown-none-elf] 7 | rustflags = [ 8 | "-C", "link-arg=-Tlinker32.ld", 9 | ] 10 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virt-addr-kernel" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | r0 = "1" 10 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 11 | riscv = { git = "https://github.com/rust-embedded/riscv", rev = "16e4870f", features = ["inline-asm"] } 12 | buddy_system_allocator = "0.8" 13 | spin = "0.9" 14 | bitflags = "1.2" 15 | bit_field = "0.10" 16 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::io::Write; 3 | use std::fs; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-changed=src/linker64.ld"); 9 | 10 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 11 | 12 | fs::File::create(out_dir.join("linker64.ld")) 13 | .unwrap() 14 | .write_all(include_bytes!("src/linker64.ld")) 15 | .unwrap(); 16 | println!("cargo:rustc-link-search={}", out_dir.display()); 17 | } 18 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::sbi::console_putchar; 2 | use core::fmt::{self, Write}; 3 | 4 | struct Stdout; 5 | 6 | impl Write for Stdout { 7 | fn write_str(&mut self, s: &str) -> fmt::Result { 8 | for c in s.chars() { 9 | console_putchar(c as usize); 10 | } 11 | Ok(()) 12 | } 13 | } 14 | 15 | pub fn print(args: fmt::Arguments) { 16 | Stdout.write_fmt(args).unwrap(); 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! print { 21 | ($fmt: literal $(, $($arg: tt)+)?) => { 22 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 23 | } 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! println { 28 | ($fmt: literal $(, $($arg: tt)+)?) => { 29 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/executor.rs: -------------------------------------------------------------------------------- 1 | use riscv::register::{ 2 | sstatus::{self, Sstatus, SPP}, 3 | scause::{self, Trap, Exception}, 4 | stvec::{self, TrapMode}, stval, 5 | satp::Satp, 6 | }; 7 | use core::{ 8 | pin::Pin, 9 | ops::{Generator, GeneratorState}, 10 | }; 11 | use crate::mm; 12 | 13 | pub fn init(trampoline_va_start: mm::VirtAddr) { 14 | extern "C" { fn strampoline(); } 15 | let trampoline_pa_start = strampoline as usize; 16 | let trap_entry_fn_pa = trampoline_trap_entry as usize; 17 | let trap_entry_fn_va = trap_entry_fn_pa - trampoline_pa_start + trampoline_va_start.0; 18 | let mut addr = trap_entry_fn_va; 19 | if addr & 0x2 != 0 { 20 | addr += 0x2; // 必须对齐到4个字节 21 | } 22 | unsafe { stvec::write(addr, TrapMode::Direct) }; 23 | } 24 | 25 | #[repr(C)] 26 | pub struct Runtime { 27 | user_satp: Satp, 28 | trampoline_resume: fn(*mut ResumeContext, Satp), 29 | current_user_stack: mm::VirtAddr, 30 | context_addr: mm::VirtAddr, 31 | } 32 | 33 | impl Runtime { 34 | pub fn new_user(new_sepc: usize, user_stack_addr: mm::VirtAddr, new_satp: Satp, trampoline_va_start: mm::VirtAddr, context_addr: mm::VirtAddr) -> Self { 35 | let mut ans: Runtime = Runtime { 36 | user_satp: unsafe { core::mem::MaybeUninit::zeroed().assume_init() }, 37 | current_user_stack: user_stack_addr, 38 | trampoline_resume: { 39 | extern "C" { fn strampoline(); } 40 | let trampoline_pa_start = strampoline as usize; 41 | let resume_fn_pa = trampoline_resume as usize; 42 | let resume_fn_va = resume_fn_pa - trampoline_pa_start + trampoline_va_start.0; 43 | // println!("pa start = {:x?}, pa = {:x?}, va = {:x?}",trampoline_pa_start, resume_fn_pa, resume_fn_va); 44 | unsafe { core::mem::transmute(resume_fn_va) } 45 | }, 46 | context_addr, 47 | }; 48 | unsafe { ans.prepare_next_app(new_sepc, new_satp) }; 49 | ans 50 | } 51 | 52 | unsafe fn reset(&mut self) { 53 | self.context_mut().sp = self.current_user_stack.0; 54 | sstatus::set_spp(SPP::User); 55 | self.context_mut().sstatus = sstatus::read(); 56 | self.context_mut().kernel_stack = 0x233333666666; // 将会被resume函数覆盖 57 | } 58 | 59 | // 在处理异常的时候,使用context_mut得到运行时当前用户的上下文,可以改变上下文的内容 60 | pub unsafe fn context_mut(&mut self) -> &mut ResumeContext { 61 | &mut *(self.context_addr.0 as *mut ResumeContext) 62 | } 63 | 64 | pub unsafe fn prepare_next_app(&mut self, new_sepc: usize, new_satp: Satp) { 65 | self.reset(); 66 | self.context_mut().sepc = new_sepc; 67 | self.user_satp = new_satp; 68 | } 69 | } 70 | 71 | impl Generator for Runtime { 72 | type Yield = KernelTrap; 73 | type Return = (); 74 | fn resume(mut self: Pin<&mut Self>, _arg: ()) -> GeneratorState { 75 | (self.trampoline_resume)( 76 | unsafe { self.context_mut() } as *mut _, 77 | self.user_satp 78 | ); 79 | let stval = stval::read(); 80 | let trap = match scause::read().cause() { 81 | Trap::Exception(Exception::UserEnvCall) => KernelTrap::Syscall(), 82 | Trap::Exception(Exception::LoadFault) => KernelTrap::LoadAccessFault(stval), 83 | Trap::Exception(Exception::StoreFault) => KernelTrap::StoreAccessFault(stval), 84 | Trap::Exception(Exception::IllegalInstruction) => KernelTrap::IllegalInstruction(stval), 85 | e => panic!("unhandled exception: {:?}! stval: {:#x?}, ctx: {:#x?}", e, stval, unsafe { self.context_mut() }) 86 | }; 87 | GeneratorState::Yielded(trap) 88 | } 89 | } 90 | 91 | #[derive(Debug)] 92 | #[repr(C)] 93 | pub enum KernelTrap { 94 | Syscall(), 95 | LoadAccessFault(usize), 96 | StoreAccessFault(usize), 97 | IllegalInstruction(usize), 98 | } 99 | 100 | // 应当放到跳板数据页上,用户和内核 101 | #[derive(Debug)] 102 | #[repr(C)] 103 | pub struct ResumeContext { 104 | pub ra: usize, // 0 105 | pub sp: usize, 106 | pub gp: usize, 107 | pub tp: usize, 108 | pub t0: usize, 109 | pub t1: usize, 110 | pub t2: usize, 111 | pub s0: usize, 112 | pub s1: usize, 113 | pub a0: usize, 114 | pub a1: usize, 115 | pub a2: usize, 116 | pub a3: usize, 117 | pub a4: usize, 118 | pub a5: usize, 119 | pub a6: usize, 120 | pub a7: usize, 121 | pub s2: usize, 122 | pub s3: usize, 123 | pub s4: usize, 124 | pub s5: usize, 125 | pub s6: usize, 126 | pub s7: usize, 127 | pub s8: usize, 128 | pub s9: usize, 129 | pub s10: usize, 130 | pub s11: usize, 131 | pub t3: usize, 132 | pub t4: usize, 133 | pub t5: usize, 134 | pub t6: usize, // 30 135 | pub sstatus: Sstatus, // 31 136 | pub sepc: usize, // 32 137 | pub kernel_stack: usize, // 33 138 | pub kernel_satp: Satp, // 34 139 | } 140 | 141 | /* 142 | 跳板页设计: 143 | 1. 共享的代码跳板页 - 整个系统里有一个 144 | 2. 数据跳板页 - 每个处理核一个,暂时中转目前内核和用户的上下文 145 | trampoline段:保存共享的代码 146 | 分配的其它页,每个核一个:保存数据跳板页 147 | */ 148 | 149 | // 函数作用: 150 | // 1. 先保存寄存器 151 | // 2. 再切换地址空间 152 | // a0 = 生成器上下文 153 | // sp = 内核栈 154 | // sscratch = 用户的a0值 155 | // 注意:_ctx必须也映射到跳板页里面 156 | #[naked] 157 | #[link_section = ".trampoline"] 158 | unsafe extern "C" fn trampoline_resume(_ctx: *mut ResumeContext, _user_satp: usize) { 159 | asm!( 160 | // a0 = 生成器上下文, a1 = 用户的地址空间配置, sp = 内核栈 161 | "addi sp, sp, -15*8", 162 | "sd ra, 0*8(sp) 163 | sd gp, 1*8(sp) 164 | sd tp, 2*8(sp) 165 | sd s0, 3*8(sp) 166 | sd s1, 4*8(sp) 167 | sd s2, 5*8(sp) 168 | sd s3, 6*8(sp) 169 | sd s4, 7*8(sp) 170 | sd s5, 8*8(sp) 171 | sd s6, 9*8(sp) 172 | sd s7, 10*8(sp) 173 | sd s8, 11*8(sp) 174 | sd s9, 12*8(sp) 175 | sd s10, 13*8(sp) 176 | sd s11, 14*8(sp)", // 保存子函数寄存器,到内核栈 177 | "csrrw a1, satp, a1", // 写用户的地址空间配置到satp,读内核的satp到a1 178 | "sfence.vma", // 立即切换地址空间 179 | // a0 = 生成器上下文, a1 = 内核的地址空间配置, sp = 内核栈 180 | "sd sp, 33*8(a0)", // 保存内核栈位置 181 | "mv sp, a0", 182 | // a1 = 内核的地址空间配置, sp = 生成器上下文 183 | "sd a1, 34*8(sp)", // 保存内核的地址空间配置 184 | "ld t0, 31*8(sp) 185 | ld t1, 32*8(sp) 186 | csrw sstatus, t0 187 | csrw sepc, t1 188 | ld ra, 0*8(sp) 189 | ld gp, 2*8(sp) 190 | ld tp, 3*8(sp) 191 | ld t0, 4*8(sp) 192 | ld t1, 5*8(sp) 193 | ld t2, 6*8(sp) 194 | ld s0, 7*8(sp) 195 | ld s1, 8*8(sp) 196 | ld a0, 9*8(sp) 197 | ld a1, 10*8(sp) 198 | ld a2, 11*8(sp) 199 | ld a3, 12*8(sp) 200 | ld a4, 13*8(sp) 201 | ld a5, 14*8(sp) 202 | ld a6, 15*8(sp) 203 | ld a7, 16*8(sp) 204 | ld s2, 17*8(sp) 205 | ld s3, 18*8(sp) 206 | ld s4, 19*8(sp) 207 | ld s5, 20*8(sp) 208 | ld s6, 21*8(sp) 209 | ld s7, 22*8(sp) 210 | ld s8, 23*8(sp) 211 | ld s9, 24*8(sp) 212 | ld s10, 25*8(sp) 213 | ld s11, 26*8(sp) 214 | ld t3, 27*8(sp) 215 | ld t4, 28*8(sp) 216 | ld t5, 29*8(sp) 217 | ld t6, 30*8(sp)", // 加载生成器上下文寄存器,除了a0 218 | // sp = 生成器上下文 219 | "csrw sscratch, sp", 220 | "ld sp, 1*8(sp)", // 加载用户栈 221 | // sp = 用户栈, sscratch = 生成器上下文 222 | "sret", // set priv, j sepc 223 | options(noreturn) 224 | ) 225 | } 226 | 227 | #[naked] 228 | #[link_section = ".trampoline"] 229 | unsafe extern "C" fn trampoline_trap_entry() { 230 | asm!( 231 | ".p2align 2", // 对齐到4字节 232 | // sp = 用户栈, sscratch = 生成器上下文 233 | "csrrw sp, sscratch, sp", 234 | // sp = 生成器上下文, sscratch = 用户栈 235 | "sd ra, 0*8(sp) 236 | sd gp, 2*8(sp) 237 | sd tp, 3*8(sp) 238 | sd t0, 4*8(sp) 239 | sd t1, 5*8(sp) 240 | sd t2, 6*8(sp) 241 | sd s0, 7*8(sp) 242 | sd s1, 8*8(sp) 243 | sd a0, 9*8(sp) 244 | sd a1, 10*8(sp) 245 | sd a2, 11*8(sp) 246 | sd a3, 12*8(sp) 247 | sd a4, 13*8(sp) 248 | sd a5, 14*8(sp) 249 | sd a6, 15*8(sp) 250 | sd a7, 16*8(sp) 251 | sd s2, 17*8(sp) 252 | sd s3, 18*8(sp) 253 | sd s4, 19*8(sp) 254 | sd s5, 20*8(sp) 255 | sd s6, 21*8(sp) 256 | sd s7, 22*8(sp) 257 | sd s8, 23*8(sp) 258 | sd s9, 24*8(sp) 259 | sd s10, 25*8(sp) 260 | sd s11, 26*8(sp) 261 | sd t3, 27*8(sp) 262 | sd t4, 28*8(sp) 263 | sd t5, 29*8(sp) 264 | sd t6, 30*8(sp)", 265 | "csrr t0, sstatus 266 | sd t0, 31*8(sp)", 267 | "csrr t1, sepc 268 | sd t1, 32*8(sp)", 269 | // sp = 生成器上下文, sscratch = 用户栈 270 | "csrrw t2, sscratch, sp", 271 | // sp = 生成器上下文, sscratch = 生成器上下文, t2 = 用户栈 272 | "sd t2, 1*8(sp)", // 保存用户栈 273 | "ld t3, 34*8(sp)", // t3 = 内核的地址空间配置 274 | "csrw satp, t3", // 写内核的地址空间配置;用户的地址空间配置将丢弃 275 | "sfence.vma", // 立即切换地址空间 276 | "ld sp, 33*8(sp)", 277 | // sp = 内核栈 278 | "ld ra, 0*8(sp) 279 | ld gp, 1*8(sp) 280 | ld tp, 2*8(sp) 281 | ld s0, 3*8(sp) 282 | ld s1, 4*8(sp) 283 | ld s2, 5*8(sp) 284 | ld s3, 6*8(sp) 285 | ld s4, 7*8(sp) 286 | ld s5, 8*8(sp) 287 | ld s6, 9*8(sp) 288 | ld s7, 10*8(sp) 289 | ld s8, 11*8(sp) 290 | ld s9, 12*8(sp) 291 | ld s10, 13*8(sp) 292 | ld s11, 14*8(sp) 293 | addi sp, sp, 15*8", // sp = 内核栈 294 | "jr ra", // ret指令 295 | options(noreturn) 296 | ) 297 | } 298 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/linker64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x80200000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | skernel = .; 9 | 10 | stext = .; 11 | .text : { 12 | *(.text.entry) 13 | *(.text .text.*) 14 | } 15 | 16 | . = ALIGN(4K); 17 | etext = .; 18 | strampoline = .; 19 | .trampoline : { 20 | *(.trampoline) 21 | } 22 | 23 | . = ALIGN(4K); 24 | etrampoline = .; 25 | srodata = .; 26 | .rodata : { 27 | *(.rodata .rodata.*) 28 | *(.srodata .srodata.*) 29 | } 30 | 31 | . = ALIGN(4K); 32 | erodata = .; 33 | sdata = .; 34 | .data : { 35 | *(.data .data.*) 36 | *(.sdata .sdata.*) 37 | } 38 | 39 | . = ALIGN(4K); 40 | edata = .; 41 | .bss : { 42 | *(.bss.stack) 43 | sbss = .; 44 | *(.bss .bss.*) 45 | *(.sbss .sbss.*) 46 | } 47 | 48 | . = ALIGN(4K); 49 | ebss = .; 50 | ekernel = .; 51 | } 52 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(naked_functions, asm, global_asm)] 2 | #![feature(alloc_error_handler)] 3 | #![feature(panic_info_message)] 4 | #![feature(generator_trait)] 5 | #![feature(destructuring_assignment)] 6 | #![no_std] 7 | #![no_main] 8 | 9 | extern crate alloc; 10 | 11 | #[macro_use] 12 | mod console; 13 | mod sbi; 14 | mod executor; 15 | mod mm; 16 | mod syscall; 17 | 18 | use core::panic::PanicInfo; 19 | use alloc::vec::Vec; 20 | use syscall::{syscall, SyscallOperation}; 21 | 22 | pub extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! { 23 | println!("[kernel] Hart id = {}, DTB physical address = {:#x}", hartid, dtb_pa); 24 | mm::heap_init(); 25 | mm::test_frame_alloc(); 26 | // 页帧分配器。对整个物理的地址空间来说,无论有多少个核,页帧分配器只有一个。 27 | let from = mm::PhysAddr(0x80420000).page_number::(); 28 | let to = mm::PhysAddr(0x80800000).page_number::(); // 暂时对qemu写死 29 | let frame_alloc = spin::Mutex::new(mm::StackFrameAllocator::new(from, to)); 30 | let mut kernel_addr_space = mm::PagedAddrSpace::try_new_in(mm::Sv39, &frame_alloc) 31 | .expect("allocate page to create kernel paged address space"); 32 | mm::test_map_solve(); 33 | kernel_addr_space.allocate_map( 34 | mm::VirtAddr(0x80000000).page_number::(), 35 | mm::PhysAddr(0x80000000).page_number::(), 36 | 1024, 37 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X 38 | ).expect("allocate one mapped space"); 39 | kernel_addr_space.allocate_map( 40 | mm::VirtAddr(0x80400000).page_number::(), 41 | mm::PhysAddr(0x80400000).page_number::(), 42 | 32, 43 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X 44 | ).expect("allocate user program mapped space"); 45 | kernel_addr_space.allocate_map( 46 | mm::VirtAddr(0x80420000).page_number::(), 47 | mm::PhysAddr(0x80420000).page_number::(), 48 | 1024 - 32, 49 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X 50 | ).expect("allocate remaining space"); 51 | let (vpn, ppn, n) = get_trampoline_text_paging_config::(); 52 | let trampoline_va_start = vpn.addr_begin::(); 53 | kernel_addr_space.allocate_map( 54 | vpn, ppn, n, 55 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X 56 | ).expect("allocate trampoline code mapped space"); 57 | // 跳板数据页 58 | let data_len = core::mem::size_of::(); 59 | let frame_size = 1_usize << ::FRAME_SIZE_BITS; 60 | assert!(data_len > 0, "resume context should take place in memory"); 61 | let data_frame_count = (data_len - 1) / frame_size + 1; // roundup(data_len / frame_size) 62 | let mut frames = Vec::new(); 63 | for i in 0..data_frame_count { 64 | let frame_box = mm::FrameBox::try_new_in(&frame_alloc).expect("allocate user stack frame"); 65 | kernel_addr_space.allocate_map( 66 | // 去掉代码页的数量n 67 | mm::VirtAddr(usize::MAX - n * 0x1000 - data_frame_count * 0x1000 + i * 0x1000 + 1).page_number::(), 68 | frame_box.phys_page_num(), 69 | 1, 70 | mm::Sv39Flags::R | mm::Sv39Flags::W 71 | ).expect("allocate trampoline data mapped space"); 72 | frames.push((i, frame_box)) 73 | } 74 | let trampoline_data_addr = mm::VirtAddr(usize::MAX - n * 0x1000 - data_frame_count * 0x1000 + 1); 75 | mm::test_asid_alloc(); 76 | let max_asid = mm::max_asid(); 77 | let mut asid_alloc = mm::StackAsidAllocator::new(max_asid); 78 | let kernel_asid = asid_alloc.allocate_asid().expect("alloc kernel asid"); 79 | let _kernel_satp = unsafe { 80 | mm::activate_paged_riscv_sv39(kernel_addr_space.root_page_number(), kernel_asid) 81 | }; 82 | // println!("kernel satp = {:x?}", kernel_satp); 83 | executor::init(trampoline_va_start); 84 | let (mut user_space, _user_stack, user_stack_addr) = 85 | create_sv39_app_address_space(&frame_alloc); 86 | for (idx, frame_box) in frames.iter() { 87 | user_space.allocate_map( 88 | mm::VirtAddr(usize::MAX - n * 0x1000 - data_frame_count * 0x1000 + idx * 0x1000 + 1).page_number::(), 89 | frame_box.phys_page_num(), 90 | 1, 91 | mm::Sv39Flags::R | mm::Sv39Flags::W 92 | ).expect("allocate trampoline data mapped space"); 93 | } 94 | let user_asid = asid_alloc.allocate_asid().expect("alloc user asid"); 95 | // println!("User space = {:x?}", user_space); 96 | // println!("Ppn = {:x?}", user_space.root_page_number()); 97 | let mut rt = executor::Runtime::new_user( 98 | 0x1000, 99 | user_stack_addr, 100 | mm::get_satp_sv39(user_asid, user_space.root_page_number()), 101 | trampoline_va_start, 102 | trampoline_data_addr, 103 | ); 104 | use core::pin::Pin; 105 | use core::ops::{Generator, GeneratorState}; 106 | loop { 107 | match Pin::new(&mut rt).resume(()) { 108 | GeneratorState::Yielded(executor::KernelTrap::Syscall()) => { 109 | // println!("Kernel trap syscall!"); 110 | let ctx = unsafe { rt.context_mut() }; 111 | match syscall(ctx.a7, ctx.a6, [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5], &user_space) { 112 | SyscallOperation::Return(ans) => { 113 | ctx.a0 = ans.code; 114 | ctx.a1 = ans.extra; 115 | ctx.sepc = ctx.sepc.wrapping_add(4); 116 | } 117 | SyscallOperation::Terminate(code) => { 118 | println!("[Kernel] Process returned with code {}", code); 119 | sbi::shutdown() 120 | } 121 | SyscallOperation::UserPanic(file, line, col, msg) => { 122 | let file = file.unwrap_or(""); 123 | let msg = msg.unwrap_or(""); 124 | println!("[Kernel] User process panicked at '{}', {}:{}:{}", msg, file, line, col); 125 | sbi::shutdown() 126 | } 127 | } 128 | }, 129 | GeneratorState::Yielded(executor::KernelTrap::IllegalInstruction(val)) => { 130 | println!("[Kernel] Illegal instruction {:016x}, kernel dumpped.", val); 131 | sbi::shutdown() 132 | }, 133 | GeneratorState::Yielded(trap) => { 134 | println!("[Kernel] Trap {:?}, kernel dumpped.", trap); 135 | sbi::shutdown() 136 | } 137 | GeneratorState::Complete(()) => sbi::shutdown() 138 | } 139 | } 140 | } 141 | 142 | fn get_trampoline_text_paging_config() -> (mm::VirtPageNum, mm::PhysPageNum, usize) { 143 | let (trampoline_pa_start, trampoline_pa_end) = { 144 | extern "C" { fn strampoline(); fn etrampoline(); } 145 | (strampoline as usize, etrampoline as usize) 146 | }; 147 | assert_ne!(trampoline_pa_start, trampoline_pa_end, "trampoline code not declared"); 148 | let trampoline_len = trampoline_pa_end - trampoline_pa_start; 149 | let trampoline_va_start = usize::MAX - trampoline_len + 1; 150 | let vpn = mm::VirtAddr(trampoline_va_start).page_number::(); 151 | let ppn = mm::PhysAddr(trampoline_pa_start).page_number::(); 152 | let n = trampoline_len >> M::FRAME_SIZE_BITS; 153 | // println!("va = {:x?}, pa = {:x?} {:x?}", trampoline_va_start, trampoline_pa_start, trampoline_pa_end); 154 | // println!("l = {:x?}", trampoline_len); 155 | // println!("vpn = {:x?}, ppn = {:x?}, n = {}", vpn, ppn, n); 156 | (vpn, ppn, n) 157 | } 158 | 159 | fn create_sv39_app_address_space(frame_alloc: A) -> (mm::PagedAddrSpace, Vec>, mm::VirtAddr) { 160 | let mut addr_space = mm::PagedAddrSpace::try_new_in(mm::Sv39, frame_alloc.clone()) 161 | .expect("allocate page to create kernel paged address space"); 162 | let (vpn, ppn, n) = get_trampoline_text_paging_config::(); 163 | // 跳板代码页 164 | addr_space.allocate_map( 165 | vpn, ppn, n, 166 | mm::Sv39Flags::R | mm::Sv39Flags::X // 不开U特权,因为这里从sret弹出后,才真正到用户层 167 | ).expect("allocate trampoline code mapped space"); 168 | // 用户程序空间 169 | addr_space.allocate_map( 170 | mm::VirtAddr(0x1000).page_number::(), 171 | mm::PhysAddr(0x80400000).page_number::(), 172 | 32, 173 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X | mm::Sv39Flags::U 174 | ).expect("allocate user program mapped space"); 175 | // 用户栈 176 | let mut frames = Vec::new(); 177 | let stack_frame_n = 5; 178 | for i in 0..stack_frame_n { 179 | let frame_box = mm::FrameBox::try_new_in(frame_alloc.clone()).expect("allocate user stack frame"); 180 | addr_space.allocate_map( 181 | mm::VirtAddr(0x60000000 + i * 0x1000).page_number::(), 182 | frame_box.phys_page_num(), 183 | 1, 184 | mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::U 185 | ).expect("allocate user stack mapped space"); 186 | frames.push(frame_box) 187 | } 188 | // 跳板数据页在外面处理,这里不处理 189 | /* 页表信息,调试用 */ 190 | // addr_space.allocate_map( 191 | // mm::VirtAddr(0x80420000).page_number::(), 192 | // mm::PhysAddr(0x80420000).page_number::(), 193 | // 1024 - 32, 194 | // mm::Sv39Flags::R | mm::Sv39Flags::W | mm::Sv39Flags::X | mm::Sv39Flags::U 195 | // ).expect("allocate remaining space"); 196 | let stack_addr = mm::VirtAddr(0x60000000 + stack_frame_n * 0x1000); // 栈底是高地址 197 | (addr_space, frames, stack_addr) 198 | } 199 | 200 | #[cfg_attr(not(test), panic_handler)] 201 | #[allow(unused)] 202 | fn panic(info: &PanicInfo) -> ! { 203 | if let Some(location) = info.location() { 204 | println!( 205 | "Panicked at {}:{} {}", 206 | location.file(), 207 | location.line(), 208 | info.message().unwrap() 209 | ); 210 | } else { 211 | println!("Panicked: {}", info.message().unwrap()); 212 | } 213 | sbi::shutdown() 214 | } 215 | 216 | const BOOT_STACK_SIZE: usize = 4096 * 4 * 8; 217 | 218 | #[link_section = ".bss.stack"] 219 | static mut BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0; BOOT_STACK_SIZE]; 220 | 221 | #[naked] 222 | #[link_section = ".text.entry"] 223 | #[export_name = "_start"] 224 | unsafe extern "C" fn entry() -> ! { 225 | asm!(" 226 | # 1. set sp 227 | # sp = bootstack + (hartid + 1) * 0x10000 228 | add t0, a0, 1 229 | slli t0, t0, 14 230 | 1: auipc sp, %pcrel_hi({boot_stack}) 231 | addi sp, sp, %pcrel_lo(1b) 232 | add sp, sp, t0 233 | 234 | # 2. jump to rust_main (absolute address) 235 | 1: auipc t0, %pcrel_hi({rust_main}) 236 | addi t0, t0, %pcrel_lo(1b) 237 | jr t0 238 | ", 239 | boot_stack = sym BOOT_STACK, 240 | rust_main = sym rust_main, 241 | options(noreturn)) 242 | } 243 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/mm.rs: -------------------------------------------------------------------------------- 1 | //! 虚拟内存模块 2 | 3 | use alloc::alloc::Layout; 4 | use buddy_system_allocator::LockedHeap; 5 | use core::ops::Range; 6 | 7 | const KERNEL_HEAP_SIZE: usize = 64 * 1024; 8 | 9 | static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 10 | 11 | // 全局的堆分配器 12 | #[global_allocator] 13 | static HEAP: LockedHeap<32> = LockedHeap::empty(); 14 | 15 | #[cfg_attr(not(test), alloc_error_handler)] 16 | #[allow(unused)] 17 | fn alloc_error_handler(layout: Layout) -> ! { 18 | panic!("alloc error for layout {:?}", layout) 19 | } 20 | 21 | pub(crate) fn heap_init() { 22 | unsafe { 23 | HEAP.lock().init( 24 | HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE 25 | ) 26 | } 27 | let mut vec = Vec::new(); 28 | for i in 0..5 { 29 | vec.push(i); 30 | } 31 | println!("[kernel] Alloc test: {:?}", vec); 32 | } 33 | 34 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 35 | pub struct PhysAddr(pub usize); 36 | 37 | impl PhysAddr { 38 | pub fn page_number(&self) -> PhysPageNum { 39 | PhysPageNum(self.0 >> M::FRAME_SIZE_BITS) 40 | } 41 | // pub fn page_offset(&self) -> usize { 42 | // self.0 & (PAGE_SIZE - 1) 43 | // } 44 | } 45 | 46 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 47 | pub struct VirtAddr(pub usize); 48 | 49 | impl VirtAddr { 50 | pub fn page_number(&self) -> VirtPageNum { 51 | VirtPageNum(self.0 >> M::FRAME_SIZE_BITS) 52 | } 53 | pub fn page_offset(&self, lvl: PageLevel) -> usize { 54 | // println!("{:?}, {:?}, {}", lvl, M::get_layout_for_level(lvl), M::get_layout_for_level(lvl).page_size::()); 55 | self.0 & (M::get_layout_for_level(lvl).page_size::() - 1) 56 | } 57 | } 58 | 59 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 60 | pub struct PhysPageNum(usize); 61 | 62 | impl PhysPageNum { 63 | pub fn addr_begin(&self) -> PhysAddr { 64 | PhysAddr(self.0 << M::FRAME_SIZE_BITS) 65 | } 66 | pub fn next_page(&self) -> PhysPageNum { 67 | // PhysPageNum不处理具体架构的PPN_BITS,它的合法性由具体架构保证 68 | PhysPageNum(self.0.wrapping_add(1)) 69 | } 70 | pub fn is_within_range(&self, begin: PhysPageNum, end: PhysPageNum) -> bool { 71 | if begin.0 <= end.0 { 72 | begin.0 <= self.0 && self.0 < end.0 73 | } else { 74 | begin.0 <= self.0 || self.0 < end.0 75 | } 76 | } 77 | } 78 | 79 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 80 | pub struct VirtPageNum(usize); 81 | 82 | impl VirtPageNum { 83 | pub fn addr_begin(&self) -> VirtAddr { 84 | VirtAddr(self.0 << M::FRAME_SIZE_BITS) 85 | } 86 | pub fn next_page_by_level(&self, lvl: PageLevel) -> VirtPageNum { 87 | let step = M::get_layout_for_level(lvl).frame_align(); 88 | VirtPageNum(self.0.wrapping_add(step)) 89 | } 90 | } 91 | 92 | use alloc::vec::Vec; 93 | 94 | // 页帧分配器。**对于物理空间的一个片段,只存在一个页帧分配器,无论有多少个处理核** 95 | #[derive(Debug)] 96 | pub struct StackFrameAllocator { 97 | current: PhysPageNum, 98 | end: PhysPageNum, 99 | recycled: Vec, 100 | } 101 | 102 | impl StackFrameAllocator { 103 | pub fn new(start: PhysPageNum, end: PhysPageNum) -> Self { 104 | StackFrameAllocator { current: start, end, recycled: Vec::new() } 105 | } 106 | pub fn allocate_frame(&mut self) -> Result { 107 | if let Some(ppn) = self.recycled.pop() { 108 | Ok(ppn) 109 | } else { 110 | if self.current == self.end { 111 | Err(FrameAllocError) 112 | } else { 113 | let ans = self.current; 114 | self.current = self.current.next_page(); 115 | Ok(ans) 116 | } 117 | } 118 | } 119 | pub fn deallocate_frame(&mut self, ppn: PhysPageNum) { 120 | // validity check 121 | if ppn.is_within_range(self.current, self.end) || self.recycled.iter().find(|&v| {*v == ppn}).is_some() { 122 | panic!("Frame ppn={:x?} has not been allocated!", ppn); 123 | } 124 | // recycle 125 | self.recycled.push(ppn); 126 | } 127 | } 128 | 129 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 130 | pub struct FrameAllocError; 131 | 132 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 133 | pub struct FrameLayout { 134 | // 对齐到的页帧数。比如,如果是1,说明按字节运算,对齐到4K字节, 135 | // 如果是512,对齐到2M字节;如果是512*512,对齐到1G字节。 136 | frame_align: usize, 137 | } 138 | 139 | // 应当从PageMode::get_layout_for_level中获得 140 | impl FrameLayout { 141 | // 未检查参数,用于实现PageMode 142 | pub const unsafe fn new_unchecked(frame_align: usize) -> Self { 143 | Self { frame_align } 144 | } 145 | pub const fn frame_align(&self) -> usize { 146 | self.frame_align 147 | } 148 | pub fn page_size(&self) -> usize { 149 | self.frame_align << M::FRAME_SIZE_BITS 150 | } 151 | } 152 | 153 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 154 | pub struct FrameLayoutError; 155 | 156 | pub(crate) fn test_frame_alloc() { 157 | let from = PhysPageNum(0x80000); 158 | let to = PhysPageNum(0x100000); 159 | let mut alloc = StackFrameAllocator::new(from, to); 160 | let f1 = alloc.allocate_frame(); 161 | assert_eq!(f1, Ok(PhysPageNum(0x80000)), "first allocation"); 162 | let f2 = alloc.allocate_frame(); 163 | assert_eq!(f2, Ok(PhysPageNum(0x80001)), "second allocation"); 164 | alloc.deallocate_frame(f1.unwrap()); 165 | let f3 = alloc.allocate_frame(); 166 | assert_eq!(f3, Ok(PhysPageNum(0x80000)), "after free first, third allocation"); 167 | println!("[kernel-frame-test] Frame allocator test passed"); 168 | } 169 | 170 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 171 | pub struct AddressSpaceId(u16); 172 | 173 | impl AddressSpaceId { 174 | fn next_asid(&self, max_asid: AddressSpaceId) -> Option { 175 | if self.0 >= max_asid.0 { 176 | None 177 | } else { 178 | Some(AddressSpaceId(self.0.wrapping_add(1))) 179 | } 180 | } 181 | } 182 | 183 | const DEFAULT_ASID: AddressSpaceId = AddressSpaceId(0); // RISC-V架构规定,必须实现 184 | 185 | // 每个平台上是不一样的,需要通过读写satp寄存器获得 186 | pub fn max_asid() -> AddressSpaceId { 187 | #[cfg(target_pointer_width = "64")] 188 | let mut val: usize = ((1 << 16) - 1) << 44; 189 | #[cfg(target_pointer_width = "32")] 190 | let mut val: usize = ((1 << 9) - 1) << 22; 191 | unsafe { asm!(" 192 | csrr {tmp}, satp 193 | or {val}, {tmp}, {val} 194 | csrw satp, {val} 195 | csrrw {val}, satp, {tmp} 196 | ", tmp = out(reg) _, val = inlateout(reg) val) }; 197 | #[cfg(target_pointer_width = "64")] 198 | return AddressSpaceId(((val >> 44) & ((1 << 16) - 1)) as u16); 199 | #[cfg(target_pointer_width = "32")] 200 | return AddressSpaceId(((val >> 22) & ((1 << 9) - 1)) as u16); 201 | } 202 | 203 | // 在看代码的同志们可能发现,这里分配地址空间编号的算法和StackFrameAllocator很像。 204 | // 这里需要注意的是,分配页帧的算法经常要被使用,而且包含很多参数,最好最快的写法不一定是简单的栈式回收分配, 205 | // 更好的高性能内核设计,页帧分配的算法或许会有较大的优化空间。 206 | // 可以包含的参数,比如,页帧的内存布局,包括内存对齐的选项,这是大页优化非常需要的选项。 207 | // 但是地址空间编号的分配算法而且不需要经常调用,所以可以设计得很简单,普通栈式回收的算法就足够使用了。 208 | 209 | // 地址空间编号分配器,**每个处理核都有一个** 210 | #[derive(Debug)] 211 | pub struct StackAsidAllocator { 212 | current: AddressSpaceId, 213 | exhausted: bool, 214 | max: AddressSpaceId, 215 | recycled: Vec, 216 | } 217 | 218 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 219 | pub struct AsidAllocError; 220 | 221 | impl StackAsidAllocator { 222 | pub fn new(max_asid: AddressSpaceId) -> Self { 223 | StackAsidAllocator { current: DEFAULT_ASID, exhausted: false, max: max_asid, recycled: Vec::new() } 224 | } 225 | 226 | pub fn allocate_asid(&mut self) -> Result { 227 | if let Some(asid) = self.recycled.pop() { 228 | return Ok(asid) 229 | } 230 | if self.exhausted { 231 | return Err(AsidAllocError) 232 | } 233 | if self.current == self.max { 234 | self.exhausted = true; 235 | return Ok(self.max) 236 | } 237 | if let Some(next) = self.current.next_asid(self.max) { 238 | let ans = self.current; 239 | self.current = next; 240 | Ok(ans) 241 | } else { 242 | Err(AsidAllocError) 243 | } 244 | } 245 | 246 | fn deallocate_asid(&mut self, asid: AddressSpaceId) { 247 | if asid.next_asid(self.max).is_none() || self.recycled.iter().find(|&v| {*v == asid}).is_some() { 248 | panic!("Asid {:x?} has not been allocated!", asid); 249 | } 250 | self.recycled.push(asid); 251 | } 252 | } 253 | 254 | pub(crate) fn test_asid_alloc() { 255 | let max_asid = AddressSpaceId(0xffff); 256 | let mut alloc = StackAsidAllocator::new(max_asid); 257 | let a1 = alloc.allocate_asid(); 258 | assert_eq!(a1, Ok(AddressSpaceId(0)), "first allocation"); 259 | let a2 = alloc.allocate_asid(); 260 | assert_eq!(a2, Ok(AddressSpaceId(1)), "second allocation"); 261 | alloc.deallocate_asid(a1.unwrap()); 262 | let a3 = alloc.allocate_asid(); 263 | assert_eq!(a3, Ok(AddressSpaceId(0)), "after free first one, third allocation"); 264 | for _ in 0..max_asid.0 - 2 { 265 | alloc.allocate_asid().unwrap(); 266 | } 267 | let an = alloc.allocate_asid(); 268 | assert_eq!(an, Ok(max_asid), "last asid"); 269 | let an = alloc.allocate_asid(); 270 | assert_eq!(an, Err(AsidAllocError), "when asid exhausted, allocate next"); 271 | alloc.deallocate_asid(a2.unwrap()); 272 | let an = alloc.allocate_asid(); 273 | assert_eq!(an, Ok(AddressSpaceId(1)), "after free second one, allocate next"); 274 | let an = alloc.allocate_asid(); 275 | assert_eq!(an, Err(AsidAllocError), "no asid remains, allocate next"); 276 | 277 | let mut alloc = StackAsidAllocator::new(DEFAULT_ASID); // asid not implemented 278 | let a1 = alloc.allocate_asid(); 279 | assert_eq!(a1, Ok(AddressSpaceId(0)), "asid not implemented, first allocation"); 280 | let a2 = alloc.allocate_asid(); 281 | assert_eq!(a2, Err(AsidAllocError), "asid not implemented, second allocation"); 282 | 283 | println!("[kernel-asid-test] Asid allocator test passed"); 284 | } 285 | 286 | pub trait FrameAllocator { 287 | fn allocate_frame(&self) -> Result; 288 | fn deallocate_frame(&self, ppn: PhysPageNum); 289 | } 290 | 291 | pub type DefaultFrameAllocator = spin::Mutex; 292 | 293 | impl FrameAllocator for DefaultFrameAllocator { 294 | fn allocate_frame(&self) -> Result { 295 | self.lock().allocate_frame() 296 | } 297 | fn deallocate_frame(&self, ppn: PhysPageNum) { 298 | self.lock().deallocate_frame(ppn) 299 | } 300 | } 301 | 302 | impl FrameAllocator for &A { 303 | fn allocate_frame(&self) -> Result { 304 | (**self).allocate_frame() 305 | } 306 | fn deallocate_frame(&self, ppn: PhysPageNum) { 307 | (**self).deallocate_frame(ppn) 308 | } 309 | } 310 | 311 | // 表示整个页帧内存的所有权 312 | #[derive(Debug)] 313 | pub struct FrameBox { 314 | ppn: PhysPageNum, // 相当于*mut类型的指针 315 | frame_alloc: A, 316 | } 317 | 318 | impl FrameBox { 319 | // 分配页帧并创建FrameBox 320 | pub fn try_new_in(frame_alloc: A) -> Result, FrameAllocError> { 321 | let ppn = frame_alloc.allocate_frame()?; 322 | Ok(FrameBox { ppn, frame_alloc }) 323 | } 324 | // // unsafe说明。调用者必须保证以下约定: 325 | // // 1. ppn只被一个FrameBox拥有,也就是不能破坏所有权约定 326 | // // 2. 这个ppn是由frame_alloc分配的 327 | // unsafe fn from_ppn(ppn: PhysPageNum, frame_alloc: A) -> Self { 328 | // Self { ppn, frame_alloc } 329 | // } 330 | 331 | // 得到本页帧内存的页号 332 | pub fn phys_page_num(&self) -> PhysPageNum { 333 | self.ppn 334 | } 335 | } 336 | 337 | impl Drop for FrameBox { 338 | fn drop(&mut self) { 339 | // 释放所占有的页帧 340 | self.frame_alloc.deallocate_frame(self.ppn); 341 | } 342 | } 343 | 344 | // 分页模式 345 | // 346 | // 在每个页式管理模式下,我们认为分页系统分为不同的等级,每一级如果存在大页页表,都应当有相应的对齐要求。 347 | // 然后当前的页式管理模式,一定有一个固定的最大等级。 348 | // 349 | // 如果虚拟内存的模式是直接映射或者线性映射,这将不属于分页模式的范围。应当混合使用其它的地址空间,综合成为更大的地址空间。 350 | pub trait PageMode: Copy { 351 | // 当前分页模式下,页帧大小的二进制位数。例如,4K页为12位。 352 | const FRAME_SIZE_BITS: usize; 353 | // 当前分页模式下,物理页号的位数 354 | const PPN_BITS: usize; 355 | // 得到这一层大页物理地址最低的对齐要求 356 | fn get_layout_for_level(level: PageLevel) -> FrameLayout; 357 | // 得到从高到低的页表等级 358 | fn visit_levels_until(level: PageLevel) -> &'static [PageLevel]; 359 | // 得到从高到低的页表等级,不包括level 360 | fn visit_levels_before(level: PageLevel) -> &'static [PageLevel]; 361 | // 得到从高到低的页表等级 362 | fn visit_levels_from(level: PageLevel) -> &'static [PageLevel]; 363 | // 得到一个虚拟页号对应等级的索引 364 | fn vpn_index(vpn: VirtPageNum, level: PageLevel) -> usize; 365 | // 得到一段虚拟页号对应该等级索引的区间;如果超过此段最大的索引,返回索引的结束值为索引的最大值 366 | fn vpn_index_range(vpn_range: Range, level: PageLevel) -> Range; 367 | // 得到虚拟页号在当前等级下重新索引得到的页号 368 | fn vpn_level_index(vpn: VirtPageNum, level: PageLevel, idx: usize) -> VirtPageNum; 369 | // 当前分页模式下,页表的类型 370 | type PageTable: core::ops::Index + core::ops::IndexMut; 371 | // 创建页表时,把它的所有条目设置为无效条目 372 | fn init_page_table(table: &mut Self::PageTable); 373 | // 页式管理模式,可能有效也可能无效的页表项类型 374 | type Slot; 375 | // 页式管理模式,有效的页表项类型 376 | type Entry; 377 | // 解释页表项目;如果项目无效,返回None,可以直接操作slot写入其它数据 378 | fn slot_try_get_entry(slot: &mut Self::Slot) -> Result<&mut Self::Entry, &mut Self::Slot>; 379 | // 页表项的设置 380 | type Flags : Clone; 381 | // 写数据,建立一个到子页表的页表项 382 | fn slot_set_child(slot: &mut Self::Slot, ppn: PhysPageNum); 383 | // 写数据,建立一个到内存地址的页表项 384 | fn slot_set_mapping(slot: &mut Self::Slot, ppn: PhysPageNum, flags: Self::Flags); 385 | // 判断页表项目是否是一个叶子节点 386 | fn entry_is_leaf_page(entry: &mut Self::Entry) -> bool; 387 | // 写数据到页表项目,说明这是一个叶子节点 388 | fn entry_write_ppn_flags(entry: &mut Self::Entry, ppn: PhysPageNum, flags: Self::Flags); 389 | // 得到一个页表项目包含的物理页号 390 | fn entry_get_ppn(entry: &Self::Entry) -> PhysPageNum; 391 | } 392 | 393 | // 我们认为今天的分页系统都是分为不同的等级,就是多级页表,这里表示页表的等级是多少 394 | // todo: 实现一些函数,用于分页算法 395 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 396 | pub struct PageLevel(u8); 397 | 398 | impl PageLevel { 399 | pub const fn leaf_level() -> Self { 400 | Self(0) 401 | } 402 | } 403 | 404 | // Sv39分页系统模式;RISC-V RV64下有效 405 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 406 | pub struct Sv39; 407 | 408 | impl PageMode for Sv39 { 409 | const FRAME_SIZE_BITS: usize = 12; 410 | const PPN_BITS: usize = 44; 411 | type PageTable = Sv39PageTable; 412 | fn get_layout_for_level(level: PageLevel) -> FrameLayout { 413 | unsafe { match level.0 { 414 | 0 => FrameLayout::new_unchecked(1), // 4K页,最低层页 415 | 1 => FrameLayout::new_unchecked(512), // 2M页 416 | 2 => FrameLayout::new_unchecked(512 * 512), // 1G页,最高层大页 417 | _ => unimplemented!("this level does not exist on Sv39") 418 | } } 419 | } 420 | fn visit_levels_until(level: PageLevel) -> &'static [PageLevel] { 421 | match level.0 { 422 | 0 => &[PageLevel(2), PageLevel(1), PageLevel(0)], 423 | 1 => &[PageLevel(2), PageLevel(1)], 424 | 2 => &[PageLevel(2)], 425 | _ => unimplemented!("this level does not exist on Sv39"), 426 | } 427 | } 428 | fn visit_levels_before(level: PageLevel) -> &'static [PageLevel] { 429 | match level.0 { 430 | 0 => &[PageLevel(2), PageLevel(1)], 431 | 1 => &[PageLevel(2)], 432 | 2 => &[], 433 | _ => unimplemented!("this level does not exist on Sv39"), 434 | } 435 | } 436 | fn visit_levels_from(level: PageLevel) -> &'static [PageLevel] { 437 | match level.0 { 438 | 0 => &[PageLevel(0)], 439 | 1 => &[PageLevel(1), PageLevel(0)], 440 | 2 => &[PageLevel(2), PageLevel(1), PageLevel(0)], 441 | _ => unimplemented!("this level does not exist on Sv39"), 442 | } 443 | } 444 | fn vpn_index(vpn: VirtPageNum, level: PageLevel) -> usize { 445 | (vpn.0 >> (level.0 * 9)) & 511 446 | } 447 | fn vpn_index_range(vpn_range: Range, level: PageLevel) -> Range { 448 | let start = (vpn_range.start.0 >> (level.0 * 9)) & 511; 449 | let mut end = (vpn_range.end.0 >> (level.0 * 9)) & 511; 450 | if level.0 <= 1 { 451 | let start_idx1 = vpn_range.start.0 >> ((level.0 + 1) * 9); 452 | let end_idx1 = vpn_range.end.0 >> ((level.0 + 1) * 9); 453 | if end_idx1 > start_idx1 { 454 | end = 512; 455 | } 456 | } 457 | start..end 458 | } 459 | fn vpn_level_index(vpn: VirtPageNum, level: PageLevel, idx: usize) -> VirtPageNum { 460 | VirtPageNum(match level.0 { 461 | 0 => (vpn.0 & !((1 << 9) - 1)) + idx, 462 | 1 => (vpn.0 & !((1 << 18) - 1)) + (idx << 9), 463 | 2 => (vpn.0 & !((1 << 44) - 1)) + (idx << 18), 464 | _ => unimplemented!("this level does not exist on Sv39"), 465 | }) 466 | } 467 | type Entry = Sv39PageEntry; 468 | type Slot = Sv39PageSlot; 469 | fn slot_try_get_entry(slot: &mut Sv39PageSlot) -> Result<&mut Sv39PageEntry, &mut Sv39PageSlot> { 470 | // note(unsafe): slot是合法的 471 | let ans = unsafe { &mut *(slot as *mut _ as *mut Sv39PageEntry) }; 472 | if ans.flags().contains(Sv39Flags::V) { 473 | Ok(ans) 474 | } else { 475 | Err(slot) 476 | } 477 | } 478 | fn init_page_table(table: &mut Self::PageTable) { 479 | table.entries = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; // 全零 480 | } 481 | type Flags = Sv39Flags; 482 | fn slot_set_child(slot: &mut Sv39PageSlot, ppn: PhysPageNum) { 483 | let ans = unsafe { &mut *(slot as *mut _ as *mut Sv39PageEntry) }; 484 | ans.write_ppn_flags(ppn, Sv39Flags::V); // V=1, R=W=X=0 485 | } 486 | fn slot_set_mapping(slot: &mut Sv39PageSlot, ppn: PhysPageNum, flags: Sv39Flags) { 487 | let ans = unsafe { &mut *(slot as *mut _ as *mut Sv39PageEntry) }; 488 | ans.write_ppn_flags(ppn, Sv39Flags::V | flags); 489 | } 490 | fn entry_is_leaf_page(entry: &mut Sv39PageEntry) -> bool { 491 | // 如果包含R、W或X项,就是叶子节点。 492 | entry.flags().intersects(Sv39Flags::R | Sv39Flags::W | Sv39Flags::X) 493 | } 494 | fn entry_write_ppn_flags(entry: &mut Sv39PageEntry, ppn: PhysPageNum, flags: Sv39Flags) { 495 | entry.write_ppn_flags(ppn, flags); 496 | } 497 | fn entry_get_ppn(entry: &Sv39PageEntry) -> PhysPageNum { 498 | entry.ppn() 499 | } 500 | } 501 | 502 | #[repr(C)] 503 | pub struct Sv39PageTable { 504 | entries: [Sv39PageSlot; 512], // todo: other modes 505 | } 506 | 507 | impl core::ops::Index for Sv39PageTable { 508 | type Output = Sv39PageSlot; 509 | fn index(&self, idx: usize) -> &Sv39PageSlot { 510 | &self.entries[idx] 511 | } 512 | } 513 | 514 | impl core::ops::IndexMut for Sv39PageTable { 515 | fn index_mut(&mut self, idx: usize) -> &mut Sv39PageSlot { 516 | &mut self.entries[idx] 517 | } 518 | } 519 | 520 | #[repr(C)] 521 | pub struct Sv39PageSlot { 522 | bits: usize, 523 | } 524 | 525 | #[repr(C)] 526 | pub struct Sv39PageEntry { 527 | bits: usize, 528 | } 529 | 530 | use bit_field::BitField; 531 | 532 | impl Sv39PageEntry { 533 | #[inline] 534 | pub fn ppn(&self) -> PhysPageNum { 535 | PhysPageNum(self.bits.get_bits(10..54)) 536 | } 537 | #[inline] 538 | pub fn flags(&self) -> Sv39Flags { 539 | Sv39Flags::from_bits_truncate(self.bits.get_bits(0..8) as u8) 540 | } 541 | #[inline] 542 | pub fn write_ppn_flags(&mut self, ppn: PhysPageNum, flags: Sv39Flags) { 543 | self.bits = (ppn.0 << 10) | flags.bits() as usize 544 | } 545 | } 546 | 547 | bitflags::bitflags! { 548 | pub struct Sv39Flags: u8 { 549 | const V = 1 << 0; 550 | const R = 1 << 1; 551 | const W = 1 << 2; 552 | const X = 1 << 3; 553 | const U = 1 << 4; 554 | const G = 1 << 5; 555 | const A = 1 << 6; 556 | const D = 1 << 7; 557 | } 558 | } 559 | 560 | // 表示一个分页系统实现的地址空间 561 | // 562 | // 如果属于直接映射或者线性偏移映射,不应当使用这个结构体,应当使用其它的结构体。 563 | #[derive(Debug)] 564 | pub struct PagedAddrSpace { 565 | root_frame: FrameBox, 566 | frames: Vec>, 567 | frame_alloc: A, 568 | page_mode: M, 569 | } 570 | 571 | impl PagedAddrSpace { 572 | // 创建一个空的分页地址空间。一定会产生内存的写操作 573 | pub fn try_new_in(page_mode: M, frame_alloc: A) -> Result { 574 | // 新建一个满足根页表对齐要求的帧;虽然代码没有体现,通常对齐要求是1 575 | let mut root_frame = FrameBox::try_new_in(frame_alloc.clone())?; 576 | // println!("[kernel-alloc-map-test] Root frame: {:x?}", root_frame.phys_page_num()); 577 | // 向帧里填入一个空的根页表 578 | unsafe { fill_frame_with_initialized_page_table::(&mut root_frame) }; 579 | Ok(Self { root_frame, frames: Vec::new(), frame_alloc, page_mode }) 580 | } 581 | // 得到根页表的地址 582 | pub fn root_page_number(&self) -> PhysPageNum { 583 | self.root_frame.phys_page_num() 584 | } 585 | } 586 | 587 | #[inline] unsafe fn unref_ppn_mut<'a, M: PageMode>(ppn: PhysPageNum) -> &'a mut M::PageTable { 588 | let pa = ppn.addr_begin::(); 589 | &mut *(pa.0 as *mut M::PageTable) 590 | } 591 | 592 | #[inline] unsafe fn fill_frame_with_initialized_page_table(b: &mut FrameBox) { 593 | let a = &mut *(b.ppn.addr_begin::().0 as *mut M::PageTable); 594 | M::init_page_table(a); 595 | } 596 | 597 | impl PagedAddrSpace { 598 | // 设置entry。如果寻找的过程中,中间的页表没创建,那么创建它们 599 | unsafe fn alloc_get_table(&mut self, entry_level: PageLevel, vpn_start: VirtPageNum) -> Result<&mut M::PageTable, FrameAllocError> { 600 | let mut ppn = self.root_frame.phys_page_num(); 601 | for &level in M::visit_levels_before(entry_level) { 602 | // println!("[] BEFORE PPN = {:x?}", ppn); 603 | let page_table = unref_ppn_mut::(ppn); 604 | let vidx = M::vpn_index(vpn_start, level); 605 | match M::slot_try_get_entry(&mut page_table[vidx]) { 606 | Ok(entry) => ppn = M::entry_get_ppn(entry), 607 | Err(mut slot) => { // 需要一个内部页表,这里的页表项却没有数据,我们需要填写数据 608 | let frame_box = FrameBox::try_new_in(self.frame_alloc.clone())?; 609 | M::slot_set_child(&mut slot, frame_box.phys_page_num()); 610 | // println!("[] Created a new frame box"); 611 | ppn = frame_box.phys_page_num(); 612 | self.frames.push(frame_box); 613 | } 614 | } 615 | } 616 | // println!("[kernel-alloc-map-test] in alloc_get_table PPN: {:x?}", ppn); 617 | let page_table = unref_ppn_mut::(ppn); // 此时ppn是当前所需要修改的页表 618 | // 创建了一个没有约束的生命周期。不过我们可以判断它是合法的,因为它的所有者是Self,在Self的周期内都合法 619 | Ok(&mut *(page_table as *mut _)) 620 | } 621 | pub fn allocate_map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, n: usize, flags: M::Flags) -> Result<(), FrameAllocError> { 622 | for (page_level, vpn_range) in MapPairs::solve(vpn, ppn, n, self.page_mode) { 623 | // println!("[kernel-alloc-map-test] PAGE LEVEL: {:?}, VPN RANGE: {:x?}", page_level, vpn_range); 624 | let table = unsafe { self.alloc_get_table(page_level, vpn_range.start) }?; 625 | let idx_range = M::vpn_index_range(vpn_range.clone(), page_level); 626 | // println!("[kernel-alloc-map-test] IDX RANGE: {:?}", idx_range); 627 | for vidx in idx_range { 628 | let this_ppn = PhysPageNum(ppn.0 + M::vpn_level_index(vpn_range.start, page_level, vidx).0 - vpn.0); 629 | // println!("[kernel-alloc-map-test] Table: {:p} Vidx {} -> Ppn {:x?}", table, vidx, this_ppn); 630 | match M::slot_try_get_entry(&mut table[vidx]) { 631 | Ok(_entry) => panic!("already allocated"), 632 | Err(slot) => M::slot_set_mapping(slot, this_ppn, flags.clone()) 633 | } 634 | } 635 | } 636 | Ok(()) 637 | } 638 | // pub fn unmap(&mut self, vpn: VirtPageNum) { 639 | // todo!() 640 | // } 641 | 642 | /// 根据虚拟页号查询物理页号,可能出错。 643 | pub fn find_ppn(&self, vpn: VirtPageNum) -> Result<(&M::Entry, PageLevel), PageError> { 644 | let mut ppn = self.root_frame.phys_page_num(); 645 | for &lvl in M::visit_levels_until(PageLevel::leaf_level()) { 646 | // 注意: 要求内核对页表空间有恒等映射,可以直接解释物理地址 647 | let page_table = unsafe { unref_ppn_mut::(ppn) }; 648 | let vidx = M::vpn_index(vpn, lvl); 649 | match M::slot_try_get_entry(&mut page_table[vidx]) { 650 | Ok(entry) => if M::entry_is_leaf_page(entry) { 651 | return Ok((entry, lvl)) 652 | } else { 653 | ppn = M::entry_get_ppn(entry) 654 | }, 655 | Err(_slot) => return Err(PageError::InvalidEntry) 656 | } 657 | } 658 | Err(PageError::NotLeafInLowerestPage) 659 | } 660 | } 661 | 662 | /// 查询物理页号可能出现的错误 663 | #[derive(Debug)] 664 | pub enum PageError { 665 | /// 节点不具有有效位 666 | InvalidEntry, 667 | /// 第0层页表不能是内部节点 668 | NotLeafInLowerestPage 669 | } 670 | 671 | #[derive(Debug)] 672 | pub struct MapPairs { 673 | ans_iter: alloc::vec::IntoIter<(PageLevel, Range)>, 674 | mode: M, 675 | } 676 | 677 | impl MapPairs { 678 | pub fn solve(vpn: VirtPageNum, ppn: PhysPageNum, n: usize, mode: M) -> Self { 679 | let mut ans = Vec::new(); 680 | for &i in M::visit_levels_until(PageLevel::leaf_level()) { 681 | let align = M::get_layout_for_level(i).frame_align(); 682 | if usize::wrapping_sub(vpn.0, ppn.0) % align != 0 || n < align { 683 | continue; 684 | } 685 | let (mut ve_prev, mut vs_prev) = (None, None); 686 | for &j in M::visit_levels_from(i) { 687 | let align_cur = M::get_layout_for_level(j).frame_align(); 688 | let ve_cur = align_cur * ((vpn.0 + align_cur - 1) / align_cur); // a * roundup(v / a) 689 | let vs_cur = align_cur * ((vpn.0 + n) / align_cur); // a * rounddown((v+n) / a) 690 | if let (Some(ve_prev), Some(vs_prev)) = (ve_prev, vs_prev) { 691 | if ve_cur != ve_prev { 692 | ans.push((j, VirtPageNum(ve_cur)..VirtPageNum(ve_prev))); 693 | } 694 | if vs_prev != vs_cur { 695 | ans.push((j, VirtPageNum(vs_prev)..VirtPageNum(vs_cur))); 696 | } 697 | } else { 698 | if ve_cur != vs_cur { 699 | ans.push((j, VirtPageNum(ve_cur)..VirtPageNum(vs_cur))); 700 | } 701 | } 702 | (ve_prev, vs_prev) = (Some(ve_cur), Some(vs_cur)); 703 | } 704 | break; 705 | } 706 | // println!("[SOLVE] Ans = {:x?}", ans); 707 | Self { ans_iter: ans.into_iter(), mode } 708 | } 709 | } 710 | 711 | impl Iterator for MapPairs { 712 | type Item = (PageLevel, Range); 713 | fn next(&mut self) -> Option { 714 | self.ans_iter.next() 715 | } 716 | } 717 | 718 | pub(crate) fn test_map_solve() { 719 | let pairs = MapPairs::solve(VirtPageNum(0x90_000), PhysPageNum(0x50_000), 666666, Sv39).collect::>(); 720 | assert_eq!(pairs, [ 721 | (PageLevel(2), VirtPageNum(786432)..VirtPageNum(1048576)), 722 | (PageLevel(1), VirtPageNum(589824)..VirtPageNum(786432)), 723 | (PageLevel(1), VirtPageNum(1048576)..VirtPageNum(1256448)), 724 | (PageLevel(0), VirtPageNum(1256448)..VirtPageNum(1256490)) 725 | ]); 726 | let pairs = MapPairs::solve(VirtPageNum(0x90_001), PhysPageNum(0x50_001), 77777, Sv39).collect::>(); 727 | assert_eq!(pairs, [ 728 | (PageLevel(1), VirtPageNum(590336)..VirtPageNum(667136)), 729 | (PageLevel(0), VirtPageNum(589825)..VirtPageNum(590336)), 730 | (PageLevel(0), VirtPageNum(667136)..VirtPageNum(667602)) 731 | ]); 732 | println!("[kernel-map-solve] Map solver test passed"); 733 | } 734 | 735 | // 切换地址空间,同时需要提供1.地址空间的详细设置 2.地址空间编号 736 | // 同时返回:satp寄存器的值 737 | use riscv::register::satp::Satp; 738 | pub unsafe fn activate_paged_riscv_sv39(root_ppn: PhysPageNum, asid: AddressSpaceId) -> Satp { 739 | use riscv::register::satp::{self, Mode}; 740 | satp::set(Mode::Sv39, asid.0 as usize, root_ppn.0); 741 | asm!("sfence.vma x0, {}", in(reg) asid.0 as usize); 742 | satp::read() 743 | } 744 | 745 | // 得到satp的值 746 | pub fn get_satp_sv39(asid: AddressSpaceId, ppn: PhysPageNum) -> Satp { 747 | let bits = (8 << 60) | ((asid.0 as usize) << 44) | ppn.0; 748 | unsafe { core::mem::transmute(bits) } 749 | } 750 | 751 | // 帧翻译:在空间1中访问空间2的帧。要求空间1具有恒等映射特性 752 | pub fn translate_frame_read( 753 | // as1: &PagedAddrSpace, 754 | as2: &PagedAddrSpace, 755 | vaddr2: VirtAddr, 756 | len_bytes2: usize, 757 | f: F 758 | ) -> Result<(), PageError> 759 | where 760 | // M1: PageMode, 761 | // A1: FrameAllocator + Clone, 762 | M2: PageMode, 763 | A2: FrameAllocator + Clone, 764 | F: Fn(PhysPageNum, usize, usize) // 按顺序返回空间1中的帧 765 | { 766 | // println!("vaddr2 = {:x?}, len_bytes2 = {}", vaddr2, len_bytes2); 767 | let mut vpn2 = vaddr2.page_number::(); 768 | let mut remaining_len = len_bytes2; 769 | let (mut entry, mut lvl) = as2.find_ppn(vpn2)?; 770 | let mut cur_offset = vaddr2.page_offset::(lvl); 771 | while remaining_len > 0 { 772 | let ppn = M2::entry_get_ppn(entry); 773 | let cur_frame_layout = M2::get_layout_for_level(lvl); 774 | let cur_len = if remaining_len <= cur_frame_layout.page_size::() { 775 | remaining_len 776 | } else { 777 | cur_frame_layout.page_size::() 778 | }; 779 | f(ppn, cur_offset, cur_len); 780 | // println!("[] {} {} {}", cur_frame_layout.page_size::(), cur_offset, cur_len); 781 | remaining_len -= cur_len; 782 | if remaining_len == 0 { 783 | return Ok(()) 784 | } 785 | cur_offset = 0; // 下一个帧从头开始 786 | vpn2 = vpn2.next_page_by_level::(lvl); 787 | (entry, lvl) = as2.find_ppn(vpn2)?; 788 | // println!("[] {}", remaining_len); 789 | } 790 | Ok(()) 791 | } 792 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/sbi.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | pub const EXTENSION_BASE: usize = 0x10; 4 | pub const EXTENSION_TIMER: usize = 0x54494D45; 5 | pub const EXTENSION_IPI: usize = 0x735049; 6 | pub const EXTENSION_RFENCE: usize = 0x52464E43; 7 | pub const EXTENSION_HSM: usize = 0x48534D; 8 | pub const EXTENSION_SRST: usize = 0x53525354; 9 | 10 | const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0; 11 | const FUNCTION_BASE_GET_SBI_IMPL_ID: usize = 0x1; 12 | const FUNCTION_BASE_GET_SBI_IMPL_VERSION: usize = 0x2; 13 | const FUNCTION_BASE_PROBE_EXTENSION: usize = 0x3; 14 | const FUNCTION_BASE_GET_MVENDORID: usize = 0x4; 15 | const FUNCTION_BASE_GET_MARCHID: usize = 0x5; 16 | const FUNCTION_BASE_GET_MIMPID: usize = 0x6; 17 | 18 | #[repr(C)] 19 | pub struct SbiRet { 20 | /// Error number 21 | pub error: usize, 22 | /// Result value 23 | pub value: usize, 24 | } 25 | 26 | #[inline(always)] 27 | fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet { 28 | let (error, value); 29 | match () { 30 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 31 | () => unsafe { asm!( 32 | "ecall", 33 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 34 | in("a6") function, in("a7") extension, 35 | lateout("a0") error, lateout("a1") value, 36 | ) }, 37 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 38 | () => { 39 | drop((extension, function, arg0, arg1, arg2)); 40 | unimplemented!("not RISC-V instruction set architecture") 41 | } 42 | }; 43 | SbiRet { error, value } 44 | } 45 | 46 | #[inline] 47 | pub fn get_spec_version() -> usize { 48 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0, 0).value 49 | } 50 | 51 | #[inline] 52 | pub fn get_sbi_impl_id() -> usize { 53 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_ID, 0, 0, 0).value 54 | } 55 | 56 | #[inline] 57 | pub fn get_sbi_impl_version() -> usize { 58 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_VERSION, 0, 0, 0).value 59 | } 60 | 61 | #[inline] 62 | pub fn probe_extension(extension_id: usize) -> usize { 63 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_PROBE_EXTENSION, extension_id, 0, 0).value 64 | } 65 | 66 | #[inline] 67 | pub fn get_mvendorid() -> usize { 68 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MVENDORID, 0, 0, 0).value 69 | } 70 | 71 | #[inline] 72 | pub fn get_marchid() -> usize { 73 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MARCHID, 0, 0, 0).value 74 | } 75 | 76 | #[inline] 77 | pub fn get_mimpid() -> usize { 78 | sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MIMPID, 0, 0, 0).value 79 | } 80 | 81 | #[inline(always)] 82 | fn sbi_call_legacy(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 83 | let ret; 84 | match () { 85 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 86 | () => unsafe { asm!( 87 | "ecall", 88 | in("a0") arg0, in("a1") arg1, in("a2") arg2, 89 | in("a7") which, 90 | lateout("a0") ret, 91 | ) }, 92 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 93 | () => { 94 | drop((which, arg0, arg1, arg2)); 95 | unimplemented!("not RISC-V instruction set architecture") 96 | } 97 | }; 98 | ret 99 | } 100 | 101 | const SBI_SET_TIMER: usize = 0; 102 | const SBI_CONSOLE_PUTCHAR: usize = 1; 103 | const SBI_CONSOLE_GETCHAR: usize = 2; 104 | const SBI_CLEAR_IPI: usize = 3; 105 | const SBI_SEND_IPI: usize = 4; 106 | const SBI_REMOTE_FENCE_I: usize = 5; 107 | const SBI_REMOTE_SFENCE_VMA: usize = 6; 108 | const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; 109 | const SBI_SHUTDOWN: usize = 8; 110 | 111 | pub fn console_putchar(c: usize) { 112 | sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c, 0, 0); 113 | } 114 | 115 | pub fn console_getchar() -> usize { 116 | sbi_call_legacy(SBI_CONSOLE_GETCHAR, 0, 0, 0) 117 | } 118 | 119 | pub fn shutdown() -> ! { 120 | sbi_call_legacy(SBI_SHUTDOWN, 0, 0, 0); 121 | unreachable!() 122 | } 123 | 124 | pub fn set_timer(time: usize) { 125 | sbi_call_legacy(SBI_SET_TIMER, time, 0, 0); 126 | } 127 | -------------------------------------------------------------------------------- /kernels/01-virt-addr-kernel/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use crate::mm; 2 | 3 | const MODULE_PROCESS: usize = 0x114514; 4 | const FUNCTION_PROCESS_EXIT: usize = 0x1919810; 5 | const FUNCTION_PROCESS_PANIC: usize = 0x11451419; 6 | 7 | const MODULE_TEST_INTERFACE: usize = 0x233666; 8 | const FUNCTION_TEST_WRITE: usize = 0x666233; 9 | 10 | pub enum SyscallOperation { 11 | Return(SyscallResult), 12 | Terminate(i32), 13 | UserPanic(Option<&'static str>, u32, u32, Option<&'static str>), 14 | } 15 | 16 | pub struct SyscallResult { 17 | pub code: usize, 18 | pub extra: usize, 19 | } 20 | 21 | pub fn syscall(module: usize, function: usize, args: [usize; 6], user_as: &mm::PagedAddrSpace) -> SyscallOperation 22 | where M: mm::PageMode, A: mm::FrameAllocator + Clone { 23 | match module { 24 | MODULE_PROCESS => do_process(function, args), 25 | MODULE_TEST_INTERFACE => do_test_interface(function, [args[0], args[1], args[2]], user_as), 26 | _ => panic!("Unknown syscall, module: {}, function: {}, args: {:?}", module, function, args), 27 | } 28 | } 29 | 30 | fn do_process(function: usize, args: [usize; 6]) -> SyscallOperation { 31 | match function { 32 | FUNCTION_PROCESS_EXIT => SyscallOperation::Terminate(args[0] as i32), 33 | FUNCTION_PROCESS_PANIC => { // [line as usize, col as usize, f_buf, f_len, m_buf, m_len] 34 | let [line, col, f_buf, f_len, m_buf, m_len] = args; 35 | let file_name = if f_buf == 0 { 36 | None 37 | } else { 38 | let slice = unsafe { core::slice::from_raw_parts(f_buf as *const u8, f_len) }; 39 | Some(core::str::from_utf8(slice).unwrap()) 40 | }; 41 | let msg = if m_buf == 0 { 42 | None 43 | } else { 44 | let slice = unsafe { core::slice::from_raw_parts(m_buf as *const u8, m_len) }; 45 | Some(core::str::from_utf8(slice).unwrap()) 46 | }; 47 | SyscallOperation::UserPanic(file_name, line as u32, col as u32, msg) 48 | }, 49 | _ => panic!("Unknown syscall PROCESS, function: {}, args: {:?}", function, args), 50 | } 51 | } 52 | 53 | fn do_test_interface(function: usize, args: [usize; 3], user_as: &mm::PagedAddrSpace) -> SyscallOperation 54 | where M: mm::PageMode, A: mm::FrameAllocator + Clone { 55 | match function { 56 | FUNCTION_TEST_WRITE => { // fd: usize, buffer: &[u8] fd, buffer.as_ptr() as usize, buffer.len() 57 | const STDOUT: usize = 1; 58 | let [fd, buf, len] = args; 59 | if fd == STDOUT { 60 | let buf_vaddr = mm::VirtAddr(buf); 61 | // println!("vaddr = {:x?}", buf_vaddr); 62 | mm::translate_frame_read(user_as, buf_vaddr, len, |ppn, cur_offset, cur_len| { 63 | let buf_frame_kernel_vaddr = ppn.addr_begin::().0 + cur_offset; // 只有恒等映射的内核有效 64 | let slice = unsafe { core::slice::from_raw_parts(buf_frame_kernel_vaddr as *const u8, cur_len) }; 65 | for &byte in slice { 66 | crate::sbi::console_putchar(byte as usize); 67 | } 68 | // println!("ppn = {:x?}, off = {:x}, len = {}, slice = {:x?}", ppn, cur_offset, cur_len, slice as *const _); 69 | }).expect("read user buffer"); 70 | SyscallOperation::Return(SyscallResult { code: 0, extra: len as usize }) 71 | } else { 72 | panic!("Unsupported fd {}", fd); 73 | } 74 | }, 75 | _ => panic!("Unknown syscall TEST_INTERFACE,function: {}, arg: {:?}", function, args), 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /library/tornado-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tornado-std" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | r0 = "1" 10 | -------------------------------------------------------------------------------- /library/tornado-std/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::io::Write; 3 | use std::fs; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-changed=build.rs"); 8 | println!("cargo:rerun-if-changed=src/linker64.ld"); 9 | 10 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 11 | 12 | fs::File::create(out_dir.join("linker64.ld")) 13 | .unwrap() 14 | .write_all(include_bytes!("src/linker64.ld")) 15 | .unwrap(); 16 | println!("cargo:rustc-link-search={}", out_dir.display()); 17 | } 18 | -------------------------------------------------------------------------------- /library/tornado-std/src/console.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | use super::write; 3 | 4 | struct Stdout; 5 | 6 | const STDOUT: usize = 1; 7 | 8 | impl Write for Stdout { 9 | fn write_str(&mut self, s: &str) -> fmt::Result { 10 | write(STDOUT, s.as_bytes()); 11 | Ok(()) 12 | } 13 | } 14 | 15 | pub fn print(args: fmt::Arguments) { 16 | Stdout.write_fmt(args).unwrap(); 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! print { 21 | ($fmt: literal $(, $($arg: tt)+)?) => { 22 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 23 | } 24 | } 25 | 26 | #[macro_export] 27 | macro_rules! println { 28 | ($fmt: literal $(, $($arg: tt)+)?) => { 29 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library/tornado-std/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm)] 3 | #![feature(linkage)] 4 | #![feature(panic_info_message)] 5 | 6 | #[macro_use] 7 | #[doc(hidden)] 8 | pub mod console; 9 | mod syscall; 10 | 11 | #[cfg_attr(not(test), panic_handler)] 12 | #[allow(unused)] 13 | fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { 14 | let err = panic_info.message().unwrap().as_str(); 15 | if let Some(location) = panic_info.location() { 16 | sys_panic(Some(location.file()), location.line(), location.column(), err); 17 | } else { 18 | sys_panic(None, 0, 0, err); 19 | } 20 | loop {} 21 | } 22 | 23 | #[no_mangle] 24 | #[link_section = ".text.entry"] 25 | pub extern "C" fn _start() -> ! { 26 | extern "C" { 27 | fn sbss(); fn ebss(); 28 | } 29 | unsafe { r0::zero_bss(&mut sbss as *mut _ as *mut u64, &mut ebss as *mut _ as *mut u64) }; 30 | exit(main()); 31 | panic!("unreachable after sys_exit!"); 32 | } 33 | 34 | #[linkage = "weak"] 35 | #[no_mangle] 36 | fn main() -> i32 { 37 | panic!("Tornado standard library: cannot find main!"); 38 | } 39 | 40 | use syscall::*; 41 | 42 | pub fn write(fd: usize, buf: &[u8]) -> SyscallResult { sys_write(fd, buf) } 43 | pub fn exit(exit_code: i32) -> SyscallResult { sys_exit(exit_code) } 44 | -------------------------------------------------------------------------------- /library/tornado-std/src/linker64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x1000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | 9 | stext = .; 10 | .text : { 11 | *(.text.entry) 12 | *(.text .text.*) 13 | } 14 | 15 | . = ALIGN(4K); 16 | etext = .; 17 | srodata = .; 18 | .rodata : { 19 | *(.rodata .rodata.*) 20 | *(.srodata .srodata.*) 21 | } 22 | 23 | . = ALIGN(4K); 24 | erodata = .; 25 | sdata = .; 26 | .data : { 27 | *(.data .data.*) 28 | *(.sdata .sdata.*) 29 | } 30 | 31 | . = ALIGN(4K); 32 | edata = .; 33 | sbss = .; 34 | .bss : { 35 | *(.bss .bss.*) 36 | *(.sbss .sbss.*) 37 | } 38 | 39 | . = ALIGN(4K); 40 | ebss = .; 41 | } 42 | -------------------------------------------------------------------------------- /library/tornado-std/src/syscall.rs: -------------------------------------------------------------------------------- 1 | const MODULE_PROCESS: usize = 0x114514; 2 | const FUNCTION_PROCESS_EXIT: usize = 0x1919810; 3 | const FUNCTION_PROCESS_PANIC: usize = 0x11451419; 4 | 5 | const MODULE_TEST_INTERFACE: usize = 0x233666; 6 | const FUNCTION_TEST_WRITE: usize = 0x666233; 7 | 8 | pub struct SyscallResult { 9 | pub code: usize, 10 | pub extra: usize, 11 | } 12 | 13 | fn syscall_1(module: usize, function: usize, arg: usize) -> SyscallResult { 14 | match () { 15 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 16 | () => { 17 | let (code, extra); 18 | unsafe { asm!( 19 | "ecall", 20 | in("a0") arg, 21 | in("a6") function, in("a7") module, 22 | lateout("a0") code, lateout("a1") extra, 23 | ) }; 24 | SyscallResult { code, extra } 25 | }, 26 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 27 | () => { 28 | drop((module, function, arg)); 29 | unimplemented!("not RISC-V instruction set architecture") 30 | } 31 | } 32 | } 33 | 34 | fn syscall_3(module: usize, function: usize, args: [usize; 3]) -> SyscallResult { 35 | match () { 36 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 37 | () => { 38 | let (code, extra); 39 | unsafe { asm!( 40 | "ecall", 41 | in("a0") args[0], in("a1") args[1], in("a2") args[2], 42 | in("a6") function, in("a7") module, 43 | lateout("a0") code, lateout("a1") extra, 44 | ) }; 45 | SyscallResult { code, extra } 46 | }, 47 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 48 | () => { 49 | drop((module, function, args)); 50 | unimplemented!("not RISC-V instruction set architecture") 51 | } 52 | } 53 | } 54 | 55 | fn syscall_6(module: usize, function: usize, args: [usize; 6]) -> SyscallResult { 56 | match () { 57 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 58 | () => { 59 | let (code, extra); 60 | unsafe { asm!( 61 | "ecall", 62 | in("a0") args[0], in("a1") args[1], in("a2") args[2], 63 | in("a3") args[3], in("a4") args[4], in("a5") args[5], 64 | in("a6") function, in("a7") module, 65 | lateout("a0") code, lateout("a1") extra, 66 | ) }; 67 | SyscallResult { code, extra } 68 | }, 69 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 70 | () => { 71 | drop((module, function, args)); 72 | unimplemented!("not RISC-V instruction set architecture") 73 | } 74 | } 75 | } 76 | 77 | pub fn sys_write(fd: usize, buffer: &[u8]) -> SyscallResult { 78 | syscall_3(MODULE_TEST_INTERFACE, FUNCTION_TEST_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) 79 | } 80 | 81 | pub fn sys_exit(exit_code: i32) -> SyscallResult { 82 | syscall_1(MODULE_PROCESS, FUNCTION_PROCESS_EXIT, exit_code as usize) 83 | } 84 | 85 | pub fn sys_panic(file_name: Option<&str>, line: u32, col: u32, msg: Option<&str>) -> SyscallResult { 86 | let (f_buf, f_len) = file_name.map(|s| (s.as_ptr() as usize, s.len())).unwrap_or((0, 0)); 87 | let (m_buf, m_len) = msg.map(|s| (s.as_ptr() as usize, s.len())).unwrap_or((0, 0)); 88 | syscall_6( 89 | MODULE_PROCESS, FUNCTION_PROCESS_PANIC, 90 | [line as usize, col as usize, f_buf, f_len, m_buf, m_len] 91 | ) 92 | } 93 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | authors = ["Luo Jia "] 5 | description = "interactive cargo runner" 6 | edition = "2018" 7 | publish = false 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | clap = "2.33" 13 | toml = "0.5" 14 | serde = { version = "1.0", features = ["derive"] } 15 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | path::{Path, PathBuf}, 4 | process::{self, Command}, 5 | fs, 6 | }; 7 | use serde::Deserialize; 8 | 9 | #[macro_use] 10 | extern crate clap; 11 | 12 | const DEFAULT_TARGET: &'static str = "riscv64imac-unknown-none-elf"; 13 | 14 | #[derive(Debug)] 15 | struct XtaskEnv { 16 | kernel_package_path: PathBuf, 17 | kernel_package_name: String, 18 | kernel_binary_name: String, 19 | compile_mode: CompileMode, 20 | } 21 | 22 | #[derive(Debug)] 23 | enum CompileMode { 24 | Debug, 25 | Release 26 | } 27 | 28 | fn main() { 29 | let matches = clap_app!(xtask => 30 | (version: crate_version!()) 31 | (author: crate_authors!()) 32 | (about: crate_description!()) 33 | (@subcommand make => 34 | (about: "Build project") 35 | (@arg release: --release "Build artifacts in release mode, with optimizations") 36 | ) 37 | (@subcommand asm => 38 | (about: "View asm code for project") 39 | ) 40 | (@subcommand size => 41 | (about: "View size for project") 42 | ) 43 | (@subcommand qemu => 44 | (about: "Run QEMU") 45 | (@arg release: --release "Build artifacts in release mode, with optimizations") 46 | (@arg app: "Choose the apps to be bundled") 47 | ) 48 | (@subcommand debug => 49 | (about: "Debug with QEMU and GDB stub") 50 | (@arg app: "Choose the apps to be bundled") 51 | ) 52 | (@subcommand gdb => 53 | (about: "Run GDB debugger") 54 | ) 55 | ).get_matches(); 56 | let kernel_package_path = project_root().join("kernels").join(&default_kernel_path()); 57 | let kernel_package_name = read_package_name(&kernel_package_path); 58 | let kernel_binary_name = format!("{}.bin", kernel_package_name); 59 | let mut xtask_env = XtaskEnv { 60 | kernel_package_path, 61 | kernel_package_name, 62 | kernel_binary_name, 63 | compile_mode: CompileMode::Debug, 64 | }; 65 | println!("xtask: package {}, mode: {:?}", xtask_env.kernel_package_name, xtask_env.compile_mode); 66 | if let Some(matches) = matches.subcommand_matches("make") { 67 | if matches.is_present("release") { 68 | xtask_env.compile_mode = CompileMode::Release; 69 | } 70 | xtask_build_kernel(&xtask_env); 71 | xtask_binary_kernel(&xtask_env); 72 | // xtask_build_apps(&xtask_env); // todo: multiple apps 73 | } else if let Some(matches) = matches.subcommand_matches("qemu") { 74 | if matches.is_present("release") { 75 | xtask_env.compile_mode = CompileMode::Release; 76 | } 77 | let chosen_app = "hello-world"; // todo: 目前是写死的 78 | if let Some(app_matches) = matches.values_of("app") { 79 | for app_name in app_matches { 80 | println!("xtask: building app {}", app_name); 81 | xtask_build_app(&xtask_env, app_name); 82 | xtask_binary_app(&xtask_env, app_name); 83 | } 84 | } 85 | xtask_build_kernel(&xtask_env); 86 | xtask_binary_kernel(&xtask_env); 87 | xtask_qemu_run(&xtask_env, chosen_app); 88 | } else if let Some(matches) = matches.subcommand_matches("debug") { 89 | let chosen_app = "hello-world"; // todo: 目前是写死的 90 | if let Some(app_matches) = matches.values_of("app") { 91 | for app_name in app_matches { 92 | println!("xtask: building app {}", app_name); 93 | xtask_build_app(&xtask_env, app_name); 94 | xtask_binary_app(&xtask_env, app_name); 95 | } 96 | } 97 | xtask_build_kernel(&xtask_env); 98 | xtask_binary_kernel(&xtask_env); 99 | xtask_qemu_debug(&xtask_env, chosen_app); 100 | } else if let Some(_matches) = matches.subcommand_matches("gdb") { 101 | xtask_gdb(&xtask_env); 102 | } else if let Some(_matches) = matches.subcommand_matches("asm") { 103 | xtask_build_kernel(&xtask_env); 104 | xtask_asm_kernel(&xtask_env); 105 | } else if let Some(_matches) = matches.subcommand_matches("size") { 106 | xtask_build_kernel(&xtask_env); 107 | xtask_size_kernel(&xtask_env); 108 | } else { 109 | println!("Use `cargo qemu` to run, `cargo xtask --help` for help") 110 | } 111 | } 112 | 113 | fn xtask_build_kernel(xtask_env: &XtaskEnv) { 114 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 115 | let mut command = Command::new(cargo); 116 | command.current_dir(&xtask_env.kernel_package_path); 117 | command.arg("build"); 118 | match xtask_env.compile_mode { 119 | CompileMode::Debug => {}, 120 | CompileMode::Release => { command.arg("--release"); }, 121 | } 122 | command.args(&["--package", &xtask_env.kernel_package_name]); 123 | command.args(&["--target", DEFAULT_TARGET]); 124 | let status = command 125 | .status().unwrap(); 126 | if !status.success() { 127 | println!("cargo build failed"); 128 | process::exit(1); 129 | } 130 | } 131 | 132 | fn xtask_build_app(xtask_env: &XtaskEnv, app_name: &str) { 133 | let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); 134 | let mut command = Command::new(cargo); 135 | command.current_dir(project_root().join("apps").join(app_name)); 136 | command.arg("build"); 137 | match xtask_env.compile_mode { 138 | CompileMode::Debug => {}, 139 | CompileMode::Release => { command.arg("--release"); }, 140 | } 141 | command.args(&["--package", app_name]); 142 | command.args(&["--target", DEFAULT_TARGET]); 143 | let status = command 144 | .status().unwrap(); 145 | if !status.success() { 146 | println!("cargo build failed"); 147 | process::exit(1); 148 | } 149 | } 150 | 151 | fn xtask_binary_app(xtask_env: &XtaskEnv, app_name: &str) { 152 | let objcopy = "rust-objcopy"; 153 | let status = Command::new(objcopy) 154 | .current_dir(dist_dir(xtask_env)) 155 | .arg(app_name) 156 | .arg("--binary-architecture=riscv64") 157 | .arg("--strip-all") 158 | .args(&["-O", "binary", &format!("{}.bin", app_name)]) 159 | .status().unwrap(); 160 | 161 | if !status.success() { 162 | println!("objcopy binary failed"); 163 | process::exit(1); 164 | } 165 | } 166 | 167 | fn xtask_asm_kernel(xtask_env: &XtaskEnv) { 168 | // @{{objdump}} -D {{test-kernel-elf}} | less 169 | let objdump = "riscv64-unknown-elf-objdump"; 170 | Command::new(objdump) 171 | .current_dir(dist_dir(xtask_env)) 172 | .arg("-d") 173 | .arg(&xtask_env.kernel_package_name) 174 | .status().unwrap(); 175 | } 176 | 177 | fn xtask_size_kernel(xtask_env: &XtaskEnv) { 178 | // @{{size}} -A -x {{test-kernel-elf}} 179 | let size = "rust-size"; 180 | Command::new(size) 181 | .current_dir(dist_dir(xtask_env)) 182 | .arg("-A") 183 | .arg("-x") 184 | .arg(&xtask_env.kernel_package_name) 185 | .status().unwrap(); 186 | } 187 | 188 | fn xtask_binary_kernel(xtask_env: &XtaskEnv) { 189 | /* 190 | objdump := "riscv64-unknown-elf-objdump" 191 | objcopy := "rust-objcopy --binary-architecture=riscv64" 192 | 193 | build: firmware 194 | @{{objcopy}} {{test-kernel-elf}} --strip-all -O binary {{test-kernel-bin}} 195 | */ 196 | let objcopy = "rust-objcopy"; 197 | let status = Command::new(objcopy) 198 | .current_dir(dist_dir(xtask_env)) 199 | .arg(&xtask_env.kernel_package_name) 200 | .arg("--binary-architecture=riscv64") 201 | .arg("--strip-all") 202 | .args(&["-O", "binary", &xtask_env.kernel_binary_name]) 203 | .status().unwrap(); 204 | 205 | if !status.success() { 206 | println!("objcopy binary failed"); 207 | process::exit(1); 208 | } 209 | } 210 | 211 | fn xtask_qemu_run(xtask_env: &XtaskEnv, one_app: &str) { 212 | /* 213 | qemu: build 214 | @qemu-system-riscv64 \ 215 | -machine virt \ 216 | -nographic \ 217 | -bios none \ 218 | -device loader,file={{rustsbi-bin}},addr=0x80000000 \ 219 | -device loader,file={{test-kernel-bin}},addr=0x80200000 \ 220 | -smp threads={{threads}} 221 | */ 222 | let status = Command::new("qemu-system-riscv64") 223 | .current_dir(dist_dir(xtask_env)) 224 | .args(&["-machine", "virt"]) 225 | .args(&["-bios", "../../../bootloader/rustsbi-qemu.bin"]) 226 | .arg("-nographic") 227 | .args(&["-kernel", &xtask_env.kernel_binary_name]) 228 | .args(&["-device", &format!("loader,file={}.bin,addr=0x80400000", one_app)]) 229 | .status().unwrap(); 230 | 231 | if !status.success() { 232 | println!("qemu failed"); 233 | process::exit(1); 234 | } 235 | } 236 | 237 | fn xtask_qemu_debug(xtask_env: &XtaskEnv, one_app: &str) { 238 | let status = Command::new("qemu-system-riscv64") 239 | .current_dir(dist_dir(xtask_env)) 240 | .args(&["-machine", "virt"]) 241 | .args(&["-bios", "../../../bootloader/rustsbi-qemu.bin"]) 242 | .args(&["-kernel", &xtask_env.kernel_binary_name]) 243 | .arg("-nographic") 244 | .args(&["-device", &format!("loader,file={}.bin,addr=0x80400000", one_app)]) 245 | .args(&["-gdb", "tcp::1234", "-S"]) 246 | .status().unwrap(); 247 | 248 | if !status.success() { 249 | println!("qemu failed"); 250 | process::exit(1); 251 | } 252 | } 253 | 254 | fn xtask_gdb(xtask_env: &XtaskEnv) { 255 | let status = Command::new("riscv64-unknown-elf-gdb") 256 | .current_dir(dist_dir(xtask_env)) 257 | .args(&["--eval-command", &format!("file {}", &xtask_env.kernel_package_name)]) 258 | .args(&["--eval-command", "target remote localhost:1234"]) 259 | .arg("-q") 260 | .status() 261 | .unwrap(); 262 | 263 | if !status.success() { 264 | println!("qemu failed"); 265 | process::exit(1); 266 | } 267 | } 268 | 269 | fn project_root() -> PathBuf { 270 | Path::new(&env!("CARGO_MANIFEST_DIR")) 271 | .ancestors() 272 | .nth(1) 273 | .unwrap() 274 | .to_path_buf() 275 | } 276 | 277 | fn dist_dir(xtask_env: &XtaskEnv) -> PathBuf { 278 | let mut path_buf = project_root().join("target").join(DEFAULT_TARGET); 279 | path_buf = match xtask_env.compile_mode { 280 | CompileMode::Debug => path_buf.join("debug"), 281 | CompileMode::Release => path_buf.join("release"), 282 | }; 283 | path_buf 284 | } 285 | 286 | fn read_package_name(path: &Path) -> String { 287 | let path = path.join("Cargo.toml"); 288 | let buf = fs::read_to_string(path).expect("read package cargo toml file"); 289 | let cfg: PackageToml = toml::from_str(&buf).expect("deserialize package cargo toml"); 290 | cfg.package.name 291 | } 292 | 293 | #[derive(Debug, Deserialize, PartialEq)] 294 | struct PackageToml { 295 | package: Package, 296 | } 297 | 298 | #[derive(Debug, Deserialize, PartialEq)] 299 | struct Package { 300 | name: String, 301 | } 302 | 303 | fn default_kernel_path() -> String { 304 | let workspace_toml = project_root().join("Cargo.toml"); 305 | let buf = fs::read_to_string(workspace_toml).expect("read workspace cargo toml file"); 306 | let cfg: WorkspaceToml = toml::from_str(&buf).expect("deserialize workspace cargo toml"); 307 | cfg.workspace.metadata.xtask.default_kernel_path 308 | } 309 | 310 | #[derive(Debug, Deserialize, PartialEq)] 311 | struct WorkspaceToml { 312 | workspace: Workspace, 313 | } 314 | 315 | #[derive(Debug, Deserialize, PartialEq)] 316 | struct Workspace { 317 | metadata: WorkspaceMetadata 318 | } 319 | 320 | #[derive(Debug, Deserialize, PartialEq)] 321 | struct WorkspaceMetadata { 322 | xtask: XtaskMetadata, 323 | } 324 | 325 | #[derive(Debug, Deserialize, PartialEq)] 326 | struct XtaskMetadata { 327 | #[serde(rename = "default-kernel-path")] 328 | default_kernel_path: String, 329 | } 330 | --------------------------------------------------------------------------------