use core::mem::MaybeUninit;
use axerrno::{AxResult, ax_err};
pub trait AxArchPerCpu: Sized {
fn new(cpu_id: usize) -> AxResult<Self>;
fn is_enabled(&self) -> bool;
fn hardware_enable(&mut self) -> AxResult;
fn hardware_disable(&mut self) -> AxResult;
fn max_guest_page_table_levels(&self) -> usize {
4
}
}
pub struct AxPerCpu<A: AxArchPerCpu> {
cpu_id: Option<usize>,
arch: MaybeUninit<A>,
}
impl<A: AxArchPerCpu> AxPerCpu<A> {
pub const fn new_uninit() -> Self {
Self {
cpu_id: None,
arch: MaybeUninit::uninit(),
}
}
pub fn init(&mut self, cpu_id: usize) -> AxResult {
if self.cpu_id.is_some() {
ax_err!(BadState, "per-CPU state is already initialized")
} else {
self.cpu_id = Some(cpu_id);
self.arch.write(A::new(cpu_id)?);
Ok(())
}
}
pub fn arch_checked(&self) -> &A {
assert!(self.cpu_id.is_some(), "per-CPU state is not initialized");
unsafe { self.arch.assume_init_ref() }
}
pub fn arch_checked_mut(&mut self) -> &mut A {
assert!(self.cpu_id.is_some(), "per-CPU state is not initialized");
unsafe { self.arch.assume_init_mut() }
}
pub fn is_enabled(&self) -> bool {
self.arch_checked().is_enabled()
}
pub fn hardware_enable(&mut self) -> AxResult {
self.arch_checked_mut().hardware_enable()
}
pub fn hardware_disable(&mut self) -> AxResult {
self.arch_checked_mut().hardware_disable()
}
}
impl<A: AxArchPerCpu> Drop for AxPerCpu<A> {
fn drop(&mut self) {
if self.is_enabled() {
self.hardware_disable().unwrap();
}
}
}