use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use ax_hal::cpu::uspace::{ReturnReason, UserContext};
use ax_task::{runtime::RuntimeStatus, thread::TaskError};
use super::{
context::{RuntimeUserBinding, bind_current_user_context},
runtime_status_error, with_current_cpu_pin,
};
pub struct UserExecutionContext {
registers: UserContext,
binding: RuntimeUserBinding,
_not_send_or_sync: PhantomData<*mut ()>,
}
impl UserExecutionContext {
pub fn bind(registers: UserContext) -> Result<Self, TaskError> {
if crate::guard::validate_schedule_context(
ax_task::runtime::switch::RuntimeScheduleOrigin::Preempt,
) != RuntimeStatus::Success
{
return Err(TaskError::UnsafeContext);
}
let irq = crate::task::sync::IrqSaveGuard::new();
let selected_address_space = ax_task::runtime::resource::current_address_space_handle()?;
let binding = unsafe {
with_current_cpu_pin(|pin| {
let binding = bind_current_user_context(pin)?;
super::address_space::validate_current_user_address_space(
pin,
selected_address_space,
)?;
Ok::<_, RuntimeStatus>(binding)
})
}
.map_err(runtime_status_error)?;
drop(irq);
Ok(Self {
registers,
binding,
_not_send_or_sync: PhantomData,
})
}
pub fn enter(&mut self) -> Result<ReturnReason, TaskError> {
if !self.registers.has_interruptible_user_return_mode() {
return Err(TaskError::UnsafeContext);
}
crate::guard::prepare_user_return()?;
self.binding.prepare_user_fp_return();
Ok(PreparedUserEntry {
registers: &mut self.registers,
_not_send_or_sync: PhantomData,
}
.enter())
}
#[doc(hidden)]
pub fn refresh_address_space(&mut self) -> Result<(), TaskError> {
let _irq = crate::task::sync::IrqSaveGuard::new();
let selected_address_space = ax_task::runtime::resource::current_address_space_handle()?;
unsafe {
with_current_cpu_pin(|pin| {
super::address_space::validate_current_user_address_space(
pin,
selected_address_space,
)
})
}
.map_err(runtime_status_error)
}
}
impl Deref for UserExecutionContext {
type Target = UserContext;
fn deref(&self) -> &Self::Target {
&self.registers
}
}
impl DerefMut for UserExecutionContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.registers
}
}
#[must_use = "prepared user entry must be consumed immediately"]
struct PreparedUserEntry<'entry> {
registers: &'entry mut UserContext,
_not_send_or_sync: PhantomData<*mut ()>,
}
impl PreparedUserEntry<'_> {
fn enter(self) -> ReturnReason {
unsafe { self.registers.run_unchecked() }
}
}