1mod vm_state;
7mod vp_state;
8
9use crate::Error;
10use crate::ErrorInner;
11use crate::LinuxMshv;
12use crate::MshvIsolationState;
13use crate::MshvPartition;
14use crate::MshvPartitionInner;
15use crate::MshvProcessor;
16use crate::MshvProcessorBinder;
17use crate::MshvProtoPartition;
18use crate::MshvVpRunner;
19use crate::VcpuFdExt;
20use crate::common_synthetic_features;
21use crate::create_vm_with_retry;
22
23use guestmem::DoorbellRegistration;
24use guestmem::GuestMemory;
25use headervec::HeaderVec;
26use hv1_hypercall::X64RegisterIo;
27use hvdef::HvDeliverabilityNotificationsRegister;
28use hvdef::HvMessage;
29use hvdef::HvMessageType;
30use hvdef::HvPartitionPropertyCode;
31use hvdef::HvProcessorVendor;
32use hvdef::HvX64RegisterName;
33use hvdef::HvX64RegisterPage;
34use hvdef::Vtl;
35use hvdef::hypercall::HvRegisterAssoc;
36use memory_range::MemoryRange;
37use mshv_ioctls::InterruptRequest;
38use mshv_ioctls::VcpuFd;
39use pal::unix::pthread::Pthread;
40use parking_lot::Mutex;
41use pci_core::msi::SignalMsi;
42use std::os::fd::AsRawFd;
43use std::sync::Arc;
44use virt::Hv1;
45use virt::PartitionAccessState;
46use virt::PartitionConfig;
47use virt::ProtoPartition;
48use virt::ProtoPartitionConfig;
49use virt::VpHaltReason;
50use virt::VpIndex;
51use virt::io::CpuIo;
52use virt::irqcon::MsiRequest;
53use virt::state::StateElement as _;
54use virt::x86::apic_software_device::ApicSoftwareDevice;
55use virt::x86::apic_software_device::ApicSoftwareDevices;
56use virt_support_x86emu::emulate::EmuTranslateError;
57use virt_support_x86emu::emulate::EmuTranslateResult;
58use virt_support_x86emu::emulate::EmulatorSupport;
59use virt_support_x86emu::emulate::TranslateGvaSupport;
60use virt_support_x86emu::emulate::TranslateMode;
61use virt_support_x86emu::emulate::emulate_translate_gva;
62use virt_support_x86emu::translate::TranslationRegisters;
63use vmcore::reference_time::ReferenceTimeSource;
64use x86defs::RFlags;
65use x86defs::SegmentRegister;
66use zerocopy::FromBytes;
67use zerocopy::FromZeros;
68use zerocopy::IntoBytes;
69
70mod snp;
71
72pub(crate) use snp::MshvSnpConfig;
73pub(super) use snp::SnpError;
74pub(crate) use snp::SnpLaunchState;
75pub(crate) use snp::SnpPartitionState;
76pub(crate) use snp::SnpVpState;
77pub(crate) use snp::acquire_snp_host_access;
78use snp::prepare_snp_config;
79use snp::snp_cpuid_overrides;
80use snp::snp_hv_cpuid_overrides;
81use snp::snp_start_vp_vmsa_gpa;
82
83pub(crate) enum MshvProtoPartitionIsolation {
84 None,
85 Snp {
86 config: Option<Box<MshvSnpConfig>>,
87 disable_cpuid_offload: bool,
88 },
89}
90
91impl virt::Hypervisor for LinuxMshv {
92 type ProtoPartition<'a> = MshvProtoPartition<'a>;
93 type Partition = MshvPartition;
94 type Error = Error;
95
96 fn platform_info(&self) -> virt::PlatformInfo {
97 virt::PlatformInfo {}
98 }
99
100 fn new_partition<'a>(
101 &mut self,
102 config: ProtoPartitionConfig<'a>,
103 ) -> Result<MshvProtoPartition<'a>, Self::Error> {
104 let igvm_snp_config = match &config.isolation {
106 virt::ProtoPartitionIsolation::None => None,
107 virt::ProtoPartitionIsolation::Snp(snp_config) => snp_config.as_deref(),
108 _ => return Err(ErrorInner::IsolationNotSupported.into()),
109 };
110 let isolation = config.isolation.isolation_type();
111 validate_snp_cpuid_offload_config(isolation, self.snp_disable_cpuid_offload)?;
112 let snp = isolation == virt::IsolationType::Snp;
113 let x2apic = matches!(
114 config.processor_topology.apic_mode(),
115 vm_topology::processor::x86::ApicMode::X2ApicSupported
116 | vm_topology::processor::x86::ApicMode::X2ApicEnabled
117 );
118 let create_args =
119 partition_create_args(snp, x2apic, config.processor_topology.smt_enabled());
120
121 let vmfd = create_vm_with_retry(&self.mshv, &create_args)?;
122
123 if config.hv_config.is_some() || snp {
127 let synthetic_features = if snp {
128 snp_synthetic_features()
129 } else {
130 common_synthetic_features()
131 .with_access_partition_reference_tsc(true)
132 .with_access_guest_idle_reg(true)
133 .with_access_frequency_regs(true)
134 .with_enable_extended_gva_ranges_for_flush_virtual_address_list(true)
135 };
136
137 vmfd.set_partition_property(
138 HvPartitionPropertyCode::SyntheticProcFeatures.0,
139 u64::from(synthetic_features),
140 )
141 .map_err(|e| ErrorInner::SetPartitionProperty(e.into()))?;
142 }
143
144 vmfd.initialize()
145 .map_err(|e| ErrorInner::CreateVMInitFailed(e.into()))?;
146
147 if snp {
148 let snp_policy = igvm_snp_config.as_ref().map_or_else(
149 || {
150 let policy = mshv_bindings::snp::get_default_snp_guest_policy();
151 unsafe { policy.as_uint64 }
153 },
154 |config| config.policy,
155 );
156 let vmgexit_offloads = snp_vmgexit_offloads(self.snp_disable_cpuid_offload);
157 let vmgexit_offloads = unsafe { vmgexit_offloads.as_uint64 };
159
160 for (code, value) in [
161 (HvPartitionPropertyCode::IsolationPolicy, snp_policy),
162 (
163 HvPartitionPropertyCode::SevVmgexitOffloads,
164 vmgexit_offloads,
165 ),
166 (
167 HvPartitionPropertyCode::UnimplementedMsrAction,
168 mshv_bindings::hv_unimplemented_msr_action_HV_UNIMPLEMENTED_MSR_ACTION_IGNORE_WRITE_READ_ZERO
169 as u64,
170 ),
171 (HvPartitionPropertyCode::TimeFreeze, 1),
172 ] {
173 vmfd.set_partition_property(code.0, value)
174 .map_err(|e| ErrorInner::SetPartitionProperty(e.into()))?;
175 }
176 }
177
178 vmfd.set_partition_property(
180 HvPartitionPropertyCode::ProcessorsPerSocket.0,
181 config.processor_topology.reserved_vps_per_socket() as u64,
182 )
183 .map_err(|e| ErrorInner::SetPartitionProperty(e.into()))?;
184
185 let snp_config = if let Some(snp_config) = igvm_snp_config {
186 let physical_address_width =
187 vmfd.get_partition_property(HvPartitionPropertyCode::PhysicalAddressWidth.0)
188 .map_err(|e| ErrorInner::GetPartitionProperty(e.into()))? as u8;
189 Some(Box::new(prepare_snp_config(
190 snp_config,
191 physical_address_width,
192 )?))
193 } else {
194 None
195 };
196
197 let isolation = if snp {
198 MshvProtoPartitionIsolation::Snp {
199 config: snp_config,
200 disable_cpuid_offload: self.snp_disable_cpuid_offload,
201 }
202 } else {
203 MshvProtoPartitionIsolation::None
204 };
205 let mut proto = MshvProtoPartition::new(config, vmfd)?;
206 proto.isolation = isolation;
207 Ok(proto)
208 }
209}
210
211fn partition_create_args(
212 snp: bool,
213 x2apic: bool,
214 smt: bool,
215) -> mshv_bindings::mshv_create_partition_v2 {
216 let mut pt_flags =
217 1 << mshv_bindings::MSHV_PT_BIT_LAPIC | 1 << mshv_bindings::MSHV_PT_BIT_GPA_SUPER_PAGES;
218
219 if snp || x2apic {
220 pt_flags |= 1 << mshv_bindings::MSHV_PT_BIT_X2APIC;
221 }
222 if smt {
223 pt_flags |= 1 << mshv_bindings::MSHV_PT_BIT_SMT_ENABLED_GUEST;
224 }
225
226 mshv_bindings::mshv_create_partition_v2 {
227 pt_flags: pt_flags | 1 << mshv_bindings::MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES,
228 pt_isolation: if snp {
229 mshv_bindings::MSHV_PT_ISOLATION_SNP as u64
230 } else {
231 mshv_bindings::MSHV_PT_ISOLATION_NONE as u64
232 },
233 pt_num_cpu_fbanks: mshv_bindings::MSHV_NUM_CPU_FEATURES_BANKS as u16,
234 pt_cpu_fbanks: [
235 !u64::from(supported_processor_features()),
236 !u64::from(supported_processor_features1()),
237 ],
238 pt_disabled_xsave: !u64::from(supported_xsave_features()),
239 ..Default::default()
240 }
241}
242
243fn snp_synthetic_features() -> hvdef::HvPartitionSyntheticProcessorFeatures {
244 hvdef::HvPartitionSyntheticProcessorFeatures::new()
245 .with_hypervisor_present(true)
246 .with_hv1(true)
247 .with_access_partition_reference_counter(true)
248 .with_access_synic_regs(true)
249 .with_access_synthetic_timer_regs(true)
250 .with_access_frequency_regs(true)
261 .with_access_intr_ctrl_regs(true)
262 .with_access_vp_index(true)
263 .with_access_hypercall_regs(true)
264 .with_access_guest_idle_reg(true)
265 .with_tb_flush_hypercalls(true)
266 .with_synthetic_cluster_ipi(true)
267 .with_direct_synthetic_timers(true)
268}
269
270fn snp_vmgexit_offloads(disable_cpuid: bool) -> mshv_bindings::hv_sev_vmgexit_offload {
271 let mut offloads = mshv_bindings::snp::get_default_vmgexit_offload_features();
272 if disable_cpuid {
273 unsafe {
276 offloads.__bindgen_anon_1.set_nae_cpuid(0);
277 offloads.__bindgen_anon_1.set_msr_cpuid(0);
278 }
279 }
280 offloads
281}
282
283fn hv1_reference_tsc_page_supported(
284 hv1_enabled: bool,
285 isolation: virt::IsolationType,
286 advertised: bool,
287) -> bool {
288 hv1_enabled && isolation != virt::IsolationType::Snp && advertised
292}
293
294fn validate_snp_cpuid_offload_config(
295 isolation: virt::IsolationType,
296 disabled: bool,
297) -> Result<(), Error> {
298 if disabled && isolation != virt::IsolationType::Snp {
299 return Err(ErrorInner::InvalidConfiguration(
300 "snp_disable_cpuid_offload requires SNP isolation",
301 )
302 .into());
303 }
304 Ok(())
305}
306
307impl MshvProtoPartition<'_> {
308 fn caps_from_properties(&self) -> Result<virt::x86::X86PartitionCapabilities, Error> {
311 use virt::x86::X86PartitionCapabilities;
312 use virt::x86::XsaveCapabilities;
313 use x86defs::cpuid::Vendor;
314 use x86defs::xsave::XSAVE_VARIABLE_OFFSET;
315
316 let vendor_id = self
317 .vmfd
318 .get_partition_property(HvPartitionPropertyCode::ProcessorVendor.0)
319 .map_err(|e| ErrorInner::GetPartitionProperty(e.into()))?;
320
321 let vendor = match HvProcessorVendor(vendor_id as u32) {
322 HvProcessorVendor::AMD => Vendor::AMD,
323 HvProcessorVendor::INTEL => Vendor::INTEL,
324 HvProcessorVendor::HYGON => Vendor::HYGON,
325 v => return Err(ErrorInner::UnsupportedProcessorVendor(v).into()),
326 };
327
328 let xsave_states = self
329 .vmfd
330 .get_partition_property(HvPartitionPropertyCode::XsaveStates.0)
331 .map_err(|e| ErrorInner::GetPartitionProperty(e.into()))?;
332
333 let max_xsave_data_size = self
334 .vmfd
335 .get_partition_property(HvPartitionPropertyCode::MaxXsaveDataSize.0)
336 .map_err(|e| ErrorInner::GetPartitionProperty(e.into()))?;
337
338 let reset_rdx = if self.config.isolation.isolation_type() == virt::IsolationType::Snp {
339 0
340 } else {
341 let mut assoc = [HvRegisterAssoc::from((HvX64RegisterName::Rdx, 0u64))];
342 self.bsp
343 .get_hvdef_regs(&mut assoc)
344 .map_err(ErrorInner::Register)?;
345 assoc[0].value.as_u64()
346 };
347
348 let x2apic = matches!(
349 self.config.processor_topology.apic_mode(),
350 vm_topology::processor::x86::ApicMode::X2ApicSupported
351 | vm_topology::processor::x86::ApicMode::X2ApicEnabled
352 );
353 let x2apic_enabled = matches!(
354 self.config.processor_topology.apic_mode(),
355 vm_topology::processor::x86::ApicMode::X2ApicEnabled
356 );
357
358 let hv1 = self.config.hv_config.is_some();
359 Ok(X86PartitionCapabilities {
360 vendor,
361 hv1,
362 hv1_reference_tsc_page: hv1_reference_tsc_page_supported(
363 hv1,
364 self.config.isolation.isolation_type(),
365 true,
366 ),
367 xsave: XsaveCapabilities {
368 features: xsave_states,
369 supervisor_features: 0,
370 standard_len: XSAVE_VARIABLE_OFFSET as u32,
371 compact_len: max_xsave_data_size as u32,
372 feature_info: [Default::default(); 63],
373 },
374 x2apic,
375 x2apic_enabled,
376 reset_rdx,
377 cet: false,
378 cet_ss: false,
379 sgx: false,
380 tsc_aux: false,
381 vtom: None,
382 physical_address_width: self
383 .vmfd
384 .get_partition_property(HvPartitionPropertyCode::PhysicalAddressWidth.0)
385 .map_err(|e| ErrorInner::GetPartitionProperty(e.into()))?
386 as u8,
387 snp_c_bit: None,
388 can_freeze_time: false,
389 xsaves_state_bv_broken: false,
390 dr6_tsx_broken: false,
391 nxe_forced_on: false,
392 nested_virt: false,
393 })
394 }
395
396 fn max_physical_address_size(&self) -> u8 {
397 self.vmfd
398 .get_partition_property(HvPartitionPropertyCode::PhysicalAddressWidth.0)
399 .expect("failed to get physical address width") as u8
400 }
401}
402
403impl ProtoPartition for MshvProtoPartition<'_> {
404 type Partition = MshvPartition;
405 type ProcessorBinder = MshvProcessorBinder;
406 type Error = Error;
407
408 fn max_physical_address_size(&self) -> u8 {
409 self.max_physical_address_size()
410 }
411
412 fn build(
413 self,
414 config: PartitionConfig<'_>,
415 ) -> Result<(Self::Partition, Vec<Self::ProcessorBinder>), Self::Error> {
416 let snp_config = match &self.isolation {
417 MshvProtoPartitionIsolation::None => None,
418 MshvProtoPartitionIsolation::Snp { config, .. } => config.as_deref(),
419 };
420 if let Some(snp_config) = snp_config {
421 let vmsa_range =
425 MemoryRange::new(snp_config.vmsa_gpa..snp_config.vmsa_gpa + hvdef::HV_PAGE_SIZE);
426 if config
427 .mem_layout
428 .ram()
429 .iter()
430 .any(|range| range.range.overlaps(&vmsa_range))
431 {
432 return Err(ErrorInner::SnpVmsaOverlapsRam.into());
433 }
434 }
435 let mut cpuid = config.cpuid.to_vec();
436 if matches!(&self.isolation, MshvProtoPartitionIsolation::Snp { .. }) {
437 let expose_hypervisor = self.config.hv_config.is_some();
438 cpuid.extend(snp_cpuid_overrides(expose_hypervisor));
439 if expose_hypervisor {
440 let native_max_leaf = self
441 .bsp
442 .get_cpuid_values(hvdef::HV_CPUID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION, 0, 0, 0)
443 .map(|values| values[0])
444 .unwrap_or(hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION);
445 cpuid.extend(snp_hv_cpuid_overrides(native_max_leaf));
446 }
447 }
448 let cpuid = virt::CpuidLeafSet::new(cpuid);
449
450 for leaf in cpuid.leaves().iter() {
452 let input = hvdef::hypercall::RegisterInterceptResultCpuid {
453 partition_id: 0,
454 vp_index: hvdef::HV_ANY_VP,
455 intercept_type: hvdef::hypercall::HvInterceptType::HvInterceptTypeX64Cpuid,
456 parameters: hvdef::hypercall::HvRegisterX64CpuidResultParameters {
457 input: hvdef::hypercall::HvRegisterX64CpuidResultParametersInput {
458 eax: leaf.function,
459 ecx: leaf.index.unwrap_or(0),
460 subleaf_specific: u8::from(leaf.index.is_some()),
461 always_override: 1,
462 padding: 0,
463 },
464 result: hvdef::hypercall::HvRegisterX64CpuidResultParametersOutput {
465 eax: leaf.result[0],
466 eax_mask: leaf.mask[0],
467 ebx: leaf.result[1],
468 ebx_mask: leaf.mask[1],
469 ecx: leaf.result[2],
470 ecx_mask: leaf.mask[2],
471 edx: leaf.result[3],
472 edx_mask: leaf.mask[3],
473 },
474 },
475 _reserved: 0,
476 };
477 let mut args = mshv_bindings::mshv_root_hvcall {
478 code: hvdef::HypercallCode::HvCallRegisterInterceptResult.0,
479 in_sz: size_of_val(&input) as u16,
480 in_ptr: std::ptr::addr_of!(input) as u64,
481 ..Default::default()
482 };
483 self.vmfd
484 .hvcall(&mut args)
485 .map_err(|e| ErrorInner::RegisterCpuid(e.into()))?;
486 }
487
488 let caps = {
489 let mut cpuid_error = None;
490 let cpuid_caps = virt::PartitionCapabilities::from_cpuid(
491 self.config.processor_topology,
492 &mut |function, index| {
493 self.bsp
494 .get_cpuid_values(function, index, 0, 0)
495 .unwrap_or_else(|err| {
496 cpuid_error.get_or_insert(err);
497 [0; 4]
498 })
499 },
500 );
501 let mut caps = match (cpuid_caps, cpuid_error) {
502 (Ok(caps), None) => caps,
503 (result, error) => {
504 tracing::warn!(
505 error = error.as_ref().map(|err| err as &dyn std::error::Error),
506 capabilities_error = result
507 .err()
508 .as_ref()
509 .map(|err| err as &dyn std::error::Error),
510 "failed to query CPUID capabilities, falling back to partition properties; some features may be unavailable"
511 );
512 self.caps_from_properties()?
513 }
514 };
515 caps.hv1 = self.config.hv_config.is_some();
516 caps.hv1_reference_tsc_page = hv1_reference_tsc_page_supported(
517 caps.hv1,
518 self.config.isolation.isolation_type(),
519 caps.hv1_reference_tsc_page,
520 );
521 caps.xsaves_state_bv_broken = true;
522 caps.can_freeze_time = true;
523 caps
524 };
525
526 let apic_id_map = self
527 .config
528 .processor_topology
529 .vps_arch()
530 .map(|vp| vp.apic_id)
531 .collect();
532
533 let isolation = match self.isolation {
534 MshvProtoPartitionIsolation::None => MshvIsolationState::None,
535 MshvProtoPartitionIsolation::Snp {
536 config,
537 disable_cpuid_offload,
538 } => MshvIsolationState::Snp(SnpPartitionState::with_config(
539 disable_cpuid_offload,
540 config,
541 )),
542 };
543 let time_frozen = isolation.is_isolated();
544 let inner = Arc::new(MshvPartitionInner {
545 vmfd: self.vmfd,
546 bsp_vcpufd: self.bsp,
547 memory: Default::default(),
548 gm: config.guest_memory.clone(),
549 mem_layout: config.mem_layout.clone(),
550 vps: self.vps,
551 irq_routes: Default::default(),
552 gsi_states: Mutex::new(Box::new(
553 [crate::irqfd::GsiState::Unallocated; crate::irqfd::NUM_GSIS],
554 )),
555 caps,
556 synic_ports: Default::default(),
557 software_devices: ApicSoftwareDevices::new(apic_id_map),
558 isolation,
559 time_frozen: Mutex::new(time_frozen),
561 });
562 inner.add_snp_vmsa_mapping()?;
563
564 let partition = MshvPartition {
565 synic_ports: Arc::new(virt::synic::SynicPorts::new(inner.clone())),
566 inner,
567 };
568
569 let vps = self
570 .config
571 .processor_topology
572 .vps()
573 .map(|vp| MshvProcessorBinder {
574 partition: partition.inner.clone(),
575 vpindex: vp.vp_index,
576 vcpufd: None,
577 snp: None,
578 })
579 .collect();
580
581 Ok((partition, vps))
582 }
583}
584
585impl virt::Partition for MshvPartition {
586 fn initial_vp_state_source(&self) -> virt::InitialVpStateSource {
587 self.inner.isolation.initial_vp_state_source()
588 }
589
590 fn supports_initial_page_acceptance(
591 &self,
592 ) -> Option<&dyn virt::AcceptInitialPages<Error = Error>> {
593 self.inner.isolation.snp().is_some().then_some(self)
594 }
595
596 fn supports_reset(&self) -> Option<&dyn virt::ResetPartition<Error = Error>> {
597 self.inner.isolation.snp().is_none().then_some(self)
600 }
601
602 fn doorbell_registration(
603 self: &Arc<Self>,
604 _minimum_vtl: Vtl,
605 ) -> Option<Arc<dyn DoorbellRegistration>> {
606 Some(self.clone())
607 }
608
609 fn caps(&self) -> &virt::PartitionCapabilities {
610 &self.inner.caps
611 }
612
613 fn request_msi(&self, _vtl: Vtl, request: MsiRequest) {
614 self.inner.request_msi(request)
615 }
616
617 fn as_signal_msi(&self, _vtl: Vtl) -> Option<Arc<dyn SignalMsi>> {
618 Some(self.inner.clone())
619 }
620
621 fn irqfd(&self) -> Option<Arc<dyn virt::irqfd::IrqFd>> {
622 Some(Arc::new(crate::irqfd::MshvIrqFd::new(self.inner.clone())))
623 }
624
625 fn request_yield(&self, vp_index: VpIndex) {
626 let vp = self.inner.vp(vp_index);
627 if vp.needs_yield.request_yield() {
628 let thread = vp.thread.read();
629 if let Some(thread) = *thread {
630 if thread != Pthread::current() {
631 thread
632 .signal(libc::SIGRTMIN())
633 .expect("thread cancel signal failed");
634 }
635 }
636 }
637 }
638}
639
640impl virt::X86Partition for MshvPartition {
641 fn ioapic_routing(&self) -> Arc<dyn virt::irqcon::IoApicRouting> {
642 self.inner.clone()
643 }
644
645 fn pulse_lint(&self, vp_index: VpIndex, vtl: Vtl, lint: u8) {
646 tracelimit::warn_ratelimited!(?vp_index, ?vtl, lint, "ignored lint pulse");
653 }
654}
655
656impl virt::ResetPartition for MshvPartition {
657 type Error = Error;
658
659 fn reset(&self) -> Result<(), Error> {
660 use virt::x86::vm::AccessVmState;
661
662 for irq in 0..virt::irqcon::IRQ_LINES as u8 {
663 self.inner.irq_routes.set_irq_route(irq, None);
664 }
665
666 self.inner.freeze_time()?;
667
668 let bsp_vp_info = &self.inner.vps[0].vp_info;
669 self.access_state(Vtl::Vtl0)
670 .reset_all(bsp_vp_info)
671 .map_err(|e| ErrorInner::ResetState(Box::new(e)))?;
672
673 Ok(())
674 }
675}
676
677impl Hv1 for MshvPartition {
678 type Error = Error;
679 type Device = ApicSoftwareDevice;
680
681 fn reference_time_source(&self) -> Option<ReferenceTimeSource> {
682 Some(ReferenceTimeSource::from(self.inner.clone() as Arc<_>))
683 }
684
685 fn new_virtual_device(
686 &self,
687 ) -> Option<&dyn virt::DeviceBuilder<Device = Self::Device, Error = Self::Error>> {
688 Some(self)
689 }
690
691 fn synic(&self) -> anyhow::Result<Arc<dyn vmcore::synic::SynicPortAccess>> {
692 Ok(self.synic_ports.clone())
693 }
694}
695
696impl virt::DeviceBuilder for MshvPartition {
697 fn build(&self, _vtl: Vtl, device_id: u64) -> Result<Self::Device, Self::Error> {
698 Ok(self
699 .inner
700 .software_devices
701 .new_device(self.inner.clone(), device_id)
702 .map_err(ErrorInner::NewDevice)?)
703 }
704}
705
706impl MshvPartitionInner {
707 fn request_msi(&self, request: MsiRequest) {
708 let (address, data) = request.as_x86();
709 let control = request.hv_x86_interrupt_control();
710 let mshv_req = InterruptRequest {
711 interrupt_type: control.interrupt_type().0,
712 apic_id: address.virt_destination().into(),
713 vector: data.vector().into(),
714 level_triggered: control.x86_level_triggered(),
715 logical_destination_mode: control.x86_logical_destination_mode(),
716 long_mode: false,
717 };
718
719 if let Err(err) = self.vmfd.request_virtual_interrupt(&mshv_req) {
720 tracelimit::warn_ratelimited!(
721 address = request.address,
722 data = request.data,
723 error = &err as &dyn std::error::Error,
724 "failed to request msi"
725 );
726 }
727 }
728}
729
730impl SignalMsi for MshvPartitionInner {
731 fn signal_msi(&self, _devid: Option<u32>, address: u64, data: u32) {
732 self.request_msi(MsiRequest { address, data });
733 }
734}
735
736impl virt::irqcon::IoApicRouting for MshvPartitionInner {
737 fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>) {
738 self.irq_routes.set_irq_route(irq, request)
739 }
740
741 fn assert_irq(&self, irq: u8) {
742 self.irq_routes
743 .assert_irq(irq, |request| self.request_msi(request))
744 }
745}
746
747impl virt::BindProcessor for MshvProcessorBinder {
752 type Processor<'a>
753 = MshvProcessor<'a>
754 where
755 Self: 'a;
756 type Error = Error;
757
758 fn bind(&mut self) -> Result<Self::Processor<'_>, Self::Error> {
759 let inner = &self.partition.vps[self.vpindex.index() as usize];
760
761 let vcpufd = if self.vpindex.is_bsp() {
762 &self.partition.bsp_vcpufd
763 } else {
764 if self.vcpufd.is_none() {
765 let vcpufd = self
766 .partition
767 .vmfd
768 .create_vcpu(u8::try_from(self.vpindex.index()).expect("validated above"))
769 .map_err(|e| ErrorInner::CreateVcpu(e.into()))?;
770 self.vcpufd = Some(vcpufd);
771 }
772 self.vcpufd.as_ref().unwrap()
773 };
774
775 let reg_page_ptr = if self.partition.isolation.snp().is_some() {
776 None
777 } else {
778 Some(
779 vcpufd
780 .get_vp_reg_page()
781 .ok_or(ErrorInner::MissingRegisterPage)?
782 .0
783 .cast::<HvX64RegisterPage>(),
784 )
785 };
786
787 let runner = MshvVpRunner {
788 vcpufd,
789 reg_page: reg_page_ptr,
790 ghcb_page: if self.partition.isolation.snp().is_some() {
791 if self.snp.is_none() {
792 self.snp = Some(SnpVpState::new(vcpufd)?);
793 }
794 self.snp.as_mut().map(SnpVpState::page_ptr)
795 } else {
796 None
797 },
798 };
799
800 let this = MshvProcessor {
801 partition: &self.partition,
802 inner,
803 vpindex: self.vpindex,
804 runner,
805 deliverability_notifications: HvDeliverabilityNotificationsRegister::new(),
806 };
807
808 if this.partition.isolation.snp().is_none() {
809 let apic_base =
811 virt::vp::Apic::at_reset(&this.partition.caps, &this.inner.vp_info).apic_base;
812
813 let regs = &[
814 HvRegisterAssoc::from((
815 HvX64RegisterName::InitialApicId,
816 u64::from(inner.vp_info.apic_id),
817 )),
818 HvRegisterAssoc::from((HvX64RegisterName::ApicBase, apic_base)),
819 HvRegisterAssoc::from((
820 HvX64RegisterName::ApicId,
821 u64::from(inner.vp_info.apic_id),
822 )),
823 ];
824
825 let reg_count = if this.partition.caps.x2apic { 2 } else { 3 };
826
827 vcpufd
828 .set_hvdef_regs(®s[..reg_count])
829 .map_err(ErrorInner::Register)?;
830 }
831
832 Ok(this)
833 }
834}
835
836impl MshvProcessor<'_> {
837 async fn emulate(
838 &mut self,
839 message: &HvMessage,
840 devices: &impl CpuIo,
841 interruption_pending: bool,
842 ) -> Result<(), VpHaltReason> {
843 let emu_mem = virt_support_x86emu::emulate::EmulatorMemoryAccess {
844 gm: &self.partition.gm,
845 kx_gm: &self.partition.gm,
846 ux_gm: &self.partition.gm,
847 };
848
849 let mut support = MshvEmulationState {
850 partition: self.partition,
851 vcpufd: self.runner.vcpufd,
852 reg_page: self.runner.reg_page(),
853 vp_index: self.vpindex,
854 message,
855 interruption_pending,
856 };
857 virt_support_x86emu::emulate::emulate(&mut support, &emu_mem, devices).await
858 }
859
860 pub(crate) async fn handle_exit(
861 &mut self,
862 exit: &HvMessage,
863 dev: &impl CpuIo,
864 ) -> Result<(), VpHaltReason> {
865 if self.partition.isolation.snp().is_some() {
866 return self.handle_snp_exit(exit, dev).await;
867 }
868
869 match exit.header.typ {
870 HvMessageType::HvMessageTypeUnrecoverableException => {
871 return Err(VpHaltReason::TripleFault { vtl: Vtl::Vtl0 });
872 }
873 HvMessageType::HvMessageTypeX64IoPortIntercept => {
874 self.handle_io_port_intercept(exit, dev).await?;
875 }
876 HvMessageType::HvMessageTypeUnmappedGpa | HvMessageType::HvMessageTypeGpaIntercept => {
877 self.handle_mmio_intercept(exit, dev).await?;
878 }
879 HvMessageType::HvMessageTypeSynicSintDeliverable => {
880 tracing::trace!("SYNIC_SINT_DELIVERABLE");
881 let info = exit.as_message::<hvdef::HvX64SynicSintDeliverableMessage>();
882 self.handle_sint_deliverable(info.deliverable_sints);
883 }
884 HvMessageType::HvMessageTypeHypercallIntercept => {
885 tracing::trace!("HYPERCALL_INTERCEPT");
886 self.handle_hypercall_intercept(exit)?;
887 }
888 HvMessageType::HvMessageTypeX64ApicEoi => {
889 let msg = exit.as_message::<hvdef::HvX64ApicEoiMessage>();
890 dev.handle_eoi(msg.interrupt_vector);
891 }
892 exit_type => {
893 panic!("Unhandled vcpu exit code {exit_type:?}");
894 }
895 }
896 Ok(())
897 }
898
899 async fn handle_io_port_intercept(
900 &mut self,
901 message: &HvMessage,
902 devices: &impl CpuIo,
903 ) -> Result<(), VpHaltReason> {
904 let info = message.as_message::<hvdef::HvX64IoPortInterceptMessage>();
905 let access_info = info.access_info;
906
907 if access_info.string_op() || access_info.rep_prefix() {
908 let interruption_pending = info.header.execution_state.interruption_pending();
909 self.emulate(message, devices, interruption_pending).await?
910 } else {
911 let mut ret_rax = info.rax;
912 virt_support_x86emu::emulate::emulate_io(
913 self.vpindex,
914 info.header.intercept_access_type == hvdef::HvInterceptAccessType::WRITE,
915 info.port_number,
916 &mut ret_rax,
917 access_info.access_size(),
918 devices,
919 )
920 .await;
921
922 let insn_len = info.header.instruction_len() as u64;
923
924 let rp = self.runner.reg_page();
925 rp.gp_registers[x86emu::Gp::RAX as usize] = ret_rax;
926 rp.rip = info.header.rip + insn_len;
927 rp.dirty.set_general_purpose(true);
928 rp.dirty.set_instruction_pointer(true);
929 }
930
931 Ok(())
932 }
933
934 async fn handle_mmio_intercept(
935 &mut self,
936 message: &HvMessage,
937 devices: &impl CpuIo,
938 ) -> Result<(), VpHaltReason> {
939 let info = message.as_message::<hvdef::HvX64MemoryInterceptMessage>();
940 let interruption_pending = info.header.execution_state.interruption_pending();
941 self.emulate(message, devices, interruption_pending).await
942 }
943
944 fn handle_hypercall_intercept(&mut self, message: &HvMessage) -> Result<(), VpHaltReason> {
945 let info = message.as_message::<hvdef::HvX64HypercallInterceptMessage>();
946 let is_64bit =
947 info.header.execution_state.cr0_pe() && info.header.execution_state.efer_lma();
948 let mut handler = MshvHypercallHandler {
949 partition: self.partition,
950 reg_page: self.runner.reg_page(),
951 caller_vp: self.vpindex,
952 isolated: false,
953 modified_gp: 0,
954 modified_xmm: 0,
955 };
956
957 MshvHypercallHandler::DISPATCHER.dispatch(
958 &self.partition.gm,
959 X64RegisterIo::new(&mut handler, is_64bit, true),
960 );
961 Ok(())
962 }
963}
964
965struct MshvEmulationState<'a> {
970 partition: &'a MshvPartitionInner,
971 vcpufd: &'a VcpuFd,
972 reg_page: &'a mut HvX64RegisterPage,
973 vp_index: VpIndex,
974 message: &'a HvMessage,
975 interruption_pending: bool,
976}
977
978impl EmulatorSupport for MshvEmulationState<'_> {
979 fn vp_index(&self) -> VpIndex {
980 self.vp_index
981 }
982
983 fn vendor(&self) -> x86defs::cpuid::Vendor {
984 self.partition.caps.vendor
985 }
986
987 fn gp(&mut self, reg: x86emu::Gp) -> u64 {
988 self.reg_page.gp_registers[reg as usize]
989 }
990
991 fn set_gp(&mut self, reg: x86emu::Gp, v: u64) {
992 self.reg_page.gp_registers[reg as usize] = v;
993 self.reg_page.dirty.set_general_purpose(true);
994 }
995
996 fn rip(&mut self) -> u64 {
997 self.reg_page.rip
998 }
999
1000 fn set_rip(&mut self, v: u64) {
1001 self.reg_page.rip = v;
1002 self.reg_page.dirty.set_instruction_pointer(true);
1003 }
1004
1005 fn segment(&mut self, reg: x86emu::Segment) -> SegmentRegister {
1006 virt::x86::SegmentRegister::from(self.reg_page.segment[reg as usize]).into()
1007 }
1008
1009 fn efer(&mut self) -> u64 {
1010 self.reg_page.efer
1011 }
1012
1013 fn cr0(&mut self) -> u64 {
1014 self.reg_page.cr0
1015 }
1016
1017 fn rflags(&mut self) -> RFlags {
1018 RFlags::from(self.reg_page.rflags)
1019 }
1020
1021 fn set_rflags(&mut self, v: RFlags) {
1022 self.reg_page.rflags = v.into();
1023 self.reg_page.dirty.set_flags(true);
1024 }
1025
1026 fn xmm(&mut self, reg: usize) -> u128 {
1027 assert!(reg < 16);
1028 if reg < 6 {
1029 self.reg_page.xmm[reg]
1030 } else {
1031 let name = HvX64RegisterName(HvX64RegisterName::Xmm0.0 + reg as u32);
1032 let mut assoc = [HvRegisterAssoc::from((name, 0u128))];
1033 let _ = self.vcpufd.get_hvdef_regs(&mut assoc);
1034 assoc[0].value.as_u128()
1035 }
1036 }
1037
1038 fn set_xmm(&mut self, reg: usize, value: u128) {
1039 assert!(reg < 16);
1040 if reg < 6 {
1041 self.reg_page.xmm[reg] = value;
1042 self.reg_page.dirty.set_xmm(true);
1043 } else {
1044 let name = HvX64RegisterName(HvX64RegisterName::Xmm0.0 + reg as u32);
1045 let assoc = [HvRegisterAssoc::from((name, value))];
1046 self.vcpufd.set_hvdef_regs(&assoc).unwrap();
1047 }
1048 }
1049
1050 fn flush(&mut self) {}
1051
1052 fn instruction_bytes(&self) -> &[u8] {
1053 match self.message.header.typ {
1054 HvMessageType::HvMessageTypeGpaIntercept
1055 | HvMessageType::HvMessageTypeUnmappedGpa
1056 | HvMessageType::HvMessageTypeUnacceptedGpa => {
1057 let info = self
1058 .message
1059 .as_message::<hvdef::HvX64MemoryInterceptMessage>();
1060 &info.instruction_bytes[..info.instruction_byte_count as usize]
1061 }
1062 HvMessageType::HvMessageTypeX64IoPortIntercept => {
1063 let info = self
1064 .message
1065 .as_message::<hvdef::HvX64IoPortInterceptMessage>();
1066 &info.instruction_bytes[..info.instruction_byte_count as usize]
1067 }
1068 _ => unreachable!(),
1069 }
1070 }
1071
1072 fn physical_address(&self) -> Option<u64> {
1073 match self.message.header.typ {
1074 HvMessageType::HvMessageTypeGpaIntercept
1075 | HvMessageType::HvMessageTypeUnmappedGpa
1076 | HvMessageType::HvMessageTypeUnacceptedGpa => {
1077 let info = self
1078 .message
1079 .as_message::<hvdef::HvX64MemoryInterceptMessage>();
1080 Some(info.guest_physical_address)
1081 }
1082 _ => None,
1083 }
1084 }
1085
1086 fn initial_gva_translation(
1087 &mut self,
1088 ) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
1089 match self.message.header.typ {
1090 HvMessageType::HvMessageTypeGpaIntercept
1091 | HvMessageType::HvMessageTypeUnmappedGpa
1092 | HvMessageType::HvMessageTypeUnacceptedGpa => {}
1093 _ => return None,
1094 }
1095
1096 let message = self
1097 .message
1098 .as_message::<hvdef::HvX64MemoryInterceptMessage>();
1099
1100 if !message.memory_access_info.gva_gpa_valid() {
1101 return None;
1102 }
1103
1104 if let Ok(translate_mode) = TranslateMode::try_from(message.header.intercept_access_type) {
1105 Some(virt_support_x86emu::emulate::InitialTranslation {
1106 gva: message.guest_virtual_address,
1107 gpa: message.guest_physical_address,
1108 translate_mode,
1109 })
1110 } else {
1111 None
1112 }
1113 }
1114
1115 fn interruption_pending(&self) -> bool {
1116 self.interruption_pending
1117 }
1118
1119 fn check_vtl_access(
1120 &mut self,
1121 _gpa: u64,
1122 _mode: TranslateMode,
1123 ) -> Result<(), virt_support_x86emu::emulate::EmuCheckVtlAccessError> {
1124 Ok(())
1125 }
1126
1127 fn translate_gva(
1128 &mut self,
1129 gva: u64,
1130 mode: TranslateMode,
1131 ) -> Result<EmuTranslateResult, EmuTranslateError> {
1132 emulate_translate_gva(self, gva, mode)
1133 }
1134
1135 fn inject_pending_event(&mut self, event_info: hvdef::HvX64PendingEvent) {
1136 self.vcpufd
1137 .set_hvdef_regs(&[
1138 HvRegisterAssoc::from((
1139 HvX64RegisterName::PendingEvent0,
1140 u128::from(event_info.reg_0),
1141 )),
1142 HvRegisterAssoc::from((
1143 HvX64RegisterName::PendingEvent1,
1144 u128::from(event_info.reg_1),
1145 )),
1146 ])
1147 .unwrap();
1148 }
1149
1150 fn is_gpa_mapped(&self, gpa: u64, _write: bool) -> bool {
1151 self.partition
1152 .mem_layout
1153 .ram()
1154 .iter()
1155 .any(|r| r.range.contains_addr(gpa))
1156 }
1157
1158 fn lapic_base_address(&self) -> Option<u64> {
1159 None
1160 }
1161
1162 fn lapic_read(&mut self, _address: u64, _data: &mut [u8]) {
1163 unreachable!()
1164 }
1165
1166 fn lapic_write(&mut self, _address: u64, _data: &[u8]) {
1167 unreachable!()
1168 }
1169}
1170
1171impl TranslateGvaSupport for MshvEmulationState<'_> {
1172 fn guest_memory(&self) -> &GuestMemory {
1173 &self.partition.gm
1174 }
1175
1176 fn acquire_tlb_lock(&mut self) {}
1177
1178 fn registers(&mut self) -> TranslationRegisters {
1179 TranslationRegisters {
1180 cr0: self.reg_page.cr0,
1181 cr4: self.reg_page.cr4,
1182 efer: self.reg_page.efer,
1183 cr3: self.reg_page.cr3,
1184 rflags: self.reg_page.rflags,
1185 ss: virt::x86::SegmentRegister::from(
1186 self.reg_page.segment[x86emu::Segment::SS as usize],
1187 )
1188 .into(),
1189 encryption_mode: virt_support_x86emu::translate::EncryptionMode::None,
1190 }
1191 }
1192}
1193
1194impl hv1_hypercall::X64RegisterState for MshvHypercallHandler<'_> {
1199 fn rip(&mut self) -> u64 {
1200 self.reg_page.rip
1201 }
1202
1203 fn set_rip(&mut self, rip: u64) {
1204 self.reg_page.rip = rip;
1205 self.reg_page.dirty.set_instruction_pointer(true);
1206 }
1207
1208 fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
1209 self.reg_page.gp_registers[n as usize]
1210 }
1211
1212 fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
1213 let index = n as usize;
1214 self.reg_page.gp_registers[index] = value;
1215 if self.isolated {
1216 self.modified_gp |= 1 << index;
1217 } else {
1218 self.reg_page.dirty.set_general_purpose(true);
1219 }
1220 }
1221
1222 fn xmm(&mut self, n: usize) -> u128 {
1223 self.reg_page.xmm[n]
1224 }
1225
1226 fn set_xmm(&mut self, n: usize, value: u128) {
1227 self.reg_page.xmm[n] = value;
1228 if self.isolated {
1229 self.modified_xmm |= 1 << n;
1230 } else {
1231 self.reg_page.dirty.set_xmm(true);
1232 }
1233 }
1234}
1235
1236pub(crate) struct MshvHypercallHandler<'a> {
1237 pub(crate) partition: &'a MshvPartitionInner,
1238 pub(crate) reg_page: &'a mut HvX64RegisterPage,
1239 pub(crate) caller_vp: VpIndex,
1240 isolated: bool,
1241 modified_gp: u16,
1242 modified_xmm: u8,
1243}
1244
1245impl MshvHypercallHandler<'_> {
1246 const DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1247 Self,
1248 [
1249 hv1_hypercall::HvPostMessage,
1250 hv1_hypercall::HvSignalEvent,
1251 hv1_hypercall::HvRetargetDeviceInterrupt,
1252 hv1_hypercall::HvX64StartVirtualProcessor,
1253 ],
1254 );
1255}
1256
1257impl hv1_hypercall::StartVirtualProcessor<hvdef::hypercall::InitialVpContextX64>
1258 for MshvHypercallHandler<'_>
1259{
1260 fn start_virtual_processor(
1261 &mut self,
1262 partition_id: u64,
1263 target_vp: u32,
1264 target_vtl: Vtl,
1265 vp_context: &hvdef::hypercall::InitialVpContextX64,
1266 ) -> hvdef::HvResult<()> {
1267 if self.partition.isolation.snp().is_none() || partition_id != hvdef::HV_PARTITION_ID_SELF {
1268 return Err(hvdef::HvError::InvalidPartitionId);
1269 }
1270 if target_vtl != Vtl::Vtl0 {
1271 return Err(hvdef::HvError::InvalidParameter);
1272 }
1273
1274 let target_vp = VpIndex::new(target_vp);
1275 if target_vp.is_bsp()
1276 || target_vp == self.caller_vp
1277 || target_vp.index() as usize >= self.partition.vps.len()
1278 {
1279 return Err(hvdef::HvError::InvalidVpIndex);
1280 }
1281
1282 let vmsa_gpa = snp_start_vp_vmsa_gpa(vp_context).ok_or(hvdef::HvError::InvalidParameter)?;
1283 let vmsa_end = vmsa_gpa
1284 .checked_add(hvdef::HV_PAGE_SIZE)
1285 .ok_or(hvdef::HvError::InvalidParameter)?;
1286 if !self
1287 .partition
1288 .mem_layout
1289 .ram()
1290 .iter()
1291 .any(|range| range.range.contains(&MemoryRange::new(vmsa_gpa..vmsa_end)))
1292 {
1293 return Err(hvdef::HvError::InvalidParameter);
1294 }
1295
1296 let request = mshv_bindings::mshv_sev_snp_ap_create {
1297 vp_id: u64::from(target_vp.index()),
1298 vmsa_gpa,
1299 };
1300 self.partition
1301 .vmfd
1302 .sev_snp_ap_create(&request)
1303 .map_err(|err| {
1304 tracelimit::error_ratelimited!(
1305 error = &err as &dyn std::error::Error,
1306 target_vp = target_vp.index(),
1307 vmsa_gpa,
1308 "failed to handle SNP StartVirtualProcessor"
1309 );
1310 hvdef::HvError::InvalidVpState
1311 })
1312 }
1313}
1314
1315impl hv1_hypercall::RetargetDeviceInterrupt for MshvHypercallHandler<'_> {
1316 fn retarget_interrupt(
1317 &mut self,
1318 device_id: u64,
1319 address: u64,
1320 data: u32,
1321 params: hv1_hypercall::HvInterruptParameters<'_>,
1322 ) -> hvdef::HvResult<()> {
1323 let target_processors = Vec::from_iter(params.target_processors);
1324 let vpci_params = vmcore::vpci_msi::VpciInterruptParameters {
1325 vector: params.vector,
1326 multicast: params.multicast,
1327 target_processors: &target_processors,
1328 };
1329
1330 self.partition
1331 .software_devices
1332 .retarget_interrupt(device_id, address, data, &vpci_params)
1333 }
1334}
1335
1336fn supported_processor_features() -> hvdef::HvX64PartitionProcessorFeatures {
1342 hvdef::HvX64PartitionProcessorFeatures::new()
1343 .with_sse3_support(true)
1344 .with_lahf_sahf_support(true)
1345 .with_ssse3_support(true)
1346 .with_sse4_1_support(true)
1347 .with_sse4_2_support(true)
1348 .with_sse4a_support(true)
1349 .with_xop_support(true)
1350 .with_pop_cnt_support(true)
1351 .with_cmpxchg16b_support(true)
1352 .with_altmovcr8_support(true)
1353 .with_lzcnt_support(true)
1354 .with_mis_align_sse_support(true)
1355 .with_mmx_ext_support(true)
1356 .with_amd3d_now_support(true)
1357 .with_extended_amd3d_now_support(true)
1358 .with_page_1gb_support(true)
1359 .with_aes_support(true)
1360 .with_pclmulqdq_support(true)
1361 .with_pcid_support(true)
1362 .with_fma4_support(true)
1363 .with_f16c_support(true)
1364 .with_rd_rand_support(true)
1365 .with_rd_wr_fs_gs_support(true)
1366 .with_smep_support(true)
1367 .with_enhanced_fast_string_support(true)
1368 .with_bmi1_support(true)
1369 .with_bmi2_support(true)
1370 .with_movbe_support(true)
1371 .with_npiep1_support(true)
1372 .with_dep_x87_fpu_save_support(true)
1373 .with_rd_seed_support(true)
1374 .with_adx_support(true)
1375 .with_intel_prefetch_support(true)
1376 .with_smap_support(true)
1377 .with_hle_support(true)
1378 .with_rtm_support(true)
1379 .with_rdtscp_support(true)
1380 .with_clflushopt_support(true)
1381 .with_clwb_support(true)
1382 .with_sha_support(true)
1383 .with_x87_pointers_saved_support(true)
1384 .with_invpcid_support(true)
1385 .with_ibrs_support(true)
1386 .with_stibp_support(true)
1387 .with_ibpb_support(true)
1388 .with_unrestricted_guest_support(true)
1389 .with_mdd_support(true)
1390 .with_fast_short_rep_mov_support(true)
1391 .with_rdcl_no_support(true)
1392 .with_ibrs_all_support(true)
1393 .with_ssb_no_support(true)
1394 .with_rsb_a_no_support(true)
1395 .with_rd_pid_support(true)
1396 .with_umip_support(true)
1397 .with_mbs_no_support(true)
1398 .with_mb_clear_support(true)
1399 .with_taa_no_support(true)
1400 .with_tsx_ctrl_support(true)
1401}
1402
1403fn supported_processor_features1() -> hvdef::HvX64PartitionProcessorFeatures1 {
1405 hvdef::HvX64PartitionProcessorFeatures1::new()
1406 .with_a_count_m_count_support(true)
1407 .with_tsc_invariant_support(true)
1408 .with_cl_zero_support(true)
1409 .with_rdpru_support(true)
1410 .with_la57_support(true)
1411 .with_mbec_support(true)
1412 .with_nested_virt_support(true)
1413 .with_psfd_support(true)
1414 .with_cet_ss_support(true)
1415 .with_cet_ibt_support(true)
1416 .with_vmx_exception_inject_support(true)
1417 .with_umwait_tpause_support(true)
1418 .with_movdiri_support(true)
1419 .with_movdir64b_support(true)
1420 .with_cldemote_support(true)
1421 .with_serialize_support(true)
1422 .with_tsc_deadline_tmr_support(true)
1423 .with_tsc_adjust_support(true)
1424 .with_fz_l_rep_movsb(true)
1425 .with_fs_rep_stosb(true)
1426 .with_fs_rep_cmpsb(true)
1427 .with_tsx_ld_trk_support(true)
1428 .with_vmx_ins_outs_exit_info_support(true)
1429 .with_sbdr_ssdp_no_support(true)
1430 .with_fbsdp_no_support(true)
1431 .with_psdp_no_support(true)
1432 .with_fb_clear_support(true)
1433 .with_btc_no_support(true)
1434 .with_ibpb_rsb_flush_support(true)
1435 .with_stibp_always_on_support(true)
1436 .with_perf_global_ctrl_support(true)
1437 .with_npt_execute_only_support(true)
1438 .with_npt_ad_flags_support(true)
1439 .with_npt_1gb_page_support(true)
1440 .with_cmpccxadd_support(true)
1441 .with_prefetch_i_support(true)
1442 .with_sha512_support(true)
1443 .with_rfds_no_support(true)
1444 .with_rfds_clear_support(true)
1445 .with_sm3_support(true)
1446 .with_sm4_support(true)
1447}
1448
1449fn supported_xsave_features() -> hvdef::HvX64PartitionProcessorXsaveFeatures {
1451 hvdef::HvX64PartitionProcessorXsaveFeatures::new()
1452 .with_xsave_support(true)
1453 .with_xsaveopt_support(true)
1454 .with_avx_support(true)
1455 .with_avx2_support(true)
1456 .with_fma_support(true)
1457 .with_mpx_support(true)
1458 .with_avx512_support(true)
1459 .with_avx512_dq_support(true)
1460 .with_avx512_cd_support(true)
1461 .with_avx512_bw_support(true)
1462 .with_avx512_vl_support(true)
1463 .with_xsave_comp_support(true)
1464 .with_xsave_supervisor_support(true)
1465 .with_xcr1_support(true)
1466 .with_avx512_bitalg_support(true)
1467 .with_avx512_ifma_support(true)
1468 .with_avx512_vbmi_support(true)
1469 .with_avx512_vbmi2_support(true)
1470 .with_avx512_vnni_support(true)
1471 .with_gfni_support(true)
1472 .with_vaes_support(true)
1473 .with_avx512_vpopcntdq_support(true)
1474 .with_vpclmulqdq_support(true)
1475 .with_avx512_bf16_support(true)
1476 .with_avx512_vp2_intersect_support(true)
1477 .with_avx512_fp16_support(true)
1478 .with_xfd_support(true)
1479 .with_amx_tile_support(true)
1480 .with_amx_bf16_support(true)
1481 .with_amx_int8_support(true)
1482 .with_avx_vnni_support(true)
1483 .with_avx_ifma_support(true)
1484 .with_avx_ne_convert_support(true)
1485 .with_avx_vnni_int8_support(true)
1486 .with_avx_vnni_int16_support(true)
1487 .with_avx10_1_256_support(true)
1488 .with_avx10_1_512_support(true)
1489 .with_amx_fp16_support(true)
1490}
1491
1492#[cfg(test)]
1493mod tests {
1494 use super::*;
1495 use test_with_tracing::test;
1496
1497 #[test]
1498 fn can_disable_snp_cpuid_offloads() {
1499 let enabled = snp_vmgexit_offloads(false);
1500 let disabled = snp_vmgexit_offloads(true);
1501 assert!(SnpPartitionState::new(false).cpuid_offloads_enabled);
1502 assert!(!SnpPartitionState::new(true).cpuid_offloads_enabled);
1503
1504 unsafe {
1507 assert_eq!(enabled.__bindgen_anon_1.nae_cpuid(), 1);
1508 assert_eq!(enabled.__bindgen_anon_1.msr_cpuid(), 1);
1509 assert_eq!(disabled.__bindgen_anon_1.nae_cpuid(), 0);
1510 assert_eq!(disabled.__bindgen_anon_1.msr_cpuid(), 0);
1511 assert_eq!(
1512 disabled.__bindgen_anon_1.nae_rdmsr(),
1513 enabled.__bindgen_anon_1.nae_rdmsr()
1514 );
1515 }
1516 }
1517
1518 #[test]
1519 fn disabling_snp_cpuid_offloads_requires_snp_isolation() {
1520 validate_snp_cpuid_offload_config(virt::IsolationType::Snp, true).unwrap();
1521 validate_snp_cpuid_offload_config(virt::IsolationType::None, false).unwrap();
1522 assert_eq!(
1523 validate_snp_cpuid_offload_config(virt::IsolationType::None, true)
1524 .unwrap_err()
1525 .to_string(),
1526 "invalid MSHV configuration: snp_disable_cpuid_offload requires SNP isolation"
1527 );
1528 }
1529
1530 #[test]
1531 fn snp_does_not_report_reference_tsc_page_support() {
1532 assert!(!hv1_reference_tsc_page_supported(
1533 true,
1534 virt::IsolationType::Snp,
1535 true
1536 ));
1537 assert!(hv1_reference_tsc_page_supported(
1538 true,
1539 virt::IsolationType::None,
1540 true
1541 ));
1542 assert!(!hv1_reference_tsc_page_supported(
1543 false,
1544 virt::IsolationType::None,
1545 true
1546 ));
1547 assert!(!hv1_reference_tsc_page_supported(
1548 true,
1549 virt::IsolationType::None,
1550 false
1551 ));
1552 }
1553
1554 #[test]
1555 fn snp_partition_creation_uses_isolation_flags() {
1556 let args = partition_create_args(true, false, false);
1557 let pt_isolation = args.pt_isolation;
1558 let pt_num_cpu_fbanks = args.pt_num_cpu_fbanks;
1559 let pt_cpu_fbanks = args.pt_cpu_fbanks;
1560 let pt_disabled_xsave = args.pt_disabled_xsave;
1561
1562 assert_eq!(pt_isolation, mshv_bindings::MSHV_PT_ISOLATION_SNP as u64);
1563 assert_ne!(args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_LAPIC, 0);
1564 assert_ne!(
1565 args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_GPA_SUPER_PAGES,
1566 0
1567 );
1568 assert_ne!(args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_X2APIC, 0);
1569 assert_ne!(
1570 args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES,
1571 0
1572 );
1573 assert_eq!(
1574 pt_num_cpu_fbanks,
1575 mshv_bindings::MSHV_NUM_CPU_FEATURES_BANKS as u16
1576 );
1577 assert_eq!(
1578 pt_cpu_fbanks,
1579 [
1580 !u64::from(supported_processor_features()),
1581 !u64::from(supported_processor_features1()),
1582 ]
1583 );
1584 assert_eq!(pt_disabled_xsave, !u64::from(supported_xsave_features()));
1585 }
1586
1587 #[test]
1588 fn ordinary_partition_creation_keeps_feature_banks() {
1589 let args = partition_create_args(false, false, true);
1590 let pt_isolation = args.pt_isolation;
1591 let pt_num_cpu_fbanks = args.pt_num_cpu_fbanks;
1592
1593 assert_eq!(pt_isolation, mshv_bindings::MSHV_PT_ISOLATION_NONE as u64);
1594 assert_ne!(
1595 args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES,
1596 0
1597 );
1598 assert_ne!(
1599 args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_SMT_ENABLED_GUEST,
1600 0
1601 );
1602 assert_eq!(args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_X2APIC, 0);
1603 assert_ne!(
1604 args.pt_flags & 1 << mshv_bindings::MSHV_PT_BIT_GPA_SUPER_PAGES,
1605 0
1606 );
1607 assert_eq!(
1608 pt_num_cpu_fbanks,
1609 mshv_bindings::MSHV_NUM_CPU_FEATURES_BANKS as u16
1610 );
1611 }
1612}