Skip to main content

virt_mshv_vtl/processor/snp/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Processor support for SNP partitions.
5
6use super::BackingParams;
7use super::BackingPrivate;
8use super::BackingSharedParams;
9use super::HardwareIsolatedBacking;
10use super::InterceptMessageOptionalState;
11use super::InterceptMessageState;
12use super::UhEmulationState;
13use super::hardware_cvm;
14use super::hardware_cvm::HardwareIsolatedGuestTimer;
15use super::vp_state;
16use super::vp_state::UhVpStateAccess;
17use crate::BackingShared;
18use crate::Error;
19use crate::GuestVtl;
20use crate::TlbFlushLockAccess;
21use crate::UhCvmPartitionState;
22use crate::UhCvmVpState;
23use crate::UhPartitionInner;
24use crate::UhPartitionNewParams;
25use crate::WakeReason;
26use crate::devmsr;
27use crate::processor::UhHypercallHandler;
28use crate::processor::UhProcessor;
29use crate::processor::hardware_cvm::apic::ApicBacking;
30use cvm_tracing::CVM_ALLOWED;
31use cvm_tracing::CVM_CONFIDENTIAL;
32use hcl::protocol::hcl_intr_offload_flags;
33use hcl::vmsa::VmsaWrapper;
34use hv1_emulator::hv::ProcessorVtlHv;
35use hv1_emulator::synic::ProcessorSynic;
36use hv1_hypercall::HvRepResult;
37use hv1_hypercall::HypercallIo;
38use hv1_structs::ProcessorSet;
39use hv1_structs::VtlArray;
40use hvdef::HV_PAGE_SIZE;
41use hvdef::HvDeliverabilityNotificationsRegister;
42use hvdef::HvError;
43use hvdef::HvMessageType;
44use hvdef::HvX64PendingExceptionEvent;
45use hvdef::HvX64RegisterName;
46use hvdef::Vtl;
47use hvdef::hypercall::Control;
48use hvdef::hypercall::HvFlushFlags;
49use hvdef::hypercall::HvGvaRange;
50use hvdef::hypercall::HypercallOutput;
51use inspect::Inspect;
52use inspect::InspectMut;
53use inspect_counters::Counter;
54use virt::EmulatorMonitorSupport;
55use virt::Processor;
56use virt::VpHaltReason;
57use virt::VpIndex;
58use virt::io::CpuIo;
59use virt::state::StateElement;
60use virt::vp;
61use virt::vp::AccessVpState;
62use virt::vp::MpState;
63use virt::x86::MsrError;
64use virt::x86::MsrErrorExt;
65use virt::x86::SegmentRegister;
66use virt::x86::TableRegister;
67use virt_support_apic::ApicClient;
68use virt_support_x86emu::emulate::EmulatorSupport as X86EmulatorSupport;
69use virt_support_x86emu::emulate::emulate_io;
70use virt_support_x86emu::emulate::emulate_translate_gva;
71use virt_support_x86emu::translate::TranslationRegisters;
72use vmcore::vmtime::VmTime;
73use vmcore::vmtime::VmTimeAccess;
74use x86defs::RFlags;
75use x86defs::apic::X2APIC_MSR_BASE;
76use x86defs::cpuid::CpuidFunction;
77use x86defs::snp::SevAvicIncompleteIpiInfo1;
78use x86defs::snp::SevAvicIncompleteIpiInfo2;
79use x86defs::snp::SevAvicNoAccelInfo;
80use x86defs::snp::SevAvicPage;
81use x86defs::snp::SevAvicRegisterNumber;
82use x86defs::snp::SevEventInjectInfo;
83use x86defs::snp::SevExitCode;
84use x86defs::snp::SevInvlpgbEcx;
85use x86defs::snp::SevInvlpgbEdx;
86use x86defs::snp::SevInvlpgbRax;
87use x86defs::snp::SevIoAccessInfo;
88use x86defs::snp::SevNpfInfo;
89use x86defs::snp::SevSelector;
90use x86defs::snp::SevStatusMsr;
91use x86defs::snp::SevVmsa;
92use x86defs::snp::Vmpl;
93use zerocopy::FromZeros;
94use zerocopy::IntoBytes;
95
96#[derive(Debug, Error)]
97#[error("invalid vmcb")]
98struct InvalidVmcb;
99
100#[derive(Debug, Error)]
101enum SnpGhcbError {
102    #[error("failed to access GHCB page")]
103    GhcbPageAccess(#[source] guestmem::GuestMemoryError),
104    #[error("ghcb page used for vmgexit does not match overlay page")]
105    GhcbMisconfiguration,
106}
107
108#[derive(Debug, Error)]
109enum SnpRunVpError {
110    #[error("guest AVIC backing page is not validated or cannot be accessed")]
111    VpNotRestartableError,
112    #[error("failed to run")]
113    RunVpError(#[source] hcl::ioctl::Error),
114}
115
116/// A backing for SNP partitions.
117#[derive(InspectMut)]
118pub struct SnpBacked {
119    #[inspect(hex)]
120    hv_sint_notifications: u16,
121    general_stats: VtlArray<GeneralStats, 2>,
122    exit_stats: VtlArray<ExitStats, 2>,
123    synic_timer_deadline: SnpSynicTimerDeadline,
124    #[inspect(flatten)]
125    cvm: UhCvmVpState,
126}
127
128#[derive(Inspect, Default)]
129struct GeneralStats {
130    guest_busy: Counter,
131    int_ack: Counter,
132    synth_int: Counter,
133}
134
135#[derive(Inspect, Default)]
136struct ExitStats {
137    automatic_exit: Counter,
138    bus_lock: Counter,
139    cpuid: Counter,
140    hlt: Counter,
141    intr: Counter,
142    invd: Counter,
143    invlpgb: Counter,
144    ioio: Counter,
145    msr_read: Counter,
146    msr_write: Counter,
147    npf: Counter,
148    npf_no_intercept: Counter,
149    npf_spurious: Counter,
150    rdpmc: Counter,
151    vmgexit: Counter,
152    vmmcall: Counter,
153    xsetbv: Counter,
154    excp_db: Counter,
155    secure_reg_write: Counter,
156    avic_no_accel: Counter,
157    avic_incomplete_ipi: Counter,
158}
159
160#[derive(Inspect, Default)]
161struct SnpSynicTimerDeadline {
162    #[inspect(hex)]
163    armed_ref_time: Option<u64>,
164    #[inspect(hex)]
165    armed_timeout: Option<VmTime>,
166    #[inspect(hex)]
167    next_ref_time: Option<u64>,
168    deadline_seen: bool,
169}
170
171impl SnpSynicTimerDeadline {
172    fn clear_scan_deadline(&mut self) {
173        // If the previous scan did not report any deadline, the cached armed deadline
174        // is stale and should no longer be restored into VmTime.
175        if !self.deadline_seen {
176            self.armed_ref_time = None;
177            self.armed_timeout = None;
178        }
179
180        // Start a new scan with no candidate. If update_scan_deadline is called
181        // during this scan, deadline_seen preserves the armed deadline for the
182        // next scan boundary.
183        self.next_ref_time = None;
184        self.deadline_seen = false;
185    }
186
187    fn update_scan_deadline(&mut self, ref_time_next: u64) -> bool {
188        // Only the earliest deadline discovered during a scan should drive the
189        // backing timer.
190        if self
191            .next_ref_time
192            .is_some_and(|next_ref_time| ref_time_next >= next_ref_time)
193        {
194            return false;
195        }
196
197        self.next_ref_time = Some(ref_time_next);
198        self.deadline_seen = true;
199        true
200    }
201}
202
203struct SnpKernelGuestTimer {
204    fallback: hardware_cvm::VmTimeGuestTimer,
205}
206
207impl SnpKernelGuestTimer {
208    fn timeout(&self, vmtime: &VmTimeAccess, ref_time_now: u64, ref_time_next: u64) -> VmTime {
209        self.fallback.timeout(vmtime, ref_time_now, ref_time_next)
210    }
211}
212
213impl HardwareIsolatedGuestTimer<SnpBacked> for SnpKernelGuestTimer {
214    fn is_hardware_virtualized(&self) -> bool {
215        false
216    }
217
218    fn update_deadline(
219        &self,
220        vp: &mut UhProcessor<'_, SnpBacked>,
221        ref_time_now: u64,
222        ref_time_next: u64,
223    ) {
224        self.fallback
225            .update_deadline(vp, ref_time_now, ref_time_next);
226    }
227
228    fn clear_deadline(&self, vp: &mut UhProcessor<'_, SnpBacked>) {
229        self.fallback.clear_deadline(vp);
230    }
231
232    fn begin_vtl_transition(&self, vp: &mut UhProcessor<'_, SnpBacked>, vtl: GuestVtl) {
233        vp.runner.set_stimer0_config(
234            (vtl == GuestVtl::Vtl0)
235                .then(|| vp.backing.cvm.hv[GuestVtl::Vtl0].synic.stimer_config(0)),
236        );
237    }
238
239    fn end_vtl_transition(&self, vp: &mut UhProcessor<'_, SnpBacked>, _vtl: GuestVtl) {
240        if let Some(update) = vp.runner.take_stimer0_update() {
241            assert_eq!(_vtl, GuestVtl::Vtl0);
242            tracing::trace!(
243                count = update.count,
244                programmed_ref_time = update.programmed_ref_time,
245                expired = update.expired,
246                "synchronizing kernel STIMER0 update"
247            );
248            // Kernel expiry only wakes VTL2. Reconstructing the original due
249            // time lets the normal SynIC scan perform the sole delivery.
250            vp.backing.cvm.hv[GuestVtl::Vtl0].synic.set_stimer_count_at(
251                0,
252                update.count,
253                update.programmed_ref_time,
254            );
255        }
256    }
257}
258
259enum UhDirectOverlay {
260    Sipp,
261    Sifp,
262    Ghcb,
263    Count,
264}
265
266impl SnpBacked {
267    // Fix up the efer value to have the correct long mode flags and SVM flag
268    fn calculate_efer(efer: u64, cr0: u64) -> u64 {
269        let new_efer = if efer & x86defs::X64_EFER_LME != 0 && cr0 & x86defs::X64_CR0_PG != 0 {
270            efer | x86defs::X64_EFER_LMA
271        } else {
272            efer & !x86defs::X64_EFER_LMA
273        };
274        new_efer | x86defs::X64_EFER_SVME
275    }
276
277    /// Gets the number of pages that will be allocated from the shared page pool
278    /// for each CPU.
279    pub fn shared_pages_required_per_cpu() -> u64 {
280        UhDirectOverlay::Count as u64
281    }
282}
283
284impl HardwareIsolatedBacking for SnpBacked {
285    fn cvm_state(&self) -> &UhCvmVpState {
286        &self.cvm
287    }
288
289    fn cvm_state_mut(&mut self) -> &mut UhCvmVpState {
290        &mut self.cvm
291    }
292
293    fn cvm_partition_state(shared: &Self::Shared) -> &UhCvmPartitionState {
294        &shared.cvm
295    }
296
297    fn switch_vtl(this: &mut UhProcessor<'_, Self>, source_vtl: GuestVtl, target_vtl: GuestVtl) {
298        let [vmsa0, vmsa1] = this.runner.vmsas_mut();
299        let (current_vmsa, mut target_vmsa) = match (source_vtl, target_vtl) {
300            (GuestVtl::Vtl0, GuestVtl::Vtl1) => (vmsa0, vmsa1),
301            (GuestVtl::Vtl1, GuestVtl::Vtl0) => (vmsa1, vmsa0),
302            _ => unreachable!(),
303        };
304
305        target_vmsa.set_rax(current_vmsa.rax());
306        target_vmsa.set_rbx(current_vmsa.rbx());
307        target_vmsa.set_rcx(current_vmsa.rcx());
308        target_vmsa.set_rdx(current_vmsa.rdx());
309        target_vmsa.set_rbp(current_vmsa.rbp());
310        target_vmsa.set_rsi(current_vmsa.rsi());
311        target_vmsa.set_rdi(current_vmsa.rdi());
312        target_vmsa.set_r8(current_vmsa.r8());
313        target_vmsa.set_r9(current_vmsa.r9());
314        target_vmsa.set_r10(current_vmsa.r10());
315        target_vmsa.set_r11(current_vmsa.r11());
316        target_vmsa.set_r12(current_vmsa.r12());
317        target_vmsa.set_r13(current_vmsa.r13());
318        target_vmsa.set_r14(current_vmsa.r14());
319        target_vmsa.set_r15(current_vmsa.r15());
320        target_vmsa.set_xcr0(current_vmsa.xcr0());
321
322        target_vmsa.set_cr2(current_vmsa.cr2());
323
324        // DR6 not shared on AMD
325        target_vmsa.set_dr0(current_vmsa.dr0());
326        target_vmsa.set_dr1(current_vmsa.dr1());
327        target_vmsa.set_dr2(current_vmsa.dr2());
328        target_vmsa.set_dr3(current_vmsa.dr3());
329
330        target_vmsa.set_pl0_ssp(current_vmsa.pl0_ssp());
331        target_vmsa.set_pl1_ssp(current_vmsa.pl1_ssp());
332        target_vmsa.set_pl2_ssp(current_vmsa.pl2_ssp());
333        target_vmsa.set_pl3_ssp(current_vmsa.pl3_ssp());
334        target_vmsa.set_u_cet(current_vmsa.u_cet());
335
336        target_vmsa.set_x87_registers(&current_vmsa.x87_registers());
337
338        let vec_reg_count = 16;
339        for i in 0..vec_reg_count {
340            target_vmsa.set_xmm_registers(i, current_vmsa.xmm_registers(i));
341            target_vmsa.set_ymm_registers(i, current_vmsa.ymm_registers(i));
342        }
343
344        this.backing.cvm_state_mut().exit_vtl = target_vtl;
345    }
346
347    fn translation_registers(
348        &self,
349        this: &UhProcessor<'_, Self>,
350        vtl: GuestVtl,
351    ) -> TranslationRegisters {
352        let vmsa = this.runner.vmsa(vtl);
353        TranslationRegisters {
354            cr0: vmsa.cr0(),
355            cr4: vmsa.cr4(),
356            efer: vmsa.efer(),
357            cr3: vmsa.cr3(),
358            rflags: vmsa.rflags(),
359            ss: virt_seg_from_snp(vmsa.ss()).into(),
360            encryption_mode: virt_support_x86emu::translate::EncryptionMode::Vtom(
361                this.partition.caps.vtom.unwrap(),
362            ),
363        }
364    }
365
366    fn tlb_flush_lock_access<'a>(
367        vp_index: Option<VpIndex>,
368        partition: &'a UhPartitionInner,
369        shared: &'a Self::Shared,
370    ) -> impl TlbFlushLockAccess + 'a {
371        SnpTlbLockFlushAccess {
372            vp_index,
373            partition,
374            shared,
375        }
376    }
377
378    fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8> {
379        let event_inject = this.runner.vmsa(vtl).event_inject();
380        if event_inject.valid() {
381            Some(event_inject.vector())
382        } else {
383            None
384        }
385    }
386
387    fn set_pending_exception(
388        this: &mut UhProcessor<'_, Self>,
389        vtl: GuestVtl,
390        event: HvX64PendingExceptionEvent,
391    ) {
392        let inject_info = SevEventInjectInfo::new()
393            .with_valid(true)
394            .with_deliver_error_code(event.deliver_error_code())
395            .with_error_code(event.error_code())
396            .with_vector(event.vector().try_into().unwrap())
397            .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT);
398
399        this.runner.vmsa_mut(vtl).set_event_inject(inject_info);
400    }
401
402    fn cr0(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64 {
403        this.runner.vmsa(vtl).cr0()
404    }
405
406    fn cr4(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64 {
407        this.runner.vmsa(vtl).cr4()
408    }
409
410    fn intercept_message_state(
411        this: &UhProcessor<'_, Self>,
412        vtl: GuestVtl,
413        include_optional_state: bool,
414    ) -> InterceptMessageState {
415        let vmsa = this.runner.vmsa(vtl);
416
417        // next_rip may not be set properly for NPFs, so don't read it.
418        let instr_len = if SevExitCode(vmsa.guest_error_code()) == SevExitCode::NPF {
419            0
420        } else {
421            (vmsa.next_rip() - vmsa.rip()) as u8
422        };
423
424        InterceptMessageState {
425            instruction_length_and_cr8: instr_len,
426            cpl: vmsa.cpl(),
427            efer_lma: vmsa.efer() & x86defs::X64_EFER_LMA != 0,
428            cs: virt_seg_from_snp(vmsa.cs()).into(),
429            rip: vmsa.rip(),
430            rflags: vmsa.rflags(),
431            rax: vmsa.rax(),
432            rdx: vmsa.rdx(),
433            optional: if include_optional_state {
434                Some(InterceptMessageOptionalState {
435                    ds: virt_seg_from_snp(vmsa.ds()).into(),
436                    es: virt_seg_from_snp(vmsa.es()).into(),
437                })
438            } else {
439                None
440            },
441            rcx: vmsa.rcx(),
442            rsi: vmsa.rsi(),
443            rdi: vmsa.rdi(),
444        }
445    }
446
447    fn cr_intercept_registration(
448        this: &mut UhProcessor<'_, Self>,
449        intercept_control: hvdef::HvRegisterCrInterceptControl,
450    ) {
451        // Intercept control is always managed by the hypervisor, so any request
452        // here is only opportunistic. Make the request directly with the
453        // hypervisor. Since intercept control always applies to VTL 1 control of
454        // VTL 0 state, the VTL 1 intercept control register is set here.
455        this.runner
456            .set_vp_registers_hvcall(
457                Vtl::Vtl1,
458                [(
459                    HvX64RegisterName::CrInterceptControl,
460                    u64::from(intercept_control),
461                )],
462            )
463            .expect("setting intercept control succeeds");
464    }
465
466    fn is_interrupt_pending(
467        this: &mut UhProcessor<'_, Self>,
468        vtl: GuestVtl,
469        check_rflags: bool,
470        dev: &impl CpuIo,
471    ) -> bool {
472        let (avic_page, vmsa) = this.runner.secure_avic_page_vmsa_mut(vtl);
473        if vmsa.event_inject().valid()
474            && vmsa.event_inject().interruption_type() == x86defs::snp::SEV_INTR_TYPE_NMI
475        {
476            return true;
477        }
478        // A pending virtual NMI also counts as a pending interrupt.
479        if vmsa.v_intr_cntrl().nmi() {
480            return true;
481        }
482
483        let vmsa_priority = vmsa.v_intr_cntrl().priority() as u32;
484        let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
485        let ppr = lapic
486            .access(&mut SnpApicClient {
487                partition: this.partition,
488                vmsa,
489                avic_page,
490                dev,
491                vmtime: &this.vmtime,
492                vtl,
493            })
494            .get_ppr();
495        let ppr_priority = ppr >> 4;
496        if vmsa_priority <= ppr_priority {
497            return false;
498        }
499
500        let vmsa = this.runner.vmsa_mut(vtl);
501        if (check_rflags && !RFlags::from_bits(vmsa.rflags()).interrupt_enable())
502            || vmsa.v_intr_cntrl().intr_shadow()
503            || !vmsa.v_intr_cntrl().irq()
504        {
505            return false;
506        }
507
508        true
509    }
510
511    fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic> {
512        None
513    }
514
515    fn update_deadline(this: &mut UhProcessor<'_, Self>, ref_time_now: u64, next_ref_time: u64) {
516        if !this
517            .backing
518            .synic_timer_deadline
519            .update_scan_deadline(next_ref_time)
520        {
521            return;
522        }
523
524        // The generic VP loop cancels the local VmTime timeout before each scan.
525        // If the effective SynIC deadline is unchanged, restore the cached VmTime
526        // timeout without re-arming the underlying timer.
527        if this.backing.synic_timer_deadline.armed_ref_time == Some(next_ref_time) {
528            if let Some(timeout) = this.backing.synic_timer_deadline.armed_timeout {
529                this.vmtime.set_timeout_if_before(timeout);
530            }
531            return;
532        }
533
534        let timeout = this
535            .shared
536            .guest_timer
537            .timeout(&this.vmtime, ref_time_now, next_ref_time);
538
539        this.backing.synic_timer_deadline.armed_ref_time = Some(next_ref_time);
540        this.backing.synic_timer_deadline.armed_timeout = Some(timeout);
541        this.vmtime.set_timeout_if_before(timeout);
542    }
543
544    fn clear_deadline(this: &mut UhProcessor<'_, Self>) {
545        this.backing.synic_timer_deadline.clear_scan_deadline();
546        if this.backing.synic_timer_deadline.armed_ref_time.is_none() {
547            this.shared.guest_timer.clear_deadline(this);
548        }
549    }
550}
551
552/// Partition-wide shared data for SNP VPs.
553#[derive(Inspect)]
554pub struct SnpBackedShared {
555    #[inspect(flatten)]
556    pub(crate) cvm: UhCvmPartitionState,
557    invlpgb_count_max: u16,
558    tsc_aux_virtualized: bool,
559    #[inspect(debug)]
560    sev_status: SevStatusMsr,
561    /// Accessor for managing lower VTL timer deadlines.
562    #[inspect(skip)]
563    guest_timer: SnpKernelGuestTimer,
564    secure_avic: bool,
565    /// Whether virtual NMI (V_NMI) is supported by the host CPU.
566    pub(crate) vnmi: bool,
567}
568
569impl SnpBackedShared {
570    pub(crate) fn new(
571        _partition_params: &UhPartitionNewParams<'_>,
572        params: BackingSharedParams<'_>,
573    ) -> Result<Self, Error> {
574        let cvm = params.cvm_state.unwrap();
575        let invlpgb_count_max = x86defs::cpuid::ExtendedAddressSpaceSizesEdx::from(
576            params
577                .cpuid
578                .result(CpuidFunction::ExtendedAddressSpaceSizes.0, 0, &[0; 4])[3],
579        )
580        .invlpgb_count_max();
581        let extended_sev_features = x86defs::cpuid::ExtendedSevFeaturesEax::from(
582            params
583                .cpuid
584                .result(CpuidFunction::ExtendedSevFeatures.0, 0, &[0; 4])[0],
585        );
586        let tsc_aux_virtualized = extended_sev_features.tsc_aux_virtualization();
587
588        // Query SVM features for V_NMI support directly from the host CPU via
589        // CPUID Fn8000_000A_EDX. We can't use `params.cpuid` here because the
590        // CVM cpuid mask filters EDX of this leaf down to zero (SVM is not
591        // exposed to the guest), so the V_NMI bit would never make it through.
592        let svm_features_edx = x86defs::cpuid::ExtendedSvmVersionAndFeaturesEdx::from(
593            safe_intrinsics::cpuid(CpuidFunction::ExtendedSvmVersionAndFeatures.0, 0).edx,
594        );
595        let vnmi = svm_features_edx.vnmi();
596
597        // Query the SEV_FEATURES MSR to determine the features enabled on VTL2's VMSA
598        // and use that to set btb_isolation, prevent_host_ibs, VMSA register protection,
599        // and secure AVIC support.
600        let msr = devmsr::MsrDevice::new(0).expect("open msr");
601        let sev_status =
602            SevStatusMsr::from(msr.read_msr(x86defs::X86X_AMD_MSR_SEV).expect("read msr"));
603        tracing::info!(CVM_ALLOWED, ?sev_status, "SEV status");
604
605        #[cfg(feature = "disable_secure_avic")]
606        let secure_avic = false;
607        #[cfg(not(feature = "disable_secure_avic"))]
608        let secure_avic = sev_status.secure_avic();
609        tracing::info!(CVM_ALLOWED, ?secure_avic, "Secure AVIC status");
610
611        // Configure timer interface for lower VTLs.
612        let guest_timer = SnpKernelGuestTimer {
613            fallback: hardware_cvm::VmTimeGuestTimer,
614        };
615
616        Ok(Self {
617            sev_status,
618            invlpgb_count_max,
619            tsc_aux_virtualized,
620            secure_avic,
621            cvm,
622            guest_timer,
623            vnmi,
624        })
625    }
626}
627
628#[expect(private_interfaces)]
629impl BackingPrivate for SnpBacked {
630    type HclBacking<'snp> = hcl::ioctl::snp::Snp<'snp>;
631    type Shared = SnpBackedShared;
632    type EmulationCache = ();
633
634    fn shared(shared: &BackingShared) -> &Self::Shared {
635        let BackingShared::Snp(shared) = shared else {
636            unreachable!()
637        };
638        shared
639    }
640
641    fn new(params: BackingParams<'_, '_, Self>, shared: &SnpBackedShared) -> Result<Self, Error> {
642        Ok(Self {
643            hv_sint_notifications: 0,
644            general_stats: VtlArray::from_fn(|_| Default::default()),
645            exit_stats: VtlArray::from_fn(|_| Default::default()),
646            synic_timer_deadline: Default::default(),
647            cvm: UhCvmVpState::new(
648                &shared.cvm,
649                params.partition,
650                params.vp_info,
651                UhDirectOverlay::Count as usize,
652            )?,
653        })
654    }
655
656    fn init(this: &mut UhProcessor<'_, Self>) {
657        let sev_status = this.vp().shared.sev_status;
658        let vnmi = this.vp().shared.vnmi;
659        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
660            init_vmsa(
661                &mut this.runner.vmsa_mut(vtl),
662                vtl,
663                this.partition.caps.vtom,
664                sev_status,
665                vnmi,
666            );
667
668            // Reset VMSA-backed state.
669            let registers = vp::Registers::at_reset(&this.partition.caps, &this.inner.vp_info);
670            this.access_state(vtl.into())
671                .set_registers(&registers)
672                .expect("Resetting to architectural state should succeed");
673
674            let debug_registers =
675                vp::DebugRegisters::at_reset(&this.partition.caps, &this.inner.vp_info);
676
677            this.access_state(vtl.into())
678                .set_debug_regs(&debug_registers)
679                .expect("Resetting to architectural state should succeed");
680
681            let xcr0 = vp::Xcr0::at_reset(&this.partition.caps, &this.inner.vp_info);
682            this.access_state(vtl.into())
683                .set_xcr(&xcr0)
684                .expect("Resetting to architectural state should succeed");
685
686            let cache_control = vp::Mtrrs::at_reset(&this.partition.caps, &this.inner.vp_info);
687            this.access_state(vtl.into())
688                .set_mtrrs(&cache_control)
689                .expect("Resetting to architectural state should succeed");
690        }
691
692        // Configure the synic direct overlays.
693        // So far, only VTL 0 is using these (for VMBus).
694        let pfns = &this.backing.cvm.direct_overlay_handle.pfns();
695        let values: &[(HvX64RegisterName, u64); 3] = &[
696            (
697                HvX64RegisterName::Sipp,
698                hvdef::HvSynicSimpSiefp::new()
699                    .with_enabled(true)
700                    .with_base_gpn(pfns[UhDirectOverlay::Sipp as usize])
701                    .into(),
702            ),
703            (
704                HvX64RegisterName::Sifp,
705                hvdef::HvSynicSimpSiefp::new()
706                    .with_enabled(true)
707                    .with_base_gpn(pfns[UhDirectOverlay::Sifp as usize])
708                    .into(),
709            ),
710            (
711                HvX64RegisterName::Ghcb,
712                x86defs::snp::GhcbMsr::new()
713                    .with_info(x86defs::snp::GhcbInfo::REGISTER_REQUEST.0)
714                    .with_pfn(pfns[UhDirectOverlay::Ghcb as usize])
715                    .into(),
716            ),
717        ];
718
719        this.runner
720            .set_vp_registers_hvcall(Vtl::Vtl0, values)
721            .expect("set_vp_registers hypercall for direct overlays should succeed");
722
723        let using_secure_avic = this
724            .runner
725            .vmsa(GuestVtl::Vtl0)
726            .sev_features()
727            .secure_avic();
728        tracing::debug!(?using_secure_avic, "Using secure AVIC for VTL0");
729
730        if using_secure_avic {
731            let vtl0_avic_pfn = this.runner.secure_avic_vtl0_pfn(this.inner.cpu_index);
732            let mut vmsa = this.runner.vmsa_mut(GuestVtl::Vtl0);
733            let savic_ctrl = vmsa
734                .secure_avic_control()
735                .with_secure_avic_en(true)
736                .with_guest_apic_backing_page_ptr(vtl0_avic_pfn);
737            *(vmsa.secure_avic_control_mut()) = savic_ctrl;
738
739            this.set_apic_offload(GuestVtl::Vtl0, true);
740
741            this.runner
742                .set_vp_register(
743                    GuestVtl::Vtl0,
744                    HvX64RegisterName::SevAvicGpa,
745                    savic_ctrl.into_bits().into(),
746                )
747                .expect("set_vp_register hypercall for SAVIC GPA should succeed");
748        }
749
750        // No secure AVIC for VTL 1.
751        assert!(
752            !this
753                .runner
754                .vmsa(GuestVtl::Vtl1)
755                .sev_features()
756                .secure_avic()
757        );
758        this.set_apic_offload(GuestVtl::Vtl1, false);
759    }
760
761    type StateAccess<'p, 'a>
762        = UhVpStateAccess<'a, 'p, Self>
763    where
764        Self: 'a + 'p,
765        'p: 'a;
766
767    fn access_vp_state<'a, 'p>(
768        this: &'a mut UhProcessor<'p, Self>,
769        vtl: GuestVtl,
770    ) -> Self::StateAccess<'p, 'a> {
771        UhVpStateAccess::new(this, vtl)
772    }
773
774    async fn run_vp(
775        this: &mut UhProcessor<'_, Self>,
776        dev: &impl CpuIo,
777        _stop: &mut virt::StopVp<'_>,
778    ) -> Result<(), VpHaltReason> {
779        this.run_vp_snp(dev).await
780    }
781
782    fn poll_apic(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl, scan_irr: bool) {
783        // TODO: If the APIC is offloaded, we need to process the IRRs
784        // from the offloaded page.
785
786        // Clear any pending interrupt.
787        this.runner.vmsa_mut(vtl).v_intr_cntrl_mut().set_irq(false);
788
789        hardware_cvm::apic::poll_apic_core(this, vtl, scan_irr);
790
791        // TODO: handle TMRs.
792        if this.backing.cvm.lapics[vtl].lapic.is_offloaded() {
793            debug_assert!(vtl == GuestVtl::Vtl0);
794
795            let was_halted = matches!(
796                this.backing.cvm.lapics[vtl].activity,
797                MpState::Halted | MpState::Idle
798            );
799
800            let mut offloaded_interrupt = this
801                .runner
802                .secure_avic_page(vtl)
803                .irr
804                .iter()
805                .any(|irr| irr.value != 0);
806            let offload_supported =
807                match this.backing.cvm.lapics[vtl]
808                    .lapic
809                    .push_to_offload(|irr, isr, tmr| {
810                        offloaded_interrupt |= irr.iter().any(|&irr| irr != 0);
811
812                        let (apic_page, proxy_irr_vtl0) =
813                            this.runner.secure_avic_page_proxy_irr_exit_vtl0_mut();
814
815                        for (((((irr, page_irr), isr), page_isr), tmr), proxy_irr_vtl0) in irr
816                            .iter()
817                            .zip(&mut apic_page.irr)
818                            .zip(isr)
819                            .zip(&mut apic_page.isr)
820                            .zip(tmr)
821                            .zip(proxy_irr_vtl0)
822                        {
823                            page_irr.value |= *irr;
824                            page_isr.value |= *isr;
825                            *proxy_irr_vtl0 = *tmr;
826                        }
827                    }) {
828                    Ok(_) => true,
829                    Err(virt_support_apic::OffloadNotSupported) => false,
830                };
831
832            if !offload_supported {
833                tracing::info!(CVM_ALLOWED, "disabling APIC offload due to auto EOI");
834                this.set_apic_offload(vtl, false);
835                hardware_cvm::apic::poll_apic_core(this, vtl, false);
836                return;
837            }
838
839            if was_halted && offloaded_interrupt {
840                this.backing.cvm.lapics[vtl].activity = MpState::Running;
841            }
842        }
843    }
844
845    fn request_extint_readiness(_this: &mut UhProcessor<'_, Self>) {
846        unreachable!("extint managed through software apic")
847    }
848
849    fn request_untrusted_sint_readiness(this: &mut UhProcessor<'_, Self>, sints: u16) {
850        let sints = this.backing.hv_sint_notifications | sints;
851        if this.backing.hv_sint_notifications == sints {
852            return;
853        }
854        let notifications = HvDeliverabilityNotificationsRegister::new().with_sints(sints);
855        tracing::trace!(?notifications, "setting notifications");
856        this.runner
857            .set_vp_register(
858                GuestVtl::Vtl0,
859                HvX64RegisterName::DeliverabilityNotifications,
860                u64::from(notifications).into(),
861            )
862            .expect("requesting deliverability is not a fallable operation");
863
864        this.backing.hv_sint_notifications = sints;
865    }
866
867    fn inspect_extra(this: &mut UhProcessor<'_, Self>, resp: &mut inspect::Response<'_>) {
868        let vtl0_vmsa = this.runner.vmsa(GuestVtl::Vtl0);
869        let vtl1_vmsa = if this.backing.cvm_state().vtl1.is_some() {
870            Some(this.runner.vmsa(GuestVtl::Vtl1))
871        } else {
872            None
873        };
874
875        let add_vmsa_inspect = |req: inspect::Request<'_>, vmsa: VmsaWrapper<'_, &SevVmsa>| {
876            req.respond()
877                .hex("guest_error_code", vmsa.guest_error_code())
878                .hex("exit_info1", vmsa.exit_info1())
879                .hex("exit_info2", vmsa.exit_info2())
880                .hex("v_intr_cntrl", u64::from(vmsa.v_intr_cntrl()));
881        };
882
883        resp.child("vmsa_additional", |req| {
884            req.respond()
885                .child("vtl0", |inner_req| add_vmsa_inspect(inner_req, vtl0_vmsa))
886                .child("vtl1", |inner_req| {
887                    if let Some(vtl1_vmsa) = vtl1_vmsa {
888                        add_vmsa_inspect(inner_req, vtl1_vmsa);
889                    }
890                });
891        });
892    }
893
894    fn hv(&self, vtl: GuestVtl) -> Option<&ProcessorVtlHv> {
895        Some(&self.cvm.hv[vtl])
896    }
897
898    fn hv_mut(&mut self, vtl: GuestVtl) -> Option<&mut ProcessorVtlHv> {
899        Some(&mut self.cvm.hv[vtl])
900    }
901
902    fn handle_vp_start_enable_vtl_wake(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl) {
903        this.hcvm_handle_vp_start_enable_vtl(vtl)
904    }
905
906    fn vtl1_inspectable(this: &UhProcessor<'_, Self>) -> bool {
907        this.hcvm_vtl1_inspectable()
908    }
909
910    fn process_interrupts(
911        this: &mut UhProcessor<'_, Self>,
912        scan_irr: VtlArray<bool, 2>,
913        first_scan_irr: &mut bool,
914        dev: &impl CpuIo,
915    ) -> bool {
916        this.cvm_process_interrupts(scan_irr, first_scan_irr, dev)
917    }
918}
919
920impl UhProcessor<'_, SnpBacked> {
921    fn access_apic_without_offload<R>(
922        &mut self,
923        vtl: GuestVtl,
924        f: impl FnOnce(&mut Self) -> R,
925    ) -> R {
926        let offloaded = self.backing.cvm.lapics[vtl].lapic.is_offloaded();
927        self.set_apic_offload(vtl, false);
928        let r = f(self);
929        self.set_apic_offload(vtl, offloaded);
930        r
931    }
932
933    fn set_apic_offload(&mut self, vtl: GuestVtl, offload: bool) {
934        let offloaded = self.backing.cvm.lapics[vtl].lapic.is_offloaded();
935        if !offload {
936            if offloaded {
937                debug_assert!(vtl == GuestVtl::Vtl0);
938
939                let (irr, isr) = pull_apic_offload(self.runner.secure_avic_page_mut(vtl));
940                self.backing.cvm.lapics[vtl]
941                    .lapic
942                    .disable_offload(&irr, &isr);
943            }
944        } else {
945            debug_assert!(vtl == GuestVtl::Vtl0);
946            if !offloaded {
947                self.backing.cvm.lapics[vtl].lapic.enable_offload();
948            }
949        }
950    }
951}
952
953fn virt_seg_to_snp(val: SegmentRegister) -> SevSelector {
954    SevSelector {
955        selector: val.selector,
956        attrib: (val.attributes & 0xFF) | ((val.attributes >> 4) & 0xF00),
957        limit: val.limit,
958        base: val.base,
959    }
960}
961
962fn virt_table_to_snp(val: TableRegister) -> SevSelector {
963    SevSelector {
964        limit: val.limit as u32,
965        base: val.base,
966        ..FromZeros::new_zeroed()
967    }
968}
969
970fn virt_seg_from_snp(selector: SevSelector) -> SegmentRegister {
971    SegmentRegister {
972        base: selector.base,
973        limit: selector.limit,
974        selector: selector.selector,
975        attributes: (selector.attrib & 0xFF) | ((selector.attrib & 0xF00) << 4),
976    }
977}
978
979fn virt_table_from_snp(selector: SevSelector) -> TableRegister {
980    TableRegister {
981        limit: selector.limit as u16,
982        base: selector.base,
983    }
984}
985
986fn init_vmsa(
987    vmsa: &mut VmsaWrapper<'_, &mut SevVmsa>,
988    vtl: GuestVtl,
989    vtom: Option<u64>,
990    sev_status: SevStatusMsr,
991    vnmi: bool,
992) {
993    // BUGBUG: this isn't fully accurate--the hypervisor can try running
994    // from this at any time, so we need to be careful to set the field
995    // that makes this valid last.
996    vmsa.reset(sev_status.vmsa_reg_prot());
997    vmsa.sev_features_mut()
998        .set_snp_btb_isolation(sev_status.snp_btb_isolation());
999    vmsa.sev_features_mut()
1000        .set_ibpb_on_entry(sev_status.ibpb_on_entry());
1001    vmsa.sev_features_mut()
1002        .set_prevent_host_ibs(sev_status.prevent_host_ibs());
1003    vmsa.sev_features_mut()
1004        .set_vmsa_reg_prot(sev_status.vmsa_reg_prot());
1005    vmsa.sev_features_mut().set_snp(true);
1006    vmsa.sev_features_mut().set_vtom(vtom.is_some());
1007    vmsa.set_virtual_tom(vtom.unwrap_or(0));
1008
1009    // Enable VC reflection to enable the paravisor to handle intercepts using
1010    // trustworthy information.
1011    vmsa.sev_features_mut().set_reflect_vc(true);
1012    vmsa.sev_features_mut().set_debug_swap(true);
1013
1014    // Configure the interrupt injection mode. Secure AVIC and alternate injection
1015    // are mutually exclusive (AMD PPR 15.36.16, 15.36.21).
1016    let use_secure_avic = cfg!(not(feature = "disable_secure_avic"))
1017        && vtl == GuestVtl::Vtl0
1018        && sev_status.secure_avic();
1019
1020    if use_secure_avic {
1021        vmsa.sev_features_mut().set_secure_avic(true);
1022        vmsa.sev_features_mut().set_guest_intercept_control(true);
1023    } else {
1024        vmsa.sev_features_mut().set_alternate_injection(true);
1025    }
1026
1027    vmsa.v_intr_cntrl_mut().set_guest_busy(true);
1028
1029    // Enable virtual NMI delivery if the host CPU supports it (VTL0 only).
1030    if vnmi && vtl == GuestVtl::Vtl0 {
1031        vmsa.v_intr_cntrl_mut().set_nmi_enable(true);
1032    }
1033
1034    // Note: The VMSA pages for VTL0 and VTL1 are converted to a VMSA page
1035    // in the RMP by the kernel, in mshv_configure_vmsa_page. The VTL2 VMSA
1036    // page is converted via SNP_LAUNCH_UPDATE.
1037
1038    let vmpl = match vtl {
1039        GuestVtl::Vtl0 => Vmpl::Vmpl2,
1040        GuestVtl::Vtl1 => Vmpl::Vmpl1,
1041    };
1042    vmsa.set_vmpl(vmpl.into());
1043
1044    // Mark the VMSA with a benign exit code so that any attempt to process intercepts prior
1045    // to VM execution will not result in erroneous intercept delivery.
1046    vmsa.set_guest_error_code(SevExitCode::INTR.0);
1047
1048    // Efer has a value that is different than the architectural default (for SNP, efer
1049    // must always have the SVME bit set).
1050    vmsa.set_efer(x86defs::X64_EFER_SVME);
1051}
1052
1053struct SnpApicClient<'a, T> {
1054    partition: &'a UhPartitionInner,
1055    vmsa: VmsaWrapper<'a, &'a mut SevVmsa>,
1056    avic_page: &'a mut SevAvicPage,
1057    dev: &'a T,
1058    vmtime: &'a VmTimeAccess,
1059    vtl: GuestVtl,
1060}
1061
1062impl<T: CpuIo> ApicClient for SnpApicClient<'_, T> {
1063    fn cr8(&mut self) -> u32 {
1064        self.vmsa.v_intr_cntrl().tpr().into()
1065    }
1066
1067    fn set_cr8(&mut self, value: u32) {
1068        self.vmsa.v_intr_cntrl_mut().set_tpr(value as u8);
1069    }
1070
1071    fn set_apic_base(&mut self, _value: u64) {
1072        // No-op--the APIC base is stored in the APIC itself.
1073    }
1074
1075    fn wake(&mut self, vp_index: VpIndex) {
1076        self.partition.vps[vp_index.index() as usize].wake(self.vtl, WakeReason::INTCON);
1077    }
1078
1079    fn eoi(&mut self, vector: u8) {
1080        debug_assert_eq!(self.vtl, GuestVtl::Vtl0);
1081        self.dev.handle_eoi(vector.into())
1082    }
1083
1084    fn now(&mut self) -> VmTime {
1085        self.vmtime.now()
1086    }
1087
1088    fn pull_offload(&mut self) -> ([u32; 8], [u32; 8]) {
1089        assert_eq!(self.vtl, GuestVtl::Vtl0);
1090        pull_apic_offload(self.avic_page)
1091    }
1092}
1093
1094fn pull_apic_offload(page: &mut SevAvicPage) -> ([u32; 8], [u32; 8]) {
1095    let mut irr = [0; 8];
1096    let mut isr = [0; 8];
1097    for (((irr, page_irr), isr), page_isr) in irr
1098        .iter_mut()
1099        .zip(page.irr.iter_mut())
1100        .zip(isr.iter_mut())
1101        .zip(page.isr.iter_mut())
1102    {
1103        *irr = std::mem::take(&mut page_irr.value);
1104        *isr = std::mem::take(&mut page_isr.value);
1105    }
1106    (irr, isr)
1107}
1108
1109impl UhHypercallHandler<'_, '_, SnpBacked> {
1110    // Trusted hypercalls from the guest.
1111    const TRUSTED_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1112        Self,
1113        [
1114            hv1_hypercall::HvModifySparseGpaPageHostVisibility,
1115            hv1_hypercall::HvQuerySparseGpaPageHostVisibility,
1116            hv1_hypercall::HvX64StartVirtualProcessor,
1117            hv1_hypercall::HvGetVpIndexFromApicId,
1118            hv1_hypercall::HvGetVpRegisters,
1119            hv1_hypercall::HvEnablePartitionVtl,
1120            hv1_hypercall::HvRetargetDeviceInterrupt,
1121            hv1_hypercall::HvPostMessage,
1122            hv1_hypercall::HvSignalEvent,
1123            hv1_hypercall::HvX64EnableVpVtl,
1124            hv1_hypercall::HvExtQueryCapabilities,
1125            hv1_hypercall::HvVtlCall,
1126            hv1_hypercall::HvVtlReturn,
1127            hv1_hypercall::HvFlushVirtualAddressList,
1128            hv1_hypercall::HvFlushVirtualAddressListEx,
1129            hv1_hypercall::HvFlushVirtualAddressSpace,
1130            hv1_hypercall::HvFlushVirtualAddressSpaceEx,
1131            hv1_hypercall::HvSetVpRegisters,
1132            hv1_hypercall::HvModifyVtlProtectionMask,
1133            hv1_hypercall::HvX64TranslateVirtualAddress,
1134            hv1_hypercall::HvSendSyntheticClusterIpi,
1135            hv1_hypercall::HvSendSyntheticClusterIpiEx,
1136            hv1_hypercall::HvInstallIntercept,
1137            hv1_hypercall::HvAssertVirtualInterrupt,
1138        ],
1139    );
1140
1141    // These are untrusted hypercalls from the hypervisor (hopefully originally
1142    // from the guest). Only allow HvPostMessage and HvSignalEvent.
1143    const UNTRUSTED_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
1144        Self,
1145        [hv1_hypercall::HvPostMessage, hv1_hypercall::HvSignalEvent],
1146    );
1147}
1148
1149struct GhcbEnlightenedHypercall<'a, 'b> {
1150    handler: UhHypercallHandler<'a, 'b, SnpBacked>,
1151    control: u64,
1152    output_gpa: u64,
1153    input_gpa: u64,
1154    result: u64,
1155}
1156
1157impl<'a, 'b> hv1_hypercall::AsHandler<UhHypercallHandler<'a, 'b, SnpBacked>>
1158    for &mut GhcbEnlightenedHypercall<'a, 'b>
1159{
1160    fn as_handler(&mut self) -> &mut UhHypercallHandler<'a, 'b, SnpBacked> {
1161        &mut self.handler
1162    }
1163}
1164
1165impl HypercallIo for GhcbEnlightenedHypercall<'_, '_> {
1166    fn advance_ip(&mut self) {
1167        // No-op for GHCB hypercall ABI
1168    }
1169
1170    fn retry(&mut self, control: u64) {
1171        // The GHCB ABI does not support automatically retrying hypercalls by
1172        // updating the control and reissuing the instruction, since doing so
1173        // would require the hypervisor (the normal implementor of the GHCB
1174        // hypercall ABI) to be able to control the instruction pointer.
1175        //
1176        // Instead, explicitly return `HV_STATUS_TIMEOUT` to indicate that the
1177        // guest should retry the hypercall after setting `rep_start` to the
1178        // number of elements processed.
1179        let control = Control::from(control);
1180        self.set_result(
1181            HypercallOutput::from(HvError::Timeout)
1182                .with_elements_processed(control.rep_start())
1183                .into(),
1184        );
1185    }
1186
1187    fn control(&mut self) -> u64 {
1188        self.control
1189    }
1190
1191    fn input_gpa(&mut self) -> u64 {
1192        self.input_gpa
1193    }
1194
1195    fn output_gpa(&mut self) -> u64 {
1196        self.output_gpa
1197    }
1198
1199    fn fast_register_pair_count(&mut self) -> usize {
1200        0
1201    }
1202
1203    fn extended_fast_hypercalls_ok(&mut self) -> bool {
1204        false
1205    }
1206
1207    fn fast_input(&mut self, _buf: &mut [[u64; 2]], _output_register_pairs: usize) -> usize {
1208        unimplemented!("not supported for secure enlightened abi")
1209    }
1210
1211    fn fast_output(&mut self, _starting_pair_index: usize, _buf: &[[u64; 2]]) {
1212        unimplemented!("not supported for secure enlightened abi")
1213    }
1214
1215    fn vtl_input(&mut self) -> u64 {
1216        unimplemented!("not supported for secure enlightened abi")
1217    }
1218
1219    fn set_result(&mut self, n: u64) {
1220        self.result = n;
1221    }
1222
1223    fn fast_regs(&mut self, _starting_pair_index: usize, _buf: &mut [[u64; 2]]) {
1224        unimplemented!("not supported for secure enlightened abi")
1225    }
1226}
1227
1228impl<'b> ApicBacking<'b, SnpBacked> for UhProcessor<'b, SnpBacked> {
1229    fn vp(&mut self) -> &mut UhProcessor<'b, SnpBacked> {
1230        self
1231    }
1232
1233    fn handle_interrupt(&mut self, vtl: GuestVtl, vector: u8) {
1234        let mut vmsa = self.runner.vmsa_mut(vtl);
1235        vmsa.v_intr_cntrl_mut().set_vector(vector);
1236        vmsa.v_intr_cntrl_mut().set_priority((vector >> 4).into());
1237        vmsa.v_intr_cntrl_mut().set_ignore_tpr(false);
1238        vmsa.v_intr_cntrl_mut().set_irq(true);
1239        self.backing.cvm.lapics[vtl].activity = MpState::Running;
1240    }
1241
1242    fn handle_nmi(&mut self, vtl: GuestVtl) {
1243        // Don't forget to update is_interrupt_pending if this code changes.
1244
1245        if self.shared.vnmi && vtl == GuestVtl::Vtl0 {
1246            {
1247                let mut vmsa = self.runner.vmsa_mut(vtl);
1248                vmsa.v_intr_cntrl_mut().set_nmi_enable(true);
1249                vmsa.v_intr_cntrl_mut().set_nmi(true);
1250            }
1251        } else {
1252            let mut vmsa = self.runner.vmsa_mut(vtl);
1253            // TODO GUEST VSM: Don't inject the NMI if there's already an event
1254            // pending.
1255            vmsa.set_event_inject(
1256                SevEventInjectInfo::new()
1257                    .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_NMI)
1258                    .with_vector(2)
1259                    .with_valid(true),
1260            );
1261        }
1262        self.backing.cvm.lapics[vtl].nmi_pending = false;
1263        self.backing.cvm.lapics[vtl].activity = MpState::Running;
1264    }
1265
1266    fn handle_sipi(&mut self, vtl: GuestVtl, cs: SegmentRegister) {
1267        let mut vmsa = self.runner.vmsa_mut(vtl);
1268        vmsa.set_cs(virt_seg_to_snp(cs));
1269        vmsa.set_rip(0);
1270        self.backing.cvm.lapics[vtl].activity = MpState::Running;
1271    }
1272}
1273
1274impl UhProcessor<'_, SnpBacked> {
1275    fn handle_synic_deliverable_exit(&mut self) {
1276        let message = self
1277            .runner
1278            .exit_message()
1279            .as_message::<hvdef::HvX64SynicSintDeliverableMessage>();
1280
1281        tracing::trace!(
1282            deliverable_sints = message.deliverable_sints,
1283            "sint deliverable"
1284        );
1285
1286        self.backing.hv_sint_notifications &= !message.deliverable_sints;
1287
1288        // These messages are always VTL0, as VTL1 does not own any VMBUS channels.
1289        self.deliver_synic_messages(GuestVtl::Vtl0, message.deliverable_sints);
1290    }
1291
1292    fn handle_vmgexit(
1293        &mut self,
1294        _dev: &impl CpuIo,
1295        intercepted_vtl: GuestVtl,
1296    ) -> Result<(), SnpGhcbError> {
1297        let message = self
1298            .runner
1299            .exit_message()
1300            .as_message::<hvdef::HvX64VmgexitInterceptMessage>();
1301
1302        let ghcb_msr = x86defs::snp::GhcbMsr::from(message.ghcb_msr);
1303        let flags = message.flags;
1304        let sw_exit_code = message.ghcb_page.standard.sw_exit_code;
1305        let sw_exit_info1 = message.ghcb_page.standard.sw_exit_info1;
1306        let sw_exit_info2 = message.ghcb_page.standard.sw_exit_info2;
1307        tracing::trace!(?ghcb_msr, "vmgexit intercept");
1308
1309        match x86defs::snp::GhcbInfo(ghcb_msr.info()) {
1310            x86defs::snp::GhcbInfo::NORMAL => {
1311                assert!(message.flags.ghcb_page_valid());
1312                let ghcb_pfn = ghcb_msr.pfn();
1313
1314                let ghcb_overlay =
1315                    self.backing.cvm.direct_overlay_handle.pfns()[UhDirectOverlay::Ghcb as usize];
1316
1317                // TODO SNP: Should allow arbitrary page to be used for GHCB
1318                if ghcb_pfn != ghcb_overlay {
1319                    tracelimit::warn_ratelimited!(
1320                        CVM_ALLOWED,
1321                        vmgexit_pfn = ghcb_pfn,
1322                        overlay_pfn = ghcb_overlay,
1323                        "ghcb page used for vmgexit does not match overlay page"
1324                    );
1325
1326                    return Err(SnpGhcbError::GhcbMisconfiguration);
1327                }
1328
1329                match x86defs::snp::GhcbUsage(message.ghcb_page.ghcb_usage) {
1330                    x86defs::snp::GhcbUsage::HYPERCALL => {
1331                        let guest_memory = &self.shared.cvm.shared_memory;
1332                        // Read GHCB parameters from guest memory before
1333                        // dispatching.
1334                        let overlay_base = ghcb_overlay * HV_PAGE_SIZE;
1335                        let x86defs::snp::GhcbHypercallParameters {
1336                            output_gpa,
1337                            input_control,
1338                        } = guest_memory
1339                            .read_plain(
1340                                overlay_base
1341                                    + x86defs::snp::GHCB_PAGE_HYPERCALL_PARAMETERS_OFFSET as u64,
1342                            )
1343                            .map_err(SnpGhcbError::GhcbPageAccess)?;
1344
1345                        let mut handler = GhcbEnlightenedHypercall {
1346                            handler: UhHypercallHandler {
1347                                vp: self,
1348                                trusted: false,
1349                                intercepted_vtl,
1350                            },
1351                            control: input_control,
1352                            output_gpa,
1353                            input_gpa: overlay_base,
1354                            result: 0,
1355                        };
1356
1357                        UhHypercallHandler::UNTRUSTED_DISPATCHER
1358                            .dispatch(guest_memory, &mut handler);
1359
1360                        // Commit the hypercall result outside the dispatcher
1361                        // incase memory access fails so we can return an
1362                        // appropriate error.
1363                        //
1364                        // Note that we should only be returning this error if
1365                        // something is catastrophically wrong, as we already
1366                        // accessed this page earlier to read input parameters.
1367                        guest_memory
1368                            .write_at(
1369                                overlay_base
1370                                    + x86defs::snp::GHCB_PAGE_HYPERCALL_OUTPUT_OFFSET as u64,
1371                                handler.result.as_bytes(),
1372                            )
1373                            .map_err(SnpGhcbError::GhcbPageAccess)?;
1374                    }
1375                    x86defs::snp::GhcbUsage::BASE => {
1376                        match SevExitCode(sw_exit_code) {
1377                            // The hypervisor could not handle VMMCALL and forwarded the GHCB message
1378                            SevExitCode::VMMCALL => {
1379                                let shared_memory = &self.shared.cvm.shared_memory;
1380                                let overlay_base = ghcb_overlay * HV_PAGE_SIZE;
1381
1382                                let input_control: u64 = shared_memory
1383                                    .read_plain(
1384                                        overlay_base + std::mem::offset_of!(SevVmsa, rcx) as u64,
1385                                    )
1386                                    .map_err(SnpGhcbError::GhcbPageAccess)?;
1387                                let input_gpa: u64 = shared_memory
1388                                    .read_plain(
1389                                        overlay_base + std::mem::offset_of!(SevVmsa, rdx) as u64,
1390                                    )
1391                                    .map_err(SnpGhcbError::GhcbPageAccess)?;
1392                                let output_gpa: u64 = shared_memory
1393                                    .read_plain(
1394                                        overlay_base + std::mem::offset_of!(SevVmsa, r8) as u64,
1395                                    )
1396                                    .map_err(SnpGhcbError::GhcbPageAccess)?;
1397
1398                                let guest_memory = &self.shared.cvm.shared_memory;
1399                                let mut handler = GhcbEnlightenedHypercall {
1400                                    handler: UhHypercallHandler {
1401                                        vp: self,
1402                                        trusted: false,
1403                                        intercepted_vtl,
1404                                    },
1405                                    control: input_control,
1406                                    output_gpa,
1407                                    input_gpa,
1408                                    result: 0,
1409                                };
1410
1411                                UhHypercallHandler::UNTRUSTED_DISPATCHER
1412                                    .dispatch(guest_memory, &mut handler);
1413
1414                                shared_memory
1415                                    .write_at(
1416                                        overlay_base + std::mem::offset_of!(SevVmsa, rax) as u64,
1417                                        handler.result.as_bytes(),
1418                                    )
1419                                    .map_err(SnpGhcbError::GhcbPageAccess)?;
1420                            }
1421                            _ => {
1422                                let exit_code = SevExitCode(sw_exit_code);
1423                                unimplemented!("unhandled GHCB BASE sw_exit_code {exit_code:?}");
1424                            }
1425                        }
1426                    }
1427                    usage => unimplemented!(
1428                        "Invalid ghcb message.\n\
1429                         usage {usage:?}\n\
1430                         flags {flags:?}\n\
1431                         ghcb_msr {ghcb_msr:?}\n\
1432                         sw_exit_code {sw_exit_code:?}\n\
1433                         sw_exit_info1 {sw_exit_info1:?}\n\
1434                         sw_exit_info2 {sw_exit_info2:?}"
1435                    ),
1436                }
1437            }
1438            info => unimplemented!("ghcb info {info:?}"),
1439        }
1440
1441        Ok(())
1442    }
1443
1444    fn handle_msr_access(
1445        &mut self,
1446        dev: &impl CpuIo,
1447        entered_from_vtl: GuestVtl,
1448        msr: u32,
1449        is_write: bool,
1450        is_fault: bool,
1451    ) {
1452        if is_write && self.cvm_try_protect_msr_write(entered_from_vtl, msr) {
1453            return;
1454        }
1455
1456        let (avic_page, vmsa) = self.runner.secure_avic_page_vmsa_mut(entered_from_vtl);
1457        let gp = if is_write {
1458            let value = (vmsa.rax() as u32 as u64) | ((vmsa.rdx() as u32 as u64) << 32);
1459
1460            let r = self.backing.cvm.lapics[entered_from_vtl]
1461                .lapic
1462                .access(&mut SnpApicClient {
1463                    partition: self.partition,
1464                    vmsa,
1465                    avic_page,
1466                    dev,
1467                    vmtime: &self.vmtime,
1468                    vtl: entered_from_vtl,
1469                })
1470                .msr_write(msr, value)
1471                .or_else_if_unknown(|| self.write_msr_cvm(msr, value, entered_from_vtl))
1472                .or_else_if_unknown(|| self.write_msr_snp(dev, msr, value, entered_from_vtl));
1473
1474            match r {
1475                Ok(()) => false,
1476                Err(MsrError::Unknown) => {
1477                    tracing::debug!(msr, value, "unknown cvm msr write");
1478                    false
1479                }
1480                Err(MsrError::InvalidAccess) => true,
1481            }
1482        } else {
1483            let r = self.backing.cvm.lapics[entered_from_vtl]
1484                .lapic
1485                .access(&mut SnpApicClient {
1486                    partition: self.partition,
1487                    vmsa,
1488                    avic_page,
1489                    dev,
1490                    vmtime: &self.vmtime,
1491                    vtl: entered_from_vtl,
1492                })
1493                .msr_read(msr)
1494                .or_else_if_unknown(|| self.read_msr_cvm(msr, entered_from_vtl))
1495                .or_else_if_unknown(|| self.read_msr_snp(dev, msr, entered_from_vtl));
1496
1497            let value = match r {
1498                Ok(v) => Some(v),
1499                Err(MsrError::Unknown) => {
1500                    tracing::debug!(msr, "unknown cvm msr read");
1501                    Some(0)
1502                }
1503                Err(MsrError::InvalidAccess) => None,
1504            };
1505
1506            if let Some(value) = value {
1507                let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1508                vmsa.set_rax((value as u32).into());
1509                vmsa.set_rdx(((value >> 32) as u32).into());
1510                false
1511            } else {
1512                true
1513            }
1514        };
1515
1516        let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1517        if gp {
1518            vmsa.set_event_inject(
1519                SevEventInjectInfo::new()
1520                    .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT)
1521                    .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0)
1522                    .with_deliver_error_code(true)
1523                    .with_valid(true),
1524            );
1525        } else {
1526            if is_fault {
1527                advance_to_next_instruction(&mut vmsa);
1528            }
1529        }
1530    }
1531
1532    fn handle_xsetbv(&mut self, entered_from_vtl: GuestVtl) {
1533        let vmsa = self.runner.vmsa(entered_from_vtl);
1534        if let Some(value) = hardware_cvm::validate_xsetbv_exit(hardware_cvm::XsetbvExitInput {
1535            rax: vmsa.rax(),
1536            rcx: vmsa.rcx(),
1537            rdx: vmsa.rdx(),
1538            cr4: vmsa.cr4(),
1539            cpl: vmsa.cpl(),
1540        }) {
1541            if !self.cvm_try_protect_secure_register_write(
1542                entered_from_vtl,
1543                HvX64RegisterName::Xfem,
1544                value,
1545            ) {
1546                let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1547                vmsa.set_xcr0(value);
1548                advance_to_next_instruction(&mut vmsa);
1549            }
1550        } else {
1551            let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1552            vmsa.set_event_inject(
1553                SevEventInjectInfo::new()
1554                    .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT)
1555                    .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0)
1556                    .with_deliver_error_code(true)
1557                    .with_valid(true),
1558            );
1559        }
1560    }
1561
1562    fn handle_crx_intercept(&mut self, entered_from_vtl: GuestVtl, reg: HvX64RegisterName) {
1563        let vmsa = self.runner.vmsa(entered_from_vtl);
1564        let mov_crx_drx = x86defs::snp::MovCrxDrxInfo::from(vmsa.exit_info1());
1565        let reg_value = {
1566            let gpr_name =
1567                HvX64RegisterName(HvX64RegisterName::Rax.0 + mov_crx_drx.gpr_number() as u32);
1568
1569            match gpr_name {
1570                HvX64RegisterName::Rax => vmsa.rax(),
1571                HvX64RegisterName::Rbx => vmsa.rbx(),
1572                HvX64RegisterName::Rcx => vmsa.rcx(),
1573                HvX64RegisterName::Rdx => vmsa.rdx(),
1574                HvX64RegisterName::Rsp => vmsa.rsp(),
1575                HvX64RegisterName::Rbp => vmsa.rbp(),
1576                HvX64RegisterName::Rsi => vmsa.rsi(),
1577                HvX64RegisterName::Rdi => vmsa.rdi(),
1578                HvX64RegisterName::R8 => vmsa.r8(),
1579                HvX64RegisterName::R9 => vmsa.r9(),
1580                HvX64RegisterName::R10 => vmsa.r10(),
1581                HvX64RegisterName::R11 => vmsa.r11(),
1582                HvX64RegisterName::R12 => vmsa.r12(),
1583                HvX64RegisterName::R13 => vmsa.r13(),
1584                HvX64RegisterName::R14 => vmsa.r14(),
1585                HvX64RegisterName::R15 => vmsa.r15(),
1586                _ => unreachable!("unexpected register"),
1587            }
1588        };
1589
1590        // Special case: LMSW/CLTS/SMSW intercepts do not provide decode assist
1591        // information. No support to emulate these instructions yet, but the
1592        // access by the guest might be allowed by the higher VTL and therefore
1593        // crashing is not necessarily the correct behavior.
1594        //
1595        // TODO SNP: consider emulating the instruction.
1596        if !mov_crx_drx.mov_crx() {
1597            tracelimit::warn_ratelimited!(
1598                CVM_ALLOWED,
1599                "Intercepted crx access, instruction is not mov crx"
1600            );
1601            return;
1602        }
1603
1604        if !self.cvm_try_protect_secure_register_write(entered_from_vtl, reg, reg_value) {
1605            let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1606            match reg {
1607                HvX64RegisterName::Cr0 => vmsa.set_cr0(reg_value),
1608                HvX64RegisterName::Cr4 => vmsa.set_cr4(reg_value),
1609                _ => unreachable!(),
1610            }
1611            advance_to_next_instruction(&mut vmsa);
1612        }
1613    }
1614
1615    #[must_use]
1616    fn sync_lazy_eoi(&mut self, vtl: GuestVtl) -> bool {
1617        if self.backing.cvm.lapics[vtl].lapic.is_lazy_eoi_pending() {
1618            return self.backing.cvm.hv[vtl].set_lazy_eoi();
1619        }
1620
1621        false
1622    }
1623
1624    async fn run_vp_snp(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason> {
1625        let next_vtl = self.backing.cvm.exit_vtl;
1626
1627        let mut vmsa = self.runner.vmsa_mut(next_vtl);
1628        let last_interrupt_ctrl = vmsa.v_intr_cntrl();
1629
1630        // OpenHCL runs with:
1631        // * the alternate interrupt injection, and the busy bit is used by the software to
1632        //   disallow running the VMSA, OR
1633        // * the secure AVIC, where the hardware might set the busy bit on exits
1634        //   ("15.36.16 Interrupt Injection Restrictions", "15.36.21.5 Guest APIC Accesses")
1635        // Clear the guest busy bit unconditionally as the prerequisites are met in either case.
1636        vmsa.v_intr_cntrl_mut().set_guest_busy(false);
1637
1638        self.unlock_tlb_lock(Vtl::Vtl2);
1639        let tlb_halt = self.should_halt_for_tlb_unlock(next_vtl);
1640        let halt = self.backing.cvm.lapics[next_vtl].activity != MpState::Running || tlb_halt;
1641
1642        // If we are halted in the kernel due to hlt or idle, and we receive an interrupt
1643        // we'd like to unhalt, inject the interrupt, and resume vtl0 without returning to
1644        // user-mode.
1645        let activity = self.backing.cvm.lapics[next_vtl].activity;
1646        let kernel_known_state =
1647            matches!(activity, MpState::Running | MpState::Halted | MpState::Idle);
1648        let halted_other = tlb_halt || !kernel_known_state;
1649
1650        self.runner.set_halted(halt);
1651        self.runner.set_exit_vtl(next_vtl);
1652
1653        if halt && next_vtl == GuestVtl::Vtl1 && !tlb_halt {
1654            tracelimit::warn_ratelimited!(CVM_ALLOWED, "halting VTL 1, which might halt the guest");
1655        }
1656
1657        let x2apic_enabled = self.backing.cvm.lapics[next_vtl].lapic.x2apic_enabled();
1658        let offload_enabled = self.backing.cvm.lapics[next_vtl].lapic.can_offload_irr();
1659        let offload_flags = hcl_intr_offload_flags::new()
1660            .with_offload_intr_inject(offload_enabled)
1661            .with_offload_x2apic(offload_enabled && x2apic_enabled)
1662            .with_halted_other(halted_other)
1663            .with_halted_hlt(activity == MpState::Halted)
1664            .with_halted_idle(activity == MpState::Idle);
1665        *self.runner.offload_flags_mut() = offload_flags;
1666
1667        // Set the lazy EOI bit just before running.
1668        let lazy_eoi = self.sync_lazy_eoi(next_vtl);
1669
1670        self.shared.guest_timer.begin_vtl_transition(self, next_vtl);
1671
1672        let mut has_intercept = self
1673            .runner
1674            .run()
1675            .map_err(|e| dev.fatal_error(SnpRunVpError::RunVpError(e).into()))?;
1676
1677        let entered_from_vtl = next_vtl;
1678
1679        self.shared
1680            .guest_timer
1681            .end_vtl_transition(self, entered_from_vtl);
1682
1683        // Kernel offload may have set or cleared the halt/idle states while
1684        // handling VTL0 exits internally. Keep the userspace activity state in
1685        // sync before processing the exit that finally returned to userspace.
1686        if offload_enabled && kernel_known_state {
1687            let offload_flags = self.runner.offload_flags_mut();
1688
1689            self.backing.cvm.lapics[entered_from_vtl].activity =
1690                match (offload_flags.halted_hlt(), offload_flags.halted_idle()) {
1691                    (false, false) => MpState::Running,
1692                    (true, false) => MpState::Halted,
1693                    (false, true) => MpState::Idle,
1694                    (true, true) => {
1695                        tracelimit::warn_ratelimited!(
1696                            CVM_ALLOWED,
1697                            "Kernel indicates VP is both halted and idle!"
1698                        );
1699                        activity
1700                    }
1701                };
1702        }
1703
1704        let (avic_page, mut vmsa) = self.runner.secure_avic_page_vmsa_mut(entered_from_vtl);
1705
1706        // Atomically test and set the guest busy bit. This prevents the untrusted
1707        // hypervisor from re-entering the VMSA on another physical CPU while VTL2
1708        // is processing the exit.
1709        let was_busy = vmsa.guest_busy_bit_test_and_set();
1710        let exit_int_info_trace = SevEventInjectInfo::from(vmsa.exit_int_info());
1711
1712        if was_busy {
1713            self.backing.general_stats[entered_from_vtl]
1714                .guest_busy
1715                .increment();
1716
1717            let sev_error_code = SevExitCode(vmsa.guest_error_code());
1718            match sev_error_code {
1719                SevExitCode::NOT_RESTARTABLE => {
1720                    // The guest AVIC backing page is not validated in the RMP.
1721                    return Err(dev.fatal_error(SnpRunVpError::VpNotRestartableError.into()));
1722                }
1723                SevExitCode::NPF => {
1724                    let exit_info = SevNpfInfo::from(vmsa.exit_info1());
1725                    if exit_info.not_restartable() {
1726                        // An access to the guest AVIC backing page by hardware resulted
1727                        // in a nested page fault.
1728                        return Err(dev.fatal_error(SnpRunVpError::VpNotRestartableError.into()));
1729                    }
1730                }
1731                _ => {}
1732            }
1733        }
1734
1735        if vmsa.sev_features().alternate_injection() {
1736            // Software interrupts/exceptions cannot be automatically re-injected, but RIP still
1737            // points to the instruction and the event should be re-generated when the
1738            // instruction is re-executed. Note that hardware does not provide instruction
1739            // length in this case so it's impossible to directly re-inject a software event if
1740            // delivery generates an intercept.
1741            //
1742            // TODO SNP: Handle ICEBP.
1743            let exit_int_info = SevEventInjectInfo::from(vmsa.exit_int_info());
1744
1745            if exit_int_info.valid() {
1746                let inject = match exit_int_info.interruption_type() {
1747                    x86defs::snp::SEV_INTR_TYPE_EXCEPT => {
1748                        if exit_int_info.vector() != 3 && exit_int_info.vector() != 4 {
1749                            // If the event is an exception, we can inject it.
1750                            Some(exit_int_info)
1751                        } else {
1752                            None
1753                        }
1754                    }
1755                    x86defs::snp::SEV_INTR_TYPE_SW => None,
1756                    _ => Some(exit_int_info),
1757                };
1758
1759                if let Some(inject) = inject {
1760                    vmsa.set_event_inject(inject);
1761                }
1762
1763                // Since the exit interrupt information was processed, it must be
1764                // cleared so that it is not examined again on a subsequent reentry to
1765                // the HCL.
1766                vmsa.set_exit_int_info(0);
1767            } else {
1768                // Any previously injected event has been consumed.
1769            }
1770        } else {
1771            assert!(
1772                cfg!(feature = "disable_secure_avic") || vmsa.sev_features().secure_avic(),
1773                "secure AVIC must be enabled"
1774            );
1775        }
1776
1777        if last_interrupt_ctrl.irq() && !vmsa.v_intr_cntrl().irq() {
1778            self.backing.general_stats[entered_from_vtl]
1779                .int_ack
1780                .increment();
1781            // TODO: Account for the offloaded state.
1782
1783            // The guest has acknowledged the interrupt.
1784            self.backing.cvm.lapics[entered_from_vtl]
1785                .lapic
1786                .acknowledge_interrupt(last_interrupt_ctrl.vector());
1787        }
1788
1789        vmsa.v_intr_cntrl_mut().set_irq(false);
1790
1791        // Clear lazy EOI before processing the exit.
1792        if lazy_eoi && self.backing.cvm.hv[entered_from_vtl].clear_lazy_eoi() {
1793            self.backing.cvm.lapics[entered_from_vtl]
1794                .lapic
1795                .access(&mut SnpApicClient {
1796                    partition: self.partition,
1797                    vmsa,
1798                    avic_page,
1799                    dev,
1800                    vmtime: &self.vmtime,
1801                    vtl: entered_from_vtl,
1802                })
1803                .lazy_eoi();
1804        }
1805
1806        let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1807        let sev_error_code = SevExitCode(vmsa.guest_error_code());
1808
1809        let stat = match sev_error_code {
1810            SevExitCode::CPUID => {
1811                self.handle_cpuid(entered_from_vtl);
1812                &mut self.backing.exit_stats[entered_from_vtl].cpuid
1813            }
1814
1815            SevExitCode::MSR => {
1816                let is_write = vmsa.exit_info1() & 1 != 0;
1817                let msr = vmsa.rcx() as u32;
1818                let is_fault = true;
1819                self.handle_msr_access(dev, entered_from_vtl, msr, is_write, is_fault);
1820
1821                if is_write {
1822                    &mut self.backing.exit_stats[entered_from_vtl].msr_write
1823                } else {
1824                    &mut self.backing.exit_stats[entered_from_vtl].msr_read
1825                }
1826            }
1827
1828            SevExitCode::IOIO => {
1829                let io_info =
1830                    SevIoAccessInfo::from(self.runner.vmsa(entered_from_vtl).exit_info1() as u32);
1831
1832                let access_size = if io_info.access_size32() {
1833                    4
1834                } else if io_info.access_size16() {
1835                    2
1836                } else {
1837                    1
1838                };
1839
1840                let port_access_protected = self.cvm_try_protect_io_port_access(
1841                    entered_from_vtl,
1842                    io_info.port(),
1843                    io_info.read_access(),
1844                    access_size,
1845                    io_info.string_access(),
1846                    io_info.rep_access(),
1847                );
1848
1849                let vmsa = self.runner.vmsa(entered_from_vtl);
1850                if !port_access_protected {
1851                    if io_info.string_access() || io_info.rep_access() {
1852                        let interruption_pending = vmsa.event_inject().valid()
1853                            || SevEventInjectInfo::from(vmsa.exit_int_info()).valid();
1854
1855                        // TODO GUEST VSM: consider changing the emulation path
1856                        // to also check for io port installation, mainly for
1857                        // handling rep instructions.
1858
1859                        self.emulate(dev, interruption_pending, entered_from_vtl, ())
1860                            .await?;
1861                    } else {
1862                        let mut rax = vmsa.rax();
1863                        emulate_io(
1864                            self.inner.vp_info.base.vp_index,
1865                            !io_info.read_access(),
1866                            io_info.port(),
1867                            &mut rax,
1868                            access_size,
1869                            dev,
1870                        )
1871                        .await;
1872
1873                        let mut vmsa = self.runner.vmsa_mut(entered_from_vtl);
1874                        vmsa.set_rax(rax);
1875                        advance_to_next_instruction(&mut vmsa);
1876                    }
1877                }
1878                &mut self.backing.exit_stats[entered_from_vtl].ioio
1879            }
1880
1881            SevExitCode::VMMCALL => {
1882                let is_64bit = self.long_mode(entered_from_vtl);
1883                let guest_memory = &self.partition.gm[entered_from_vtl];
1884                let handler = UhHypercallHandler {
1885                    trusted: !self.cvm_partition().hide_isolation,
1886                    vp: &mut *self,
1887                    intercepted_vtl: entered_from_vtl,
1888                };
1889
1890                // Note: Successful VtlCall/Return handling will change the
1891                // current/last vtl
1892                UhHypercallHandler::TRUSTED_DISPATCHER.dispatch(
1893                    guest_memory,
1894                    hv1_hypercall::X64RegisterIo::new(handler, is_64bit, true),
1895                );
1896                &mut self.backing.exit_stats[entered_from_vtl].vmmcall
1897            }
1898
1899            SevExitCode::SHUTDOWN => {
1900                return Err(VpHaltReason::TripleFault {
1901                    vtl: entered_from_vtl.into(),
1902                });
1903            }
1904
1905            SevExitCode::WBINVD | SevExitCode::INVD => {
1906                // TODO SNP: reissue these locally to forward them to the
1907                // hypervisor. This isn't pressing because the hypervisor
1908                // currently doesn't do anything with these for guest VMs.
1909                advance_to_next_instruction(&mut vmsa);
1910                &mut self.backing.exit_stats[entered_from_vtl].invd
1911            }
1912
1913            SevExitCode::NPF if has_intercept => {
1914                // TODO SNP: This code needs to be fixed to not rely on the
1915                // hypervisor message to check the validity of the NPF, rather
1916                // we should look at the SNP hardware exit info only like we do
1917                // with TDX.
1918                //
1919                // TODO SNP: This code should be fixed so we do not attempt to
1920                // emulate a NPF with an address that has the wrong shared bit,
1921                // as this will cause the emulator to raise an internal error,
1922                // and instead inject a machine check like TDX.
1923                //
1924                // Determine whether an NPF needs to be handled. If not, assume
1925                // this fault is spurious and that the instruction can be
1926                // retried. The intercept itself may be presented by the
1927                // hypervisor as either a GPA intercept or an exception
1928                // intercept. The hypervisor configures the NPT to generate a
1929                // #VC inside the guest for accesses to unmapped memory. This
1930                // means that accesses to unmapped memory for lower VTLs will be
1931                // forwarded to underhill as a #VC exception.
1932                let gpa = vmsa.exit_info2();
1933                let interruption_pending = vmsa.event_inject().valid()
1934                    || SevEventInjectInfo::from(vmsa.exit_int_info()).valid();
1935                let exit_info = SevNpfInfo::from(vmsa.exit_info1());
1936                let exit_message = self.runner.exit_message();
1937                let real = match exit_message.header.typ {
1938                    HvMessageType::HvMessageTypeExceptionIntercept => {
1939                        let exception_message =
1940                            exit_message.as_message::<hvdef::HvX64ExceptionInterceptMessage>();
1941
1942                        exception_message.vector
1943                            == x86defs::Exception::SEV_VMM_COMMUNICATION.0 as u16
1944                    }
1945                    HvMessageType::HvMessageTypeUnmappedGpa
1946                    | HvMessageType::HvMessageTypeGpaIntercept
1947                    | HvMessageType::HvMessageTypeUnacceptedGpa => {
1948                        let gpa_message =
1949                            exit_message.as_message::<hvdef::HvX64MemoryInterceptMessage>();
1950
1951                        // Only the page numbers need to match.
1952                        (gpa_message.guest_physical_address >> hvdef::HV_PAGE_SHIFT)
1953                            == (gpa >> hvdef::HV_PAGE_SHIFT)
1954                    }
1955                    _ => false,
1956                };
1957
1958                if real {
1959                    has_intercept = false;
1960                    if self.check_mem_fault(entered_from_vtl, gpa, exit_info.is_write(), exit_info)
1961                    {
1962                        self.emulate(dev, interruption_pending, entered_from_vtl, ())
1963                            .await?;
1964                    }
1965                    &mut self.backing.exit_stats[entered_from_vtl].npf
1966                } else {
1967                    &mut self.backing.exit_stats[entered_from_vtl].npf_spurious
1968                }
1969            }
1970
1971            SevExitCode::NPF => &mut self.backing.exit_stats[entered_from_vtl].npf_no_intercept,
1972
1973            SevExitCode::HLT | SevExitCode::IDLE_HLT => {
1974                self.backing.cvm.lapics[entered_from_vtl].activity = MpState::Halted;
1975                // RIP has already advanced. Clear interrupt shadow.
1976                vmsa.v_intr_cntrl_mut().set_intr_shadow(false);
1977                &mut self.backing.exit_stats[entered_from_vtl].hlt
1978            }
1979
1980            SevExitCode::INVALID_VMCB => {
1981                return Err(dev.fatal_error(InvalidVmcb.into()));
1982            }
1983
1984            SevExitCode::INVLPGB | SevExitCode::ILLEGAL_INVLPGB => {
1985                vmsa.set_event_inject(
1986                    SevEventInjectInfo::new()
1987                        .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT)
1988                        .with_vector(x86defs::Exception::INVALID_OPCODE.0)
1989                        .with_valid(true),
1990                );
1991                &mut self.backing.exit_stats[entered_from_vtl].invlpgb
1992            }
1993
1994            SevExitCode::RDPMC => {
1995                // AMD64 always supports at least 4 core performance counters (PerfCtr0-3). Return 0
1996                // when the guest reads one of the core perf counters, otherwise inject an exception.
1997                let cr4 = vmsa.cr4();
1998                if ((vmsa.cpl() > 0) && (cr4 & x86defs::X64_CR4_PCE == 0))
1999                    || (vmsa.rcx() as u32 >= 4)
2000                {
2001                    vmsa.set_event_inject(
2002                        SevEventInjectInfo::new()
2003                            .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT)
2004                            .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0)
2005                            .with_deliver_error_code(true)
2006                            .with_valid(true),
2007                    );
2008                } else {
2009                    vmsa.set_rax(0);
2010                    vmsa.set_rdx(0);
2011                    advance_to_next_instruction(&mut vmsa);
2012                }
2013                &mut self.backing.exit_stats[entered_from_vtl].rdpmc
2014            }
2015
2016            SevExitCode::VMGEXIT if has_intercept => {
2017                has_intercept = false;
2018                match self.runner.exit_message().header.typ {
2019                    HvMessageType::HvMessageTypeX64SevVmgexitIntercept => {
2020                        self.handle_vmgexit(dev, entered_from_vtl)
2021                            .map_err(|e| dev.fatal_error(e.into()))?;
2022                    }
2023                    _ => has_intercept = true,
2024                }
2025                &mut self.backing.exit_stats[entered_from_vtl].vmgexit
2026            }
2027
2028            SevExitCode::NMI | SevExitCode::PAUSE | SevExitCode::SMI | SevExitCode::VMGEXIT => {
2029                // Ignore intercept processing if the guest exited due to an automatic exit.
2030                &mut self.backing.exit_stats[entered_from_vtl].automatic_exit
2031            }
2032
2033            SevExitCode::BUSLOCK => {
2034                // The guest performs a misaligned atomic operation,
2035                // or updating A/D bits in the PTEs. Might help in investigating
2036                // performance issues.
2037                &mut self.backing.exit_stats[entered_from_vtl].bus_lock
2038            }
2039
2040            SevExitCode::VINTR => {
2041                // Receipt of a virtual interrupt intercept indicates that a virtual interrupt is ready
2042                // for injection but injection cannot complete due to the intercept. Rewind the pending
2043                // virtual interrupt so it is reinjected as a fixed interrupt.
2044
2045                // TODO SNP ALTERNATE INJECTION: Rewind the interrupt.
2046                unimplemented!("SevExitCode::VINTR");
2047            }
2048
2049            SevExitCode::INTR => {
2050                // No action is necessary after a physical interrupt intercept. A physical interrupt
2051                // code is also used as a sentinel value to overwrite the previous error code.
2052                &mut self.backing.exit_stats[entered_from_vtl].intr
2053            }
2054
2055            SevExitCode::XSETBV => {
2056                self.handle_xsetbv(entered_from_vtl);
2057                &mut self.backing.exit_stats[entered_from_vtl].xsetbv
2058            }
2059
2060            SevExitCode::EXCP_DB => &mut self.backing.exit_stats[entered_from_vtl].excp_db,
2061
2062            SevExitCode::CR0_WRITE => {
2063                self.handle_crx_intercept(entered_from_vtl, HvX64RegisterName::Cr0);
2064                &mut self.backing.exit_stats[entered_from_vtl].secure_reg_write
2065            }
2066            SevExitCode::CR4_WRITE => {
2067                self.handle_crx_intercept(entered_from_vtl, HvX64RegisterName::Cr4);
2068                &mut self.backing.exit_stats[entered_from_vtl].secure_reg_write
2069            }
2070
2071            tr_exit_code @ (SevExitCode::GDTR_WRITE
2072            | SevExitCode::IDTR_WRITE
2073            | SevExitCode::LDTR_WRITE
2074            | SevExitCode::TR_WRITE) => {
2075                let reg = match tr_exit_code {
2076                    SevExitCode::GDTR_WRITE => HvX64RegisterName::Gdtr,
2077                    SevExitCode::IDTR_WRITE => HvX64RegisterName::Idtr,
2078                    SevExitCode::LDTR_WRITE => HvX64RegisterName::Ldtr,
2079                    SevExitCode::TR_WRITE => HvX64RegisterName::Tr,
2080                    _ => unreachable!(),
2081                };
2082
2083                if !self.cvm_try_protect_secure_register_write(entered_from_vtl, reg, 0) {
2084                    // This is an unexpected intercept: should only have received an
2085                    // intercept for these registers if a VTL (i.e. VTL 1) requested
2086                    // it. If an unexpected intercept has been received, then the
2087                    // host must have enabled an intercept that was not desired.
2088                    // Since the intercept cannot correctly be emulated, this must
2089                    // be treated as a fatal error.
2090                    panic!("unexpected secure register");
2091                }
2092
2093                &mut self.backing.exit_stats[entered_from_vtl].secure_reg_write
2094            }
2095
2096            SevExitCode::AVIC_NOACCEL => {
2097                let no_accel_info = SevAvicNoAccelInfo::from(vmsa.exit_info1());
2098                tracing::debug!("AVIC no acceleration SEV exit: {no_accel_info:x?}");
2099
2100                if !matches!(
2101                    no_accel_info.apic_register_number(),
2102                    SevAvicRegisterNumber::APIC_ID
2103                        | SevAvicRegisterNumber::VERSION
2104                        | SevAvicRegisterNumber::TPR
2105                        | SevAvicRegisterNumber::APR
2106                        | SevAvicRegisterNumber::PPR
2107                        | SevAvicRegisterNumber::EOI
2108                        | SevAvicRegisterNumber::REMOTE_READ
2109                        | SevAvicRegisterNumber::LDR
2110                        | SevAvicRegisterNumber::DFR
2111                        | SevAvicRegisterNumber::SPURIOUS
2112                        | SevAvicRegisterNumber::ISR0
2113                        | SevAvicRegisterNumber::ISR1
2114                        | SevAvicRegisterNumber::ISR2
2115                        | SevAvicRegisterNumber::ISR3
2116                        | SevAvicRegisterNumber::ISR4
2117                        | SevAvicRegisterNumber::ISR5
2118                        | SevAvicRegisterNumber::ISR6
2119                        | SevAvicRegisterNumber::ISR7
2120                        | SevAvicRegisterNumber::TMR0
2121                        | SevAvicRegisterNumber::TMR1
2122                        | SevAvicRegisterNumber::TMR2
2123                        | SevAvicRegisterNumber::TMR3
2124                        | SevAvicRegisterNumber::TMR4
2125                        | SevAvicRegisterNumber::TMR5
2126                        | SevAvicRegisterNumber::TMR6
2127                        | SevAvicRegisterNumber::TMR7
2128                        | SevAvicRegisterNumber::IRR0
2129                        | SevAvicRegisterNumber::IRR1
2130                        | SevAvicRegisterNumber::IRR2
2131                        | SevAvicRegisterNumber::IRR3
2132                        | SevAvicRegisterNumber::IRR4
2133                        | SevAvicRegisterNumber::IRR5
2134                        | SevAvicRegisterNumber::IRR6
2135                        | SevAvicRegisterNumber::IRR7
2136                        | SevAvicRegisterNumber::ERROR
2137                        | SevAvicRegisterNumber::ICR_LOW
2138                        | SevAvicRegisterNumber::ICR_HIGH
2139                        | SevAvicRegisterNumber::TIMER_LVT
2140                        | SevAvicRegisterNumber::THERMAL_LVT
2141                        | SevAvicRegisterNumber::PERFMON_LVT
2142                        | SevAvicRegisterNumber::LINT0_LVT
2143                        | SevAvicRegisterNumber::LINT1_LVT
2144                        | SevAvicRegisterNumber::ERROR_LVT
2145                        | SevAvicRegisterNumber::INITIAL_COUNT
2146                        | SevAvicRegisterNumber::CURRENT_COUNT
2147                        | SevAvicRegisterNumber::DIVIDER
2148                        | SevAvicRegisterNumber::SELF_IPI
2149                ) {
2150                    tracelimit::error_ratelimited!(
2151                        register_number = no_accel_info.apic_register_number().0,
2152                        "unexpected AVIC register number"
2153                    );
2154                }
2155
2156                // Might be a fault (where the hardware doesn't advance the
2157                // instruction pointer) or a trap (where the hardware
2158                // advances the instruction pointer).
2159                let is_write = no_accel_info.write_access();
2160                let is_fault = matches!(
2161                    no_accel_info.apic_register_number(),
2162                    SevAvicRegisterNumber::VERSION
2163                        | SevAvicRegisterNumber::APR
2164                        | SevAvicRegisterNumber::PPR
2165                        | SevAvicRegisterNumber::ISR0
2166                        | SevAvicRegisterNumber::ISR1
2167                        | SevAvicRegisterNumber::ISR2
2168                        | SevAvicRegisterNumber::ISR3
2169                        | SevAvicRegisterNumber::ISR4
2170                        | SevAvicRegisterNumber::ISR5
2171                        | SevAvicRegisterNumber::ISR6
2172                        | SevAvicRegisterNumber::ISR7
2173                        | SevAvicRegisterNumber::TMR0
2174                        | SevAvicRegisterNumber::TMR1
2175                        | SevAvicRegisterNumber::TMR2
2176                        | SevAvicRegisterNumber::TMR3
2177                        | SevAvicRegisterNumber::TMR4
2178                        | SevAvicRegisterNumber::TMR5
2179                        | SevAvicRegisterNumber::TMR6
2180                        | SevAvicRegisterNumber::TMR7
2181                        | SevAvicRegisterNumber::IRR0
2182                        | SevAvicRegisterNumber::IRR1
2183                        | SevAvicRegisterNumber::IRR2
2184                        | SevAvicRegisterNumber::IRR3
2185                        | SevAvicRegisterNumber::IRR4
2186                        | SevAvicRegisterNumber::IRR5
2187                        | SevAvicRegisterNumber::IRR6
2188                        | SevAvicRegisterNumber::IRR7
2189                        | SevAvicRegisterNumber::CURRENT_COUNT
2190                );
2191                let msr = X2APIC_MSR_BASE + no_accel_info.apic_register_number().0;
2192                self.handle_msr_access(dev, entered_from_vtl, msr, is_write, is_fault);
2193
2194                &mut self.backing.exit_stats[entered_from_vtl].avic_no_accel
2195            }
2196
2197            SevExitCode::AVIC_INCOMPLETE_IPI => {
2198                let ipi_info1 = SevAvicIncompleteIpiInfo1::from(vmsa.exit_info1());
2199                let ipi_info2 = SevAvicIncompleteIpiInfo2::from(vmsa.exit_info2());
2200                let icr = x86defs::apic::Icr::from_bits(vmsa.exit_info1());
2201
2202                tracing::debug!(
2203                    "AVIC incomplete IPI SEV exit: {ipi_info1:x?} {ipi_info2:x?}, {icr:x?}"
2204                );
2205
2206                // This a trap, and the hardware has already advanced the instruction pointer:
2207                // "15.36.21.5 Guest APIC Accesses".
2208                let is_fault = false;
2209                let is_write = true;
2210                let msr = X2APIC_MSR_BASE + x86defs::apic::ApicRegister::ICR0.0 as u32;
2211
2212                // As the ICR is accessed through the `wrmsr` instruction (secure AVIC allows only
2213                // the x2 APIC access), we already have `rax` and `rdx` set to the desired value by
2214                // the guest.
2215                self.handle_msr_access(dev, entered_from_vtl, msr, is_write, is_fault);
2216
2217                &mut self.backing.exit_stats[entered_from_vtl].avic_incomplete_ipi
2218            }
2219
2220            _ => {
2221                tracing::error!(
2222                    CVM_CONFIDENTIAL,
2223                    "SEV exit code {sev_error_code:x?} sev features {:x?} v_intr_control {:x?} event inject {:x?} \
2224                    vmpl {:x?} cpl {:x?} exit_info1 {:x?} exit_info2 {:x?} exit_int_info {:x?} virtual_tom {:x?} \
2225                    efer {:x?} cr4 {:x?} cr3 {:x?} cr0 {:x?} rflag {:x?} rip {:x?} next rip {:x?}",
2226                    vmsa.sev_features(),
2227                    vmsa.v_intr_cntrl(),
2228                    vmsa.event_inject(),
2229                    vmsa.vmpl(),
2230                    vmsa.cpl(),
2231                    vmsa.exit_info1(),
2232                    vmsa.exit_info2(),
2233                    exit_int_info_trace,
2234                    vmsa.virtual_tom(),
2235                    vmsa.efer(),
2236                    vmsa.cr4(),
2237                    vmsa.cr3(),
2238                    vmsa.cr0(),
2239                    vmsa.rflags(),
2240                    vmsa.rip(),
2241                    vmsa.next_rip(),
2242                );
2243                panic!("Received unexpected SEV exit code {sev_error_code:x?}");
2244            }
2245        };
2246        stat.increment();
2247
2248        // Process debug exceptions before handling other intercepts.
2249        if cfg!(feature = "gdb") && sev_error_code == SevExitCode::EXCP_DB {
2250            return self.handle_debug_exception(dev, entered_from_vtl);
2251        }
2252
2253        // If there is an unhandled intercept message from the hypervisor, then
2254        // it may be a synthetic message that should be handled regardless of
2255        // the SNP exit code.
2256        if has_intercept {
2257            self.backing.general_stats[entered_from_vtl]
2258                .synth_int
2259                .increment();
2260            match self.runner.exit_message().header.typ {
2261                HvMessageType::HvMessageTypeSynicSintDeliverable => {
2262                    self.handle_synic_deliverable_exit();
2263                }
2264                HvMessageType::HvMessageTypeX64Halt
2265                | HvMessageType::HvMessageTypeExceptionIntercept => {
2266                    // Ignore. Note: it is possible to get the ExceptionIntercept
2267                    // message for reflect #VC.
2268                }
2269                message_type => {
2270                    tracelimit::error_ratelimited!(
2271                        CVM_ALLOWED,
2272                        ?message_type,
2273                        "unknown synthetic exit"
2274                    );
2275                }
2276            }
2277        }
2278
2279        // Update the guest error code in the vmsa to be a no-op. This prevents the hypervisor from
2280        // presenting the same #VC twice. A repeated #VC could result in incorrect operation since the
2281        // first instance would modify state that could be read by the second instance. This must be
2282        // done regardless of whether the vmsa is runnable or not, since a non-runnable vmsa will still
2283        // be processed when a proxy interrupt arrives and makes it runnable. It must be done
2284        // immediately after processing the intercept since another SINT can be taken to process proxy
2285        // interrupts, regardless of whether the lower VTL has executed.
2286
2287        self.runner
2288            .vmsa_mut(entered_from_vtl)
2289            .set_guest_error_code(SevExitCode::INTR.0);
2290        Ok(())
2291    }
2292
2293    fn long_mode(&self, vtl: GuestVtl) -> bool {
2294        let vmsa = self.runner.vmsa(vtl);
2295        vmsa.cr0() & x86defs::X64_CR0_PE != 0 && vmsa.efer() & x86defs::X64_EFER_LMA != 0
2296    }
2297
2298    fn handle_cpuid(&mut self, vtl: GuestVtl) {
2299        let vmsa = self.runner.vmsa(vtl);
2300        let leaf = vmsa.rax() as u32;
2301        let subleaf = vmsa.rcx() as u32;
2302        let [mut eax, mut ebx, mut ecx, mut edx] = self.cvm_cpuid_result(vtl, leaf, subleaf);
2303
2304        // Apply SNP specific fixups. These must be runtime changes only, for
2305        // parts of cpuid that are dynamic (either because it's a function of
2306        // the current VP's identity or the current VP or partition state).
2307        //
2308        // We rely on the cpuid set being accurate during partition startup,
2309        // without running through this code, so violations of this principle
2310        // may cause the partition to be constructed improperly.
2311        match CpuidFunction(leaf) {
2312            CpuidFunction::ProcessorTopologyDefinition => {
2313                let apic_id = self.inner.vp_info.apic_id;
2314                let vps_per_socket = self.cvm_partition().vps_per_socket;
2315                eax = x86defs::cpuid::ProcessorTopologyDefinitionEax::from(eax)
2316                    .with_extended_apic_id(apic_id)
2317                    .into();
2318
2319                let topology_ebx = x86defs::cpuid::ProcessorTopologyDefinitionEbx::from(ebx);
2320                let mut new_unit_id = apic_id & (vps_per_socket - 1);
2321
2322                if topology_ebx.threads_per_compute_unit() > 0 {
2323                    new_unit_id /= 2;
2324                }
2325
2326                ebx = topology_ebx.with_compute_unit_id(new_unit_id as u8).into();
2327
2328                // TODO SNP: Ideally we would use the actual value of this property from the host, but
2329                // we currently have no way of obtaining it. 1 is the default value for all current VMs.
2330                let amd_nodes_per_socket = 1u32;
2331
2332                let node_id = apic_id
2333                    >> (vps_per_socket
2334                        .trailing_zeros()
2335                        .saturating_sub(amd_nodes_per_socket.trailing_zeros()));
2336                // TODO: just set this part statically.
2337                let nodes_per_processor = amd_nodes_per_socket - 1;
2338
2339                ecx = x86defs::cpuid::ProcessorTopologyDefinitionEcx::from(ecx)
2340                    .with_node_id(node_id as u8)
2341                    .with_nodes_per_processor(nodes_per_processor as u8)
2342                    .into();
2343            }
2344            CpuidFunction::ExtendedSevFeatures => {
2345                // SEV features are not exposed to lower VTLs at this time, but
2346                // we can still query them, so we can't mask them out in the
2347                // cached CPUID leaf.
2348                eax = 0;
2349                ebx = 0;
2350                ecx = 0;
2351                edx = 0;
2352            }
2353            _ => {}
2354        }
2355
2356        let mut vmsa = self.runner.vmsa_mut(vtl);
2357        vmsa.set_rax(eax.into());
2358        vmsa.set_rbx(ebx.into());
2359        vmsa.set_rcx(ecx.into());
2360        vmsa.set_rdx(edx.into());
2361        advance_to_next_instruction(&mut vmsa);
2362    }
2363}
2364
2365impl<T: CpuIo> X86EmulatorSupport for UhEmulationState<'_, '_, T, SnpBacked> {
2366    fn flush(&mut self) {
2367        //AMD SNP does not require an emulation cache
2368    }
2369
2370    fn vp_index(&self) -> VpIndex {
2371        self.vp.vp_index()
2372    }
2373
2374    fn vendor(&self) -> x86defs::cpuid::Vendor {
2375        self.vp.partition.caps.vendor
2376    }
2377
2378    fn gp(&mut self, reg: x86emu::Gp) -> u64 {
2379        let vmsa = self.vp.runner.vmsa(self.vtl);
2380        match reg {
2381            x86emu::Gp::RAX => vmsa.rax(),
2382            x86emu::Gp::RCX => vmsa.rcx(),
2383            x86emu::Gp::RDX => vmsa.rdx(),
2384            x86emu::Gp::RBX => vmsa.rbx(),
2385            x86emu::Gp::RSP => vmsa.rsp(),
2386            x86emu::Gp::RBP => vmsa.rbp(),
2387            x86emu::Gp::RSI => vmsa.rsi(),
2388            x86emu::Gp::RDI => vmsa.rdi(),
2389            x86emu::Gp::R8 => vmsa.r8(),
2390            x86emu::Gp::R9 => vmsa.r9(),
2391            x86emu::Gp::R10 => vmsa.r10(),
2392            x86emu::Gp::R11 => vmsa.r11(),
2393            x86emu::Gp::R12 => vmsa.r12(),
2394            x86emu::Gp::R13 => vmsa.r13(),
2395            x86emu::Gp::R14 => vmsa.r14(),
2396            x86emu::Gp::R15 => vmsa.r15(),
2397        }
2398    }
2399
2400    fn set_gp(&mut self, reg: x86emu::Gp, v: u64) {
2401        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2402        match reg {
2403            x86emu::Gp::RAX => vmsa.set_rax(v),
2404            x86emu::Gp::RCX => vmsa.set_rcx(v),
2405            x86emu::Gp::RDX => vmsa.set_rdx(v),
2406            x86emu::Gp::RBX => vmsa.set_rbx(v),
2407            x86emu::Gp::RSP => vmsa.set_rsp(v),
2408            x86emu::Gp::RBP => vmsa.set_rbp(v),
2409            x86emu::Gp::RSI => vmsa.set_rsi(v),
2410            x86emu::Gp::RDI => vmsa.set_rdi(v),
2411            x86emu::Gp::R8 => vmsa.set_r8(v),
2412            x86emu::Gp::R9 => vmsa.set_r9(v),
2413            x86emu::Gp::R10 => vmsa.set_r10(v),
2414            x86emu::Gp::R11 => vmsa.set_r11(v),
2415            x86emu::Gp::R12 => vmsa.set_r12(v),
2416            x86emu::Gp::R13 => vmsa.set_r13(v),
2417            x86emu::Gp::R14 => vmsa.set_r14(v),
2418            x86emu::Gp::R15 => vmsa.set_r15(v),
2419        };
2420    }
2421
2422    fn xmm(&mut self, index: usize) -> u128 {
2423        self.vp.runner.vmsa_mut(self.vtl).xmm_registers(index)
2424    }
2425
2426    fn set_xmm(&mut self, index: usize, v: u128) {
2427        self.vp
2428            .runner
2429            .vmsa_mut(self.vtl)
2430            .set_xmm_registers(index, v);
2431    }
2432
2433    fn rip(&mut self) -> u64 {
2434        let vmsa = self.vp.runner.vmsa(self.vtl);
2435        vmsa.rip()
2436    }
2437
2438    fn set_rip(&mut self, v: u64) {
2439        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2440        vmsa.set_rip(v);
2441    }
2442
2443    fn segment(&mut self, index: x86emu::Segment) -> x86defs::SegmentRegister {
2444        let vmsa = self.vp.runner.vmsa(self.vtl);
2445        match index {
2446            x86emu::Segment::ES => virt_seg_from_snp(vmsa.es()),
2447            x86emu::Segment::CS => virt_seg_from_snp(vmsa.cs()),
2448            x86emu::Segment::SS => virt_seg_from_snp(vmsa.ss()),
2449            x86emu::Segment::DS => virt_seg_from_snp(vmsa.ds()),
2450            x86emu::Segment::FS => virt_seg_from_snp(vmsa.fs()),
2451            x86emu::Segment::GS => virt_seg_from_snp(vmsa.gs()),
2452        }
2453        .into()
2454    }
2455
2456    fn efer(&mut self) -> u64 {
2457        let vmsa = self.vp.runner.vmsa(self.vtl);
2458        vmsa.efer()
2459    }
2460
2461    fn cr0(&mut self) -> u64 {
2462        let vmsa = self.vp.runner.vmsa(self.vtl);
2463        vmsa.cr0()
2464    }
2465
2466    fn rflags(&mut self) -> RFlags {
2467        let vmsa = self.vp.runner.vmsa(self.vtl);
2468        vmsa.rflags().into()
2469    }
2470
2471    fn set_rflags(&mut self, v: RFlags) {
2472        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2473        vmsa.set_rflags(v.into());
2474    }
2475
2476    fn instruction_bytes(&self) -> &[u8] {
2477        &[]
2478    }
2479
2480    fn physical_address(&self) -> Option<u64> {
2481        Some(self.vp.runner.vmsa(self.vtl).exit_info2())
2482    }
2483
2484    fn initial_gva_translation(
2485        &mut self,
2486    ) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
2487        None
2488    }
2489
2490    fn interruption_pending(&self) -> bool {
2491        self.interruption_pending
2492    }
2493
2494    fn check_vtl_access(
2495        &mut self,
2496        _gpa: u64,
2497        _mode: virt_support_x86emu::emulate::TranslateMode,
2498    ) -> Result<(), virt_support_x86emu::emulate::EmuCheckVtlAccessError> {
2499        // Nothing to do here, the guest memory object will handle the check.
2500        Ok(())
2501    }
2502
2503    fn translate_gva(
2504        &mut self,
2505        gva: u64,
2506        mode: virt_support_x86emu::emulate::TranslateMode,
2507    ) -> Result<
2508        virt_support_x86emu::emulate::EmuTranslateResult,
2509        virt_support_x86emu::emulate::EmuTranslateError,
2510    > {
2511        emulate_translate_gva(self, gva, mode)
2512    }
2513
2514    fn inject_pending_event(&mut self, event_info: hvdef::HvX64PendingEvent) {
2515        assert!(event_info.reg_0.event_pending());
2516        assert_eq!(
2517            event_info.reg_0.event_type(),
2518            hvdef::HV_X64_PENDING_EVENT_EXCEPTION
2519        );
2520
2521        let exception = HvX64PendingExceptionEvent::from(event_info.reg_0.into_bits());
2522        assert!(!self.interruption_pending);
2523
2524        // There's no interruption pending, so just inject the exception
2525        // directly without checking for double fault.
2526        SnpBacked::set_pending_exception(self.vp, self.vtl, exception);
2527    }
2528
2529    fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool {
2530        // Ignore the VTOM address bit when checking, since memory is mirrored
2531        // across the VTOM.
2532        let vtom = self.vp.partition.caps.vtom.unwrap();
2533        debug_assert!(vtom == 0 || vtom.is_power_of_two());
2534        self.vp.partition.is_gpa_mapped(gpa & !vtom, write)
2535    }
2536
2537    fn lapic_base_address(&self) -> Option<u64> {
2538        self.vp.backing.cvm.lapics[self.vtl].lapic.base_address()
2539    }
2540
2541    fn lapic_read(&mut self, address: u64, data: &mut [u8]) {
2542        let vtl = self.vtl;
2543        let (avic_page, vmsa) = self.vp.runner.secure_avic_page_vmsa_mut(vtl);
2544        self.vp.backing.cvm.lapics[vtl]
2545            .lapic
2546            .access(&mut SnpApicClient {
2547                partition: self.vp.partition,
2548                vmsa,
2549                avic_page,
2550                dev: self.devices,
2551                vmtime: &self.vp.vmtime,
2552                vtl,
2553            })
2554            .mmio_read(address, data);
2555    }
2556
2557    fn lapic_write(&mut self, address: u64, data: &[u8]) {
2558        let vtl = self.vtl;
2559        let (avic_page, vmsa) = self.vp.runner.secure_avic_page_vmsa_mut(vtl);
2560        self.vp.backing.cvm.lapics[vtl]
2561            .lapic
2562            .access(&mut SnpApicClient {
2563                partition: self.vp.partition,
2564                vmsa,
2565                avic_page,
2566                dev: self.devices,
2567                vmtime: &self.vp.vmtime,
2568                vtl,
2569            })
2570            .mmio_write(address, data);
2571    }
2572
2573    fn monitor_support(&self) -> Option<&dyn EmulatorMonitorSupport> {
2574        Some(self)
2575    }
2576}
2577
2578impl hv1_hypercall::X64RegisterState for UhHypercallHandler<'_, '_, SnpBacked> {
2579    fn rip(&mut self) -> u64 {
2580        self.vp.runner.vmsa(self.intercepted_vtl).rip()
2581    }
2582
2583    fn set_rip(&mut self, rip: u64) {
2584        self.vp.runner.vmsa_mut(self.intercepted_vtl).set_rip(rip);
2585    }
2586
2587    fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
2588        let vmsa = self.vp.runner.vmsa(self.intercepted_vtl);
2589        match n {
2590            hv1_hypercall::X64HypercallRegister::Rax => vmsa.rax(),
2591            hv1_hypercall::X64HypercallRegister::Rcx => vmsa.rcx(),
2592            hv1_hypercall::X64HypercallRegister::Rdx => vmsa.rdx(),
2593            hv1_hypercall::X64HypercallRegister::Rbx => vmsa.rbx(),
2594            hv1_hypercall::X64HypercallRegister::Rsi => vmsa.rsi(),
2595            hv1_hypercall::X64HypercallRegister::Rdi => vmsa.rdi(),
2596            hv1_hypercall::X64HypercallRegister::R8 => vmsa.r8(),
2597        }
2598    }
2599
2600    fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
2601        let mut vmsa = self.vp.runner.vmsa_mut(self.intercepted_vtl);
2602        match n {
2603            hv1_hypercall::X64HypercallRegister::Rax => vmsa.set_rax(value),
2604            hv1_hypercall::X64HypercallRegister::Rcx => vmsa.set_rcx(value),
2605            hv1_hypercall::X64HypercallRegister::Rdx => vmsa.set_rdx(value),
2606            hv1_hypercall::X64HypercallRegister::Rbx => vmsa.set_rbx(value),
2607            hv1_hypercall::X64HypercallRegister::Rsi => vmsa.set_rsi(value),
2608            hv1_hypercall::X64HypercallRegister::Rdi => vmsa.set_rdi(value),
2609            hv1_hypercall::X64HypercallRegister::R8 => vmsa.set_r8(value),
2610        }
2611    }
2612
2613    fn xmm(&mut self, n: usize) -> u128 {
2614        self.vp.runner.vmsa(self.intercepted_vtl).xmm_registers(n)
2615    }
2616
2617    fn set_xmm(&mut self, n: usize, value: u128) {
2618        self.vp
2619            .runner
2620            .vmsa_mut(self.intercepted_vtl)
2621            .set_xmm_registers(n, value);
2622    }
2623}
2624
2625impl AccessVpState for UhVpStateAccess<'_, '_, SnpBacked> {
2626    type Error = vp_state::Error;
2627
2628    fn caps(&self) -> &virt::x86::X86PartitionCapabilities {
2629        &self.vp.partition.caps
2630    }
2631
2632    fn commit(&mut self) -> Result<(), Self::Error> {
2633        Ok(())
2634    }
2635
2636    fn registers(&mut self) -> Result<vp::Registers, Self::Error> {
2637        let vmsa = self.vp.runner.vmsa(self.vtl);
2638
2639        Ok(vp::Registers {
2640            rax: vmsa.rax(),
2641            rcx: vmsa.rcx(),
2642            rdx: vmsa.rdx(),
2643            rbx: vmsa.rbx(),
2644            rsp: vmsa.rsp(),
2645            rbp: vmsa.rbp(),
2646            rsi: vmsa.rsi(),
2647            rdi: vmsa.rdi(),
2648            r8: vmsa.r8(),
2649            r9: vmsa.r9(),
2650            r10: vmsa.r10(),
2651            r11: vmsa.r11(),
2652            r12: vmsa.r12(),
2653            r13: vmsa.r13(),
2654            r14: vmsa.r14(),
2655            r15: vmsa.r15(),
2656            rip: vmsa.rip(),
2657            rflags: vmsa.rflags(),
2658            cs: virt_seg_from_snp(vmsa.cs()),
2659            ds: virt_seg_from_snp(vmsa.ds()),
2660            es: virt_seg_from_snp(vmsa.es()),
2661            fs: virt_seg_from_snp(vmsa.fs()),
2662            gs: virt_seg_from_snp(vmsa.gs()),
2663            ss: virt_seg_from_snp(vmsa.ss()),
2664            tr: virt_seg_from_snp(vmsa.tr()),
2665            ldtr: virt_seg_from_snp(vmsa.ldtr()),
2666            gdtr: virt_table_from_snp(vmsa.gdtr()),
2667            idtr: virt_table_from_snp(vmsa.idtr()),
2668            cr0: vmsa.cr0(),
2669            cr2: vmsa.cr2(),
2670            cr3: vmsa.cr3(),
2671            cr4: vmsa.cr4(),
2672            cr8: vmsa.v_intr_cntrl().tpr().into(),
2673            efer: vmsa.efer(),
2674        })
2675    }
2676
2677    fn set_registers(&mut self, value: &vp::Registers) -> Result<(), Self::Error> {
2678        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2679
2680        let vp::Registers {
2681            rax,
2682            rcx,
2683            rdx,
2684            rbx,
2685            rsp,
2686            rbp,
2687            rsi,
2688            rdi,
2689            r8,
2690            r9,
2691            r10,
2692            r11,
2693            r12,
2694            r13,
2695            r14,
2696            r15,
2697            rip,
2698            rflags,
2699            cs,
2700            ds,
2701            es,
2702            fs,
2703            gs,
2704            ss,
2705            tr,
2706            ldtr,
2707            gdtr,
2708            idtr,
2709            cr0,
2710            cr2,
2711            cr3,
2712            cr4,
2713            cr8,
2714            efer,
2715        } = *value;
2716        vmsa.set_rax(rax);
2717        vmsa.set_rcx(rcx);
2718        vmsa.set_rdx(rdx);
2719        vmsa.set_rbx(rbx);
2720        vmsa.set_rsp(rsp);
2721        vmsa.set_rbp(rbp);
2722        vmsa.set_rsi(rsi);
2723        vmsa.set_rdi(rdi);
2724        vmsa.set_r8(r8);
2725        vmsa.set_r9(r9);
2726        vmsa.set_r10(r10);
2727        vmsa.set_r11(r11);
2728        vmsa.set_r12(r12);
2729        vmsa.set_r13(r13);
2730        vmsa.set_r14(r14);
2731        vmsa.set_r15(r15);
2732        vmsa.set_rip(rip);
2733        vmsa.set_rflags(rflags);
2734        vmsa.set_cs(virt_seg_to_snp(cs));
2735        vmsa.set_ds(virt_seg_to_snp(ds));
2736        vmsa.set_es(virt_seg_to_snp(es));
2737        vmsa.set_fs(virt_seg_to_snp(fs));
2738        vmsa.set_gs(virt_seg_to_snp(gs));
2739        vmsa.set_ss(virt_seg_to_snp(ss));
2740        vmsa.set_tr(virt_seg_to_snp(tr));
2741        vmsa.set_ldtr(virt_seg_to_snp(ldtr));
2742        vmsa.set_gdtr(virt_table_to_snp(gdtr));
2743        vmsa.set_idtr(virt_table_to_snp(idtr));
2744        vmsa.set_cr0(cr0);
2745        vmsa.set_cr2(cr2);
2746        vmsa.set_cr3(cr3);
2747        vmsa.set_cr4(cr4);
2748        vmsa.v_intr_cntrl_mut().set_tpr(cr8 as u8);
2749        vmsa.set_efer(SnpBacked::calculate_efer(efer, cr0));
2750        Ok(())
2751    }
2752
2753    fn activity(&mut self) -> Result<vp::Activity, Self::Error> {
2754        let lapic = &self.vp.backing.cvm.lapics[self.vtl];
2755
2756        Ok(vp::Activity {
2757            mp_state: lapic.activity,
2758            nmi_pending: lapic.nmi_pending,
2759            nmi_masked: false,          // TODO SNP
2760            interrupt_shadow: false,    // TODO SNP
2761            pending_event: None,        // TODO SNP
2762            pending_interruption: None, // TODO SNP
2763        })
2764    }
2765
2766    fn set_activity(&mut self, value: &vp::Activity) -> Result<(), Self::Error> {
2767        let &vp::Activity {
2768            mp_state,
2769            nmi_pending,
2770            nmi_masked: _,           // TODO SNP
2771            interrupt_shadow: _,     // TODO SNP
2772            pending_event: _,        // TODO SNP
2773            pending_interruption: _, // TODO SNP
2774        } = value;
2775        let lapic = &mut self.vp.backing.cvm.lapics[self.vtl];
2776        lapic.activity = mp_state;
2777        lapic.nmi_pending = nmi_pending;
2778
2779        Ok(())
2780    }
2781
2782    fn xsave(&mut self) -> Result<vp::Xsave, Self::Error> {
2783        Err(vp_state::Error::Unimplemented("xsave"))
2784    }
2785
2786    fn set_xsave(&mut self, _value: &vp::Xsave) -> Result<(), Self::Error> {
2787        Err(vp_state::Error::Unimplemented("xsave"))
2788    }
2789
2790    fn apic(&mut self) -> Result<vp::Apic, Self::Error> {
2791        self.vp.access_apic_without_offload(self.vtl, |vp| {
2792            Ok(vp.backing.cvm.lapics[self.vtl].lapic.save())
2793        })
2794    }
2795
2796    fn set_apic(&mut self, value: &vp::Apic) -> Result<(), Self::Error> {
2797        self.vp.access_apic_without_offload(self.vtl, |vp| {
2798            vp.backing.cvm.lapics[self.vtl]
2799                .lapic
2800                .restore(value)
2801                .map_err(vp_state::Error::InvalidApicBase)?;
2802
2803            Ok(())
2804        })
2805    }
2806
2807    fn xcr(&mut self) -> Result<vp::Xcr0, Self::Error> {
2808        let vmsa = self.vp.runner.vmsa(self.vtl);
2809        Ok(vp::Xcr0 { value: vmsa.xcr0() })
2810    }
2811
2812    fn set_xcr(&mut self, value: &vp::Xcr0) -> Result<(), Self::Error> {
2813        let vp::Xcr0 { value } = *value;
2814        self.vp.runner.vmsa_mut(self.vtl).set_xcr0(value);
2815        Ok(())
2816    }
2817
2818    fn xss(&mut self) -> Result<vp::Xss, Self::Error> {
2819        let vmsa = self.vp.runner.vmsa(self.vtl);
2820        Ok(vp::Xss { value: vmsa.xss() })
2821    }
2822
2823    fn set_xss(&mut self, value: &vp::Xss) -> Result<(), Self::Error> {
2824        let vp::Xss { value } = *value;
2825        self.vp.runner.vmsa_mut(self.vtl).set_xss(value);
2826        Ok(())
2827    }
2828
2829    fn mtrrs(&mut self) -> Result<vp::Mtrrs, Self::Error> {
2830        Ok(vp::Mtrrs {
2831            msr_mtrr_def_type: 0,
2832            fixed: [0; 11],
2833            variable: [0; 16],
2834        })
2835    }
2836
2837    fn set_mtrrs(&mut self, _value: &vp::Mtrrs) -> Result<(), Self::Error> {
2838        Ok(())
2839    }
2840
2841    fn pat(&mut self) -> Result<vp::Pat, Self::Error> {
2842        let vmsa = self.vp.runner.vmsa(self.vtl);
2843        Ok(vp::Pat { value: vmsa.pat() })
2844    }
2845
2846    fn set_pat(&mut self, value: &vp::Pat) -> Result<(), Self::Error> {
2847        let vp::Pat { value } = *value;
2848        self.vp.runner.vmsa_mut(self.vtl).set_pat(value);
2849        Ok(())
2850    }
2851
2852    fn virtual_msrs(&mut self) -> Result<vp::VirtualMsrs, Self::Error> {
2853        let vmsa = self.vp.runner.vmsa(self.vtl);
2854
2855        Ok(vp::VirtualMsrs {
2856            kernel_gs_base: vmsa.kernel_gs_base(),
2857            sysenter_cs: vmsa.sysenter_cs(),
2858            sysenter_eip: vmsa.sysenter_eip(),
2859            sysenter_esp: vmsa.sysenter_esp(),
2860            star: vmsa.star(),
2861            lstar: vmsa.lstar(),
2862            cstar: vmsa.cstar(),
2863            sfmask: vmsa.sfmask(),
2864        })
2865    }
2866
2867    fn set_virtual_msrs(&mut self, value: &vp::VirtualMsrs) -> Result<(), Self::Error> {
2868        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2869        let vp::VirtualMsrs {
2870            kernel_gs_base,
2871            sysenter_cs,
2872            sysenter_eip,
2873            sysenter_esp,
2874            star,
2875            lstar,
2876            cstar,
2877            sfmask,
2878        } = *value;
2879        vmsa.set_kernel_gs_base(kernel_gs_base);
2880        vmsa.set_sysenter_cs(sysenter_cs);
2881        vmsa.set_sysenter_eip(sysenter_eip);
2882        vmsa.set_sysenter_esp(sysenter_esp);
2883        vmsa.set_star(star);
2884        vmsa.set_lstar(lstar);
2885        vmsa.set_cstar(cstar);
2886        vmsa.set_sfmask(sfmask);
2887
2888        Ok(())
2889    }
2890
2891    fn debug_regs(&mut self) -> Result<vp::DebugRegisters, Self::Error> {
2892        let vmsa = self.vp.runner.vmsa(self.vtl);
2893        Ok(vp::DebugRegisters {
2894            dr0: vmsa.dr0(),
2895            dr1: vmsa.dr1(),
2896            dr2: vmsa.dr2(),
2897            dr3: vmsa.dr3(),
2898            dr6: vmsa.dr6(),
2899            dr7: vmsa.dr7(),
2900        })
2901    }
2902
2903    fn set_debug_regs(&mut self, value: &vp::DebugRegisters) -> Result<(), Self::Error> {
2904        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2905        let vp::DebugRegisters {
2906            dr0,
2907            dr1,
2908            dr2,
2909            dr3,
2910            dr6,
2911            dr7,
2912        } = *value;
2913        vmsa.set_dr0(dr0);
2914        vmsa.set_dr1(dr1);
2915        vmsa.set_dr2(dr2);
2916        vmsa.set_dr3(dr3);
2917        vmsa.set_dr6(dr6);
2918        vmsa.set_dr7(dr7);
2919        Ok(())
2920    }
2921
2922    fn tsc(&mut self) -> Result<vp::Tsc, Self::Error> {
2923        Err(vp_state::Error::Unimplemented("tsc"))
2924    }
2925
2926    fn set_tsc(&mut self, _value: &vp::Tsc) -> Result<(), Self::Error> {
2927        Err(vp_state::Error::Unimplemented("tsc"))
2928    }
2929
2930    fn tsc_aux(&mut self) -> Result<vp::TscAux, Self::Error> {
2931        let vmsa = self.vp.runner.vmsa(self.vtl);
2932        Ok(vp::TscAux {
2933            value: vmsa.tsc_aux() as u64,
2934        })
2935    }
2936
2937    fn set_tsc_aux(&mut self, value: &vp::TscAux) -> Result<(), Self::Error> {
2938        let vp::TscAux { value } = *value;
2939        self.vp.runner.vmsa_mut(self.vtl).set_tsc_aux(value as u32);
2940        Ok(())
2941    }
2942
2943    fn cet(&mut self) -> Result<vp::Cet, Self::Error> {
2944        let vmsa = self.vp.runner.vmsa(self.vtl);
2945        Ok(vp::Cet { scet: vmsa.s_cet() })
2946    }
2947
2948    fn set_cet(&mut self, value: &vp::Cet) -> Result<(), Self::Error> {
2949        let vp::Cet { scet } = *value;
2950        self.vp.runner.vmsa_mut(self.vtl).set_s_cet(scet);
2951        Ok(())
2952    }
2953
2954    fn cet_ss(&mut self) -> Result<vp::CetSs, Self::Error> {
2955        let vmsa = self.vp.runner.vmsa(self.vtl);
2956        Ok(vp::CetSs {
2957            ssp: vmsa.ssp(),
2958            interrupt_ssp_table_addr: vmsa.interrupt_ssp_table_addr(),
2959        })
2960    }
2961
2962    fn set_cet_ss(&mut self, value: &vp::CetSs) -> Result<(), Self::Error> {
2963        let mut vmsa = self.vp.runner.vmsa_mut(self.vtl);
2964        let vp::CetSs {
2965            ssp,
2966            interrupt_ssp_table_addr,
2967        } = *value;
2968        vmsa.set_ssp(ssp);
2969        vmsa.set_interrupt_ssp_table_addr(interrupt_ssp_table_addr);
2970        Ok(())
2971    }
2972
2973    fn synic_msrs(&mut self) -> Result<vp::SyntheticMsrs, Self::Error> {
2974        Err(vp_state::Error::Unimplemented("synic_msrs"))
2975    }
2976
2977    fn set_synic_msrs(&mut self, _value: &vp::SyntheticMsrs) -> Result<(), Self::Error> {
2978        Err(vp_state::Error::Unimplemented("synic_msrs"))
2979    }
2980
2981    fn synic_message_page(&mut self) -> Result<vp::SynicMessagePage, Self::Error> {
2982        Err(vp_state::Error::Unimplemented("synic_message_page"))
2983    }
2984
2985    fn set_synic_message_page(&mut self, _value: &vp::SynicMessagePage) -> Result<(), Self::Error> {
2986        Err(vp_state::Error::Unimplemented("synic_message_page"))
2987    }
2988
2989    fn synic_event_flags_page(&mut self) -> Result<vp::SynicEventFlagsPage, Self::Error> {
2990        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
2991    }
2992
2993    fn set_synic_event_flags_page(
2994        &mut self,
2995        _value: &vp::SynicEventFlagsPage,
2996    ) -> Result<(), Self::Error> {
2997        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
2998    }
2999
3000    fn synic_message_queues(&mut self) -> Result<vp::SynicMessageQueues, Self::Error> {
3001        Err(vp_state::Error::Unimplemented("synic_message_queues"))
3002    }
3003
3004    fn set_synic_message_queues(
3005        &mut self,
3006        _value: &vp::SynicMessageQueues,
3007    ) -> Result<(), Self::Error> {
3008        Err(vp_state::Error::Unimplemented("synic_message_queues"))
3009    }
3010
3011    fn synic_timers(&mut self) -> Result<vp::SynicTimers, Self::Error> {
3012        Err(vp_state::Error::Unimplemented("synic_timers"))
3013    }
3014
3015    fn set_synic_timers(&mut self, _value: &vp::SynicTimers) -> Result<(), Self::Error> {
3016        Err(vp_state::Error::Unimplemented("synic_timers"))
3017    }
3018
3019    fn nested_state(&mut self) -> Result<vp::NestedState, Self::Error> {
3020        Err(vp_state::Error::Unimplemented("nested_state"))
3021    }
3022
3023    fn set_nested_state(&mut self, _value: &vp::NestedState) -> Result<(), Self::Error> {
3024        Err(vp_state::Error::Unimplemented("nested_state"))
3025    }
3026}
3027
3028/// Advances the instruction pointer.
3029///
3030/// The hardware may have provided the next instruction pointer in the VMSA, so we
3031/// use that if available. That is always the case for the automatic exits (exit on #VC
3032/// when ReflectVC is set in VMSA SEV features). If the hypervisor interaction is not
3033/// required, there would be no #VC exit, and the next instruction pointer would not be
3034/// populated by the hardware. See these AMD PPR sections for details:
3035/// * 15.35.4 Types of Exits
3036/// * 15.35.5 #VC Exception
3037fn advance_to_next_instruction(vmsa: &mut VmsaWrapper<'_, &mut SevVmsa>) {
3038    match SevExitCode(vmsa.guest_error_code()) {
3039        SevExitCode::AVIC_NOACCEL => {
3040            // With Secure AVIC, x2APIC MSR accesses that are not accelerated
3041            // cause AVIC_NOACCEL exits. These accesses are via WRMSR/RDMSR
3042            // (2-byte opcodes: 0x0F 0x30 / 0x0F 0x32), and the hardware does
3043            // not provide next_rip for this exit type.
3044            // See AMD PPR: 15.36.21.5 "Guest APIC Accesses".
3045            vmsa.set_rip(vmsa.rip() + 2);
3046        }
3047        _ => vmsa.set_rip(vmsa.next_rip()),
3048    }
3049
3050    // TODO SNP: provide the precise implementation for
3051    // the next instruction pointer. For now, as a heuristic, we
3052    // report on `0`'s in the rip field. The guest would need to
3053    // execute an instruction at the top of the VA space to make the
3054    // instruction pointer wrap around to `0` or fault at `0` --
3055    // seems unlikely.
3056    if vmsa.rip() == 0 {
3057        tracing::warn!("rip is zero, might need to parse the instruction stream");
3058    }
3059
3060    vmsa.v_intr_cntrl_mut().set_intr_shadow(false);
3061}
3062
3063impl UhProcessor<'_, SnpBacked> {
3064    fn read_msr_snp(
3065        &mut self,
3066        _dev: &impl CpuIo,
3067        msr: u32,
3068        vtl: GuestVtl,
3069    ) -> Result<u64, MsrError> {
3070        let vmsa = self.runner.vmsa(vtl);
3071        let value = match msr {
3072            x86defs::X64_MSR_FS_BASE => vmsa.fs().base,
3073            x86defs::X64_MSR_GS_BASE => vmsa.gs().base,
3074            x86defs::X64_MSR_KERNEL_GS_BASE => vmsa.kernel_gs_base(),
3075            x86defs::X86X_MSR_TSC_AUX => {
3076                if self.shared.tsc_aux_virtualized {
3077                    vmsa.tsc_aux() as u64
3078                } else {
3079                    return Err(MsrError::InvalidAccess);
3080                }
3081            }
3082            x86defs::X86X_MSR_SPEC_CTRL => vmsa.spec_ctrl(),
3083            x86defs::X86X_MSR_U_CET => vmsa.u_cet(),
3084            x86defs::X86X_MSR_S_CET => vmsa.s_cet(),
3085            x86defs::X86X_MSR_PL0_SSP => vmsa.pl0_ssp(),
3086            x86defs::X86X_MSR_PL1_SSP => vmsa.pl1_ssp(),
3087            x86defs::X86X_MSR_PL2_SSP => vmsa.pl2_ssp(),
3088            x86defs::X86X_MSR_PL3_SSP => vmsa.pl3_ssp(),
3089            x86defs::X86X_MSR_INTERRUPT_SSP_TABLE_ADDR => vmsa.interrupt_ssp_table_addr(),
3090            x86defs::X86X_MSR_CR_PAT => vmsa.pat(),
3091            x86defs::X86X_MSR_EFER => vmsa.efer(),
3092            x86defs::X86X_MSR_STAR => vmsa.star(),
3093            x86defs::X86X_MSR_LSTAR => vmsa.lstar(),
3094            x86defs::X86X_MSR_CSTAR => vmsa.cstar(),
3095            x86defs::X86X_MSR_SFMASK => vmsa.sfmask(),
3096            x86defs::X86X_MSR_SYSENTER_CS => vmsa.sysenter_cs(),
3097            x86defs::X86X_MSR_SYSENTER_ESP => vmsa.sysenter_esp(),
3098            x86defs::X86X_MSR_SYSENTER_EIP => vmsa.sysenter_eip(),
3099            x86defs::X86X_MSR_XSS => vmsa.xss(),
3100            x86defs::X86X_AMD_MSR_VM_CR => 0,
3101            x86defs::X86X_MSR_TSC => safe_intrinsics::rdtsc(),
3102            x86defs::X86X_MSR_MC_UPDATE_PATCH_LEVEL => 0xffff_ffff,
3103            x86defs::X86X_MSR_MTRR_CAP => {
3104                // Advertise the absence of MTRR capabilities, but include the availability of write
3105                // combining.
3106                0x400
3107            }
3108            x86defs::X86X_MSR_MTRR_DEF_TYPE => {
3109                // Because the MTRR registers are advertised via CPUID, even though no actual ranges
3110                // are supported a guest may choose to write to this MSR. Implement it as read as
3111                // zero/write ignore.
3112                0
3113            }
3114            x86defs::X86X_AMD_MSR_SYSCFG
3115            | x86defs::X86X_MSR_MCG_CAP
3116            | x86defs::X86X_MSR_MCG_STATUS => 0,
3117            hvdef::HV_X64_MSR_GUEST_IDLE => {
3118                self.backing.cvm.lapics[vtl].activity = MpState::Idle;
3119                let mut vmsa = self.runner.vmsa_mut(vtl);
3120                vmsa.v_intr_cntrl_mut().set_intr_shadow(false);
3121                0
3122            }
3123            _ => return Err(MsrError::Unknown),
3124        };
3125        Ok(value)
3126    }
3127
3128    fn write_msr_snp(
3129        &mut self,
3130        _dev: &impl CpuIo,
3131        msr: u32,
3132        value: u64,
3133        vtl: GuestVtl,
3134    ) -> Result<(), MsrError> {
3135        hardware_cvm::validate_cvm_msr_write(msr, value, &self.partition.caps.xsave)?;
3136
3137        let mut vmsa = self.runner.vmsa_mut(vtl);
3138        match msr {
3139            x86defs::X64_MSR_FS_BASE => {
3140                // The FS base is loaded on the very next VMRUN, so it must
3141                // be canonical in the guest's current paging mode.
3142                if !hardware_cvm::validate_canonical_address(value, vmsa.efer(), vmsa.cr4()) {
3143                    return Err(MsrError::InvalidAccess);
3144                }
3145                let fs = vmsa.fs();
3146                vmsa.set_fs(SevSelector {
3147                    attrib: fs.attrib,
3148                    selector: fs.selector,
3149                    limit: fs.limit,
3150                    base: value,
3151                });
3152            }
3153            x86defs::X64_MSR_GS_BASE => {
3154                // The GS base is loaded on the very next VMRUN, so it must
3155                // be canonical in the guest's current paging mode.
3156                if !hardware_cvm::validate_canonical_address(value, vmsa.efer(), vmsa.cr4()) {
3157                    return Err(MsrError::InvalidAccess);
3158                }
3159                let gs = vmsa.gs();
3160                vmsa.set_gs(SevSelector {
3161                    attrib: gs.attrib,
3162                    selector: gs.selector,
3163                    limit: gs.limit,
3164                    base: value,
3165                });
3166            }
3167            x86defs::X64_MSR_KERNEL_GS_BASE => vmsa.set_kernel_gs_base(value),
3168            x86defs::X86X_MSR_TSC_AUX => {
3169                if self.shared.tsc_aux_virtualized {
3170                    vmsa.set_tsc_aux(value as u32);
3171                } else {
3172                    return Err(MsrError::InvalidAccess);
3173                }
3174            }
3175            x86defs::X86X_MSR_SPEC_CTRL => vmsa.set_spec_ctrl(value),
3176            x86defs::X86X_MSR_U_CET => vmsa.set_u_cet(value),
3177            x86defs::X86X_MSR_S_CET => vmsa.set_s_cet(value),
3178            x86defs::X86X_MSR_PL0_SSP => vmsa.set_pl0_ssp(value),
3179            x86defs::X86X_MSR_PL1_SSP => vmsa.set_pl1_ssp(value),
3180            x86defs::X86X_MSR_PL2_SSP => vmsa.set_pl2_ssp(value),
3181            x86defs::X86X_MSR_PL3_SSP => vmsa.set_pl3_ssp(value),
3182            x86defs::X86X_MSR_INTERRUPT_SSP_TABLE_ADDR => vmsa.set_interrupt_ssp_table_addr(value),
3183
3184            x86defs::X86X_MSR_CR_PAT => vmsa.set_pat(value),
3185            x86defs::X86X_MSR_EFER => vmsa.set_efer(SnpBacked::calculate_efer(value, vmsa.cr0())),
3186
3187            x86defs::X86X_MSR_STAR => vmsa.set_star(value),
3188            x86defs::X86X_MSR_LSTAR => vmsa.set_lstar(value),
3189            x86defs::X86X_MSR_CSTAR => vmsa.set_cstar(value),
3190            x86defs::X86X_MSR_SFMASK => vmsa.set_sfmask(value),
3191            x86defs::X86X_MSR_SYSENTER_CS => vmsa.set_sysenter_cs(value),
3192            x86defs::X86X_MSR_SYSENTER_ESP => vmsa.set_sysenter_esp(value),
3193            x86defs::X86X_MSR_SYSENTER_EIP => vmsa.set_sysenter_eip(value),
3194            x86defs::X86X_MSR_XSS => vmsa.set_xss(value),
3195
3196            x86defs::X86X_MSR_TSC => {} // ignore writes to the TSC for now
3197            x86defs::X86X_MSR_MC_UPDATE_PATCH_LEVEL => {}
3198            x86defs::X86X_MSR_MTRR_DEF_TYPE => {}
3199
3200            x86defs::X86X_AMD_MSR_VM_CR
3201            | x86defs::X86X_MSR_MTRR_CAP
3202            | x86defs::X86X_AMD_MSR_SYSCFG
3203            | x86defs::X86X_MSR_MCG_CAP => return Err(MsrError::InvalidAccess),
3204
3205            x86defs::X86X_MSR_MCG_STATUS => {
3206                // Writes are swallowed, except for reserved bits violations
3207                if x86defs::X86xMcgStatusRegister::from(value).reserved0() != 0 {
3208                    return Err(MsrError::InvalidAccess);
3209                }
3210            }
3211            _ => {
3212                tracing::debug!(msr, value, "unknown cvm msr write");
3213            }
3214        }
3215        Ok(())
3216    }
3217}
3218
3219impl hv1_hypercall::VtlSwitchOps for UhHypercallHandler<'_, '_, SnpBacked> {
3220    fn advance_ip(&mut self) {
3221        let is_64bit = self.vp.long_mode(self.intercepted_vtl);
3222        let mut io = hv1_hypercall::X64RegisterIo::new(self, is_64bit, true);
3223        io.advance_ip();
3224    }
3225
3226    fn inject_invalid_opcode_fault(&mut self) {
3227        self.vp
3228            .runner
3229            .vmsa_mut(self.intercepted_vtl)
3230            .set_event_inject(
3231                SevEventInjectInfo::new()
3232                    .with_valid(true)
3233                    .with_interruption_type(x86defs::snp::SEV_INTR_TYPE_EXCEPT)
3234                    .with_vector(x86defs::Exception::INVALID_OPCODE.0),
3235            );
3236    }
3237}
3238
3239impl hv1_hypercall::FlushVirtualAddressList for UhHypercallHandler<'_, '_, SnpBacked> {
3240    fn flush_virtual_address_list(
3241        &mut self,
3242        processor_set: ProcessorSet<'_>,
3243        flags: HvFlushFlags,
3244        gva_ranges: &[HvGvaRange],
3245    ) -> HvRepResult {
3246        hv1_hypercall::FlushVirtualAddressListEx::flush_virtual_address_list_ex(
3247            self,
3248            processor_set,
3249            flags,
3250            gva_ranges,
3251        )
3252    }
3253}
3254
3255impl hv1_hypercall::FlushVirtualAddressListEx for UhHypercallHandler<'_, '_, SnpBacked> {
3256    fn flush_virtual_address_list_ex(
3257        &mut self,
3258        processor_set: ProcessorSet<'_>,
3259        flags: HvFlushFlags,
3260        gva_ranges: &[HvGvaRange],
3261    ) -> HvRepResult {
3262        self.hcvm_validate_flush_inputs(processor_set, flags, true)
3263            .map_err(|e| (e, 0))?;
3264
3265        // As a performance optimization if we are asked to do too large an amount of work
3266        // just do a flush entire instead.
3267        if gva_ranges.len() > 16 || gva_ranges.iter().any(|range| if flags.use_extended_range_format() { range.as_extended().additional_pages() } else { range.as_simple().additional_pages() } > 16) {
3268            self.do_flush_virtual_address_space(processor_set, flags);
3269        } else {
3270            self.do_flush_virtual_address_list(flags, gva_ranges);
3271        }
3272
3273        // Mark that this VP needs to wait for all TLB locks to be released before returning.
3274        self.vp.set_wait_for_tlb_locks(self.intercepted_vtl);
3275        Ok(())
3276    }
3277}
3278
3279impl hv1_hypercall::FlushVirtualAddressSpace for UhHypercallHandler<'_, '_, SnpBacked> {
3280    fn flush_virtual_address_space(
3281        &mut self,
3282        processor_set: ProcessorSet<'_>,
3283        flags: HvFlushFlags,
3284    ) -> hvdef::HvResult<()> {
3285        hv1_hypercall::FlushVirtualAddressSpaceEx::flush_virtual_address_space_ex(
3286            self,
3287            processor_set,
3288            flags,
3289        )
3290    }
3291}
3292
3293impl hv1_hypercall::FlushVirtualAddressSpaceEx for UhHypercallHandler<'_, '_, SnpBacked> {
3294    fn flush_virtual_address_space_ex(
3295        &mut self,
3296        processor_set: ProcessorSet<'_>,
3297        flags: HvFlushFlags,
3298    ) -> hvdef::HvResult<()> {
3299        self.hcvm_validate_flush_inputs(processor_set, flags, false)?;
3300
3301        self.do_flush_virtual_address_space(processor_set, flags);
3302
3303        // Mark that this VP needs to wait for all TLB locks to be released before returning.
3304        self.vp.set_wait_for_tlb_locks(self.intercepted_vtl);
3305        Ok(())
3306    }
3307}
3308
3309impl UhHypercallHandler<'_, '_, SnpBacked> {
3310    fn do_flush_virtual_address_list(&mut self, flags: HvFlushFlags, gva_ranges: &[HvGvaRange]) {
3311        for range in gva_ranges {
3312            let mut rax = SevInvlpgbRax::new()
3313                .with_asid_valid(true)
3314                .with_va_valid(true)
3315                .with_global(!flags.non_global_mappings_only());
3316            let mut ecx = SevInvlpgbEcx::new();
3317            let mut count;
3318            let mut gpn;
3319
3320            if flags.use_extended_range_format() && range.as_extended().large_page() {
3321                ecx.set_large_page(true);
3322                if range.as_extended_large_page().page_size() {
3323                    let range = range.as_extended_large_page();
3324                    count = range.additional_pages();
3325                    gpn = range.gva_large_page_number();
3326                } else {
3327                    let range = range.as_extended();
3328                    count = range.additional_pages();
3329                    gpn = range.gva_page_number();
3330                }
3331            } else {
3332                let range = range.as_simple();
3333                count = range.additional_pages();
3334                gpn = range.gva_page_number();
3335            }
3336            count += 1; // account for self
3337
3338            while count > 0 {
3339                rax.set_virtual_page_number(gpn);
3340                ecx.set_additional_count(std::cmp::min(
3341                    count - 1,
3342                    self.vp.shared.invlpgb_count_max.into(),
3343                ));
3344
3345                let edx = SevInvlpgbEdx::new();
3346                self.vp
3347                    .partition
3348                    .hcl
3349                    .invlpgb(rax.into(), edx.into(), ecx.into());
3350
3351                count -= ecx.additional_count() + 1;
3352                gpn += ecx.additional_count() + 1;
3353            }
3354        }
3355
3356        self.vp.partition.hcl.tlbsync();
3357    }
3358
3359    fn do_flush_virtual_address_space(
3360        &mut self,
3361        processor_set: ProcessorSet<'_>,
3362        flags: HvFlushFlags,
3363    ) {
3364        let only_self = [self.vp.vp_index().index()].into_iter().eq(processor_set);
3365        if only_self && flags.non_global_mappings_only() {
3366            self.vp.runner.vmsa_mut(self.intercepted_vtl).set_pcpu_id(0);
3367        } else {
3368            self.vp.partition.hcl.invlpgb(
3369                SevInvlpgbRax::new()
3370                    .with_asid_valid(true)
3371                    .with_global(!flags.non_global_mappings_only())
3372                    .into(),
3373                SevInvlpgbEdx::new().into(),
3374                SevInvlpgbEcx::new().into(),
3375            );
3376            self.vp.partition.hcl.tlbsync();
3377        }
3378    }
3379}
3380
3381struct SnpTlbLockFlushAccess<'a> {
3382    vp_index: Option<VpIndex>,
3383    partition: &'a UhPartitionInner,
3384    shared: &'a SnpBackedShared,
3385}
3386
3387impl TlbFlushLockAccess for SnpTlbLockFlushAccess<'_> {
3388    fn flush(&mut self, vtl: GuestVtl) {
3389        // SNP provides no mechanism to flush a single VTL across multiple VPs
3390        // Do a flush entire, but only wait on the VTL that was asked for
3391        self.partition.hcl.invlpgb(
3392            SevInvlpgbRax::new()
3393                .with_asid_valid(true)
3394                .with_global(true)
3395                .into(),
3396            SevInvlpgbEdx::new().into(),
3397            SevInvlpgbEcx::new().into(),
3398        );
3399        self.partition.hcl.tlbsync();
3400        self.set_wait_for_tlb_locks(vtl);
3401    }
3402
3403    fn flush_entire(&mut self) {
3404        self.partition.hcl.invlpgb(
3405            SevInvlpgbRax::new()
3406                .with_asid_valid(true)
3407                .with_global(true)
3408                .into(),
3409            SevInvlpgbEdx::new().into(),
3410            SevInvlpgbEcx::new().into(),
3411        );
3412        self.partition.hcl.tlbsync();
3413        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
3414            self.set_wait_for_tlb_locks(vtl);
3415        }
3416    }
3417
3418    fn set_wait_for_tlb_locks(&mut self, vtl: GuestVtl) {
3419        if let Some(vp_index) = self.vp_index {
3420            hardware_cvm::tlb_lock::TlbLockAccess {
3421                vp_index,
3422                cvm_partition: &self.shared.cvm,
3423            }
3424            .set_wait_for_tlb_locks(vtl);
3425        }
3426    }
3427}
3428
3429mod save_restore {
3430    use super::SnpBacked;
3431    use super::UhProcessor;
3432    use vmcore::save_restore::RestoreError;
3433    use vmcore::save_restore::SaveError;
3434    use vmcore::save_restore::SaveRestore;
3435    use vmcore::save_restore::SavedStateNotSupported;
3436
3437    impl SaveRestore for UhProcessor<'_, SnpBacked> {
3438        type SavedState = SavedStateNotSupported;
3439
3440        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
3441            Err(SaveError::NotSupported)
3442        }
3443
3444        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
3445            match state {}
3446        }
3447    }
3448}