You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.1 KiB
307 lines
9.1 KiB
#[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: 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 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
|
|
impl TrapFrame {
|
|
/*
|
|
* @param:
|
|
* entry: program entry for the thread
|
|
* arg: a0
|
|
* sp: stack top
|
|
* @brief:
|
|
* generate a trapfram for building a new kernel thread
|
|
* @retval:
|
|
* the trapframe for new kernel thread
|
|
*/
|
|
fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, sp: usize) -> Self {
|
|
use core::mem::zeroed;
|
|
let mut tf: Self = unsafe { zeroed() };
|
|
tf.x[10] = arg; // a0
|
|
tf.x[2] = sp;
|
|
tf.sepc = entry as usize;
|
|
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
|
|
}
|
|
|
|
/*
|
|
* @param:
|
|
* entry_addr: program entry for the thread
|
|
* sp: stack top
|
|
* @brief:
|
|
* generate a trapfram for building a new user thread
|
|
* @retval:
|
|
* the trapframe for new user thread
|
|
*/
|
|
fn new_user_thread(entry_addr: usize, sp: usize) -> Self {
|
|
use core::mem::zeroed;
|
|
let mut tf: Self = unsafe { zeroed() };
|
|
tf.x[2] = sp;
|
|
tf.sepc = entry_addr;
|
|
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
|
|
}
|
|
}
|
|
|
|
use core::fmt::{Debug, Formatter, Error};
|
|
impl Debug for TrapFrame {
|
|
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
|
struct Regs<'a>(&'a [usize; 32]);
|
|
impl<'a> Debug for Regs<'a> {
|
|
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
|
const REG_NAME: [&str; 32] = [
|
|
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
|
|
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
|
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
|
"t3", "t4", "t5", "t6"];
|
|
f.debug_map().entries(REG_NAME.iter().zip(self.0)).finish()
|
|
}
|
|
}
|
|
f.debug_struct("TrapFrame")
|
|
.field("regs", &Regs(&self.x))
|
|
.field("sstatus", &self.sstatus)
|
|
.field("sepc", &self.sepc)
|
|
.field("stval", &self.stval)
|
|
.field("scause", &self.scause)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// kernel stack contents for a new thread
|
|
#[derive(Debug)]
|
|
#[repr(C)]
|
|
pub struct InitStack {
|
|
context: ContextData,
|
|
tf: TrapFrame,
|
|
}
|
|
|
|
impl InitStack {
|
|
/*
|
|
* @param:
|
|
* stack_top: the pointer to kernel stack stop
|
|
* @brief:
|
|
* save the InitStack on the kernel stack stop
|
|
* @retval:
|
|
* a Context with ptr in it
|
|
*/
|
|
unsafe fn push_at(self, stack_top: usize) -> Context {
|
|
let ptr = (stack_top as *mut Self).offset(-1); //real kernel stack top
|
|
*ptr = self;
|
|
Context(ptr as usize)
|
|
}
|
|
}
|
|
|
|
extern {
|
|
fn __trapret();
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
#[repr(C)]
|
|
struct ContextData {
|
|
ra: usize,
|
|
satp: usize,
|
|
s: [usize; 12],
|
|
}
|
|
|
|
impl ContextData {
|
|
fn new(satp: usize) -> Self {
|
|
// satp(asid) just like cr3, save the physical address for Page directory?
|
|
ContextData { ra: __trapret as usize, satp, ..ContextData::default() }
|
|
}
|
|
}
|
|
|
|
/// A struct only contain one usize element
|
|
#[derive(Debug)]
|
|
pub struct Context(usize);
|
|
|
|
impl Context {
|
|
/// Switch to another kernel thread.
|
|
///
|
|
/// Push all callee-saved registers at the current kernel stack.
|
|
/// Store current sp, switch to target.
|
|
/// Pop all callee-saved registers, then return to the target.
|
|
#[naked]
|
|
#[inline(never)]
|
|
pub unsafe extern fn switch(&mut self, _target: &mut Self) {
|
|
#[cfg(target_arch = "riscv32")]
|
|
asm!(r"
|
|
.equ XLENB, 4
|
|
.macro Load reg, mem
|
|
lw \reg, \mem
|
|
.endm
|
|
.macro Store reg, mem
|
|
sw \reg, \mem
|
|
.endm");
|
|
#[cfg(target_arch = "riscv64")]
|
|
asm!(r"
|
|
.equ XLENB, 8
|
|
.macro Load reg, mem
|
|
ld \reg, \mem
|
|
.endm
|
|
.macro Store reg, mem
|
|
sd \reg, \mem
|
|
.endm");
|
|
asm!("
|
|
// save from's registers
|
|
addi sp, sp, (-XLENB*14)
|
|
Store sp, 0(a0)
|
|
Store ra, 0*XLENB(sp)
|
|
Store s0, 2*XLENB(sp)
|
|
Store s1, 3*XLENB(sp)
|
|
Store s2, 4*XLENB(sp)
|
|
Store s3, 5*XLENB(sp)
|
|
Store s4, 6*XLENB(sp)
|
|
Store s5, 7*XLENB(sp)
|
|
Store s6, 8*XLENB(sp)
|
|
Store s7, 9*XLENB(sp)
|
|
Store s8, 10*XLENB(sp)
|
|
Store s9, 11*XLENB(sp)
|
|
Store s10, 12*XLENB(sp)
|
|
Store s11, 13*XLENB(sp)
|
|
csrr s11, satp
|
|
Store s11, 1*XLENB(sp)
|
|
|
|
// restore to's registers
|
|
Load sp, 0(a1)
|
|
Load s11, 1*XLENB(sp)
|
|
csrw satp, s11
|
|
Load ra, 0*XLENB(sp)
|
|
Load s0, 2*XLENB(sp)
|
|
Load s1, 3*XLENB(sp)
|
|
Load s2, 4*XLENB(sp)
|
|
Load s3, 5*XLENB(sp)
|
|
Load s4, 6*XLENB(sp)
|
|
Load s5, 7*XLENB(sp)
|
|
Load s6, 8*XLENB(sp)
|
|
Load s7, 9*XLENB(sp)
|
|
Load s8, 10*XLENB(sp)
|
|
Load s9, 11*XLENB(sp)
|
|
Load s10, 12*XLENB(sp)
|
|
Load s11, 13*XLENB(sp)
|
|
addi sp, sp, (XLENB*14)
|
|
|
|
Store zero, 0(a1)
|
|
ret"
|
|
: : : : "volatile" )
|
|
}
|
|
|
|
/*
|
|
* @brief:
|
|
* generate a null Context
|
|
* @retval:
|
|
* a null Context
|
|
*/
|
|
pub unsafe fn null() -> Self {
|
|
Context(0)
|
|
}
|
|
|
|
/*
|
|
* @param:
|
|
* entry: program entry for the thread
|
|
* arg: a0
|
|
* kstack_top: kernel stack top
|
|
* cr3: cr3 register, save the physical address of Page directory
|
|
* @brief:
|
|
* generate the content of kernel stack for the new kernel thread and save it's address at kernel stack top - 1
|
|
* @retval:
|
|
* a Context struct with the pointer to the kernel stack top - 1 as its only element
|
|
*/
|
|
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, cr3: usize) -> Self {
|
|
InitStack {
|
|
context: ContextData::new(cr3),
|
|
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
|
|
}.push_at(kstack_top)
|
|
}
|
|
|
|
/*
|
|
* @param:
|
|
* entry_addr: program entry for the thread
|
|
* ustack_top: user stack top
|
|
* kstack_top: kernel stack top
|
|
* is32: whether the cpu is 32 bit or not
|
|
* cr3: cr3 register, save the physical address of Page directory
|
|
* @brief:
|
|
* generate the content of kernel stack for the new user thread and save it's address at kernel stack top - 1
|
|
* @retval:
|
|
* a Context struct with the pointer to the kernel stack top - 1 as its only element
|
|
*/
|
|
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, _is32: bool, cr3: usize) -> Self {
|
|
InitStack {
|
|
context: ContextData::new(cr3),
|
|
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
|
|
}.push_at(kstack_top)
|
|
}
|
|
|
|
/*
|
|
* @param:
|
|
* TrapFrame: the trapframe of the forked process(thread)
|
|
* kstack_top: kernel stack top
|
|
* cr3: cr3 register, save the physical address of Page directory
|
|
* @brief:
|
|
* fork and generate a new process(thread) Context according to the TrapFrame and save it's address at kernel stack top - 1
|
|
* @retval:
|
|
* a Context struct with the pointer to the kernel stack top - 1 as its only element
|
|
*/
|
|
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, cr3: usize) -> Self {
|
|
InitStack {
|
|
context: ContextData::new(cr3),
|
|
tf: {
|
|
let mut tf = tf.clone();
|
|
// fork function's ret value, the new process is 0
|
|
tf.x[10] = 0; // a0
|
|
tf
|
|
},
|
|
}.push_at(kstack_top)
|
|
}
|
|
|
|
pub unsafe fn new_clone(tf: &TrapFrame, ustack_top: usize, kstack_top: usize, cr3: usize, tls: usize) -> Self {
|
|
InitStack {
|
|
context: ContextData::new(cr3),
|
|
tf: {
|
|
let mut tf = tf.clone();
|
|
tf.x[2] = ustack_top; // sp
|
|
tf.x[4] = tls; // tp
|
|
tf.x[10] = 0; // a0
|
|
tf
|
|
},
|
|
}.push_at(kstack_top)
|
|
}
|
|
|
|
/// Called at a new user context
|
|
/// To get the init TrapFrame in sys_exec
|
|
pub unsafe fn get_init_tf(&self) -> TrapFrame {
|
|
(*(self.0 as *const InitStack)).tf.clone()
|
|
}
|
|
}
|