Skip to content

Commit 87b6429

Browse files
authored
[SNP Guest VSM] Implement VTL Return hypercall handling (#224)
Implements VTL return hypercall handling for guest vsm support. Tested: - SNP VM boots - SNP + VSM returns back to VTL 0
1 parent 84926d3 commit 87b6429

File tree

4 files changed

+140
-52
lines changed

4 files changed

+140
-52
lines changed

openhcl/virt_mshv_vtl/src/processor/hardware_cvm/mod.rs

Lines changed: 118 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ use hvdef::HvError;
1919
use hvdef::HvMapGpaFlags;
2020
use hvdef::HvRegisterVsmPartitionConfig;
2121
use hvdef::HvResult;
22+
use hvdef::HvVtlEntryReason;
23+
use hvdef::HvX64RegisterName;
2224
use hvdef::Vtl;
2325
use std::iter::zip;
2426
use virt::io::CpuIo;
27+
use virt::vp::AccessVpState;
28+
use virt::Processor;
2529
use zerocopy::FromZeroes;
2630

2731
impl<T: CpuIo, B: HardwareIsolatedBacking> UhHypercallHandler<'_, '_, T, B> {
@@ -200,7 +204,7 @@ impl<T: CpuIo, B: HardwareIsolatedBacking> UhHypercallHandler<'_, '_, T, B> {
200204
*target_vp.hv_start_enable_vtl_vp[vtl].lock() = Some(Box::new(*vp_context));
201205
target_vp.wake(vtl, WakeReason::HV_START_ENABLE_VP_VTL);
202206

203-
tracing::debug!("enabled vtl 1 on vp {}", vp_index);
207+
tracing::debug!(vp_index, "enabled vtl 1 on vp");
204208

205209
Ok(())
206210
}
@@ -238,18 +242,29 @@ impl<T: CpuIo, B: HardwareIsolatedBacking> UhHypercallHandler<'_, '_, T, B> {
238242
vtl: GuestVtl,
239243
) -> HvResult<hvdef::HvRegisterValue> {
240244
match name.into() {
241-
hvdef::HvX64RegisterName::VsmCodePageOffsets => Ok(u64::from(
245+
HvX64RegisterName::VsmCodePageOffsets => Ok(u64::from(
242246
self.vp.hv[vtl]
243247
.as_ref()
244248
.expect("hv emulator exists for cvm")
245249
.vsm_code_page_offsets(true),
246250
)
247251
.into()),
248-
hvdef::HvX64RegisterName::VsmCapabilities => Ok(u64::from(
252+
HvX64RegisterName::VsmCapabilities => Ok(u64::from(
249253
hvdef::HvRegisterVsmCapabilities::new().with_deny_lower_vtl_startup(true),
250254
)
251255
.into()),
252-
_ => Err(HvError::InvalidParameter),
256+
HvX64RegisterName::VpAssistPage => Ok(self.vp.hv[vtl]
257+
.as_ref()
258+
.expect("hv emulator exists for cvm")
259+
.vp_assist_page()
260+
.into()),
261+
_ => {
262+
tracing::error!(
263+
?name,
264+
"guest invoked getvpregister with unsupported register"
265+
);
266+
Err(HvError::InvalidParameter)
267+
}
253268
}
254269
}
255270

@@ -329,32 +344,6 @@ impl<T: CpuIo, B: HardwareIsolatedBacking> UhHypercallHandler<'_, '_, T, B> {
329344
// TODO should we check the all_virtual_address_spaces flag? we don't check this flag or the address space input arg anywhere in the hcl
330345
Ok(())
331346
}
332-
333-
pub fn hcvm_is_vtl_call_allowed(&self) -> bool {
334-
tracing::trace!("checking if vtl call is allowed");
335-
336-
// Only allowed from VTL 0
337-
if self.intercepted_vtl != GuestVtl::Vtl0 {
338-
false
339-
} else if !*self.vp.inner.hcvm_vtl1_enabled.lock() {
340-
// VTL 1 must be enabled on the vp
341-
false
342-
} else {
343-
true
344-
}
345-
}
346-
347-
pub fn hcvm_vtl_call(&mut self) {
348-
tracing::trace!("handling vtl call");
349-
350-
self.vp.switch_vtl(self.intercepted_vtl, GuestVtl::Vtl1);
351-
self.vp.backing.cvm_state_mut().exit_vtl = GuestVtl::Vtl1;
352-
353-
// TODO GUEST_VSM: Force reevaluation of the VTL 1 APIC in case delivery of
354-
// low-priority interrupts was suppressed while in VTL 0.
355-
356-
// TODO GUEST_VSM: Track which VTLs are runnable and mark VTL as runnable
357-
}
358347
}
359348

360349
impl<T, B: HardwareIsolatedBacking> hv1_hypercall::SetVpRegisters
@@ -380,20 +369,112 @@ impl<T, B: HardwareIsolatedBacking> hv1_hypercall::SetVpRegisters
380369
.map_err(|_| (HvError::InvalidParameter, 0))?;
381370

382371
for (i, reg) in registers.iter().enumerate() {
383-
if reg.name == hvdef::HvX64RegisterName::VsmPartitionConfig.into() {
384-
let value = HvRegisterVsmPartitionConfig::from(reg.value.as_u64());
385-
self.vp
386-
.set_vsm_partition_config(value, target_vtl)
387-
.map_err(|e| (e, i))?;
388-
} else {
389-
return Err((HvError::InvalidParameter, i));
372+
match HvX64RegisterName::from(reg.name) {
373+
HvX64RegisterName::VsmPartitionConfig => {
374+
self.vp
375+
.set_vsm_partition_config(
376+
HvRegisterVsmPartitionConfig::from(reg.value.as_u64()),
377+
target_vtl,
378+
)
379+
.map_err(|e| (e, i))?;
380+
}
381+
HvX64RegisterName::VpAssistPage => {
382+
self.vp.hv[target_vtl]
383+
.as_mut()
384+
.expect("has hv emulator")
385+
.msr_write(hvdef::HV_X64_MSR_VP_ASSIST_PAGE, reg.value.as_u64())
386+
.map_err(|_| (HvError::InvalidRegisterValue, 0))?;
387+
}
388+
_ => {
389+
tracing::error!(
390+
?reg,
391+
"guest invoked SetVpRegisters with unsupported register"
392+
);
393+
return Err((HvError::InvalidParameter, i));
394+
}
390395
}
391396
}
392397

393398
Ok(())
394399
}
395400
}
396401

402+
impl<T, B: HardwareIsolatedBacking> hv1_hypercall::VtlCall for UhHypercallHandler<'_, '_, T, B> {
403+
fn is_vtl_call_allowed(&self) -> bool {
404+
tracing::trace!("checking if vtl call is allowed");
405+
406+
// Only allowed from VTL 0
407+
if self.intercepted_vtl != GuestVtl::Vtl0 {
408+
false
409+
} else if !*self.vp.inner.hcvm_vtl1_enabled.lock() {
410+
// VTL 1 must be enabled on the vp
411+
false
412+
} else {
413+
true
414+
}
415+
}
416+
417+
fn vtl_call(&mut self) {
418+
tracing::trace!("handling vtl call");
419+
420+
B::switch_vtl_state(self.vp, self.intercepted_vtl, GuestVtl::Vtl1);
421+
self.vp.backing.cvm_state_mut().exit_vtl = GuestVtl::Vtl1;
422+
423+
// TODO GUEST VSM: reevaluate if the return reason should be set here or
424+
// during VTL 2 exit handling
425+
self.vp.hv[GuestVtl::Vtl1]
426+
.as_ref()
427+
.unwrap()
428+
.set_return_reason(HvVtlEntryReason::VTL_CALL)
429+
.expect("setting return reason cannot fail");
430+
431+
// TODO GUEST_VSM: Force reevaluation of the VTL 1 APIC in case delivery of
432+
// low-priority interrupts was suppressed while in VTL 0.
433+
434+
// TODO GUEST_VSM: Track which VTLs are runnable and mark VTL as runnable
435+
}
436+
}
437+
438+
impl<T, B: HardwareIsolatedBacking> hv1_hypercall::VtlReturn for UhHypercallHandler<'_, '_, T, B> {
439+
fn is_vtl_return_allowed(&self) -> bool {
440+
tracing::trace!("checking if vtl return is allowed");
441+
442+
// Only allowed from VTL 1
443+
self.intercepted_vtl != GuestVtl::Vtl0
444+
}
445+
446+
fn vtl_return(&mut self, fast: bool) {
447+
tracing::trace!("handling vtl return");
448+
449+
self.vp.unlock_tlb_lock(Vtl::Vtl1);
450+
451+
B::switch_vtl_state(self.vp, self.intercepted_vtl, GuestVtl::Vtl0);
452+
self.vp.backing.cvm_state_mut().exit_vtl = GuestVtl::Vtl0;
453+
454+
// TODO CVM GUEST_VSM:
455+
// - rewind interrupts
456+
// - reset VINA
457+
458+
if !fast {
459+
let [rax, rcx] = self.vp.hv[GuestVtl::Vtl1]
460+
.as_ref()
461+
.unwrap()
462+
.return_registers()
463+
.expect("getting return registers shouldn't fail");
464+
let mut vp_state = self.vp.access_state(Vtl::Vtl0);
465+
let mut registers = vp_state
466+
.registers()
467+
.expect("getting registers shouldn't fail");
468+
registers.rax = rax;
469+
registers.rcx = rcx;
470+
471+
vp_state
472+
.set_registers(&registers)
473+
.expect("setting registers shouldn't fail");
474+
}
475+
}
476+
}
477+
397478
impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
398479
fn set_vsm_partition_config(
399480
&mut self,

openhcl/virt_mshv_vtl/src/processor/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,11 +1112,6 @@ impl<'a, T: Backing> UhProcessor<'a, T> {
11121112

11131113
self.request_sint_notifications(vtl, pending_sints);
11141114
}
1115-
1116-
#[cfg_attr(guest_arch = "aarch64", allow(dead_code))]
1117-
fn switch_vtl(&mut self, source_vtl: GuestVtl, target_vtl: GuestVtl) {
1118-
T::switch_vtl_state(self, source_vtl, target_vtl);
1119-
}
11201115
}
11211116

11221117
fn signal_mnf(dev: &impl CpuIo, connection_id: u32) {

openhcl/virt_mshv_vtl/src/processor/snp/mod.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ impl<T: CpuIo> UhHypercallHandler<'_, '_, T, SnpBacked> {
634634
hv1_hypercall::HvX64EnableVpVtl,
635635
hv1_hypercall::HvExtQueryCapabilities,
636636
hv1_hypercall::HvVtlCall,
637+
hv1_hypercall::HvVtlReturn,
637638
hv1_hypercall::HvFlushVirtualAddressList,
638639
hv1_hypercall::HvFlushVirtualAddressListEx,
639640
hv1_hypercall::HvFlushVirtualAddressSpace,
@@ -2212,16 +2213,6 @@ impl<T: CpuIo> hv1_hypercall::RetargetDeviceInterrupt for UhHypercallHandler<'_,
22122213
}
22132214
}
22142215

2215-
impl<T: CpuIo> hv1_hypercall::VtlCall for UhHypercallHandler<'_, '_, T, SnpBacked> {
2216-
fn is_vtl_call_allowed(&self) -> bool {
2217-
self.hcvm_is_vtl_call_allowed()
2218-
}
2219-
2220-
fn vtl_call(&mut self) {
2221-
self.hcvm_vtl_call()
2222-
}
2223-
}
2224-
22252216
impl<T: CpuIo> hv1_hypercall::VtlSwitchOps for UhHypercallHandler<'_, '_, T, SnpBacked> {
22262217
fn advance_ip(&mut self) {
22272218
let is_64bit = self.vp.long_mode(self.intercepted_vtl);

vm/hv1/hv1_emulator/src/hv.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use super::synic::ProcessorSynic;
1010
use guestmem::GuestMemory;
1111
use guestmem::GuestMemoryError;
1212
use hvdef::HvRegisterVpAssistPage;
13+
use hvdef::HvVpVtlControl;
14+
use hvdef::HvVtlEntryReason;
1315
use hvdef::HV_PAGE_SIZE;
1416
use hvdef::HV_PAGE_SIZE_USIZE;
1517
use hvdef::HV_REFERENCE_TSC_SEQUENCE_INVALID;
@@ -456,6 +458,25 @@ impl ProcessorVtlHv {
456458
false
457459
}
458460
}
461+
462+
/// Get the register values to restore on vtl return
463+
pub fn return_registers(&self) -> Result<[u64; 2], GuestMemoryError> {
464+
let gpa = (self.vp_assist_page.gpa_page_number() * HV_PAGE_SIZE)
465+
+ offset_of!(hvdef::HvVpAssistPage, vtl_control) as u64;
466+
467+
let v: HvVpVtlControl = self.guest_memory.read_plain(gpa)?;
468+
469+
Ok(v.registers)
470+
}
471+
472+
/// Set the reason for the vtl return into the vp assist page
473+
pub fn set_return_reason(&self, reason: HvVtlEntryReason) -> Result<(), GuestMemoryError> {
474+
let gpa = (self.vp_assist_page.gpa_page_number() * HV_PAGE_SIZE)
475+
+ offset_of!(hvdef::HvVpAssistPage, vtl_control) as u64
476+
+ offset_of!(HvVpVtlControl, entry_reason) as u64;
477+
478+
self.guest_memory.write_plain(gpa, &(reason.0))
479+
}
459480
}
460481

461482
struct HypercallPage {

0 commit comments

Comments
 (0)