axvm 0.5.11

Virtual Machine resource management crate for ArceOS's hypervisor variant.
//! Architecture component glue owned by AxVM.

#[cfg(target_arch = "x86_64")]
mod x86_64 {
    use alloc::boxed::Box;
    use core::time::Duration;

    use ax_crate_interface::impl_interface;
    use ax_errno::AxResult;
    use ax_memory_addr::{PhysAddr, VirtAddr};
    use axvcpu::get_current_vcpu;
    use axvm_types::{InterruptVector, VCpuId, VMId};
    use x86_vcpu::host::X86VcpuHostIf;
    use x86_vlapic::host::X86VlapicHostIf;

    use crate::{
        host::{HostConsole, HostMemory, HostTime, default_host},
        manager,
        vcpu::AxArchVCpuImpl,
    };

    struct X86VcpuHostIfImpl;

    #[impl_interface]
    impl X86VcpuHostIf for X86VcpuHostIfImpl {
        fn alloc_frame() -> Option<PhysAddr> {
            default_host().alloc_frame()
        }

        fn dealloc_frame(paddr: PhysAddr) {
            default_host().dealloc_frame(paddr);
        }

        fn alloc_contiguous_frames(frame_count: usize, frame_align: usize) -> Option<PhysAddr> {
            default_host().alloc_contiguous_frames(frame_count, frame_align)
        }

        fn dealloc_contiguous_frames(start_paddr: PhysAddr, frame_count: usize) {
            default_host().dealloc_contiguous_frames(start_paddr, frame_count);
        }

        fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
            default_host().phys_to_virt(paddr)
        }

        fn nanos_to_ticks(nanos: u64) -> u64 {
            default_host().nanos_to_ticks(nanos)
        }
    }

    struct X86VlapicHostIfImpl;

    #[impl_interface]
    impl X86VlapicHostIf for X86VlapicHostIfImpl {
        fn alloc_frame() -> Option<PhysAddr> {
            default_host().alloc_frame()
        }

        fn dealloc_frame(paddr: PhysAddr) {
            default_host().dealloc_frame(paddr);
        }

        fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
            default_host().phys_to_virt(paddr)
        }

        fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
            default_host().virt_to_phys(vaddr)
        }

        fn current_time() -> Duration {
            default_host().monotonic_time()
        }

        fn current_time_nanos() -> u64 {
            default_host().monotonic_time().as_nanos() as u64
        }

        fn register_timer(
            deadline: Duration,
            callback: Box<dyn FnOnce(Duration) + Send + 'static>,
        ) -> usize {
            default_host().register_timer(deadline.as_nanos() as u64, callback)
        }

        fn cancel_timer(token: usize) {
            default_host().cancel_timer(token);
        }

        fn write_bytes(bytes: &[u8]) {
            default_host().write_bytes(bytes);
        }

        fn read_bytes(bytes: &mut [u8]) -> usize {
            default_host().read_bytes(bytes)
        }

        fn current_vm_id() -> VMId {
            get_current_vcpu::<AxArchVCpuImpl>()
                .expect("current x86 vCPU is not set")
                .vm_id()
        }

        fn current_vm_vcpu_num() -> usize {
            let vm_id = Self::current_vm_id();
            manager::with_vm(vm_id, |vm| vm.vcpu_num()).unwrap_or(0)
        }

        fn current_vm_active_vcpus() -> usize {
            manager::active_vcpu_mask(Self::current_vm_id()).unwrap_or(0)
        }

        fn active_vcpus(vm_id: VMId) -> Option<usize> {
            manager::active_vcpu_mask(vm_id)
        }

        fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector) -> AxResult {
            manager::inject_interrupt(vm_id, vcpu_id, vector as usize)
        }
    }
}

#[cfg(target_arch = "riscv64")]
mod riscv64 {
    use ax_crate_interface::impl_interface;
    use ax_memory_addr::{PhysAddr, VirtAddr};
    #[cfg(feature = "plat-dyn")]
    use axdevice_base::AccessWidth;
    #[cfg(feature = "plat-dyn")]
    use axvm_types::GuestPhysAddr;
    use riscv_vcpu::host::RiscvVcpuHostIf;
    use riscv_vplic::host::RiscvVplicHostIf;

    use crate::host::{HostMemory, default_host};

    #[cfg(feature = "plat-dyn")]
    const GUEST_PLIC_PADDR: usize = 0x0c00_0000;

    struct RiscvVcpuHostIfImpl;

