Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVM: refactor ohcl/ovmm emulator for flexible register retrieval and update #482

Merged
merged 46 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
01b9953
emulator changes, rebased on emulator cache commit
babayet2 Jan 15, 2025
3bc2119
remove register wrappers
babayet2 Dec 11, 2024
6c768af
emulator changes, rebased on emulator cache commit
babayet2 Dec 11, 2024
2f7168c
use state object for all backends
babayet2 Dec 13, 2024
b2151be
update names for clarity, fix compiler warnings
babayet2 Dec 14, 2024
c119eb8
fmt fix
babayet2 Dec 14, 2024
d6fdf00
comments
babayet2 Dec 14, 2024
2e81bc2
compiling again after arm rebase
babayet2 Dec 21, 2024
9027697
remove unnecessary iced_x86 imports
babayet2 Dec 26, 2024
ad18d18
restrict bitness functions to x86emu crate
babayet2 Dec 27, 2024
2a8bb18
fmt fix
babayet2 Dec 27, 2024
d919a11
seperate out imports
babayet2 Dec 27, 2024
7950fe2
remove emulation cache for snp
babayet2 Dec 27, 2024
24baa6b
kill load_registers for tdx/snp
babayet2 Dec 27, 2024
8142323
remove gp_sign_extend from EmulatorSupport trait
babayet2 Dec 30, 2024
44d5d19
pass cache from backing into emulate call
babayet2 Dec 31, 2024
7fc70bd
use cache for mshv
babayet2 Dec 31, 2024
7762a17
remove generic "emulate fast path", fmt fix
babayet2 Dec 31, 2024
bb8e62b
move register flushes back to original locations
babayet2 Dec 31, 2024
a473d32
temporarily stub out mshv impl for non cvm impls
babayet2 Jan 3, 2025
7a51736
replace segment/gp usize indexes with enums
babayet2 Jan 4, 2025
f7757b3
only cache expensive get/set in mshv emu
babayet2 Jan 4, 2025
f923911
misc feedback from Steven
babayet2 Jan 4, 2025
adb44d8
fmt fix
babayet2 Jan 4, 2025
904e915
non-cvm emulator sup impls compiling
babayet2 Jan 7, 2025
39dbfd8
mshv compiler errors, and flush rsp to cpu_context
babayet2 Jan 7, 2025
cbcdd87
begin refactor of unit tests
babayet2 Jan 10, 2025
ac3c524
restore functionality to common x86emu test code
babayet2 Jan 10, 2025
905568f
all unit tests compiling, some failing
babayet2 Jan 11, 2025
cd1811b
bring back original cpu state for seg tests
babayet2 Jan 11, 2025
8114133
fix incorrect mask for 8-bit registers
babayet2 Jan 12, 2025
7984508
fmt fix
babayet2 Jan 12, 2025
3edff24
flush in emulator fast path
babayet2 Jan 12, 2025
3c7c986
remove double flush
babayet2 Jan 12, 2025
8f5ef72
fix fuzzer
babayet2 Jan 14, 2025
a917a40
missing 'let'
babayet2 Jan 14, 2025
130b781
tdx emulator should only support vtl0
babayet2 Jan 15, 2025
b5716e8
feedback on tests
babayet2 Jan 14, 2025
6433836
don't drop errors
babayet2 Jan 15, 2025
a93d107
virt_support_x86emu tests and error handling
babayet2 Jan 15, 2025
48435b7
tdx: vtl specific efer/cr0, fmt fix
babayet2 Jan 15, 2025
354b02e
building on arm
babayet2 Jan 16, 2025
7da9e79
clippy, clean error handling, doc comments
babayet2 Jan 16, 2025
c44d238
restore mutability comments for snp
babayet2 Jan 16, 2025
198cebe
Fix last few clippy lints
smalis-msft Jan 16, 2025
0db1832
Cleanups
smalis-msft Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9072,7 +9072,6 @@ dependencies = [
name = "x86emu"
version = "0.0.0"
dependencies = [
"arbitrary",
"futures",
"iced-x86",
"thiserror 2.0.0",
Expand Down
28 changes: 11 additions & 17 deletions openhcl/virt_mshv_vtl/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ mod private {

pub trait BackingPrivate: 'static + Sized + InspectMut + Sized {
type HclBacking: hcl::ioctl::Backing;
type EmulationCache: Default;
type EmulationCache;
type Shared;

fn shared(shared: &BackingShared) -> &Self::Shared;
Expand Down Expand Up @@ -1029,24 +1029,21 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
devices: &D,
interruption_pending: bool,
vtl: GuestVtl,
cache: T::EmulationCache,
) -> Result<(), VpHaltReason<UhRunVpError>>
where
for<'b> UhEmulationState<'b, 'a, D, T>:
virt_support_x86emu::emulate::EmulatorSupport<Error = UhRunVpError>,
{
let guest_memory = &self.partition.gm[vtl];
virt_support_x86emu::emulate::emulate(
&mut UhEmulationState {
vp: &mut *self,
interruption_pending,
devices,
vtl,
cache: T::EmulationCache::default(),
},
guest_memory,
let mut emulation_state = UhEmulationState {
vp: &mut *self,
interruption_pending,
devices,
)
.await
vtl,
cache,
};
virt_support_x86emu::emulate::emulate(&mut emulation_state, guest_memory, devices).await
}

/// Emulates an instruction due to a memory access exit.
Expand All @@ -1056,6 +1053,7 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
devices: &D,
intercept_state: &aarch64emu::InterceptState,
vtl: GuestVtl,
cache: T::EmulationCache,
) -> Result<(), VpHaltReason<UhRunVpError>>
where
for<'b> UhEmulationState<'b, 'a, D, T>:
Expand All @@ -1068,7 +1066,7 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
interruption_pending: intercept_state.interruption_pending,
devices,
vtl,
cache: T::EmulationCache::default(),
cache,
},
intercept_state,
guest_memory,
Expand Down Expand Up @@ -1171,10 +1169,6 @@ struct UhEmulationState<'a, 'b, T: CpuIo, U: Backing> {
interruption_pending: bool,
devices: &'a T,
vtl: GuestVtl,
#[cfg_attr(
guest_arch = "x86_64",
expect(dead_code, reason = "not used yet in x86_64")
)]
cache: U::EmulationCache,
}

