From 461e31e6d869a0d4adf756c0c08702a33f535f98 Mon Sep 17 00:00:00 2001 From: Jinank Jain Date: Wed, 12 Feb 2025 11:09:34 +0000 Subject: [PATCH] hypervisor: Instruction emulator for ARM64 guest on MSHV Currently it would be using the syndrome register for instruction decoding which is what KVM has been using in-kernel to decode instructions for ARM64 guests. In future, it could be extended with an actual instruction emulator if required. But most Linux guests works well with the instruction decoder using syndrome register. Signed-off-by: Jinank Jain --- hypervisor/src/mshv/aarch64/emulator.rs | 113 ++++++++++++++++++++++++ hypervisor/src/mshv/aarch64/mod.rs | 1 + 2 files changed, 114 insertions(+) create mode 100644 hypervisor/src/mshv/aarch64/emulator.rs diff --git a/hypervisor/src/mshv/aarch64/emulator.rs b/hypervisor/src/mshv/aarch64/emulator.rs new file mode 100644 index 000000000..08311aa93 --- /dev/null +++ b/hypervisor/src/mshv/aarch64/emulator.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause +// +// Copyright © 2025, Microsoft Corporation +// + +use crate::arch::aarch64::regs::{EsrEl2, ExceptionClass, IssDataAbort}; +use crate::arch::emulator::PlatformError; +use crate::cpu::Vcpu; +use crate::mshv::MshvVcpu; + +pub struct MshvEmulatorContext<'a> { + pub vcpu: &'a MshvVcpu, + pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor + pub syndrome: u64, + pub instruction_bytes: [u8; 4], + pub instruction_byte_count: u8, + pub interruption_pending: bool, + pub pc: u64, +} + +pub struct Emulator<'a> { + pub context: MshvEmulatorContext<'a>, +} + +impl<'a> Emulator<'a> { + /// Create a new emulator instance. + pub fn new(context: MshvEmulatorContext<'a>) -> Self { + Emulator { context } + } + + /// Decode the instruction using the syndrome register. + pub fn emulate_with_syndrome(&mut self) -> Result { + let esr_el2 = EsrEl2::from(self.context.syndrome); + if !matches!( + ExceptionClass(esr_el2.ec()), + ExceptionClass::DATA_ABORT | ExceptionClass::DATA_ABORT_LOWER + ) { + return Ok(false); + } + + let iss = IssDataAbort::from(esr_el2.iss()); + if !iss.isv() { + return Ok(false); + } + let len = 1 << iss.sas(); + let sign_extend = iss.sse(); + let reg_index = iss.srt(); + + let mut regs = self + .context + .vcpu + .get_regs() + .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?; + let mut gprs = regs.get_regs(); + + if iss.wnr() { + let data: [u8; 8] = match reg_index { + 0..=30 => gprs[reg_index as usize], + 31 => 0u64, + _ => unreachable!(), + } + .to_ne_bytes(); + + if let Some(vm_ops) = &self.context.vcpu.vm_ops { + vm_ops + .mmio_write(self.context.map.1, &data[0..len]) + .map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?; + } + } else { + let mut data = [0_u8; 8]; + if let Some(vm_ops) = &self.context.vcpu.vm_ops { + vm_ops + .mmio_read(self.context.map.1, &mut data[0..len]) + .map_err(|e| PlatformError::MemoryReadFailure(e.into()))?; + } + + let mut data = u64::from_ne_bytes(data); + if sign_extend { + let shift = 64 - len * 8; + data = ((data as i64) << shift >> shift) as u64; + if !iss.sf() { + data &= 0xffffffff; + } + } + gprs[reg_index as usize] = data; + } + + let pc = regs.get_pc(); + regs.set_pc(if esr_el2.il() { pc + 4 } else { pc + 2 }); + regs.set_regs(gprs); + + self.context + .vcpu + .set_regs(®s) + .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?; + + Ok(true) + } + + /// Emulate the instruction. + pub fn emulate(&mut self) -> Result<(), PlatformError> { + match self.emulate_with_syndrome() { + Ok(true) => Ok(()), + Ok(false) => Err(PlatformError::InvalidState(anyhow!( + "Failed to decode instruction using syndrome register" + ))), + Err(e) => Err(e), + } + // TODO: Add support for instruction decoding in case of failure from + // decode_with_syndrome. This will require aarch64 instruction emulator + // implementation like x86_64. + } +} diff --git a/hypervisor/src/mshv/aarch64/mod.rs b/hypervisor/src/mshv/aarch64/mod.rs index 79da40738..b24043b00 100644 --- a/hypervisor/src/mshv/aarch64/mod.rs +++ b/hypervisor/src/mshv/aarch64/mod.rs @@ -2,6 +2,7 @@ // // Copyright © 2025, Microsoft Corporation // +pub mod emulator; pub mod gic; use std::fmt;