diff --git a/Cargo.lock b/Cargo.lock index 9c43cd4c23..48424eaf5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7276,6 +7276,7 @@ dependencies = [ "guestmem", "hvdef", "thiserror 2.0.0", + "tracelimit", "tracing", "virt", "vm_topology", diff --git a/openhcl/virt_mshv_vtl/src/processor/mshv/arm64.rs b/openhcl/virt_mshv_vtl/src/processor/mshv/arm64.rs index 7a3168c087..c212ba954a 100644 --- a/openhcl/virt_mshv_vtl/src/processor/mshv/arm64.rs +++ b/openhcl/virt_mshv_vtl/src/processor/mshv/arm64.rs @@ -299,6 +299,41 @@ impl UhProcessor<'_, HypervisorBackedArm64> { Self::intercepted_vtl(&message.header).map_err(|UnsupportedGuestVtl(vtl)| { VpHaltReason::InvalidVmState(UhRunVpError::InvalidInterceptedVtl(vtl)) })?; + + // Fast path for monitor page writes. + if Some(message.guest_physical_address & !(hvdef::HV_PAGE_SIZE - 1)) + == self.partition.monitor_page.gpa() + && message.header.intercept_access_type == hvdef::HvInterceptAccessType::WRITE + && message.instruction_byte_count == 4 + { + let gpa = message.guest_physical_address; + let guest_memory = &self.partition.gm[intercepted_vtl]; + if let Some(mut bitmask) = emulate::emulate_mnf_write_fast_path( + u32::from_ne_bytes(message.instruction_bytes), + &mut UhEmulationState { + vp: &mut *self, + interruption_pending: intercept_state.interruption_pending, + devices: dev, + vtl: intercepted_vtl, + cache: UhCpuStateCache::default(), + }, + guest_memory, + dev, + ) { + let bit_offset = (gpa & (hvdef::HV_PAGE_SIZE - 1)) as u32 * 8; + while bitmask != 0 { + let bit = 63 - bitmask.leading_zeros(); + bitmask &= !(1 << bit); + if let Some(connection_id) = + self.partition.monitor_page.write_bit(bit_offset + bit) + { + signal_mnf(dev, connection_id); + } + } + return Ok(()); + } + } + self.emulate(dev, &intercept_state, intercepted_vtl).await?; Ok(()) } diff --git a/vm/aarch64/aarch64emu/src/opcodes.rs b/vm/aarch64/aarch64emu/src/opcodes.rs index e89a8ca464..940e4d7c60 100644 --- a/vm/aarch64/aarch64emu/src/opcodes.rs +++ b/vm/aarch64/aarch64emu/src/opcodes.rs @@ -133,28 +133,28 @@ fn decode_load_store_group(opcode: u32) -> Result { - if (op3 & 0x60) != 0 { + if (op3 & 0x20) != 0 { Aarch64DecodeLoadStoreGroup::Atomic } else { Aarch64DecodeLoadStoreGroup::RegisterUnscaledImmediate } } 3 | 7 | 11 | 15 if op4 == 1 => { - if (op3 & 0x60) != 0 { + if (op3 & 0x20) != 0 { Aarch64DecodeLoadStoreGroup::RegisterPac } else { Aarch64DecodeLoadStoreGroup::RegisterImmediatePostIndex } } 3 | 7 | 11 | 15 if op4 == 2 => { - if (op3 & 0x60) != 0 { + if (op3 & 0x20) != 0 { Aarch64DecodeLoadStoreGroup::RegisterOffset } else { Aarch64DecodeLoadStoreGroup::RegisterUnprivileged } } 3 | 7 | 11 | 15 => { - if (op3 & 0x60) != 0 { + if (op3 & 0x20) != 0 { Aarch64DecodeLoadStoreGroup::RegisterPac } else { Aarch64DecodeLoadStoreGroup::RegisterImmediatePreIndex diff --git a/vmm_core/virt_support_aarch64emu/Cargo.toml b/vmm_core/virt_support_aarch64emu/Cargo.toml index 3080265082..7c49c28f99 100644 --- a/vmm_core/virt_support_aarch64emu/Cargo.toml +++ b/vmm_core/virt_support_aarch64emu/Cargo.toml @@ -15,6 +15,7 @@ virt.workspace = true aarch64emu.workspace = true thiserror.workspace = true +tracelimit.workspace = true tracing.workspace = true zerocopy.workspace = true diff --git a/vmm_core/virt_support_aarch64emu/src/emulate.rs b/vmm_core/virt_support_aarch64emu/src/emulate.rs index 26958a27f6..4d40f69f1b 100644 --- a/vmm_core/virt_support_aarch64emu/src/emulate.rs +++ b/vmm_core/virt_support_aarch64emu/src/emulate.rs @@ -685,15 +685,42 @@ fn vtl_access_event( /// a monitor page GPA. /// /// Returns the bit number being set within the monitor page. -pub fn emulate_mnf_write_fast_path( - instruction_bytes: &[u8], - interruption_pending: bool, - tlb_lock_held: bool, -) -> Option { - if interruption_pending || !tlb_lock_held || instruction_bytes.is_empty() { +pub fn emulate_mnf_write_fast_path( + opcode: u32, + support: &mut T, + gm: &GuestMemory, + dev: &impl CpuIo, +) -> Option { + if support.interruption_pending() { return None; } - // TODO: Determine if there is a reasonable fast path for arm. - None + // LDSETx / STSETx. A "fast path" is possible because we can assume the + // MNF page is always zero-filled. + if (opcode & 0x38203c00) == 0x38203000 { + let mut cpu = EmulatorCpu::new(gm, dev, support, EsrEl2::from_bits(0)); + let size = (1 << (opcode >> 30)) * 8; + let rs = (opcode >> 16) as u8 & 0x1f; + let bitmask = if rs < 31 { cpu.x(rs) } else { 0 }; + let bitmask = if size == 64 { + bitmask + } else { + bitmask & ((1 << size) - 1) + }; + let rt = opcode as u8 & 0x1f; + if rt != 31 { + cpu.update_x(rt, 0); + } + + let new_pc = cpu.pc().wrapping_add(4); + cpu.update_pc(new_pc); + cpu.commit(); + Some(bitmask) + } else { + tracelimit::warn_ratelimited!( + opcode = format!("{:x}", opcode), + "MNF fast path unknown opcode" + ); + None + } }