Expand Down
4 changes: 3 additions & 1 deletion openhcl/virt_mshv_vtl/src/processor/mshv/arm64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,9 @@ impl UhProcessor<'_, HypervisorBackedArm64> {
Self::intercepted_vtl(&message.header).map_err(|UnsupportedGuestVtl(vtl)| {
VpHaltReason::InvalidVmState(UhRunVpError::InvalidInterceptedVtl(vtl))
})?;
self.emulate(dev, &intercept_state, intercepted_vtl).await?;
let cache = UhCpuStateCache::default();
self.emulate(dev, &intercept_state, intercepted_vtl, cache)
.await?;
Ok(())
}

Expand Down
224 changes: 143 additions & 81 deletions openhcl/virt_mshv_vtl/src/processor/mshv/x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ use x86defs::xsave::XsaveHeader;
use x86defs::xsave::XFEATURE_SSE;
use x86defs::xsave::XFEATURE_X87;
use x86defs::RFlags;
use x86defs::SegmentRegister;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
Expand Down Expand Up @@ -122,10 +123,23 @@ struct ProcessorStatsX86 {
exception_intercept: Counter,
}

pub struct MshvEmulationCache {
rsp: u64,
es: SegmentRegister,
ds: SegmentRegister,
fs: SegmentRegister,
gs: SegmentRegister,
ss: SegmentRegister,
cr0: u64,
efer: u64,
rip: u64,
rflags: RFlags,
}

