mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-09-19 13:30:56 +00:00
hypervisor: mshv: Refactor x86 emulator into different module
This is a preparatory work to make space for ARM64 emulator. There is no functional change in this commit, just moving code from one file to another. Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
This commit is contained in:
parent
ead4f767ff
commit
b73d94f969
@ -3,18 +3,15 @@
|
|||||||
// Copyright © 2020, Microsoft Corporation
|
// Copyright © 2020, Microsoft Corporation
|
||||||
//
|
//
|
||||||
|
|
||||||
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
use crate::arch::emulator::PlatformEmulator;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::emulator::{CpuStateManager, Emulator, EmulatorCpuState};
|
use crate::arch::x86::emulator::Emulator;
|
||||||
use crate::cpu;
|
use crate::cpu;
|
||||||
use crate::cpu::Vcpu;
|
|
||||||
use crate::hypervisor;
|
use crate::hypervisor;
|
||||||
|
use crate::mshv::emulator::MshvEmulatorContext;
|
||||||
use crate::vec_with_array_field;
|
use crate::vec_with_array_field;
|
||||||
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
use crate::vm::{self, InterruptSourceConfig, VmOps};
|
||||||
use crate::HypervisorType;
|
use crate::HypervisorType;
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use iced_x86::Register;
|
|
||||||
use mshv_bindings::*;
|
use mshv_bindings::*;
|
||||||
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
use mshv_ioctls::{set_registers_64, InterruptRequest, Mshv, NoDatamatch, VcpuFd, VmFd, VmType};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
@ -22,18 +19,15 @@ use std::collections::HashMap;
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use vfio_ioctls::VfioDeviceFd;
|
use vfio_ioctls::VfioDeviceFd;
|
||||||
use vm::DataMatch;
|
use vm::DataMatch;
|
||||||
|
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
mod snp_constants;
|
mod snp_constants;
|
||||||
// x86_64 dependencies
|
// x86_64 dependencies
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub mod x86_64;
|
pub mod x86_64;
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use snp_constants::*;
|
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::ClockData;
|
use crate::ClockData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
CpuState, IoEventAddress, IrqRoutingEntry, MpState, UserMemoryRegion,
|
||||||
USER_MEMORY_REGION_ADJUSTABLE, USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ,
|
USER_MEMORY_REGION_ADJUSTABLE, USER_MEMORY_REGION_EXECUTE, USER_MEMORY_REGION_READ,
|
||||||
@ -41,18 +35,16 @@ use crate::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
use igvm_defs::IGVM_VHS_SNP_ID_BLOCK;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
#[cfg(feature = "sev_snp")]
|
||||||
#[cfg(target_arch = "x86_64")]
|
use snp_constants::*;
|
||||||
pub use x86_64::VcpuMshvState;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub use x86_64::*;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::{CpuIdEntry, FpuState, MsrEntry};
|
pub use x86_64::*;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use x86_64::{emulator, VcpuMshvState};
|
||||||
|
|
||||||
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
const DIRTY_BITMAP_CLEAR_DIRTY: u64 = 0x4;
|
||||||
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
const DIRTY_BITMAP_SET_DIRTY: u64 = 0x8;
|
||||||
@ -1520,143 +1512,6 @@ impl MshvVcpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
struct MshvEmulatorContext<'a> {
|
|
||||||
vcpu: &'a MshvVcpu,
|
|
||||||
map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
impl<'a> MshvEmulatorContext<'a> {
|
|
||||||
// Do the actual gva -> gpa translation
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
|
|
||||||
if self.map.0 == gva {
|
|
||||||
return Ok(self.map.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (gpa, result_code) = self
|
|
||||||
.vcpu
|
|
||||||
.translate_gva(gva, flags.into())
|
|
||||||
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
|
||||||
|
|
||||||
match result_code {
|
|
||||||
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
|
|
||||||
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_memory_flags(
|
|
||||||
&self,
|
|
||||||
gva: u64,
|
|
||||||
data: &mut [u8],
|
|
||||||
flags: u32,
|
|
||||||
) -> Result<(), PlatformError> {
|
|
||||||
let gpa = self.translate(gva, flags)?;
|
|
||||||
debug!(
|
|
||||||
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
|
||||||
data.len(),
|
|
||||||
gva,
|
|
||||||
gpa
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
|
||||||
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
|
||||||
vm_ops
|
|
||||||
.mmio_read(gpa, data)
|
|
||||||
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
/// Platform emulation for Hyper-V
|
|
||||||
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|
||||||
type CpuState = EmulatorCpuState;
|
|
||||||
|
|
||||||
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
|
||||||
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
|
||||||
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
|
|
||||||
debug!(
|
|
||||||
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
|
||||||
data.len(),
|
|
||||||
gva,
|
|
||||||
gpa
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
|
||||||
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
|
||||||
vm_ops
|
|
||||||
.mmio_write(gpa, data)
|
|
||||||
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
|
||||||
if cpu_id != self.vcpu.vp_index as usize {
|
|
||||||
return Err(PlatformError::GetCpuStateFailure(anyhow!(
|
|
||||||
"CPU id mismatch {:?} {:?}",
|
|
||||||
cpu_id,
|
|
||||||
self.vcpu.vp_index
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let regs = self
|
|
||||||
.vcpu
|
|
||||||
.get_regs()
|
|
||||||
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
|
||||||
let sregs = self
|
|
||||||
.vcpu
|
|
||||||
.get_sregs()
|
|
||||||
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
|
||||||
|
|
||||||
debug!("mshv emulator: Getting new CPU state");
|
|
||||||
debug!("mshv emulator: {:#x?}", regs);
|
|
||||||
|
|
||||||
Ok(EmulatorCpuState { regs, sregs })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
|
|
||||||
if cpu_id != self.vcpu.vp_index as usize {
|
|
||||||
return Err(PlatformError::SetCpuStateFailure(anyhow!(
|
|
||||||
"CPU id mismatch {:?} {:?}",
|
|
||||||
cpu_id,
|
|
||||||
self.vcpu.vp_index
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("mshv emulator: Setting new CPU state");
|
|
||||||
debug!("mshv emulator: {:#x?}", state.regs);
|
|
||||||
|
|
||||||
self.vcpu
|
|
||||||
.set_regs(&state.regs)
|
|
||||||
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
|
|
||||||
self.vcpu
|
|
||||||
.set_sregs(&state.sregs)
|
|
||||||
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
|
||||||
let rip =
|
|
||||||
self.cpu_state(self.vcpu.vp_index as usize)?
|
|
||||||
.linearize(Register::CS, ip, false)?;
|
|
||||||
self.read_memory_flags(
|
|
||||||
rip,
|
|
||||||
instruction_bytes,
|
|
||||||
HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper over Mshv VM ioctls.
|
/// Wrapper over Mshv VM ioctls.
|
||||||
pub struct MshvVm {
|
pub struct MshvVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
|
145
hypervisor/src/mshv/x86_64/emulator.rs
Normal file
145
hypervisor/src/mshv/x86_64/emulator.rs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
|
||||||
|
//
|
||||||
|
// Copyright © 2024, Microsoft Corporation
|
||||||
|
//
|
||||||
|
|
||||||
|
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
||||||
|
use crate::arch::x86::emulator::{CpuStateManager, EmulatorCpuState};
|
||||||
|
use crate::cpu::Vcpu;
|
||||||
|
use crate::mshv::MshvVcpu;
|
||||||
|
use iced_x86::Register;
|
||||||
|
use mshv_bindings::*;
|
||||||
|
|
||||||
|
pub struct MshvEmulatorContext<'a> {
|
||||||
|
pub vcpu: &'a MshvVcpu,
|
||||||
|
pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MshvEmulatorContext<'a> {
|
||||||
|
// Do the actual gva -> gpa translation
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
|
||||||
|
if self.map.0 == gva {
|
||||||
|
return Ok(self.map.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (gpa, result_code) = self
|
||||||
|
.vcpu
|
||||||
|
.translate_gva(gva, flags.into())
|
||||||
|
.map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
|
||||||
|
|
||||||
|
match result_code {
|
||||||
|
hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
|
||||||
|
_ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_memory_flags(
|
||||||
|
&self,
|
||||||
|
gva: u64,
|
||||||
|
data: &mut [u8],
|
||||||
|
flags: u32,
|
||||||
|
) -> Result<(), PlatformError> {
|
||||||
|
let gpa = self.translate(gva, flags)?;
|
||||||
|
debug!(
|
||||||
|
"mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gpa
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
|
if vm_ops.guest_mem_read(gpa, data).is_err() {
|
||||||
|
vm_ops
|
||||||
|
.mmio_read(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Platform emulation for Hyper-V
|
||||||
|
impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
||||||
|
type CpuState = EmulatorCpuState;
|
||||||
|
|
||||||
|
fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
|
||||||
|
let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
|
||||||
|
debug!(
|
||||||
|
"mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
|
||||||
|
data.len(),
|
||||||
|
gva,
|
||||||
|
gpa
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(vm_ops) = &self.vcpu.vm_ops {
|
||||||
|
if vm_ops.guest_mem_write(gpa, data).is_err() {
|
||||||
|
vm_ops
|
||||||
|
.mmio_write(gpa, data)
|
||||||
|
.map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
||||||
|
if cpu_id != self.vcpu.vp_index as usize {
|
||||||
|
return Err(PlatformError::GetCpuStateFailure(anyhow!(
|
||||||
|
"CPU id mismatch {:?} {:?}",
|
||||||
|
cpu_id,
|
||||||
|
self.vcpu.vp_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let regs = self
|
||||||
|
.vcpu
|
||||||
|
.get_regs()
|
||||||
|
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
||||||
|
let sregs = self
|
||||||
|
.vcpu
|
||||||
|
.get_sregs()
|
||||||
|
.map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
|
||||||
|
|
||||||
|
debug!("mshv emulator: Getting new CPU state");
|
||||||
|
debug!("mshv emulator: {:#x?}", regs);
|
||||||
|
|
||||||
|
Ok(EmulatorCpuState { regs, sregs })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
|
||||||
|
if cpu_id != self.vcpu.vp_index as usize {
|
||||||
|
return Err(PlatformError::SetCpuStateFailure(anyhow!(
|
||||||
|
"CPU id mismatch {:?} {:?}",
|
||||||
|
cpu_id,
|
||||||
|
self.vcpu.vp_index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("mshv emulator: Setting new CPU state");
|
||||||
|
debug!("mshv emulator: {:#x?}", state.regs);
|
||||||
|
|
||||||
|
self.vcpu
|
||||||
|
.set_regs(&state.regs)
|
||||||
|
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
|
||||||
|
self.vcpu
|
||||||
|
.set_sregs(&state.sregs)
|
||||||
|
.map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
let rip =
|
||||||
|
self.cpu_state(self.vcpu.vp_index as usize)?
|
||||||
|
.linearize(Register::CS, ip, false)?;
|
||||||
|
self.read_memory_flags(
|
||||||
|
rip,
|
||||||
|
instruction_bytes,
|
||||||
|
HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ use crate::arch::x86::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub mod emulator;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
/// Export generically-named wrappers of mshv_bindings for Unix-based platforms
|
||||||
///
|
///
|
||||||
|
Loading…
Reference in New Issue
Block a user