pebstwck3/kernel/src/arch/mipsel/context.rs

310 lines
8.1 KiB

use mips::registers::cp0;
use mips::tlb;
/// Saved registers on a trap.
#[derive(Clone)]
#[repr(C)]
pub struct TrapFrame {
/// Non-zero if the kernel stack is not 16-byte-aligned
pub unaligned_kstack: usize,
/// unused 12 bytes
pub unused: [usize; 3],
/// CP0 status register
pub status: cp0::status::Status,
/// CP0 cause register
pub cause: cp0::cause::Cause,
/// CP0 EPC register
pub epc: usize,
/// CP0 vaddr register
pub vaddr: usize,
/// HI/LO registers
pub hi: usize,
pub lo: usize,
/// General registers
pub at: usize,
pub v0: usize,
pub v1: usize,
pub a0: usize,
pub a1: usize,
pub a2: usize,
pub a3: usize,
pub t0: usize,
pub t1: usize,
pub t2: usize,
pub t3: usize,
pub t4: usize,
pub t5: usize,
pub t6: usize,
pub t7: usize,
pub s0: usize,
pub s1: usize,
pub s2: usize,
pub s3: usize,
pub s4: usize,
pub s5: usize,
pub s6: usize,
pub s7: usize,
pub t8: usize,
pub t9: usize,
pub k0: usize,
pub k1: usize,
pub gp: usize,
pub sp: usize,
pub fp: usize,
pub ra: usize,
/// Floating-point registers (contains garbage if no FP support present)
pub f0: usize,
pub f1: usize,
pub f2: usize,
pub f3: usize,
pub f4: usize,
pub f5: usize,
pub f6: usize,
pub f7: usize,
pub f8: usize,
pub f9: usize,
pub f10: usize,
pub f11: usize,
pub f12: usize,
pub f13: usize,
pub f14: usize,
pub f15: usize,
pub f16: usize,
pub f17: usize,
pub f18: usize,
pub f19: usize,
pub f20: usize,
pub f21: usize,
pub f22: usize,
pub f23: usize,
pub f24: usize,
pub f25: usize,
pub f26: usize,
pub f27: usize,
pub f28: usize,
pub f29: usize,
pub f30: usize,
pub f31: usize,
/// Reserved
pub reserved: usize,
pub __padding: [usize; 2],
}
impl TrapFrame {
/// 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 "C" fn(usize) -> !, arg: usize, sp: usize) -> Self {
use core::mem::zeroed;
let mut tf: Self = unsafe { zeroed() };
tf.a0 = arg;
tf.sp = sp;
tf.epc = entry as usize;
tf.status = cp0::status::read();
tf.status.set_kernel_mode();
tf.status.set_ie();
tf.status.set_exl();
tf
}
/// Constructs TrapFrame for a new user thread.
///
/// The new thread starts at `entry_addr`.
/// The stack pointer will be set to `sp`.
pub fn new_user_thread(entry_addr: usize, sp: usize) -> Self {
use core::mem::zeroed;
let mut tf: Self = unsafe { zeroed() };
tf.sp = sp;
tf.epc = entry_addr;
tf.status = cp0::status::read();
tf.status.set_user_mode();
tf.status.set_ie();
tf.status.set_exl();
tf
}
}
use core::fmt::{Debug, Error, Formatter};
impl Debug for TrapFrame {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
f.debug_struct("TrapFrame")
.field("status", &self.status.bits)
.field("epc", &self.epc)
.field("cause", &self.cause.bits)
.field("vaddr", &self.vaddr)
.finish()
}
}
/// Kernel stack contents for a new thread
#[derive(Debug)]
#[repr(C)]
pub struct InitStack {
context: ContextData,
tf: TrapFrame,
}
impl InitStack {
/// 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).sub(1); //real kernel stack top
*ptr = self;
Context { sp: ptr as usize }
}
}
extern "C" {
fn trap_return();
fn _cur_tls();
}
/// Saved registers for kernel context switches.
#[derive(Debug, Default)]
#[repr(C)]
struct ContextData {
/// Return address
ra: usize,
/// Page table token
satp: usize,
/// s[0] = TLS
/// s[1] = reserved
/// s[2..11] = Callee-saved registers
s: [usize; 12],
__padding: [usize; 2],
}
impl ContextData {
fn new(satp: usize, tls: usize) -> Self {
let mut context = ContextData {
ra: trap_return as usize,
satp: satp,
..ContextData::default()
};
context.s[0] = tls;
context
}
}
/// Context of a kernel thread.
#[derive(Debug)]
#[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.
///
/// 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.
#[inline(always)]
pub unsafe fn switch(&mut self, target: &mut Self) {
extern "C" {
fn switch_context(src: *mut Context, dst: *mut Context);
}
tlb::clear_all_tlb();
switch_context(self as *mut Context, target as *mut Context);
}
/// Constructs a null Context for the current running thread.
pub unsafe fn null() -> Self {
Context { sp: 0 }
}
/// 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 "C" fn(usize) -> !,
arg: usize,
kstack_top: usize,
satp: usize,
) -> Self {
info!(
"New kernel thread @ {:x}, stack = {:x}",
entry as usize, kstack_top
);
InitStack {
context: ContextData::new(satp, 0),
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
}
.push_at(kstack_top)
}
/// 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,
satp: usize,
) -> Self {
info!(
"New user thread @ {:x}, stack = {:x}",
entry_addr, kstack_top
);
InitStack {
context: ContextData::new(satp, 0),
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
}
.push_at(kstack_top)
}
/// 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 {
let tls = unsafe { *(_cur_tls as *const usize) };
InitStack {
context: ContextData::new(satp, tls),
tf: {
let mut tf = tf.clone();
// fork function's ret value, the new process is 0
tf.v0 = 0;
tf
},
}
.push_at(kstack_top)
}
/// 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(satp, tls),
tf: {
let mut tf = tf.clone();
tf.sp = ustack_top; // sp
tf.v0 = 0; // return value
tf
},
}
.push_at(kstack_top)
}
}