impl BackingPrivate for HypervisorBackedX86 {
type HclBacking = MshvX64;
type Shared = ();
type EmulationCache = ();
type EmulationCache = MshvEmulationCache;

fn shared(_: &BackingShared) -> &Self::Shared {
&()
Expand Down Expand Up @@ -700,27 +714,34 @@ impl<'a, 'b> InterceptHandler<'a, 'b> {
== self.vp.partition.monitor_page.gpa()
&& message.header.intercept_access_type == HvInterceptAccessType::WRITE
{
let instruction_bytes = message.instruction_bytes;
let instruction_bytes = &instruction_bytes[..message.instruction_byte_count as usize];
let tlb_lock_held = message.memory_access_info.gva_gpa_valid()
|| message.memory_access_info.tlb_locked();
let mut state = self.vp.emulator_state(self.intercepted_vtl);
let guest_memory = &self.vp.partition.gm[self.intercepted_vtl];
let cache = self.vp.emulation_cache(self.intercepted_vtl);
let mut emulation_state = UhEmulationState {
vp: &mut *self.vp,
interruption_pending,
devices: dev,
vtl: self.intercepted_vtl,
cache,
};
if let Some(bit) = virt_support_x86emu::emulate::emulate_mnf_write_fast_path(
instruction_bytes,
&mut state,
&mut emulation_state,
guest_memory,
dev,
interruption_pending,
tlb_lock_held,
) {
self.vp.set_emulator_state(self.intercepted_vtl, &state);
)? {
if let Some(connection_id) = self.vp.partition.monitor_page.write_bit(bit) {
signal_mnf(dev, connection_id);
}
return Ok(());
}
}

let cache = self.vp.emulation_cache(self.intercepted_vtl);
smalis-msft marked this conversation as resolved.
Show resolved Hide resolved
self.vp
.emulate(dev, interruption_pending, self.intercepted_vtl)
.emulate(dev, interruption_pending, self.intercepted_vtl, cache)
.await?;
Ok(())
}
Expand All @@ -741,8 +762,9 @@ impl<'a, 'b> InterceptHandler<'a, 'b> {
let interruption_pending = message.header.execution_state.interruption_pending();

if message.access_info.string_op() || message.access_info.rep_prefix() {
let cache = self.vp.emulation_cache(self.intercepted_vtl);
self.vp
.emulate(dev, interruption_pending, self.intercepted_vtl)
.emulate(dev, interruption_pending, self.intercepted_vtl, cache)
.await
} else {
let next_rip = next_rip(&message.header);
Expand Down Expand Up @@ -1137,62 +1159,6 @@ impl UhProcessor<'_, HypervisorBackedX86> {
.expect("set_vp_register should succeed for pending event");
}

fn emulator_state(&mut self, vtl: GuestVtl) -> x86emu::CpuState {
const NAMES: &[HvX64RegisterName] = &[
HvX64RegisterName::Rsp,
HvX64RegisterName::Es,
HvX64RegisterName::Ds,
HvX64RegisterName::Fs,
HvX64RegisterName::Gs,
HvX64RegisterName::Ss,
HvX64RegisterName::Cr0,
HvX64RegisterName::Efer,
];
let mut values = [FromZeroes::new_zeroed(); NAMES.len()];
self.runner
.get_vp_registers(vtl, NAMES, &mut values)
.expect("register query should not fail");

let [rsp, es, ds, fs, gs, ss, cr0, efer] = values;

let mut gps = self.runner.cpu_context().gps;
gps[x86emu::CpuState::RSP] = rsp.as_u64();

let message = self.runner.exit_message();
let header = HvX64InterceptMessageHeader::ref_from_prefix(message.payload()).unwrap();

x86emu::CpuState {
gps,
segs: [
from_seg(es.into()),
from_seg(header.cs_segment),
from_seg(ss.into()),
from_seg(ds.into()),
from_seg(fs.into()),
from_seg(gs.into()),
],
rip: header.rip,
rflags: header.rflags.into(),
cr0: cr0.as_u64(),
efer: efer.as_u64(),
}
}

fn set_emulator_state(&mut self, vtl: GuestVtl, state: &x86emu::CpuState) {
self.runner
.set_vp_registers(
vtl,
[
(HvX64RegisterName::Rip, state.rip),
(HvX64RegisterName::Rflags, state.rflags.into()),
(HvX64RegisterName::Rsp, state.gps[x86emu::CpuState::RSP]),
],
)
.unwrap();

self.runner.cpu_context_mut().gps = state.gps;
}

fn set_vsm_partition_config(
&mut self,
vtl: GuestVtl,
Expand Down Expand Up @@ -1297,11 +1263,64 @@ impl UhProcessor<'_, HypervisorBackedX86> {

Ok(())
}

///Eagerly load registers for emulation
babayet2 marked this conversation as resolved.
Show resolved Hide resolved
///Typically we load expensive registers lazily, however some registers will always be used,
///and the underlying ioctl supports batching multiple register retrievals into a single call
fn emulation_cache(&mut self, vtl: GuestVtl) -> MshvEmulationCache {
const NAMES: &[HvX64RegisterName] = &[
HvX64RegisterName::Rsp,
HvX64RegisterName::Es,
HvX64RegisterName::Ds,
HvX64RegisterName::Fs,
HvX64RegisterName::Gs,
HvX64RegisterName::Ss,
HvX64RegisterName::Cr0,
HvX64RegisterName::Efer,
];
let mut values = [FromZeroes::new_zeroed(); NAMES.len()];
self.runner
.get_vp_registers(vtl, NAMES, &mut values)
.expect("register query should not fail");

let [rsp, es, ds, fs, gs, ss, cr0, efer] = values;

let message = self.runner.exit_message();
let header = HvX64InterceptMessageHeader::ref_from_prefix(message.payload()).unwrap();

MshvEmulationCache {
rsp: rsp.as_u64(),
es: from_seg(es.into()),
ds: from_seg(ds.into()),
fs: from_seg(fs.into()),
gs: from_seg(gs.into()),
ss: from_seg(ss.into()),
cr0: cr0.as_u64(),
efer: efer.as_u64(),
rip: header.rip,
rflags: header.rflags.into(),
}
}
}

impl<T: CpuIo> EmulatorSupport for UhEmulationState<'_, '_, T, HypervisorBackedX86> {
type Error = UhRunVpError;

fn flush(&mut self) -> Result<(), Self::Error> {
self.vp
.runner
.set_vp_registers(
self.vtl,
[
(HvX64RegisterName::Rip, self.cache.rip),
(HvX64RegisterName::Rflags, self.cache.rflags.into()),
(HvX64RegisterName::Rsp, self.cache.rsp),
],
)
.unwrap();
Ok(())
}

fn vp_index(&self) -> VpIndex {
self.vp.vp_index()
}
Expand All @@ -1310,15 +1329,69 @@ impl<T: CpuIo> EmulatorSupport for UhEmulationState<'_, '_, T, HypervisorBackedX
self.vp.partition.caps.vendor
}

fn state(&mut self) -> Result<x86emu::CpuState, Self::Error> {
Ok(self.vp.emulator_state(self.vtl))
fn gp(&mut self, reg: x86emu::Gp) -> u64 {
match reg {
x86emu::Gp::RSP => self.cache.rsp,
_ => self.vp.runner.cpu_context().gps[reg as usize],
}
}

fn set_gp(&mut self, reg: x86emu::Gp, v: u64) {
if reg == x86emu::Gp::RSP {
self.cache.rsp = v;
}
self.vp.runner.cpu_context_mut().gps[reg as usize] = v;
}

fn set_state(&mut self, state: x86emu::CpuState) -> Result<(), Self::Error> {
self.vp.set_emulator_state(self.vtl, &state);
fn xmm(&mut self, index: usize) -> u128 {
u128::from_le_bytes(self.vp.runner.cpu_context().fx_state.xmm[index])
}

fn set_xmm(&mut self, index: usize, v: u128) -> Result<(), Self::Error> {
self.vp.runner.cpu_context_mut().fx_state.xmm[index] = v.to_le_bytes();
Ok(())
}

fn rip(&mut self) -> u64 {
self.cache.rip
}

fn set_rip(&mut self, v: u64) {
self.cache.rip = v;
}

fn segment(&mut self, index: x86emu::Segment) -> SegmentRegister {
match index {
x86emu::Segment::CS => {
let message = self.vp.runner.exit_message();
let header =
HvX64InterceptMessageHeader::ref_from_prefix(message.payload()).unwrap();
from_seg(header.cs_segment)
}
x86emu::Segment::ES => self.cache.es,
x86emu::Segment::SS => self.cache.ss,
x86emu::Segment::DS => self.cache.ds,
x86emu::Segment::FS => self.cache.fs,
x86emu::Segment::GS => self.cache.gs,
}
}

fn efer(&mut self) -> u64 {
self.cache.efer
}

fn cr0(&mut self) -> u64 {
self.cache.cr0
}

fn rflags(&mut self) -> RFlags {
self.cache.rflags
}

fn set_rflags(&mut self, v: RFlags) {
self.cache.rflags = v;
}

fn instruction_bytes(&self) -> &[u8] {
let message = self.vp.runner.exit_message();
match message.header.typ {
Expand Down Expand Up @@ -1521,17 +1594,6 @@ impl<T: CpuIo> EmulatorSupport for UhEmulationState<'_, '_, T, HypervisorBackedX
.expect("set_vp_registers hypercall for setting pending event should not fail");
}

fn get_xmm(&mut self, reg: usize) -> Result<u128, Self::Error> {
Ok(u128::from_le_bytes(
self.vp.runner.cpu_context().fx_state.xmm[reg],
))
}

fn set_xmm(&mut self, reg: usize, value: u128) -> Result<(), Self::Error> {
self.vp.runner.cpu_context_mut().fx_state.xmm[reg] = value.to_le_bytes();
Ok(())
}

fn check_monitor_write(&self, gpa: u64, bytes: &[u8]) -> bool {
self.vp
.partition
Expand Down
Loading
Loading