diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f23219..b32ea82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2024-12-25 - name: Build docs continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} run: | diff --git a/Cargo.toml b/Cargo.toml index e193ea6..371b918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,10 @@ tock-registers = "0.8" numeric-enum-macro = "0.2" axerrno = "0.1.0" -percpu = "0.1.4" +percpu = { version = "0.2.0", features = ["arm-el2"] } aarch64_sysreg = "0.1.1" axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } -axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git" } +axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git", branch = "inject_interrupt" } +axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git", branch = "inject_interrupt" } +axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api.git", branch = "inject_interrupt" } diff --git a/src/context_frame.rs b/src/context_frame.rs index 6da0d38..a9833bf 100644 --- a/src/context_frame.rs +++ b/src/context_frame.rs @@ -156,6 +156,7 @@ pub struct GuestSystemRegisters { cntv_ctl_el0: u32, cntp_tval_el0: u32, cntv_tval_el0: u32, + pub cnthctl_el2: u64, // vpidr and vmpidr vpidr_el2: u32, @@ -220,6 +221,7 @@ impl GuestSystemRegisters { asm!("mrs {0:x}, CNTP_TVAL_EL0", out(reg) self.cntp_tval_el0); asm!("mrs {0:x}, CNTV_TVAL_EL0", out(reg) self.cntv_tval_el0); asm!("mrs {0}, CNTVCT_EL0", out(reg) self.cntvct_el0); + asm!("mrs {0}, CNTHCTL_EL2", out(reg) self.cnthctl_el2); // MRS!("self.vpidr_el2, VPIDR_EL2, "x"); asm!("mrs {0}, VMPIDR_EL2", out(reg) self.vmpidr_el2); @@ -265,6 +267,7 @@ impl GuestSystemRegisters { asm!("msr CNTV_CVAL_EL0, {0}", in(reg) self.cntv_cval_el0); asm!("msr CNTKCTL_EL1, {0:x}", in (reg) self.cntkctl_el1); asm!("msr CNTV_CTL_EL0, {0:x}", in (reg) self.cntv_ctl_el0); + asm!("msr CNTHCTL_EL2, {0}", in(reg) self.cnthctl_el2); // The restoration of SP_EL0 is done in `exception_return_el2`, // which move the value from `self.ctx.sp_el0` to `SP_EL0`. // asm!("msr SP_EL0, {0}", in(reg) self.sp_el0); diff --git a/src/exception.S b/src/exception.S index 0c4dcb6..9513703 100644 --- a/src/exception.S +++ b/src/exception.S @@ -71,6 +71,7 @@ .macro HANDLE_CURRENT_IRQ .p2align 7 SAVE_REGS_FROM_EL1 + mov x0, sp bl current_el_irq_handler b .Lexception_return_el2 .endm @@ -78,6 +79,7 @@ .macro HANDLE_CURRENT_SYNC .p2align 7 SAVE_REGS_FROM_EL1 + mov x0, sp bl current_el_sync_handler b .Lexception_return_el2 .endm diff --git a/src/exception.rs b/src/exception.rs index 738178e..40047e8 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -1,9 +1,5 @@ use aarch64_cpu::registers::{ESR_EL2, HCR_EL2, Readable, SCTLR_EL1, VTCR_EL2, VTTBR_EL2}; -use axaddrspace::GuestPhysAddr; -use axerrno::{AxError, AxResult}; -use axvcpu::{AccessWidth, AxVCpuExitReason}; - use crate::TrapFrame; use crate::exception_utils::{ exception_class, exception_class_value, exception_data_abort_access_is_write, @@ -13,6 +9,10 @@ use crate::exception_utils::{ exception_esr, exception_fault_addr, exception_next_instruction_step, exception_sysreg_addr, exception_sysreg_direction_write, exception_sysreg_gpr, }; +use axaddrspace::GuestPhysAddr; +use axaddrspace::device::{AccessWidth, SysRegAddr}; +use axerrno::{AxError, AxResult}; +use axvcpu::AxVCpuExitReason; numeric_enum_macro::numeric_enum! { #[repr(u8)] @@ -203,11 +203,14 @@ fn handle_system_register(context_frame: &mut TrapFrame) -> AxResult for SysCntpCtlEl0 { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTConsole + } + + fn address_range(&self) -> SysRegAddrRange { + SysRegAddrRange { + start: SysRegAddr::new(SystemRegType::CNTP_CTL_EL0 as usize), + end: SysRegAddr::new(SystemRegType::CNTP_CTL_EL0 as usize), + } + } + + fn handle_read( + &self, + _addr: ::Addr, + _width: AccessWidth, + ) -> AxResult { + todo!() + } + + fn handle_write( + &self, + addr: ::Addr, + _width: AccessWidth, + val: usize, + ) -> AxResult { + info!("Write to emulator register: {:?}, value: {}", addr, val); + Ok(()) + } +} + +pub struct SysCntpCtlEl0 { + // Fields +} + +impl SysCntpCtlEl0 { + pub fn new() -> Self { + Self { + // Initialize fields + } + } +} diff --git a/src/sysreg/cntp_tval_el0.rs b/src/sysreg/cntp_tval_el0.rs new file mode 100644 index 0000000..a685168 --- /dev/null +++ b/src/sysreg/cntp_tval_el0.rs @@ -0,0 +1,62 @@ +extern crate alloc; + +use aarch64_sysreg::SystemRegType; + +use axaddrspace::device::{AccessWidth, DeviceAddrRange, SysRegAddr, SysRegAddrRange}; +use axdevice_base::{BaseDeviceOps, EmuDeviceType}; +use axerrno::AxResult; +use axvisor_api::time::{current_time_nanos, register_timer}; + +use alloc::boxed::Box; +use core::time::Duration; + +impl BaseDeviceOps for SysCntpTvalEl0 { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTConsole + } + + fn address_range(&self) -> SysRegAddrRange { + SysRegAddrRange { + start: SysRegAddr::new(SystemRegType::CNTP_TVAL_EL0 as usize), + end: SysRegAddr::new(SystemRegType::CNTP_TVAL_EL0 as usize), + } + } + + fn handle_read( + &self, + _addr: ::Addr, + _width: AccessWidth, + ) -> AxResult { + todo!() + } + + fn handle_write( + &self, + addr: ::Addr, + _width: AccessWidth, + val: usize, + ) -> AxResult { + info!("Write to emulator register: {:?}, value: {}", addr, val); + let now = current_time_nanos(); + info!("Current time: {}, deadline: {}", now, now + val as u64); + register_timer( + Duration::from_nanos(now + val as u64), + Box::new(|_| { + axvisor_api::arch::hardware_inject_virtual_interrupt(30); + }), + ); + Ok(()) + } +} + +pub struct SysCntpTvalEl0 { + // Fields +} + +impl SysCntpTvalEl0 { + pub fn new() -> Self { + Self { + // Initialize fields + } + } +} diff --git a/src/sysreg/cntpct_el0.rs b/src/sysreg/cntpct_el0.rs new file mode 100644 index 0000000..43e6cc8 --- /dev/null +++ b/src/sysreg/cntpct_el0.rs @@ -0,0 +1,54 @@ +use aarch64_sysreg::SystemRegType; + +use aarch64_cpu::registers::{CNTPCT_EL0, Readable}; + +use axaddrspace:: + device::{AccessWidth, DeviceAddrRange, SysRegAddr, SysRegAddrRange} +; + +use axdevice_base::{BaseDeviceOps, EmuDeviceType}; + +use axerrno::AxResult; + +impl BaseDeviceOps for SysCntpctEl0 { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTConsole + } + + fn address_range(&self) -> SysRegAddrRange { + SysRegAddrRange { + start: SysRegAddr::new(SystemRegType::CNTPCT_EL0 as usize), + end: SysRegAddr::new(SystemRegType::CNTPCT_EL0 as usize), + } + } + + fn handle_read( + &self, + _addr: ::Addr, + _width: AccessWidth, + ) -> AxResult { + Ok(CNTPCT_EL0.get() as usize) + } + + fn handle_write( + &self, + addr: ::Addr, + _width: AccessWidth, + val: usize, + ) -> AxResult { + info!("Write to emulator register: {:?}, value: {}", addr, val); + Ok(()) + } +} + +pub struct SysCntpctEl0 { + // Fields +} + +impl SysCntpctEl0 { + pub fn new() -> Self { + Self { + // Initialize fields + } + } +} diff --git a/src/sysreg/mod.rs b/src/sysreg/mod.rs new file mode 100644 index 0000000..bafcf1e --- /dev/null +++ b/src/sysreg/mod.rs @@ -0,0 +1,22 @@ +extern crate alloc; + +use alloc::sync::Arc; +use alloc::{vec, vec::Vec}; +use axdevice_base::BaseSysRegDeviceOps; + +mod cntp_ctl_el0; +pub use cntp_ctl_el0::SysCntpCtlEl0; + +mod cntpct_el0; +pub use cntpct_el0::SysCntpctEl0; + +mod cntp_tval_el0; +pub use cntp_tval_el0::SysCntpTvalEl0; + +pub fn get_sysreg_device() -> Vec> { + vec![ + Arc::new(SysCntpCtlEl0::new()), + Arc::new(SysCntpctEl0::new()), + Arc::new(SysCntpTvalEl0::new()), + ] +} diff --git a/src/vcpu.rs b/src/vcpu.rs index e64ae57..46e9d4f 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -3,14 +3,13 @@ use core::marker::PhantomData; use aarch64_cpu::registers::{CNTHCTL_EL2, HCR_EL2, SP_EL0, SPSR_EL1, VTCR_EL2}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; -use axaddrspace::{GuestPhysAddr, HostPhysAddr}; -use axerrno::AxResult; -use axvcpu::{AxVCpuExitReason, AxVCpuHal}; - use crate::TrapFrame; use crate::context_frame::GuestSystemRegisters; use crate::exception::{TrapKind, handle_exception_sync}; use crate::exception_utils::exception_class_value; +use axaddrspace::{GuestPhysAddr, HostPhysAddr}; +use axerrno::AxResult; +use axvcpu::{AxVCpuExitReason, AxVCpuHal}; #[percpu::def_percpu] static HOST_SP_EL0: u64 = 0; @@ -121,6 +120,11 @@ impl axvcpu::AxArchVCpu for Aarch64VCpu { fn set_gpr(&mut self, idx: usize, val: usize) { self.ctx.set_gpr(idx, val); } + + // fn inject_interrupt(&mut self, vector: usize) -> AxResult { + // axvisor_api::arch::hardware_inject_virtual_interrupt(vector as u8); + // Ok(()) + // } } // Private function @@ -151,9 +155,13 @@ impl Aarch64VCpu { + VTCR_EL2::SL0.val(0b01) + VTCR_EL2::T0SZ.val(64 - 39)) .into(); - self.guest_system_regs.hcr_el2 = - (HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::TSC::EnableTrapEl1SmcToEl2) - .into(); + self.guest_system_regs.hcr_el2 = (HCR_EL2::VM::Enable + + HCR_EL2::RW::EL1IsAarch64 + + HCR_EL2::IMO::EnableVirtualIRQ + + HCR_EL2::FMO::EnableVirtualFIQ + + HCR_EL2::TSC::EnableTrapEl1SmcToEl2 + + HCR_EL2::RW::EL1IsAarch64) + .into(); // self.system_regs.hcr_el2 |= 1<<27; // + HCR_EL2::IMO::EnableVirtualIRQ).into(); @@ -183,27 +191,43 @@ impl Aarch64VCpu { /// /// When a VM-Exit happens when guest's vCpu is running, /// the control flow will be redirected to this function through `return_run_guest`. - #[inline(never)] - unsafe fn run_guest(&mut self) -> usize { + #[naked] + unsafe extern "C" fn run_guest(&mut self) -> usize { + // Fixes: https://github.com/arceos-hypervisor/arm_vcpu/issues/22 + // + // The original issue seems to be caused by an unexpected compiler optimization that takes + // the dummy return value `0` of `run_guest` as the actual return value. By replacing the + // original `run_guest` with the current naked one, we eliminate the dummy code path of the + // original version, and ensure that the compiler does not perform any unexpected return + // value optimization. unsafe { - // Save function call context. - core::arch::asm!( + core::arch::naked_asm!( // Save host context. save_regs_to_stack!(), + // Save current host stack top to `self.host_stack_top`. + // + // 'extern "C"' here specifies the aapcs64 calling convention, according to which + // the first and only parameter, the pointer of self, should be in x0: "mov x9, sp", - "mov x10, x11", - // Save current host stack top in the `Aarch64VCpu` struct. - "str x9, [x10]", - "mov x0, x11", + "add x0, x0, {host_stack_top_offset}", + "str x9, [x0]", + // Go to `context_vm_entry`. "b context_vm_entry", - // in(reg) here is dangerous, because the compiler may use the register we want to use, creating a conflict. - in("x11") &self.host_stack_top as *const _ as usize, - options(nostack) + // Panic if the control flow comes back here, which should never happen. + "b {run_guest_panic}", + host_stack_top_offset = const core::mem::size_of::(), + run_guest_panic = sym Self::run_guest_panic, ); } + } - // the dummy return value, the real return value is in x0 when `return_run_guest` returns - 0 + /// This function is called when the control flow comes back to `run_guest`. To provide a error + /// message for debugging purposes. + /// + /// This function may fail as the stack may have been corrupted when this function is called. + /// But we won't handle it here for now. + unsafe fn run_guest_panic() -> ! { + panic!("run_guest_panic"); } /// Restores guest system control registers.