Skip to content

Commit 19e0e7a

Browse files
authored
cvm guest vsm: install intercept hypercall handling (#1265)
Handles the install intercept hypercall on io ports for guest vsm support in cvms. Tested: SNP +/- guest vsm boots
1 parent 1f63b4a commit 19e0e7a

File tree

6 files changed

+333
-78
lines changed

6 files changed

+333
-78
lines changed

openhcl/virt_mshv_vtl/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ impl<T: Inspect> GuestVsmState<T> {
519519
}
520520
}
521521

522-
#[derive(Default, Inspect)]
522+
#[derive(Inspect)]
523523
struct CvmVtl1State {
524524
/// Whether VTL 1 has been enabled on any vp
525525
enabled_on_any_vp: bool,
@@ -531,6 +531,25 @@ struct CvmVtl1State {
531531
pub mbec_enabled: bool,
532532
/// Whether shadow supervisor stack is enabled.
533533
pub shadow_supervisor_stack_enabled: bool,
534+
#[inspect(with = "|bb| inspect::iter_by_index(bb.iter().map(|v| *v))")]
535+
io_read_intercepts: BitBox<u64>,
536+
#[inspect(with = "|bb| inspect::iter_by_index(bb.iter().map(|v| *v))")]
537+
io_write_intercepts: BitBox<u64>,
538+
}
539+
540+
#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
541+
impl CvmVtl1State {
542+
fn new(mbec_enabled: bool) -> Self {
543+
Self {
544+
enabled_on_any_vp: false,
545+
zero_memory_on_reset: false,
546+
deny_lower_vtl_startup: false,
547+
mbec_enabled,
548+
shadow_supervisor_stack_enabled: false,
549+
io_read_intercepts: BitVec::repeat(false, u16::MAX as usize + 1).into_boxed_bitslice(),
550+
io_write_intercepts: BitVec::repeat(false, u16::MAX as usize + 1).into_boxed_bitslice(),
551+
}
552+
}
534553
}
535554

536555
#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]

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

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use hvdef::HvVtlEntryReason;
3333
use hvdef::HvX64PendingExceptionEvent;
3434
use hvdef::HvX64RegisterName;
3535
use hvdef::Vtl;
36+
use hvdef::hypercall::HV_INTERCEPT_ACCESS_MASK_READ;
37+
use hvdef::hypercall::HV_INTERCEPT_ACCESS_MASK_WRITE;
3638
use hvdef::hypercall::HostVisibilityType;
3739
use hvdef::hypercall::HvFlushFlags;
3840
use hvdef::hypercall::TranslateGvaResultCode;
@@ -1230,10 +1232,7 @@ impl<T, B: HardwareIsolatedBacking> hv1_hypercall::EnablePartitionVtl
12301232
)?;
12311233

12321234
*gvsm_state = GuestVsmState::Enabled {
1233-
vtl1: CvmVtl1State {
1234-
mbec_enabled: flags.enable_mbec(),
1235-
..Default::default()
1236-
},
1235+
vtl1: CvmVtl1State::new(flags.enable_mbec()),
12371236
};
12381237

