From fcf5074500551efc00c942f4e00307ce33816463 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sat, 1 Dec 2018 17:37:37 +0800 Subject: [PATCH] impl M-mode kernel for riscv32 --- crate/process/src/interrupt.rs | 28 +++++-- kernel/Cargo.lock | 2 +- kernel/Cargo.toml | 5 ++ kernel/Makefile | 12 +-- kernel/src/arch/riscv32/boot/trap.asm | 30 ++++--- kernel/src/arch/riscv32/context.rs | 46 +++++++---- kernel/src/arch/riscv32/interrupt.rs | 115 +++++++++----------------- kernel/src/arch/riscv32/io.rs | 30 ++++--- kernel/src/arch/riscv32/mod.rs | 46 ++++++++++- kernel/src/arch/riscv32/timer.rs | 3 + kernel/src/fs.rs | 8 ++ riscv-pk | 2 +- 12 files changed, 193 insertions(+), 134 deletions(-) diff --git a/crate/process/src/interrupt.rs b/crate/process/src/interrupt.rs index 17d62ce..c33be0c 100644 --- a/crate/process/src/interrupt.rs +++ b/crate/process/src/interrupt.rs @@ -6,14 +6,6 @@ pub unsafe fn disable_and_store() -> usize { rflags & (1 << 9) } -#[inline(always)] -#[cfg(target_arch = "riscv32")] -pub unsafe fn disable_and_store() -> usize { - let sstatus: usize; - asm!("csrrci $0, 0x100, 2" : "=r"(sstatus)); - sstatus & 2 -} - #[inline(always)] #[cfg(target_arch = "x86_64")] pub unsafe fn restore(flags: usize) { @@ -22,10 +14,28 @@ pub unsafe fn restore(flags: usize) { } } +#[inline(always)] +#[cfg(target_arch = "riscv32")] +pub unsafe fn disable_and_store() -> usize { + if option_env!("m_mode").is_some() { + let mstatus: usize; + asm!("csrrci $0, 0x300, 1 << 3" : "=r"(mstatus)); + mstatus & (1 << 3) + } else { + let sstatus: usize; + asm!("csrrci $0, 0x100, 1 << 1" : "=r"(sstatus)); + sstatus & (1 << 1) + } +} + #[inline(always)] #[cfg(target_arch = "riscv32")] pub unsafe fn restore(flags: usize) { - asm!("csrs 0x100, $0" :: "r"(flags)); + if option_env!("m_mode").is_some() { + asm!("csrs 0x300, $0" :: "r"(flags)); + } else { + asm!("csrs 0x100, $0" :: "r"(flags)); + } } #[inline(always)] diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 5a0a2b9..91d5bfd 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "riscv" version = "0.3.0" -source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#f358204af01f2374ab6ed6ea059f724cd5f2fe6f" +source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#966eb26d5e8d77677f645d5e32877c678dcee572" dependencies = [ "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8f3f947..00fcf23 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,8 +5,13 @@ authors = ["Runji Wang "] edition = "2018" [features] +# Without BBL (for riscv32 FPGA board) no_bbl = [] +# Disable paging (for riscv32) no_mmu = [] +# Kernel in M-mode (for riscv32) +m_mode = ["no_mmu"] +# (for aarch64 RaspberryPi3) board_raspi3 = [] [profile.dev] diff --git a/kernel/Makefile b/kernel/Makefile index 1deeeac..5ab7d19 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -15,14 +15,16 @@ # smp = 1 | 2 | ... SMP core number # board = fpga Only available on riscv32, build without bbl, run on board # | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+ -# nommu Only available on riscv32, build for M-Mode, without MMU +# m_mode Only available on riscv32, build for M-Mode, without MMU arch ?= riscv32 board ?= raspi3 mode ?= debug LOG ?= debug smp ?= 4 -nommu ?= +# NOTE: crate 'process' use this name 'm_mode' as an environment +# to set interrupt (MIE or SIE) +m_mode ?= target := $(arch)-blog_os kernel := target/$(target)/$(mode)/ucore @@ -45,7 +47,7 @@ else ifeq ($(arch), riscv32) qemu_opts += \ -machine virt \ -kernel $(bin) -ifdef nommu +ifdef m_mode qemu_opts += -cpu rv32imacu-nommu endif @@ -67,8 +69,8 @@ features += no_bbl endif endif -ifdef nommu -features += no_mmu +ifdef m_mode +features += no_mmu m_mode bbl_m_mode := --enable-boot-machine endif diff --git a/kernel/src/arch/riscv32/boot/trap.asm b/kernel/src/arch/riscv32/boot/trap.asm index bf4aba5..0f3bc3d 100644 --- a/kernel/src/arch/riscv32/boot/trap.asm +++ b/kernel/src/arch/riscv32/boot/trap.asm @@ -1,11 +1,19 @@ +# Constants / Macros defined in Rust code: +# xscratch +# xstatus +# xepc +# xcause +# xtval +# XRET + .macro SAVE_ALL # If coming from userspace, preserve the user stack pointer and load # the kernel stack pointer. If we came from the kernel, sscratch # will contain 0, and we should continue on the current stack. - csrrw sp, 0x140, sp # sscratch + csrrw sp, (xscratch), sp bnez sp, _save_context _restore_kernel_sp: - csrr sp, 0x140 # sscratch + csrr sp, (xscratch) # sscratch = previous-sp, sp = kernel-sp _save_context: # provide room for trap frame @@ -45,11 +53,11 @@ _save_context: # get sp, sstatus, sepc, stval, scause # set sscratch = 0 - csrrw s0, 0x140, x0 # sscratch - csrr s1, 0x100 # sstatus - csrr s2, 0x141 # sepc - csrr s3, 0x143 # stval - csrr s4, 0x142 # scause + csrrw s0, (xscratch), x0 + csrr s1, (xstatus) + csrr s2, (xepc) + csrr s3, (xtval) + csrr s4, (xcause) # store sp, sstatus, sepc, sbadvaddr, scause sw s0, 2*4(sp) sw s1, 32*4(sp) @@ -65,11 +73,11 @@ _save_context: bnez s0, _restore_context # back to S-mode? (sstatus.SPP = 1) _save_kernel_sp: addi s0, sp, 36*4 - csrw 0x140, s0 # sscratch = kernel-sp + csrw (xscratch), s0 # sscratch = kernel-sp _restore_context: # restore sstatus, sepc - csrw 0x100, s1 - csrw 0x141, s2 + csrw (xstatus), s1 + csrw (xepc), s2 # restore x registers except x2 (sp) lw x1, 1*4(sp) @@ -116,4 +124,4 @@ __alltraps: __trapret: RESTORE_ALL # return from supervisor call - sret + XRET diff --git a/kernel/src/arch/riscv32/context.rs b/kernel/src/arch/riscv32/context.rs index 56f806c..11c657b 100644 --- a/kernel/src/arch/riscv32/context.rs +++ b/kernel/src/arch/riscv32/context.rs @@ -1,13 +1,24 @@ -use riscv::register::*; +#[cfg(feature = "m_mode")] +use riscv::register::{ + mstatus as xstatus, + mstatus::Mstatus as Xstatus, + mcause::Mcause, +}; +#[cfg(not(feature = "m_mode"))] +use riscv::register::{ + sstatus as xstatus, + sstatus::Sstatus as Xstatus, + mcause::Mcause, +}; #[derive(Clone)] #[repr(C)] pub struct TrapFrame { pub x: [usize; 32], // general registers - pub sstatus: sstatus::Sstatus, // Supervisor Status Register + pub sstatus: Xstatus, // Supervisor Status Register pub sepc: usize, // Supervisor exception program counter, save the trap virtual address (here is used to save the process program entry addr?) - pub sbadaddr: usize, // Supervisor bad address - pub scause: scause::Scause, // scause register: record the cause of exception/interrupt/trap + pub stval: usize, // Supervisor trap value + pub scause: Mcause, // scause register: record the cause of exception/interrupt/trap } /// Generate the trapframe for building new thread in kernel @@ -28,13 +39,13 @@ impl TrapFrame { tf.x[10] = arg; // a0 tf.x[2] = sp; tf.sepc = entry as usize; - tf.sstatus = sstatus::read(); - // Supervisor Previous Interrupt Enable - tf.sstatus.set_spie(true); - // Supervisor Interrupt Disable - tf.sstatus.set_sie(false); - // Supervisor Previous Privilege Mode is Supervisor - tf.sstatus.set_spp(sstatus::SPP::Supervisor); + tf.sstatus = xstatus::read(); + tf.sstatus.set_xpie(true); + tf.sstatus.set_xie(false); + #[cfg(feature = "m_mode")] + tf.sstatus.set_mpp(xstatus::MPP::Machine); + #[cfg(not(feature = "m_mode"))] + tf.sstatus.set_spp(xstatus::SPP::Supervisor); tf } @@ -52,10 +63,13 @@ impl TrapFrame { let mut tf: Self = unsafe { zeroed() }; tf.x[2] = sp; tf.sepc = entry_addr; - tf.sstatus = sstatus::read(); - tf.sstatus.set_spie(true); - tf.sstatus.set_sie(false); - tf.sstatus.set_spp(sstatus::SPP::User); + tf.sstatus = xstatus::read(); + tf.sstatus.set_xpie(true); + tf.sstatus.set_xie(false); + #[cfg(feature = "m_mode")] + tf.sstatus.set_mpp(xstatus::MPP::User); + #[cfg(not(feature = "m_mode"))] + tf.sstatus.set_spp(xstatus::SPP::User); tf } } @@ -78,7 +92,7 @@ impl Debug for TrapFrame { .field("regs", &Regs(&self.x)) .field("sstatus", &self.sstatus) .field("sepc", &self.sepc) - .field("sbadaddr", &self.sbadaddr) + .field("stval", &self.stval) .field("scause", &self.scause) .finish() } diff --git a/kernel/src/arch/riscv32/interrupt.rs b/kernel/src/arch/riscv32/interrupt.rs index 4d253fc..22a3674 100644 --- a/kernel/src/arch/riscv32/interrupt.rs +++ b/kernel/src/arch/riscv32/interrupt.rs @@ -1,4 +1,16 @@ -use riscv::register::*; +#[cfg(feature = "m_mode")] +use riscv::register::{ + mstatus as xstatus, + mscratch as xscratch, + mtvec as xtvec, +}; +#[cfg(not(feature = "m_mode"))] +use riscv::register::{ + sstatus as xstatus, + sscratch as xscratch, + stvec as xtvec, +}; +use riscv::register::{mcause, mepc, sie}; pub use self::context::*; use crate::memory::{MemorySet, InactivePageTable0}; use log::*; @@ -17,13 +29,16 @@ pub fn init() { unsafe { // Set sscratch register to 0, indicating to exception vector that we are // presently executing in the kernel - sscratch::write(0); + xscratch::write(0); // Set the exception vector address - stvec::write(__alltraps as usize, stvec::TrapMode::Direct); + xtvec::write(__alltraps as usize, xtvec::TrapMode::Direct); // Enable IPI sie::set_ssoft(); // Enable serial interrupt sie::set_sext(); + // NOTE: In M-mode: mie.MSIE is set by BBL. + // mie.MEIE can not be set in QEMU v3.0 + // (seems like a bug) } info!("interrupt: init end"); } @@ -34,7 +49,7 @@ pub fn init() { */ #[inline(always)] pub unsafe fn enable() { - sstatus::set_sie(); + xstatus::set_xie(); } /* @@ -45,8 +60,8 @@ pub unsafe fn enable() { */ #[inline(always)] pub unsafe fn disable_and_store() -> usize { - let e = sstatus::read().sie() as usize; - sstatus::clear_sie(); + let e = xstatus::read().xie() as usize; + xstatus::clear_xie(); e } @@ -59,7 +74,7 @@ pub unsafe fn disable_and_store() -> usize { #[inline(always)] pub unsafe fn restore(flags: usize) { if flags != 0 { - sstatus::set_sie(); + xstatus::set_xie(); } } @@ -71,9 +86,15 @@ pub unsafe fn restore(flags: usize) { */ #[no_mangle] pub extern fn rust_trap(tf: &mut TrapFrame) { - use riscv::register::scause::{Trap, Interrupt as I, Exception as E}; + use self::mcause::{Trap, Interrupt as I, Exception as E}; trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), tf.scause.cause()); match tf.scause.cause() { + // M-mode only + Trap::Interrupt(I::MachineExternal) => serial(), + Trap::Interrupt(I::MachineSoft) => ipi(), + Trap::Interrupt(I::MachineTimer) => timer(), + Trap::Exception(E::MachineEnvCall) => sbi(tf), + Trap::Interrupt(I::SupervisorExternal) => serial(), Trap::Interrupt(I::SupervisorSoft) => ipi(), Trap::Interrupt(I::SupervisorTimer) => timer(), @@ -87,6 +108,12 @@ pub extern fn rust_trap(tf: &mut TrapFrame) { trace!("Interrupt end"); } +/// Call BBL SBI functions for M-mode kernel +fn sbi(tf: &mut TrapFrame) { + (super::BBL.mcall_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc); + tf.sepc += 4; +} + fn serial() { crate::trap::serial(super::io::getchar()); } @@ -101,8 +128,8 @@ fn ipi() { * process timer interrupt */ fn timer() { - crate::trap::timer(); super::timer::set_next(); + crate::trap::timer(); } /* @@ -124,9 +151,8 @@ fn syscall(tf: &mut TrapFrame) { * process IllegalInstruction exception */ fn illegal_inst(tf: &mut TrapFrame) { - if !emulate_mul_div(tf) { - crate::trap::error(tf); - } + (super::BBL.illegal_insn_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc); + tf.sepc = mepc::read(); } /* @@ -136,73 +162,10 @@ fn illegal_inst(tf: &mut TrapFrame) { * process page fault exception */ fn page_fault(tf: &mut TrapFrame) { - let addr = stval::read(); + let addr = tf.stval; trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); if !crate::memory::page_fault_handler(addr) { crate::trap::error(tf); } } - -/// Migrate from riscv-pk -/* -* @param: -* TrapFrame: the Trapframe for the illegal inst exception -* @brief: -* emulate the multiply and divide operation (if not this kind of operation return false) -* @retval: -* a bool indicates whether emulate the multiply and divide operation successfully -*/ -fn emulate_mul_div(tf: &mut TrapFrame) -> bool { - let insn = unsafe { *(tf.sepc as *const usize) }; - let rs1 = tf.x[get_reg(insn, RS1)]; - let rs2 = tf.x[get_reg(insn, RS2)]; - - let rd = if (insn & MASK_MUL) == MATCH_MUL { - rs1 * rs2 - } else if (insn & MASK_DIV) == MATCH_DIV { - ((rs1 as i32) / (rs2 as i32)) as usize - } else if (insn & MASK_DIVU) == MATCH_DIVU { - rs1 / rs2 - } else if (insn & MASK_REM) == MATCH_REM { - ((rs1 as i32) % (rs2 as i32)) as usize - } else if (insn & MASK_REMU) == MATCH_REMU { - rs1 % rs2 - } else if (insn & MASK_MULH) == MATCH_MULH { - (((rs1 as i32 as i64) * (rs2 as i32 as i64)) >> 32) as usize - } else if (insn & MASK_MULHU) == MATCH_MULHU { - (((rs1 as i64) * (rs2 as i64)) >> 32) as usize - } else if (insn & MASK_MULHSU) == MATCH_MULHSU { - (((rs1 as i32 as i64) * (rs2 as i64)) >> 32) as usize - } else { - return false; - }; - tf.x[get_reg(insn, RD)] = rd; - tf.sepc += 4; // jump to next instruction - return true; - - fn get_reg(inst: usize, offset: usize) -> usize { - (inst >> offset) & 0x1f - } - - const RS1: usize = 15; - const RS2: usize = 20; - const RD: usize = 7; - - const MATCH_MUL: usize = 0x2000033; - const MASK_MUL: usize = 0xfe00707f; - const MATCH_MULH: usize = 0x2001033; - const MASK_MULH: usize = 0xfe00707f; - const MATCH_MULHSU: usize = 0x2002033; - const MASK_MULHSU: usize = 0xfe00707f; - const MATCH_MULHU: usize = 0x2003033; - const MASK_MULHU: usize = 0xfe00707f; - const MATCH_DIV: usize = 0x2004033; - const MASK_DIV: usize = 0xfe00707f; - const MATCH_DIVU: usize = 0x2005033; - const MASK_DIVU: usize = 0xfe00707f; - const MATCH_REM: usize = 0x2006033; - const MASK_REM: usize = 0xfe00707f; - const MATCH_REMU: usize = 0x2007033; - const MASK_REMU: usize = 0xfe00707f; -} \ No newline at end of file diff --git a/kernel/src/arch/riscv32/io.rs b/kernel/src/arch/riscv32/io.rs index 09e946e..b2c49d2 100644 --- a/kernel/src/arch/riscv32/io.rs +++ b/kernel/src/arch/riscv32/io.rs @@ -20,23 +20,29 @@ impl Write for SerialPort { } fn putchar(c: u8) { - #[cfg(feature = "no_bbl")] - unsafe { - while read_volatile(STATUS) & CAN_WRITE == 0 {} - write_volatile(DATA, c as u8); + if cfg!(feature = "no_bbl") { + unsafe { + while read_volatile(STATUS) & CAN_WRITE == 0 {} + write_volatile(DATA, c as u8); + } + } else if cfg!(feature = "m_mode") { + (super::BBL.mcall_console_putchar)(c); + } else { + sbi::console_putchar(c as usize); } - #[cfg(not(feature = "no_bbl"))] - sbi::console_putchar(c as usize); } pub fn getchar() -> char { - #[cfg(feature = "no_bbl")] - let c = unsafe { - while read_volatile(STATUS) & CAN_READ == 0 {} - read_volatile(DATA) + let c = if cfg!(feature = "no_bbl") { + unsafe { + // while read_volatile(STATUS) & CAN_READ == 0 {} + read_volatile(DATA) + } + } else if cfg!(feature = "m_mode") { + (super::BBL.mcall_console_getchar)() as u8 + } else { + sbi::console_getchar() as u8 }; - #[cfg(not(feature = "no_bbl"))] - let c = sbi::console_getchar() as u8; match c { 255 => '\0', // null diff --git a/kernel/src/arch/riscv32/mod.rs b/kernel/src/arch/riscv32/mod.rs index 549fbc2..eb2a17a 100644 --- a/kernel/src/arch/riscv32/mod.rs +++ b/kernel/src/arch/riscv32/mod.rs @@ -8,9 +8,10 @@ pub mod consts; pub mod cpu; #[no_mangle] -pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize) -> ! { +pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize, functions: usize) -> ! { unsafe { cpu::set_cpu_id(hartid); } - println!("Hello RISCV! in hart {}, {}, {}", hartid, dtb, hart_mask); + unsafe { BBL_FUNCTIONS_PTR = functions as *const _; } + println!("Hello RISCV! in hart {}, dtb @ {:#x}, functions @ {:#x}", hartid, dtb, functions); if hartid != 0 { while unsafe { !cpu::has_started(hartid) } { } @@ -35,7 +36,46 @@ fn others_main() -> ! { crate::kmain(); } + +/// Constant & Macro for `trap.asm` +#[cfg(feature = "m_mode")] +global_asm!(" + .equ xstatus, 0x300 + .equ xscratch, 0x340 + .equ xepc, 0x341 + .equ xcause, 0x342 + .equ xtval, 0x343 + .macro XRET\n mret\n .endm +"); +#[cfg(not(feature = "m_mode"))] +global_asm!(" + .equ xstatus, 0x100 + .equ xscratch, 0x140 + .equ xepc, 0x141 + .equ xcause, 0x142 + .equ xtval, 0x143 + .macro XRET\n sret\n .endm +"); + #[cfg(feature = "no_bbl")] global_asm!(include_str!("boot/boot.asm")); global_asm!(include_str!("boot/entry.asm")); -global_asm!(include_str!("boot/trap.asm")); \ No newline at end of file +global_asm!(include_str!("boot/trap.asm")); + + +/// Some symbols passed from BBL. +/// Used in M-mode kernel. +#[repr(C)] +struct BBLFunctions { + mcall_trap: BBLTrapHandler, + illegal_insn_trap: BBLTrapHandler, + mcall_console_putchar: extern fn(u8), + mcall_console_getchar: extern fn() -> usize, +} + +type BBLTrapHandler = extern fn(regs: *const usize, mcause: usize, mepc: usize); +static mut BBL_FUNCTIONS_PTR: *const BBLFunctions = ::core::ptr::null(); +use lazy_static::lazy_static; +lazy_static! { + static ref BBL: BBLFunctions = unsafe { BBL_FUNCTIONS_PTR.read() }; +} \ No newline at end of file diff --git a/kernel/src/arch/riscv32/timer.rs b/kernel/src/arch/riscv32/timer.rs index c6e5e2b..7588deb 100644 --- a/kernel/src/arch/riscv32/timer.rs +++ b/kernel/src/arch/riscv32/timer.rs @@ -33,6 +33,9 @@ pub fn get_cycle() -> u64 { */ pub fn init() { // Enable supervisor timer interrupt + #[cfg(feature = "m_mode")] + unsafe { mie::set_mtimer(); } + #[cfg(not(feature = "m_mode"))] unsafe { sie::set_stimer(); } set_next(); diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 3779ab5..7ef2208 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -88,6 +88,14 @@ impl Stdin { self.pushed.notify_one(); } pub fn pop(&self) -> char { + // QEMU v3.0 don't support M-mode external interrupt (bug?) + // So we have to use polling. + #[cfg(feature = "m_mode")] + loop { + let c = crate::arch::io::getchar(); + if c != '\0' { return c; } + } + #[cfg(not(feature = "m_mode"))] loop { let ret = self.buf.lock().pop_front(); match ret { diff --git a/riscv-pk b/riscv-pk index 98d2ffb..71b2add 160000 --- a/riscv-pk +++ b/riscv-pk @@ -1 +1 @@ -Subproject commit 98d2ffbb3203e1d9a936b067c51f8522a5ff9677 +Subproject commit 71b2addd7ada2f07ca8e6f02787d706afa4fbe66