use core::mem;
use aarch64_cpu::registers::*;
use super::host::ArmHostOps;
use crate::{
ArmVcpuResult,
enable::{EL2_ENABLE_STEPS, El2EnableStep},
};
#[repr(C)]
#[repr(align(4096))]
pub struct ArmPerCpu {
pub cpu_id: usize,
pub original_vbar_el2: u64,
timer_frequency_hz: u64,
}
unsafe extern "C" {
fn exception_vector_base_vcpu();
}
impl ArmPerCpu {
pub fn new(cpu_id: usize) -> ArmVcpuResult<Self> {
let timer_frequency_hz = CNTFRQ_EL0.get();
if timer_frequency_hz == 0 {
return Err(crate::ArmVcpuError::Unsupported);
}
Ok(Self {
cpu_id,
original_vbar_el2: 0,
timer_frequency_hz,
})
}
pub fn is_enabled(&self) -> bool {
HCR_EL2.is_set(HCR_EL2::VM)
}
pub fn hardware_enable<H: ArmHostOps>(&mut self) -> ArmVcpuResult {
self.original_vbar_el2 = VBAR_EL2.get();
for step in EL2_ENABLE_STEPS {
match step {
El2EnableStep::InstallCurrentElIrqHandler => {
super::host::install_current_el_irq_handler::<H>();
}
El2EnableStep::InstallExceptionVector => {
VBAR_EL2.set(exception_vector_base_vcpu as *const () as usize as _);
}
El2EnableStep::SynchronizeContext => synchronize_context(),
El2EnableStep::EnableVirtualization => HCR_EL2.modify(
HCR_EL2::VM::Enable
+ HCR_EL2::RW::EL1IsAarch64
+ HCR_EL2::TSC::EnableTrapEl1SmcToEl2,
),
}
}
Ok(())
}
pub fn hardware_disable(&mut self) -> ArmVcpuResult {
VBAR_EL2.set(mem::take(&mut self.original_vbar_el2));
HCR_EL2.set(HCR_EL2::VM::Disable.into());
super::host::clear_current_el_irq_handler();
Ok(())
}
pub fn max_guest_page_table_levels(&self) -> usize {
super::vcpu::max_gpt_level(super::vcpu::pa_bits())
}
pub fn guest_phys_addr_bits(&self) -> usize {
super::vcpu::pa_bits()
}
pub const fn timer_frequency_hz(&self) -> u64 {
self.timer_frequency_hz
}
}
fn synchronize_context() {
unsafe {
core::arch::asm!("isb", options(nostack, preserves_flags));
}
}