12391238
let protector = &self.vp.cvm_partition().isolated_memory_protector;
@@ -2049,12 +2048,12 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
20492048
) -> bool {
20502049
let send_intercept = self.cvm_is_protected_register_write(vtl, reg, value);
20512050
if send_intercept {
2052-
let message_state = B::intercept_message_state(self, vtl);
2051+
let message_state = B::intercept_message_state(self, vtl, false);
20532052

20542053
self.send_intercept_message(
20552054
GuestVtl::Vtl1,
20562055
&crate::processor::InterceptMessageType::Register { reg, value }
2057-
.generate_hv_message(self.vp_index(), vtl, message_state),
2056+
.generate_hv_message(self.vp_index(), vtl, message_state, false),
20582057
);
20592058
}
20602059

@@ -2083,7 +2082,7 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
20832082
// Note: writes to X86X_IA32_MSR_MISC_ENABLE are dropped, so don't
20842083
// need to check the mask.
20852084

2086-
let generate_intercept = match msr {
2085+
let send_intercept = match msr {
20872086
x86defs::X86X_MSR_LSTAR => configured_intercepts.msr_lstar_write(),
20882087
x86defs::X86X_MSR_STAR => configured_intercepts.msr_star_write(),
20892088
x86defs::X86X_MSR_CSTAR => configured_intercepts.msr_cstar_write(),
@@ -2105,15 +2104,16 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
21052104
_ => false,
21062105
};
21072106

2108-
if generate_intercept {
2109-
let message_state = B::intercept_message_state(self, vtl);
2107+
if send_intercept {
2108+
let message_state = B::intercept_message_state(self, vtl, false);
21102109

21112110
self.send_intercept_message(
21122111
GuestVtl::Vtl1,
21132112
&crate::processor::InterceptMessageType::Msr { msr }.generate_hv_message(
21142113
self.vp_index(),
21152114
vtl,
21162115
message_state,
2116+
false,
21172117
),
21182118
);
21192119

@@ -2123,6 +2123,61 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> {
21232123
false
21242124
}
21252125

2126+
/// Checks if a higher VTL registered for intercepts on io port and sends
2127+
/// the intercept as required.
2128+
///
2129+
/// If an intercept message is posted then no further processing is
2130+
/// required. The instruction pointer should not be advanced, since the
2131+
/// instruction pointer must continue to point to the instruction that
2132+
/// generated the intercept.
2133+
pub(crate) fn cvm_try_protect_io_port_access(
2134+
&mut self,
2135+
vtl: GuestVtl,
2136+
port_number: u16,
2137+
is_read: bool,
2138+
access_size: u8,
2139+
string_access: bool,
2140+
rep_access: bool,
2141+
) -> bool {
2142+
if vtl == GuestVtl::Vtl0 {
2143+
let send_intercept = {
2144+
if let GuestVsmState::Enabled { vtl1 } = &*self.cvm_partition().guest_vsm.read() {
2145+
if is_read {
2146+
vtl1.io_read_intercepts[port_number as usize]
2147+
} else {
2148+
vtl1.io_write_intercepts[port_number as usize]
2149+
}
2150+
} else {
2151+
false
2152+
}
2153+
};
2154+
2155+
if send_intercept {
2156+
let message_state = B::intercept_message_state(self, vtl, true);
2157+
2158+
self.send_intercept_message(
2159+
GuestVtl::Vtl1,
2160+
&crate::processor::InterceptMessageType::IoPort {
2161+
port_number,
2162+
access_size,
2163+
string_access,
2164+
rep_access,
2165+
}
2166+
.generate_hv_message(
2167+
self.vp_index(),
2168+
vtl,
2169+
message_state,
2170+
is_read,
2171+
),
2172+
);
2173+
2174+
return true;
2175+
}
2176+
}
2177+
2178+
false
2179+
}
2180+
21262181
fn cvm_send_synthetic_cluster_ipi(
21272182
&mut self,
21282183
vtl: GuestVtl,
@@ -2354,3 +2409,66 @@ impl<T, B: HardwareIsolatedBacking> hv1_hypercall::SendSyntheticClusterIpiEx
23542409
.cvm_send_synthetic_cluster_ipi(target_vtl, vector, processor_set)
23552410
}
23562411
}
2412+
2413+
impl<T, B: HardwareIsolatedBacking> hv1_hypercall::InstallIntercept
2414+
for UhHypercallHandler<'_, '_, T, B>
2415+
{
2416+
fn install_intercept(
2417+
&mut self,
2418+
partition_id: u64,
2419+
access_type_mask: u32,
2420+
intercept_type: hvdef::hypercall::HvInterceptType,
2421+
intercept_parameters: hvdef::hypercall::HvInterceptParameters,
2422+
) -> HvResult<()> {
2423+
tracing::debug!(
2424+
vp_index = self.vp.vp_index().index(),
2425+
?access_type_mask,
2426+
?intercept_type,
2427+
?intercept_parameters,
2428+
"HvInstallIntercept"
2429+
);
2430+
2431+
if partition_id != hvdef::HV_PARTITION_ID_SELF {
2432+
return Err(HvError::AccessDenied);
2433+
}
2434+
2435+
if self.intercepted_vtl == GuestVtl::Vtl0 {
2436+
return Err(HvError::AccessDenied);
2437+
}
2438+
2439+
match intercept_type {
2440+
hvdef::hypercall::HvInterceptType::HvInterceptTypeX64IoPort => {
2441+
if access_type_mask
2442+
& !(HV_INTERCEPT_ACCESS_MASK_READ | HV_INTERCEPT_ACCESS_MASK_WRITE)
2443+
!= 0
2444+
{
2445+
return Err(HvError::InvalidParameter);
2446+
}
2447+
2448+
let mut gvsm_lock = self.vp.cvm_partition().guest_vsm.write();
2449+
2450+
let GuestVsmState::Enabled { vtl1, .. } = &mut *gvsm_lock else {
2451+
return Err(HvError::InvalidVtlState);
2452+
};
2453+
2454+
let io_port = intercept_parameters.io_port() as usize;
2455+
2456+
vtl1.io_read_intercepts.set(
2457+
io_port,
2458+
access_type_mask & HV_INTERCEPT_ACCESS_MASK_READ != 0,
2459+
);
2460+
2461+
vtl1.io_write_intercepts.set(
2462+
io_port,
2463+
access_type_mask & HV_INTERCEPT_ACCESS_MASK_WRITE != 0,
2464+
);
2465+
2466+
// TODO GUEST VSM: flush io port accesses on other VPs before
2467+
// returning back to VTL 0
2468+
}
2469+
_ => return Err(HvError::InvalidParameter),
2470+
}
2471+
2472+
Ok(())
2473+
}
2474+
}

openhcl/virt_mshv_vtl/src/processor/mod.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ enum InterceptMessageType {
294294
Msr {
295295
msr: u32,
296296
},
297+
#[cfg(guest_arch = "x86_64")]
298+
IoPort {
299+
port_number: u16,
300+
access_size: u8,
301+
string_access: bool,
302+
rep_access: bool,
303+
},
297304
}
298305

299306
/// Per-arch state required to generate an intercept message.
@@ -302,11 +309,24 @@ struct InterceptMessageState {
302309
instruction_length_and_cr8: u8,
303310
cpl: u8,
304311
efer_lma: bool,
305-
cs_segment: hvdef::HvX64SegmentRegister,
312+
cs: hvdef::HvX64SegmentRegister,
306313
rip: u64,
307314
rflags: u64,
308315
rax: u64,
309316
rdx: u64,
317+
rcx: u64,
318+
rsi: u64,
319+
rdi: u64,
320+
optional: Option<InterceptMessageOptionalState>,
321+
}
322+
323+
#[cfg_attr(guest_arch = "aarch64", expect(dead_code))]
324+
/// Additional per-arch state required to generate an intercept message. Used
325+
/// for state that is not common across the intercept message types and that
326+
/// might be slower to retrieve on certain architectures.
327+
struct InterceptMessageOptionalState {
328+
ds: hvdef::HvX64SegmentRegister,
329+
es: hvdef::HvX64SegmentRegister,
310330
}
311331

312332
impl InterceptMessageType {
@@ -316,23 +336,28 @@ impl InterceptMessageType {
316336
vp_index: VpIndex,
317337
vtl: GuestVtl,
318338
state: InterceptMessageState,
339+
is_read: bool,
319340
) -> HvMessage {
320-
let write_header = hvdef::HvX64InterceptMessageHeader {
341+
let header = hvdef::HvX64InterceptMessageHeader {
321342
vp_index: vp_index.index(),
322343
instruction_length_and_cr8: state.instruction_length_and_cr8,
323-
intercept_access_type: hvdef::HvInterceptAccessType::WRITE,
344+
intercept_access_type: if is_read {
345+
hvdef::HvInterceptAccessType::READ
346+
} else {
347+
hvdef::HvInterceptAccessType::WRITE
348+
},
324349
execution_state: hvdef::HvX64VpExecutionState::new()
325350
.with_cpl(state.cpl)
326351
.with_vtl(vtl.into())
327352
.with_efer_lma(state.efer_lma),
328-
cs_segment: state.cs_segment,
353+
cs_segment: state.cs,
329354
rip: state.rip,
330355
rflags: state.rflags,
331356
};
332357
match self {
333358
InterceptMessageType::Register { reg, value } => {
334359
let intercept_message = hvdef::HvX64RegisterInterceptMessage {
335-
header: write_header,
360+
header,
336361
flags: hvdef::HvX64RegisterInterceptMessageFlags::new(),
337362
rsvd: 0,
338363
rsvd2: 0,
@@ -349,7 +374,7 @@ impl InterceptMessageType {
349374
}
350375
InterceptMessageType::Msr { msr } => {
351376
let intercept_message = hvdef::HvX64MsrInterceptMessage {
352-
header: write_header,
377+
header,
353378
msr_number: *msr,
354379
rax: state.rax,
355380
rdx: state.rdx,
@@ -362,6 +387,35 @@ impl InterceptMessageType {
362387
intercept_message.as_bytes(),
363388
)
364389
}
390+
InterceptMessageType::IoPort {
391+
port_number,
392+
access_size,
393+
string_access,
394+
rep_access,
395+
} => {
396+
let access_info =
397+
hvdef::HvX64IoPortAccessInfo::new(*access_size, *string_access, *rep_access);
398+
let intercept_message = hvdef::HvX64IoPortInterceptMessage {
399+
header,
400+
port_number: *port_number,
401+
access_info,
402+
instruction_byte_count: 0,
403+
reserved: 0,
404+
rax: state.rax,
405+
instruction_bytes: [0u8; 16],
406+
ds_segment: state.optional.as_ref().unwrap().ds,
407+
es_segment: state.optional.as_ref().unwrap().es,
408+
rcx: state.rcx,
409+
rsi: state.rsi,
410+
rdi: state.rdi,
411+
};
412+
413+
HvMessage::new(
414+
hvdef::HvMessageType::HvMessageTypeX64IoPortIntercept,
415+
0,
416+
intercept_message.as_bytes(),
417+
)
418+
}
365419
}
366420
}
367421
}
@@ -407,6 +461,7 @@ trait HardwareIsolatedBacking: Backing {
407461
fn intercept_message_state(
408462
this: &UhProcessor<'_, Self>,
409463
vtl: GuestVtl,
464+
include_optional_state: bool,
410465
) -> InterceptMessageState;
411466

412467
/// Individual register for CPUID and crx intercept handling, since

0 commit comments

Comments
 (0)