blob: 6c03dc31201a75454eb6edff32c8db805566b92f [file] [log] [blame]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::task::CurrentTask;
use extended_pstate::{ExtendedPstatePointer, ExtendedPstateState};
use starnix_registers::{HeapRegs, RegisterState, RegisterStorage, RegisterStorageEnum};
use starnix_sync::{Locked, Unlocked};
use starnix_syscalls::SyscallResult;
use starnix_types::arch::ArchWidth;
use starnix_uapi::errors::{Errno, ErrnoCode};
use starnix_uapi::user_address::ArchSpecific;
#[derive(Clone)]
pub enum ArchExtendedPstateStorage {
// Storage for 64 bit restricted mode.
State64(Box<ExtendedPstateState>),
#[cfg(target_arch = "aarch64")]
// Storage for 32 bit arm restricted mode.
State32(Box<extended_pstate::ExtendedAarch32PstateState>),
}
impl ArchExtendedPstateStorage {
/// Returns a type-erased pointer to the underlying storage currently in use.
pub fn as_ptr(&mut self) -> ExtendedPstatePointer {
match self {
ArchExtendedPstateStorage::State64(state) => {
ExtendedPstatePointer { extended_pstate: state.as_mut() as *mut _ }
}
#[cfg(target_arch = "aarch64")]
ArchExtendedPstateStorage::State32(state) => {
ExtendedPstatePointer { extended_aarch32_pstate: state.as_mut() as *mut _ }
}
}
}
pub fn reset(&mut self) {
match self {
ArchExtendedPstateStorage::State64(state) => state.reset(),
#[cfg(target_arch = "aarch64")]
ArchExtendedPstateStorage::State32(state) => state.reset(),
}
}
fn with_arch(arch_width: ArchWidth) -> Self {
#[cfg(target_arch = "aarch64")]
if arch_width == ArchWidth::Arch32 {
return ArchExtendedPstateStorage::State32(Box::new(
extended_pstate::ExtendedAarch32PstateState::default(),
));
}
let _ = arch_width;
ArchExtendedPstateStorage::State64(Box::new(ExtendedPstateState::default()))
}
}
/// The thread related information of a `CurrentTask`. The information should never be used outside
/// of the thread owning the `CurrentTask`.
pub struct ThreadState<T: RegisterStorage> {
/// A copy of the registers associated with the Zircon thread. Up-to-date values can be read
/// from `self.handle.read_state_general_regs()`. To write these values back to the thread, call
/// `self.handle.write_state_general_regs(self.thread_state.registers.into())`.
pub registers: RegisterState<T>,
/// Copy of the current extended processor state including floating point and vector registers.
pub extended_pstate: ArchExtendedPstateStorage,
/// The errno code (if any) that indicated this task should restart a syscall.
pub restart_code: Option<ErrnoCode>,
/// A custom function to resume a syscall that has been interrupted by SIGSTOP.
/// To use, call set_syscall_restart_func and return ERESTART_RESTARTBLOCK. sys_restart_syscall
/// will eventually call it.
pub syscall_restart_func: Option<Box<SyscallRestartFunc>>,
}
impl<T: RegisterStorage + Default> Default for ThreadState<T> {
// TODO(https://fxbug.dev/407084069): Implementing default doesn't make much
// sense - we should only initialize thread state when we know the target
// architecture and we should initialize for that target specifically.
fn default() -> Self {
let registers = RegisterState::<T>::default();
let extended_pstate = ArchExtendedPstateStorage::with_arch(ArchWidth::Arch64);
Self { registers, extended_pstate, restart_code: None, syscall_restart_func: None }
}
}
impl<T: RegisterStorage> ThreadState<T> {
pub fn arch_width(&self) -> ArchWidth {
#[cfg(target_arch = "aarch64")]
{
return if self.registers.is_arch32() { ArchWidth::Arch32 } else { ArchWidth::Arch64 };
}
#[cfg(not(target_arch = "aarch64"))]
ArchWidth::Arch64
}
/// Returns a new `ThreadState` with the same `registers` as this one.
pub fn snapshot<R: RegisterStorage>(&self) -> ThreadState<R>
where
RegisterState<R>: From<RegisterState<T>>,
{
ThreadState::<R> {
registers: self.registers.clone().into(),
extended_pstate: self.extended_pstate.clone(),
restart_code: self.restart_code,
syscall_restart_func: None,
}
}
pub fn extended_snapshot<R: RegisterStorage>(&self) -> ThreadState<R>
where
RegisterState<R>: From<RegisterState<T>>,
{
ThreadState::<R> {
registers: self.registers.clone().into(),
extended_pstate: self.extended_pstate.clone(),
restart_code: self.restart_code,
syscall_restart_func: None,
}
}
pub fn replace_registers<O: RegisterStorage>(&mut self, other: &ThreadState<O>) {
let self_arch = self.arch_width();
let other_arch = other.arch_width();
self.registers.load(*other.registers);
// If we're switching between 32 and 64 bit mode, re-initialize the extended processor state.
self.extended_pstate = if self_arch == other_arch {
other.extended_pstate.clone()
} else {
ArchExtendedPstateStorage::with_arch(other_arch)
};
}
pub fn get_user_register(&mut self, offset: usize) -> Result<usize, Errno> {
let mut result: usize = 0;
self.registers.apply_user_register(offset, &mut |register| result = *register as usize)?;
Ok(result)
}
pub fn set_user_register(&mut self, offset: usize, value: usize) -> Result<(), Errno> {
let self_arch = self.arch_width();
let result =
self.registers.apply_user_register(offset, &mut |register| *register = value as u64);
// If setting the CPSR register to switch between 32 and 64 bit mode, re-initialize the extended processor state.
if self_arch != self.arch_width() {
self.extended_pstate = ArchExtendedPstateStorage::with_arch(self.arch_width());
}
result
}
}
impl From<ThreadState<HeapRegs>> for ThreadState<RegisterStorageEnum> {
fn from(value: ThreadState<HeapRegs>) -> Self {
ThreadState {
registers: value.registers.into(),
extended_pstate: value.extended_pstate,
restart_code: value.restart_code,
syscall_restart_func: value.syscall_restart_func,
}
}
}
impl<T: RegisterStorage> ArchSpecific for ThreadState<T> {
fn is_arch32(&self) -> bool {
#[cfg(target_arch = "aarch64")]
return self.registers.is_arch32();
#[cfg(not(target_arch = "aarch64"))]
false
}
}
pub type SyscallRestartFunc = dyn FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> Result<SyscallResult, Errno>
+ Send
+ Sync;