Skip to content
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

Open
wants to merge 1 commit into
base: monolithic
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 168 additions & 3 deletions modules/axhal/src/arch/aarch64/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,145 @@ pub struct TrapFrame {
pub spsr: u64,
}

impl TrapFrame {
/// Gets the 0th syscall argument.
pub const fn arg0(&self) -> usize {
Copy link
Contributor

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?

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think UserSpaceContext is a better name.


#[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() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Default::default()?

}

/// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ip or pc?

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)]
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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) }
}
}
Expand Down
2 changes: 2 additions & 0 deletions modules/axhal/src/arch/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub(crate) mod trap;
use core::arch::asm;

use aarch64_cpu::registers::{DAIF, TPIDR_EL0, TTBR0_EL1, TTBR1_EL1, VBAR_EL1};
#[cfg(feature = "uspace")]
pub use context::UspaceContext;
use memory_addr::{PhysAddr, VirtAddr};
use tock_registers::interfaces::{Readable, Writeable};

Expand Down
7 changes: 7 additions & 0 deletions modules/axhal/src/arch/aarch64/trap.S
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
mrs x11, spsr_el1
stp x30, x9, [sp, 30 * 8]
stp x10, x11, [sp, 32 * 8]
/*
* We may have interrupted userspace, or a guest, or exit-from or
* return-to either of those. So we can't trust sp_el0, and need to
* restore it.
*/
bl current_task_ptr_raw
msr sp_el0, x0
.endm

.macro RESTORE_REGS
Expand Down
3 changes: 2 additions & 1 deletion modules/axhal/src/arch/aarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ fn handle_sync_exception(tf: &mut TrapFrame) {
let esr = ESR_EL1.extract();
let iss = esr.read(ESR_EL1::ISS);
match esr.read_as_enum(ESR_EL1::EC) {
#[cfg(feature = "uspace")]
Some(ESR_EL1::EC::Value::SVC64) => {
warn!("No syscall is supported currently!");
tf.r[0] = crate::trap::handle_syscall(tf, tf.r[8] as usize) as u64;
}
Some(ESR_EL1::EC::Value::InstrAbortLowerEL) => handle_instruction_abort(tf, iss, true),
Some(ESR_EL1::EC::Value::InstrAbortCurrentEL) => handle_instruction_abort(tf, iss, false),
Expand Down
Loading
Loading