use core::ops::{Deref, DerefMut};
use aarch64_cpu::registers::{ESR_EL1, FAR_EL1, Readable};
use ax_memory_addr::VirtAddr;
use tock_registers::LocalRegisterCopy;
use super::trap::{TrapKind, is_valid_page_fault};
pub use crate::uspace_common::{ExceptionKind, ReturnReason};
use crate::{TrapFrame, trap::PageFaultFlags};
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy)]
pub struct UserContext {
tf: TrapFrame,
pub sp: u64,
pub tpidr: u64,
}
impl UserContext {
const PAD_MAGIC: u64 = 0x1234_5678_9abc_def0;
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 {
tf: TrapFrame {
x: regs,
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,
__pad: Self::PAD_MAGIC,
},
sp: ustack_top.as_usize() as _,
tpidr: 0,
}
}
pub fn prepare_clone_child_return_state(&mut self) {
use aarch64_cpu::registers::SPSR_EL1;
self.tf.spsr = (self.tf.spsr
& !(SPSR_EL1::M.mask
| SPSR_EL1::D.mask
| SPSR_EL1::A.mask
| SPSR_EL1::I.mask
| SPSR_EL1::F.mask))
| (SPSR_EL1::M::EL0t
+ SPSR_EL1::D::Masked
+ SPSR_EL1::A::Masked
+ SPSR_EL1::I::Unmasked
+ SPSR_EL1::F::Masked)
.value;
}
pub const fn sp(&self) -> usize {
self.sp as _
}
pub const fn set_sp(&mut self, sp: usize) {
self.sp = sp as _;
}
pub const fn tls(&self) -> usize {
self.tpidr as _
}
pub const fn set_tls(&mut self, tls: usize) {
self.tpidr = tls as _;
}
pub fn run(&mut self) -> ReturnReason {
unsafe extern "C" {
fn enter_user(uctx: &mut UserContext) -> TrapKind;
}
crate::asm::disable_irqs();
let kind = unsafe { enter_user(self) };
let ret = match kind {
TrapKind::Irq => {
crate::trap::irq_handler(0);
ReturnReason::Interrupt
}
TrapKind::Fiq | TrapKind::SError => ReturnReason::Unknown,
TrapKind::Synchronous => {
let esr = ESR_EL1.extract();
let far = FAR_EL1.get() as usize;
let iss = esr.read(ESR_EL1::ISS);
match esr.read_as_enum(ESR_EL1::EC) {
Some(ESR_EL1::EC::Value::SVC64) => ReturnReason::Syscall,
Some(ESR_EL1::EC::Value::InstrAbortLowerEL) if is_valid_page_fault(iss) => {
ReturnReason::PageFault(
va!(far),
PageFaultFlags::EXECUTE | PageFaultFlags::USER,
)
}
Some(ESR_EL1::EC::Value::DataAbortLowerEL) if is_valid_page_fault(iss) => {
let wnr = (iss & (1 << 6)) != 0; let cm = (iss & (1 << 8)) != 0; ReturnReason::PageFault(
va!(far),
if wnr & !cm {
PageFaultFlags::WRITE
} else {
PageFaultFlags::READ
} | PageFaultFlags::USER,
)
}
_ => ReturnReason::Exception(ExceptionInfo { esr, far }),
}
}
};
crate::asm::enable_irqs();
ret
}
}
impl Deref for UserContext {
type Target = TrapFrame;
fn deref(&self) -> &Self::Target {
&self.tf
}
}
impl DerefMut for UserContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.tf
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExceptionInfo {
pub esr: LocalRegisterCopy<u64, ESR_EL1::Register>,
pub far: usize,
}
impl ExceptionInfo {
pub fn esr_value(&self) -> u64 {
self.esr.get()
}
pub fn ec_value(&self) -> u64 {
self.esr.read(ESR_EL1::EC)
}
pub fn iss_value(&self) -> u64 {
self.esr.read(ESR_EL1::ISS)
}
pub fn kind(&self) -> ExceptionKind {
match self.esr.read_as_enum(ESR_EL1::EC) {
Some(ESR_EL1::EC::Value::Brk64) | Some(ESR_EL1::EC::Value::Bkpt32) => {
ExceptionKind::Breakpoint
}
Some(ESR_EL1::EC::Value::IllegalExecutionState) | Some(ESR_EL1::EC::Value::Unknown) => {
ExceptionKind::IllegalInstruction
}
Some(ESR_EL1::EC::Value::PCAlignmentFault)
| Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned,
_ => ExceptionKind::Other,
}
}
}