    #[impl_interface]
    impl RiscvVcpuHostIf for RiscvVcpuHostIfImpl {
        fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
            default_host().virt_to_phys(vaddr)
        }
    }

    struct RiscvVplicHostIfImpl;

    #[impl_interface]
    impl RiscvVplicHostIf for RiscvVplicHostIfImpl {
        fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
            default_host().phys_to_virt(paddr)
        }
    }

    #[cfg(feature = "plat-dyn")]
    pub(crate) fn register_platform_irq_injector() {
        axplat_dyn::register_virtual_irq_injector(inject_virtual_irq);
    }

    #[cfg(not(feature = "plat-dyn"))]
    compile_error!("riscv64 Axvisor requires the plat-dyn feature");

    #[cfg(feature = "plat-dyn")]
    fn inject_virtual_irq(irq_id: usize) -> bool {
        debug!("injecting RISC-V virtual IRQ id: {irq_id}");

        let Some(vm_id) = crate::current_vm_id() else {
            warn!("cannot inject RISC-V virtual IRQ without current VM context");
            return false;
        };

        let Some(injected) = crate::manager::with_vm(vm_id, |vm| {
            let Some(vplic) = vm
                .get_devices()
                .find_mmio_dev(GuestPhysAddr::from_usize(GUEST_PLIC_PADDR))
            else {
                warn!("VM[{vm_id}] has no virtual PLIC device");
                return false;
            };

            let reg_offset = riscv_vplic::PLIC_PENDING_OFFSET + (irq_id / 32) * 4;
            let addr = GuestPhysAddr::from_usize(GUEST_PLIC_PADDR + reg_offset);
            let val: u32 = 1 << (irq_id % 32);

            if let Err(err) = vplic.handle_write(addr, AccessWidth::Dword, val as _) {
                warn!("failed to inject RISC-V virtual IRQ {irq_id}: {err:?}");
                return false;
            }
            true
        }) else {
            warn!("cannot inject RISC-V virtual IRQ {irq_id}: VM[{vm_id}] not found");
            return false;
        };

        injected
    }
}

#[cfg(target_arch = "riscv64")]
pub(crate) fn register_platform_irq_injector() {
    riscv64::register_platform_irq_injector();
}

#[cfg(not(target_arch = "riscv64"))]
pub(crate) fn register_platform_irq_injector() {}

#[cfg(target_arch = "loongarch64")]
mod loongarch64 {
    use ax_crate_interface::impl_interface;
    use ax_memory_addr::{PhysAddr, VirtAddr};
    use loongarch_vcpu::host::LoongArchVcpuHostIf;

    use crate::host::{HostMemory, default_host};

    struct LoongArchVcpuHostIfImpl;

    #[impl_interface]
    impl LoongArchVcpuHostIf for LoongArchVcpuHostIfImpl {
        fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
            default_host().virt_to_phys(vaddr)
        }
    }
}

#[cfg(target_arch = "aarch64")]
mod aarch64 {
    use alloc::boxed::Box;
    use core::time::Duration;

    use arm_vcpu::host::ArmVcpuHostIf;
    use arm_vgic::host::ArmVgicHostIf;
    use ax_crate_interface::impl_interface;
    use ax_memory_addr::{PhysAddr, VirtAddr};

    use crate::host::{HostCpu, HostMemory, HostTime, default_host, gic};

    struct ArmVcpuHostIfImpl;

    #[impl_interface]
    impl ArmVcpuHostIf for ArmVcpuHostIfImpl {
        fn hardware_inject_virtual_interrupt(vector: u8) {
            gic::inject_interrupt(vector as usize);
        }

        fn fetch_irq() -> usize {
            gic::fetch_irq()
        }

        fn handle_irq() {
            gic::handle_current_irq();
        }
    }

    struct ArmVgicHostIfImpl;

    #[impl_interface]
    impl ArmVgicHostIf for ArmVgicHostIfImpl {
        fn alloc_contiguous_frames(frame_count: usize, frame_align: usize) -> Option<PhysAddr> {
            default_host().alloc_contiguous_frames(frame_count, frame_align)
        }

        fn dealloc_contiguous_frames(start_paddr: PhysAddr, frame_count: usize) {
            default_host().dealloc_contiguous_frames(start_paddr, frame_count);
        }

        fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
            default_host().phys_to_virt(paddr)
        }

        fn host_cpu_num() -> usize {
            default_host().cpu_count()
        }

        fn current_vcpu_id() -> usize {
            crate::current_vcpu_id().expect("current AArch64 vCPU is not set")
        }

        fn current_time_nanos() -> u64 {
            default_host().monotonic_time().as_nanos() as u64
        }

        fn register_timer(
            deadline: Duration,
            callback: Box<dyn FnOnce(Duration) + Send + 'static>,
        ) {
            let _ = default_host().register_timer(deadline.as_nanos() as u64, callback);
        }

        fn read_vgicd_iidr() -> u32 {
            gic::read_gicd_iidr()
        }

        fn read_vgicd_typer() -> u32 {
            gic::read_gicd_typer()
        }

        fn get_host_gicd_base() -> PhysAddr {
            gic::host_gicd_base()
        }

        fn get_host_gicr_base() -> PhysAddr {
            gic::host_gicr_base()
        }

        fn hardware_inject_virtual_interrupt(vector: u8) {
            gic::inject_interrupt(vector as usize);
        }
    }
}