//! TrapFrame and context definitions for aarch64. use spin::Mutex; use lazy_static::lazy_static; use aarch64::barrier; use aarch64::addr::PhysAddr; use aarch64::paging::PhysFrame; use aarch64::asm::{tlb_invalidate_all, ttbr_el1_read, ttbr_el1_write_asid}; #[repr(C)] #[derive(Default, Debug, Copy, Clone)] pub struct TrapFrame { pub elr: usize, pub spsr: usize, pub sp: usize, pub tpidr: usize, // currently unused // pub q0to31: [u128; 32], // disable SIMD/FP registers pub x1to29: [usize; 29], pub __reserved: usize, pub x30: usize, // lr pub x0: usize, } /// 用于在内核栈中构造新线程的中断帧 impl TrapFrame { fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, sp: usize) -> Self { use core::mem::zeroed; let mut tf: Self = unsafe { zeroed() }; tf.x0 = arg; tf.sp = sp; tf.elr = entry as usize; tf.spsr = 0b1101_00_0101; // To EL 1, enable IRQ tf } fn new_user_thread(entry_addr: usize, sp: usize) -> Self { use core::mem::zeroed; let mut tf: Self = unsafe { zeroed() }; tf.sp = sp; tf.elr = entry_addr; tf.spsr = 0b1101_00_0000; // To EL 0, enable IRQ tf } pub fn is_user(&self) -> bool { unimplemented!() } } /// 新线程的内核栈初始内容 #[derive(Debug)] #[repr(C)] pub struct InitStack { context: ContextData, tf: TrapFrame, } impl InitStack { unsafe fn push_at(self, stack_top: usize, ttbr: usize) -> Context { let ptr = (stack_top as *mut Self).offset(-1); *ptr = self; Context { stack_top: ptr as usize, ttbr: PhysFrame::containing_address(PhysAddr::new(ttbr as u64)), asid: Asid::default(), } } } extern { fn __trapret(); } #[derive(Debug, Default)] #[repr(C)] struct ContextData { x19to29: [usize; 11], lr: usize, } impl ContextData { fn new() -> Self { ContextData { lr: __trapret as usize, ..ContextData::default() } } } #[derive(Debug)] pub struct Context { stack_top: usize, ttbr: PhysFrame, asid: Asid, } impl Context { /// Switch to another kernel thread. /// /// Defined in `trap.S`. /// /// 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)] unsafe extern fn __switch(_self_stack: &mut usize, _target_stack: &mut usize) { asm!( " mov x10, #-(12 * 8) add x8, sp, x10 str x8, [x0] stp x19, x20, [x8], #16 // store callee-saved registers stp x21, x22, [x8], #16 stp x23, x24, [x8], #16 stp x25, x26, [x8], #16 stp x27, x28, [x8], #16 stp x29, lr, [x8], #16 ldr x8, [x1] ldp x19, x20, [x8], #16 // restore callee-saved registers ldp x21, x22, [x8], #16 ldp x23, x24, [x8], #16 ldp x25, x26, [x8], #16 ldp x27, x28, [x8], #16 ldp x29, lr, [x8], #16 mov sp, x8 str xzr, [x1] ret" : : : : "volatile" ); } pub unsafe fn switch(&mut self, target: &mut Self) { self.ttbr = ttbr_el1_read(0); target.asid = ASID_ALLOCATOR.lock().alloc(target.asid); // with ASID we needn't flush TLB frequently ttbr_el1_write_asid(0, target.asid.value, target.ttbr); barrier::dsb(barrier::ISH); Self::__switch(&mut self.stack_top, &mut target.stack_top); } pub unsafe fn null() -> Self { Context { stack_top: 0, ttbr: PhysFrame::containing_address(PhysAddr::new(0)), asid: Asid::default(), } } pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr: usize) -> Self { InitStack { context: ContextData::new(), tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top), }.push_at(kstack_top, ttbr) } pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, _is32: bool, ttbr: usize) -> Self { InitStack { context: ContextData::new(), tf: TrapFrame::new_user_thread(entry_addr, ustack_top), }.push_at(kstack_top, ttbr) } pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr: usize) -> Self { InitStack { context: ContextData::new(), tf: { let mut tf = tf.clone(); tf.x0 = 0; tf }, }.push_at(kstack_top, ttbr) } pub unsafe fn new_clone(tf: &TrapFrame, ustack_top: usize, kstack_top: usize, ttbr: usize, tls: usize) -> Self { InitStack { context: ContextData::new(), tf: { let mut tf = tf.clone(); tf.sp = ustack_top; tf.tpidr = tls; tf.x0 = 0; tf }, }.push_at(kstack_top, ttbr) } /// Called at a new user context /// To get the init TrapFrame in sys_exec pub unsafe fn get_init_tf(&self) -> TrapFrame { (*(self.stack_top as *const InitStack)).tf.clone() } } const ASID_MASK: u16 = 0xffff; #[derive(Debug, Copy, Clone, Default)] #[repr(C)] struct Asid { value: u16, generation: u16, } struct AsidAllocator(Asid); impl AsidAllocator { fn new() -> Self { AsidAllocator(Asid { value: 0, generation: 1 }) } fn alloc(&mut self, old_asid: Asid) -> Asid { if self.0.generation == old_asid.generation { return old_asid; } if self.0.value == ASID_MASK { self.0.value = 0; self.0.generation = self.0.generation.wrapping_add(1); if self.0.generation == 0 { self.0.generation += 1; } tlb_invalidate_all(); } self.0.value += 1; return self.0; } } lazy_static! { static ref ASID_ALLOCATOR: Mutex = Mutex::new(AsidAllocator::new()); }