impl M-mode kernel for riscv32

master
WangRunji 6 years ago
parent 2f8cfabbca
commit fcf5074500

@ -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)]

2
kernel/Cargo.lock generated

@ -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)",

@ -5,8 +5,13 @@ authors = ["Runji Wang <wangrunji0408@163.com>"]
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]

@ -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

@ -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

@ -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()
}

@ -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;
}

@ -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

@ -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"));
/// 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() };
}

@ -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();

@ -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 {

@ -1 +1 @@
Subproject commit 98d2ffbb3203e1d9a936b067c51f8522a5ff9677
Subproject commit 71b2addd7ada2f07ca8e6f02787d706afa4fbe66
Loading…
Cancel
Save