|
|
|
@ -1,67 +1,58 @@
|
|
|
|
|
use riscv::register::{
|
|
|
|
|
sstatus as xstatus,
|
|
|
|
|
sstatus::Sstatus as Xstatus,
|
|
|
|
|
mcause::Mcause,
|
|
|
|
|
sstatus,
|
|
|
|
|
sstatus::Sstatus,
|
|
|
|
|
scause::Scause,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Saved registers on a trap.
|
|
|
|
|
#[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
|
|
|
|
|
pub _hartid: usize, // reserve space
|
|
|
|
|
/// General registers
|
|
|
|
|
pub x: [usize; 32],
|
|
|
|
|
/// Supervisor Status
|
|
|
|
|
pub sstatus: Sstatus,
|
|
|
|
|
/// Supervisor Exception Program Counter
|
|
|
|
|
pub sepc: usize,
|
|
|
|
|
/// Supervisor Trap Value
|
|
|
|
|
pub stval: usize,
|
|
|
|
|
/// Supervisor Cause
|
|
|
|
|
pub scause: Scause,
|
|
|
|
|
/// Reserve space for hartid
|
|
|
|
|
pub _hartid: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
|
*/
|
|
|
|
|
/// Constructs TrapFrame for a new kernel thread.
|
|
|
|
|
///
|
|
|
|
|
/// The new thread starts at function `entry` with an usize argument `arg`.
|
|
|
|
|
/// The stack pointer will be set to `sp`.
|
|
|
|
|
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_spie(true);
|
|
|
|
|
tf.sstatus.set_sie(false);
|
|
|
|
|
tf.sstatus.set_spp(xstatus::SPP::Supervisor);
|
|
|
|
|
}
|
|
|
|
|
tf.sstatus = sstatus::read();
|
|
|
|
|
tf.sstatus.set_spie(true);
|
|
|
|
|
tf.sstatus.set_sie(false);
|
|
|
|
|
tf.sstatus.set_spp(sstatus::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
|
|
|
|
|
*/
|
|
|
|
|
/// Constructs TrapFrame for a new user thread.
|
|
|
|
|
///
|
|
|
|
|
/// The new thread starts at `entry_addr`.
|
|
|
|
|
/// The stack pointer will be set to `sp`.
|
|
|
|
|
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_spie(true);
|
|
|
|
|
tf.sstatus.set_sie(false);
|
|
|
|
|
tf.sstatus.set_spp(xstatus::SPP::User);
|
|
|
|
|
}
|
|
|
|
|
tf.sstatus = sstatus::read();
|
|
|
|
|
tf.sstatus.set_spie(true);
|
|
|
|
|
tf.sstatus.set_sie(false);
|
|
|
|
|
tf.sstatus.set_spp(sstatus::SPP::User);
|
|
|
|
|
tf
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -85,12 +76,12 @@ impl Debug for TrapFrame {
|
|
|
|
|
.field("sstatus", &self.sstatus)
|
|
|
|
|
.field("sepc", &self.sepc)
|
|
|
|
|
.field("stval", &self.stval)
|
|
|
|
|
.field("scause", &self.scause)
|
|
|
|
|
.field("scause", &self.scause.cause())
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// kernel stack contents for a new thread
|
|
|
|
|
/// Kernel stack contents for a new thread
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
pub struct InitStack {
|
|
|
|
@ -99,18 +90,11 @@ pub struct InitStack {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
/// Push the InitStack on the stack and transfer to a Context.
|
|
|
|
|
unsafe fn push_at(self, stack_top: usize) -> Context {
|
|
|
|
|
let ptr = (stack_top as *mut Self).offset(-1); //real kernel stack top
|
|
|
|
|
let ptr = (stack_top as *mut Self).sub(1); //real kernel stack top
|
|
|
|
|
*ptr = self;
|
|
|
|
|
Context(ptr as usize)
|
|
|
|
|
Context { sp: ptr as usize }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -118,24 +102,32 @@ extern {
|
|
|
|
|
fn trap_return();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Saved registers for kernel context switches.
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
struct ContextData {
|
|
|
|
|
/// Return address
|
|
|
|
|
ra: usize,
|
|
|
|
|
/// Page table token
|
|
|
|
|
satp: usize,
|
|
|
|
|
/// Callee-saved registers
|
|
|
|
|
s: [usize; 12],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ContextData {
|
|
|
|
|
fn new(satp: usize) -> Self {
|
|
|
|
|
// satp(asid) just like cr3, save the physical address for Page directory?
|
|
|
|
|
ContextData { ra: trap_return as usize, satp, ..ContextData::default() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A struct only contain one usize element
|
|
|
|
|
/// Context of a kernel thread.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct Context(usize);
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
pub struct Context {
|
|
|
|
|
/// The stack pointer of the suspended thread.
|
|
|
|
|
/// A `ContextData` is stored here.
|
|
|
|
|
sp: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Context {
|
|
|
|
|
/// Switch to another kernel thread.
|
|
|
|
@ -208,66 +200,43 @@ impl Context {
|
|
|
|
|
: : : : "volatile" )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* @brief:
|
|
|
|
|
* generate a null Context
|
|
|
|
|
* @retval:
|
|
|
|
|
* a null Context
|
|
|
|
|
*/
|
|
|
|
|
/// Constructs a null Context for the current running thread.
|
|
|
|
|
pub unsafe fn null() -> Self {
|
|
|
|
|
Context(0)
|
|
|
|
|
Context { sp: 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 {
|
|
|
|
|
/// Constructs Context for a new kernel thread.
|
|
|
|
|
///
|
|
|
|
|
/// The new thread starts at function `entry` with an usize argument `arg`.
|
|
|
|
|
/// The stack pointer will be set to `kstack_top`.
|
|
|
|
|
/// The SATP register will be set to `satp`.
|
|
|
|
|
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, satp: usize) -> Self {
|
|
|
|
|
InitStack {
|
|
|
|
|
context: ContextData::new(cr3),
|
|
|
|
|
context: ContextData::new(satp),
|
|
|
|
|
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 {
|
|
|
|
|
/// Constructs Context for a new user thread.
|
|
|
|
|
///
|
|
|
|
|
/// The new thread starts at `entry_addr`.
|
|
|
|
|
/// The stack pointer of user and kernel mode will be set to `ustack_top`, `kstack_top`.
|
|
|
|
|
/// The SATP register will be set to `satp`.
|
|
|
|
|
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, _is32: bool, satp: usize) -> Self {
|
|
|
|
|
InitStack {
|
|
|
|
|
context: ContextData::new(cr3),
|
|
|
|
|
context: ContextData::new(satp),
|
|
|
|
|
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 {
|
|
|
|
|
/// Fork a user process and get the new Context.
|
|
|
|
|
///
|
|
|
|
|
/// The stack pointer in kernel mode will be set to `kstack_top`.
|
|
|
|
|
/// The SATP register will be set to `satp`.
|
|
|
|
|
/// All the other registers are same as the original.
|
|
|
|
|
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, satp: usize) -> Self {
|
|
|
|
|
InitStack {
|
|
|
|
|
context: ContextData::new(cr3),
|
|
|
|
|
context: ContextData::new(satp),
|
|
|
|
|
tf: {
|
|
|
|
|
let mut tf = tf.clone();
|
|
|
|
|
// fork function's ret value, the new process is 0
|
|
|
|
@ -277,9 +246,16 @@ impl Context {
|
|
|
|
|
}.push_at(kstack_top)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub unsafe fn new_clone(tf: &TrapFrame, ustack_top: usize, kstack_top: usize, cr3: usize, tls: usize) -> Self {
|
|
|
|
|
/// Fork a user thread and get the new Context.
|
|
|
|
|
///
|
|
|
|
|
/// The stack pointer in kernel mode will be set to `kstack_top`.
|
|
|
|
|
/// The SATP register will be set to `satp`.
|
|
|
|
|
/// The new user stack will be set to `ustack_top`.
|
|
|
|
|
/// The new thread pointer will be set to `tls`.
|
|
|
|
|
/// All the other registers are same as the original.
|
|
|
|
|
pub unsafe fn new_clone(tf: &TrapFrame, ustack_top: usize, kstack_top: usize, satp: usize, tls: usize) -> Self {
|
|
|
|
|
InitStack {
|
|
|
|
|
context: ContextData::new(cr3),
|
|
|
|
|
context: ContextData::new(satp),
|
|
|
|
|
tf: {
|
|
|
|
|
let mut tf = tf.clone();
|
|
|
|
|
tf.x[2] = ustack_top; // sp
|
|
|
|
@ -290,9 +266,8 @@ impl Context {
|
|
|
|
|
}.push_at(kstack_top)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Called at a new user context
|
|
|
|
|
/// To get the init TrapFrame in sys_exec
|
|
|
|
|
/// Used for getting the init TrapFrame of a new user context in `sys_exec`.
|
|
|
|
|
pub unsafe fn get_init_tf(&self) -> TrapFrame {
|
|
|
|
|
(*(self.0 as *const InitStack)).tf.clone()
|
|
|
|
|
(*(self.sp as *const InitStack)).tf.clone()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|