use core::marker::PhantomData;
use aarch64_cpu::registers::{HCR_EL2, Readable, VBAR_EL2, Writeable};
use crate::{VirtAddr, virtualization::VirtualizationError};
#[derive(Debug)]
struct HostState {
vector: u64,
control: u64,
}
#[derive(Debug, Default)]
pub struct PerCpu {
host: Option<HostState>,
_not_send_sync: PhantomData<*mut ()>,
}
impl PerCpu {
pub const fn new() -> Self {
Self {
host: None,
_not_send_sync: PhantomData,
}
}
pub const fn is_enabled(&self) -> bool {
self.host.is_some()
}
pub unsafe fn enable(&mut self, vector: VirtAddr) -> Result<(), VirtualizationError> {
if self.host.is_some() {
return Err(VirtualizationError::AlreadyEnabled);
}
if crate::registers::current_exception_level() != 2 {
return Err(VirtualizationError::Unavailable);
}
if !vector.as_usize().is_multiple_of(2048) {
return Err(VirtualizationError::InvalidVector);
}
let host = HostState {
vector: VBAR_EL2.get(),
control: HCR_EL2.get(),
};
VBAR_EL2.set(vector.as_usize() as u64);
unsafe {
core::arch::asm!("isb", options(nostack, preserves_flags));
}
HCR_EL2.set(host.control | (1 << 0) | (1 << 31) | (1 << 19));
unsafe {
core::arch::asm!("isb", options(nostack, preserves_flags));
}
self.host = Some(host);
Ok(())
}
pub unsafe fn disable(&mut self) -> Result<(), VirtualizationError> {
let host = self.host.take().ok_or(VirtualizationError::NotEnabled)?;
HCR_EL2.set(host.control);
VBAR_EL2.set(host.vector);
unsafe {
core::arch::asm!("isb", options(nostack, preserves_flags));
}
Ok(())
}
}