-
Notifications
You must be signed in to change notification settings - Fork 250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[hal] Support user space for riscv64 and aarch64 #179
base: monolithic
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,145 @@ pub struct TrapFrame { | |
pub spsr: u64, | ||
} | ||
|
||
impl TrapFrame { | ||
/// Gets the 0th syscall argument. | ||
pub const fn arg0(&self) -> usize { | ||
self.r[0] as _ | ||
} | ||
|
||
/// Gets the 1st syscall argument. | ||
pub const fn arg1(&self) -> usize { | ||
self.r[1] as _ | ||
} | ||
|
||
/// Gets the 2nd syscall argument. | ||
pub const fn arg2(&self) -> usize { | ||
self.r[2] as _ | ||
} | ||
|
||
/// Gets the 3rd syscall argument. | ||
pub const fn arg3(&self) -> usize { | ||
self.r[3] as _ | ||
} | ||
|
||
/// Gets the 4th syscall argument. | ||
pub const fn arg4(&self) -> usize { | ||
self.r[4] as _ | ||
} | ||
|
||
/// Gets the 5th syscall argument. | ||
pub const fn arg5(&self) -> usize { | ||
self.r[5] as _ | ||
} | ||
} | ||
|
||
/// Context to enter user space. | ||
#[cfg(feature = "uspace")] | ||
pub struct UspaceContext(TrapFrame); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
|
||
#[cfg(feature = "uspace")] | ||
impl UspaceContext { | ||
/// Creates an empty context with all registers set to zero. | ||
pub const fn empty() -> Self { | ||
unsafe { core::mem::MaybeUninit::zeroed().assume_init() } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe |
||
} | ||
|
||
/// Creates a new context with the given entry point, user stack pointer, | ||
/// and the argument. | ||
pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self { | ||
use aarch64_cpu::registers::SPSR_EL1; | ||
let mut regs = [0; 31]; | ||
regs[0] = arg0 as _; | ||
Self(TrapFrame { | ||
r: regs, | ||
usp: ustack_top.as_usize() as _, | ||
elr: entry as _, | ||
spsr: (SPSR_EL1::M::EL0t | ||
+ SPSR_EL1::D::Masked | ||
+ SPSR_EL1::A::Masked | ||
+ SPSR_EL1::I::Unmasked | ||
+ SPSR_EL1::F::Masked) | ||
.value, | ||
}) | ||
} | ||
|
||
/// Creates a new context from the given [`TrapFrame`]. | ||
pub const fn from(trap_frame: &TrapFrame) -> Self { | ||
Self(*trap_frame) | ||
} | ||
|
||
/// Gets the instruction pointer. | ||
pub const fn get_ip(&self) -> usize { | ||
self.0.elr as _ | ||
} | ||
|
||
/// Gets the stack pointer. | ||
pub const fn get_sp(&self) -> usize { | ||
self.0.usp as _ | ||
} | ||
|
||
/// Sets the instruction pointer. | ||
pub const fn set_ip(&mut self, pc: usize) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
self.0.elr = pc as _; | ||
} | ||
|
||
/// Sets the stack pointer. | ||
pub const fn set_sp(&mut self, sp: usize) { | ||
self.0.usp = sp as _; | ||
} | ||
|
||
/// Sets the return value register. | ||
pub const fn set_retval(&mut self, retval: usize) { | ||
self.0.r[8] = retval as _; | ||
} | ||
|
||
/// Enters user space. | ||
/// | ||
/// It restores the user registers and jumps to the user entry point | ||
/// (saved in `sepc`). | ||
/// When an exception or syscall occurs, the kernel stack pointer is | ||
/// switched to `kstack_top`. | ||
/// | ||
/// # Safety | ||
/// | ||
/// This function is unsafe because it changes processor mode and the stack. | ||
#[inline(never)] | ||
#[no_mangle] | ||
pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { | ||
super::disable_irqs(); | ||
// We don't process elxt trap, so we don't need to save the current kstack ptr to the taskctx. | ||
asm!( | ||
" | ||
mov sp, x1 | ||
ldp x30, x9, [x0, 30 * 8] | ||
ldp x10, x11, [x0, 32 * 8] | ||
msr sp_el0, x9 | ||
msr elr_el1, x10 | ||
msr spsr_el1, x11 | ||
|
||
ldp x28, x29, [x0, 28 * 8] | ||
ldp x26, x27, [x0, 26 * 8] | ||
ldp x24, x25, [x0, 24 * 8] | ||
ldp x22, x23, [x0, 22 * 8] | ||
ldp x20, x21, [x0, 20 * 8] | ||
ldp x18, x19, [x0, 18 * 8] | ||
ldp x16, x17, [x0, 16 * 8] | ||
ldp x14, x15, [x0, 14 * 8] | ||
ldp x12, x13, [x0, 12 * 8] | ||
ldp x10, x11, [x0, 10 * 8] | ||
ldp x8, x9, [x0, 8 * 8] | ||
ldp x6, x7, [x0, 6 * 8] | ||
ldp x4, x5, [x0, 4 * 8] | ||
ldp x2, x3, [x0, 2 * 8] | ||
ldp x0, x1, [x0] | ||
eret", | ||
in("x0") &self.0, | ||
in("x1") kstack_top.as_usize() , | ||
options(noreturn), | ||
) | ||
} | ||
} | ||
|
||
/// FP & SIMD registers. | ||
#[repr(C, align(16))] | ||
#[derive(Debug, Default)] | ||
|
@@ -47,7 +186,7 @@ impl FpState { | |
/// and the next task restores its context from memory to CPU. | ||
#[allow(missing_docs)] | ||
#[repr(C)] | ||
#[derive(Debug)] | ||
#[derive(Debug, Default)] | ||
pub struct TaskContext { | ||
pub sp: u64, | ||
pub tpidr_el0: u64, | ||
|
@@ -65,6 +204,9 @@ pub struct TaskContext { | |
pub lr: u64, // r30 | ||
#[cfg(feature = "fp_simd")] | ||
pub fp_state: FpState, | ||
/// The `ttbr0_el1` register value, i.e., the page table root. | ||
#[cfg(feature = "uspace")] | ||
pub ttbr0_el1: memory_addr::PhysAddr, | ||
} | ||
|
||
impl TaskContext { | ||
|
@@ -75,25 +217,48 @@ impl TaskContext { | |
/// | ||
/// [`init`]: TaskContext::init | ||
/// [`switch_to`]: TaskContext::switch_to | ||
pub const fn new() -> Self { | ||
unsafe { core::mem::MaybeUninit::zeroed().assume_init() } | ||
pub fn new() -> Self { | ||
Self { | ||
#[cfg(feature = "uspace")] | ||
ttbr0_el1: crate::paging::kernel_page_table_root(), | ||
..Default::default() | ||
} | ||
} | ||
|
||
/// Initializes the context for a new task, with the given entry point and | ||
/// kernel stack. | ||
pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { | ||
self.sp = kstack_top.as_usize() as u64; | ||
self.lr = entry as u64; | ||
// When under `uspace` feature, kernel will not use this register. | ||
self.tpidr_el0 = tls_area.as_usize() as u64; | ||
} | ||
|
||
/// Changes the page table root for user space (`ttbr0_el1` register for aarch64 in el1 level). | ||
/// | ||
/// If not set, the kernel page table root is used (obtained by | ||
/// [`axhal::paging::kernel_page_table_root`][1]). | ||
/// | ||
/// [1]: crate::paging::kernel_page_table_root | ||
#[cfg(feature = "uspace")] | ||
pub fn set_page_table_root(&mut self, ttbr0_el1: memory_addr::PhysAddr) { | ||
self.ttbr0_el1 = ttbr0_el1; | ||
} | ||
|
||
/// Switches to another task. | ||
/// | ||
/// It first saves the current task's context from CPU to this place, and then | ||
/// restores the next task's context from `next_ctx` to CPU. | ||
pub fn switch_to(&mut self, next_ctx: &Self) { | ||
#[cfg(feature = "fp_simd")] | ||
self.fp_state.switch_to(&next_ctx.fp_state); | ||
|
||
#[cfg(feature = "uspace")] | ||
{ | ||
if self.ttbr0_el1 != next_ctx.ttbr0_el1 { | ||
unsafe { super::write_page_table_root0(next_ctx.ttbr0_el1) }; | ||
} | ||
} | ||
unsafe { context_switch(self, next_ctx) } | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use
pub const fn arg(&self, index: usize) -> usize
?