Skip to main content

virt_mshv_vtl/processor/tdx/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Processor support for TDX partitions.
5
6mod tlb_flush;
7
8use super::BackingPrivate;
9use super::BackingSharedParams;
10use super::HardwareIsolatedBacking;
11use super::UhEmulationState;
12use super::UhHypercallHandler;
13use super::hardware_cvm;
14use super::vp_state;
15use super::vp_state::UhVpStateAccess;
16use crate::BackingShared;
17use crate::GuestVtl;
18use crate::IsolationType;
19use crate::TlbFlushLockAccess;
20use crate::UhCvmPartitionState;
21use crate::UhCvmVpState;
22use crate::UhPartitionInner;
23use crate::UhPartitionNewParams;
24use crate::UhProcessor;
25use crate::WakeReason;
26use crate::get_tsc_frequency;
27use cvm_tracing::CVM_ALLOWED;
28use cvm_tracing::CVM_CONFIDENTIAL;
29use guestmem::GuestMemory;
30use hcl::ioctl::ProcessorRunner;
31use hcl::ioctl::tdx::Tdx;
32use hcl::ioctl::tdx::TdxPrivateRegs;
33use hcl::protocol::hcl_intr_offload_flags;
34use hcl::protocol::tdx_tdg_vp_enter_exit_info;
35use hv1_emulator::hv::ProcessorVtlHv;
36use hv1_emulator::synic::GlobalSynic;
37use hv1_emulator::synic::ProcessorSynic;
38use hv1_hypercall::AsHandler;
39use hv1_hypercall::HvRepResult;
40use hv1_hypercall::HypercallIo;
41use hv1_structs::ProcessorSet;
42use hv1_structs::VtlArray;
43use hvdef::HV_PAGE_SIZE;
44use hvdef::HvError;
45use hvdef::HvSynicSimpSiefp;
46use hvdef::HvX64PendingExceptionEvent;
47use hvdef::HvX64RegisterName;
48use hvdef::Vtl;
49use hvdef::hypercall::HvFlushFlags;
50use hvdef::hypercall::HvGvaRange;
51use inspect::Inspect;
52use inspect::InspectMut;
53use inspect_counters::Counter;
54use std::sync::atomic::AtomicU8;
55use std::sync::atomic::Ordering;
56use thiserror::Error;
57use tlb_flush::FLUSH_GVA_LIST_SIZE;
58use tlb_flush::TdxFlushState;
59use tlb_flush::TdxPartitionFlushState;
60use virt::EmulatorMonitorSupport;
61use virt::Processor;
62use virt::VpHaltReason;
63use virt::VpIndex;
64use virt::io::CpuIo;
65use virt::state::StateElement;
66use virt::vp;
67use virt::vp::AccessVpState;
68use virt::vp::MpState;
69use virt::vp::Registers;
70use virt::x86::MsrError;
71use virt::x86::MsrErrorExt;
72use virt::x86::SegmentRegister;
73use virt::x86::TableRegister;
74use virt_support_apic::ApicClient;
75use virt_support_apic::OffloadNotSupported;
76use virt_support_x86emu::emulate::EmulatedMemoryOperation;
77use virt_support_x86emu::emulate::EmulatorSupport as X86EmulatorSupport;
78use virt_support_x86emu::emulate::TranslateMode;
79use virt_support_x86emu::emulate::emulate_insn_memory_op;
80use virt_support_x86emu::emulate::emulate_io;
81use virt_support_x86emu::emulate::emulate_translate_gva;
82use virt_support_x86emu::translate::TranslationRegisters;
83use vmcore::vmtime::VmTimeAccess;
84use x86defs::ApicRegisterValue;
85use x86defs::RFlags;
86use x86defs::X64_CR0_ET;
87use x86defs::X64_CR0_NE;
88use x86defs::X64_CR0_PE;
89use x86defs::X64_CR0_PG;
90use x86defs::X64_CR4_MCE;
91use x86defs::X64_CR4_UMIP;
92use x86defs::X64_CR4_VMXE;
93use x86defs::X64_EFER_FFXSR;
94use x86defs::X64_EFER_LMA;
95use x86defs::X64_EFER_LME;
96use x86defs::X64_EFER_NXE;
97use x86defs::X64_EFER_SVME;
98use x86defs::X86X_MSR_EFER;
99use x86defs::apic::X2APIC_MSR_BASE;
100use x86defs::tdx::TdCallResultCode;
101use x86defs::tdx::TdVmCallR10Result;
102use x86defs::tdx::TdxGp;
103use x86defs::tdx::TdxInstructionInfo;
104use x86defs::tdx::TdxL2Ctls;
105use x86defs::tdx::TdxVpEnterRaxResult;
106use x86defs::vmx::CR_ACCESS_TYPE_LMSW;
107use x86defs::vmx::CR_ACCESS_TYPE_MOV_TO_CR;
108use x86defs::vmx::CrAccessQualification;
109use x86defs::vmx::ExitQualificationIo;
110use x86defs::vmx::GdtrOrIdtrInstruction;
111use x86defs::vmx::GdtrOrIdtrInstructionInfo;
112use x86defs::vmx::INTERRUPT_TYPE_EXTERNAL;
113use x86defs::vmx::INTERRUPT_TYPE_HARDWARE_EXCEPTION;
114use x86defs::vmx::INTERRUPT_TYPE_NMI;
115use x86defs::vmx::IO_SIZE_8_BIT;
116use x86defs::vmx::IO_SIZE_16_BIT;
117use x86defs::vmx::IO_SIZE_32_BIT;
118use x86defs::vmx::Ia32FeatureControl;
119use x86defs::vmx::Interruptibility;
120use x86defs::vmx::InterruptionInformation;
121use x86defs::vmx::LdtrOrTrInstruction;
122use x86defs::vmx::LdtrOrTrInstructionInfo;
123use x86defs::vmx::ProcessorControls;
124use x86defs::vmx::SecondaryProcessorControls;
125use x86defs::vmx::VMX_ENTRY_CONTROL_LONG_MODE_GUEST;
126use x86defs::vmx::VmcsField;
127use x86defs::vmx::VmxApicPage;
128use x86defs::vmx::VmxEptExitQualification;
129use x86defs::vmx::VmxExit;
130use x86defs::vmx::VmxExitBasic;
131use x86emu::Gp;
132use x86emu::Segment;
133
134/// MSRs that are allowed to be read by the guest without interception.
135const MSR_ALLOWED_READ: &[u32] = &[
136    x86defs::X86X_MSR_TSC,
137    x86defs::X86X_MSR_TSC_AUX,
138    X86X_MSR_EFER,
139    x86defs::X86X_MSR_STAR,
140    x86defs::X86X_MSR_LSTAR,
141    x86defs::X86X_MSR_SFMASK,
142    x86defs::X86X_MSR_SYSENTER_CS,
143    x86defs::X86X_MSR_SYSENTER_ESP,
144    x86defs::X86X_MSR_SYSENTER_EIP,
145];
146
147/// MSRs that are allowed to be read and written by the guest without interception.
148const MSR_ALLOWED_READ_WRITE: &[u32] = &[
149    x86defs::X64_MSR_FS_BASE,
150    x86defs::X64_MSR_GS_BASE,
151    x86defs::X64_MSR_KERNEL_GS_BASE,
152    x86defs::X86X_MSR_SPEC_CTRL,
153    x86defs::X86X_MSR_U_CET,
154    x86defs::X86X_MSR_S_CET,
155    x86defs::X86X_MSR_PL0_SSP,
156    x86defs::X86X_MSR_PL1_SSP,
157    x86defs::X86X_MSR_PL2_SSP,
158    x86defs::X86X_MSR_PL3_SSP,
159    x86defs::X86X_MSR_INTERRUPT_SSP_TABLE_ADDR,
160    x86defs::X86X_IA32_MSR_XFD,
161    x86defs::X86X_IA32_MSR_XFD_ERR,
162];
163
164#[derive(Debug, Error)]
165#[error("unknown exit {0:#x?}")]
166struct UnknownVmxExit(VmxExit);
167
168#[derive(Debug, Error)]
169#[error("bad guest state on VP.ENTER")]
170struct VmxBadGuestState;
171
172#[derive(Debug, Error)]
173#[error("failed to run")]
174struct TdxRunVpError(#[source] hcl::ioctl::Error);
175
176#[derive(Debug)]
177struct TdxExit<'a>(&'a tdx_tdg_vp_enter_exit_info);
178
179impl TdxExit<'_> {
180    fn code(&self) -> TdxVpEnterRaxResult {
181        self.0.rax.into()
182    }
183    fn qualification(&self) -> u64 {
184        self.0.rcx
185    }
186    fn gla(&self) -> Option<u64> {
187        // Only valid for EPT exits.
188        if self.code().vmx_exit().basic_reason() == VmxExitBasic::EPT_VIOLATION {
189            Some(self.0.rdx)
190        } else {
191            None
192        }
193    }
194    fn gpa(&self) -> Option<u64> {
195        // Only valid for EPT exits.
196        if self.code().vmx_exit().basic_reason() == VmxExitBasic::EPT_VIOLATION {
197            Some(self.0.r8)
198        } else {
199            None
200        }
201    }
202    fn _exit_interruption_info(&self) -> InterruptionInformation {
203        (self.0.r9 as u32).into()
204    }
205    fn _exit_interruption_error_code(&self) -> u32 {
206        (self.0.r9 >> 32) as u32
207    }
208    fn idt_vectoring_info(&self) -> InterruptionInformation {
209        (self.0.r10 as u32).into()
210    }
211    fn idt_vectoring_error_code(&self) -> u32 {
212        (self.0.r10 >> 32) as u32
213    }
214    fn instr_info(&self) -> TdxInstructionInfo {
215        self.0.r11.into()
216    }
217    fn cs(&self) -> SegmentRegister {
218        SegmentRegister {
219            selector: self.0.rsi as u16,
220            base: self.0.rdi,
221            limit: (self.0.rsi >> 32) as u32,
222            attributes: (self.0.rsi >> 16) as u16,
223        }
224    }
225    fn cpl(&self) -> u8 {
226        self.0.r12 as u8 & 3
227    }
228}
229
230/// Registers that can be virtual and shadowed.
231#[derive(Debug, Inspect)]
232enum ShadowedRegister {
233    Cr0,
234    Cr4,
235}
236
237impl ShadowedRegister {
238    fn name(&self) -> &'static str {
239        match self {
240            Self::Cr0 => "cr0",
241            Self::Cr4 => "cr4",
242        }
243    }
244
245    fn physical_vmcs_field(&self) -> VmcsField {
246        match self {
247            Self::Cr0 => VmcsField::VMX_VMCS_GUEST_CR0,
248            Self::Cr4 => VmcsField::VMX_VMCS_GUEST_CR4,
249        }
250    }
251
252    fn shadow_vmcs_field(&self) -> VmcsField {
253        match self {
254            Self::Cr0 => VmcsField::VMX_VMCS_CR0_READ_SHADOW,
255            Self::Cr4 => VmcsField::VMX_VMCS_CR4_READ_SHADOW,
256        }
257    }
258
259    fn guest_owned_mask(&self) -> u64 {
260        // Control register bits that are guest owned by default. A bit is guest
261        // owned when the physical register bit is always set to the virtual
262        // register bit (subject to validation of the virtual register).
263        match self {
264            Self::Cr0 => {
265                X64_CR0_ET
266                    | x86defs::X64_CR0_MP
267                    | x86defs::X64_CR0_EM
268                    | x86defs::X64_CR0_TS
269                    | x86defs::X64_CR0_WP
270                    | x86defs::X64_CR0_AM
271                    | X64_CR0_PE
272                    | X64_CR0_PG
273            }
274            Self::Cr4 => {
275                x86defs::X64_CR4_VME
276                    | x86defs::X64_CR4_PVI
277                    | x86defs::X64_CR4_TSD
278                    | x86defs::X64_CR4_DE
279                    | x86defs::X64_CR4_PSE
280                    | x86defs::X64_CR4_PAE
281                    | x86defs::X64_CR4_PGE
282                    | x86defs::X64_CR4_PCE
283                    | x86defs::X64_CR4_FXSR
284                    | x86defs::X64_CR4_XMMEXCPT
285                    | X64_CR4_UMIP
286                    | x86defs::X64_CR4_LA57
287                    | x86defs::X64_CR4_RWFSGS
288                    | x86defs::X64_CR4_PCIDE
289                    | x86defs::X64_CR4_OSXSAVE
290                    | x86defs::X64_CR4_SMEP
291                    | x86defs::X64_CR4_SMAP
292                    | x86defs::X64_CR4_CET
293            }
294        }
295    }
296}
297
298/// A virtual register that is shadowed by the virtstack.
299///
300/// Some bits are owned by the guest while others are owned by the virtstack,
301/// due to TDX requirements.
302#[derive(Inspect)]
303struct VirtualRegister {
304    /// The register being shadowed.
305    register: ShadowedRegister,
306    /// The VTL this register is shadowed for.
307    vtl: GuestVtl,
308    /// The value the guest sees.
309    shadow_value: u64,
310    /// Additional constraints on bits.
311    allowed_bits: u64,
312}
313
314impl VirtualRegister {
315    fn new(reg: ShadowedRegister, vtl: GuestVtl, initial_value: u64, allowed_bits: u64) -> Self {
316        Self {
317            register: reg,
318            vtl,
319            shadow_value: initial_value,
320            allowed_bits,
321        }
322    }
323
324    /// Write a new value to the virtual register. This updates host owned bits
325    /// in the shadowed value, and updates guest owned bits in the physical
326    /// register in the vmcs.
327    fn write<'a>(
328        &mut self,
329        value: u64,
330        runner: &mut ProcessorRunner<'a, Tdx<'a>>,
331    ) -> Result<(), vp_state::Error> {
332        tracing::trace!(?self.register, value, "write virtual register");
333
334        if value & !self.allowed_bits != 0 {
335            return Err(vp_state::Error::InvalidValue(
336                value,
337                self.register.name(),
338                "disallowed bit set",
339            ));
340        }
341
342        // If guest owned bits of the physical register have changed, then update
343        // the guest owned bits of the physical field.
344        let old_physical_reg = runner.read_vmcs64(self.vtl, self.register.physical_vmcs_field());
345
346        tracing::trace!(old_physical_reg, "old_physical_reg");
347
348        let guest_owned_mask = self.register.guest_owned_mask();
349        if (old_physical_reg ^ value) & guest_owned_mask != 0 {
350            let new_physical_reg =
351                (old_physical_reg & !guest_owned_mask) | (value & guest_owned_mask);
352
353            tracing::trace!(new_physical_reg, "new_physical_reg");
354
355            runner.write_vmcs64(
356                self.vtl,
357                self.register.physical_vmcs_field(),
358                !0,
359                new_physical_reg,
360            );
361        }
362
363        self.shadow_value = value;
364        runner.write_vmcs64(self.vtl, self.register.shadow_vmcs_field(), !0, value);
365        Ok(())
366    }
367
368    fn read<'a>(&self, runner: &ProcessorRunner<'a, Tdx<'a>>) -> u64 {
369        let physical_reg = runner.read_vmcs64(self.vtl, self.register.physical_vmcs_field());
370
371        // Get the bits owned by the host from the shadow and the bits owned by the
372        // guest from the physical value.
373        let guest_owned_mask = self.register.guest_owned_mask();
374        (self.shadow_value & !self.register.guest_owned_mask()) | (physical_reg & guest_owned_mask)
375    }
376}
377
378/// Interface for managing lower VTL timer deadlines via TDX L2-VM TSC Deadline
379/// Timer capability.
380///
381/// This allows VTL2 to set an execution deadline for lower VTLs, in absolute
382/// virtual TSC units. If the lower VTL is running when the deadline time
383/// arrives, it exits to VTL2 with exit reason `VmxExitBasic::TIMER_EXPIRED`.
384/// If the TSC deadline is in the past during entry into lower VTL (i.e., TSC
385/// deadline value is lower than the current virtual TSC value), it will immediately
386/// exit back to VTL2 with exit reason `VmxExitBasic::TIMER_EXPIRED`.
387///
388/// The TSC deadline is set using `TDG.VP.WR` for `TDVPS.TSC_DEADLINE[L2-VM Index]`.
389/// The actual `TDG.VP.WR` call to set the deadline is made by the `mshv_vtl` driver
390///  before entering the lower VTL.
391struct TdxTscDeadlineService {
392    // Fixed-point scale factor to convert 100ns to TSC units.
393    tsc_scale_100ns: u128,
394}
395
396impl TdxTscDeadlineService {
397    /// Convert hypervisor reference time (in 100ns) to TSC units.
398    fn ref_time_to_tsc(&self, ref_time: u64) -> u64 {
399        // Use fixed-point multiplication to calculate:
400        // tsc_ticks = (time_100ns /  10_000_000) * tsc_frequency
401        ((ref_time as u128 * self.tsc_scale_100ns) >> 64) as u64
402    }
403
404    /// Returns true if `ref_time` is before `ref_time_last`.
405    ///
406    /// Note that this uses wrapping arithmetic to handle 64-bit timestamp wraparound
407    ///  and hence this is not transitive: if `a` is before `b`, and `b` is before `c`,
408    /// `a` may still appear after `c` if they are too far apart in the circular space.
409    fn is_before(ref_time: u64, ref_time_last: u64) -> bool {
410        let delta = ref_time.wrapping_sub(ref_time_last);
411        (delta as i64) < 0
412    }
413}
414
415impl hardware_cvm::HardwareIsolatedGuestTimer<TdxBacked> for TdxTscDeadlineService {
416    fn is_hardware_virtualized(&self) -> bool {
417        true
418    }
419
420    /// Update the virtual timer deadline in the processor's context shared with kernel.
421    /// This deadline will be set by `mshv_vtl` using
422    /// `TDG.VP.WR(TDVPS.TSC_DEADLINE[L2-VM Index])` before entering into lower VTL.
423    fn update_deadline(
424        &self,
425        vp: &mut UhProcessor<'_, TdxBacked>,
426        ref_time_now: u64,
427        ref_time_next: u64,
428    ) {
429        let vp_state = vp
430            .backing
431            .tsc_deadline_state
432            .as_mut()
433            .expect("TdxTscDeadlineService requires tsc_deadline_state");
434
435        // Update needed only if no deadline is set or the new time is earlier.
436        if vp_state
437            .deadline_100ns
438            .is_none_or(|last| Self::is_before(ref_time_next, last))
439        {
440            // Record the new reference time.
441            vp_state.deadline_100ns = Some(ref_time_next);
442
443            let state = vp.runner.tdx_l2_tsc_deadline_state_mut();
444            if vp_state
445                .last_deadline_100ns
446                .is_none_or(|last| last != ref_time_next)
447            {
448                let ref_time_from_now = ref_time_next.saturating_sub(ref_time_now);
449                let tsc_delta = self.ref_time_to_tsc(ref_time_from_now);
450                let deadline = safe_intrinsics::rdtsc().wrapping_add(tsc_delta);
451
452                state.deadline = deadline;
453                state.update_deadline = 1;
454
455                tracing::trace!(
456                    ref_time_from_now,
457                    tsc_delta,
458                    deadline,
459                    "updating deadline for TDX L2-VM TSC deadline timer"
460                );
461            } else {
462                state.update_deadline = 0;
463            }
464        }
465    }
466
467    /// Clears the virtual timer deadline in the processor context.
468    fn clear_deadline(&self, vp: &mut UhProcessor<'_, TdxBacked>) {
469        let vp_state = vp
470            .backing
471            .tsc_deadline_state
472            .as_mut()
473            .expect("TdxTscDeadlineService requires tsc_deadline_state");
474
475        vp_state.deadline_100ns = None;
476
477        let state = vp.runner.tdx_l2_tsc_deadline_state_mut();
478        state.update_deadline = 0;
479    }
480
481    fn begin_vtl_transition(&self, _vp: &mut UhProcessor<'_, TdxBacked>, _vtl: GuestVtl) {}
482
483    fn end_vtl_transition(&self, vp: &mut UhProcessor<'_, TdxBacked>, _vtl: GuestVtl) {
484        let vp_state = vp
485            .backing
486            .tsc_deadline_state
487            .as_mut()
488            .expect("TdxTscDeadlineService requires tsc_deadline_state");
489
490        vp_state.last_deadline_100ns = vp_state.deadline_100ns;
491    }
492}
493
494/// Per-VP state for TDX L2-VM TSC deadline timer.
495#[derive(Inspect, Default)]
496struct TdxTscDeadline {
497    /// Next deadline to be armed (in 100ns units).
498    #[inspect(hex)]
499    deadline_100ns: Option<u64>,
500    /// Deadline (in 100ns units) armed by `mshv_vtl` driver during previous entry
501    /// into lower VTL.
502    #[inspect(hex)]
503    last_deadline_100ns: Option<u64>,
504}
505
506/// Backing for TDX partitions.
507#[derive(InspectMut)]
508pub struct TdxBacked {
509    #[inspect(mut)]
510    vtls: VtlArray<TdxVtl, 2>,
511
512    untrusted_synic: Option<ProcessorSynic>,
513    #[inspect(hex, iter_by_index)]
514    eoi_exit_bitmap: [u64; 4],
515
516    /// A mapped page used for issuing INVGLA hypercalls.
517    #[inspect(skip)]
518    flush_page: user_driver::memory::MemoryBlock,
519
520    #[inspect(flatten)]
521    cvm: UhCvmVpState,
522
523    /// Per-processor state for [`TdxTscDeadlineService`].
524    #[inspect(flatten)]
525    tsc_deadline_state: Option<TdxTscDeadline>,
526}
527
528#[derive(InspectMut)]
529struct TdxVtl {
530    /// The EFER value for this VP.
531    efer: u64,
532    /// Virtual cr0.
533    cr0: VirtualRegister,
534    /// Virtual cr4.
535    cr4: VirtualRegister,
536
537    // CSTAR doesn't exist on TDX, but Windows likes to verify that values are sticky.
538    msr_cstar: u64,
539
540    tpr_threshold: u8,
541    #[inspect(skip)]
542    processor_controls: ProcessorControls,
543    #[inspect(skip)]
544    interruption_information: InterruptionInformation,
545    exception_error_code: u32,
546    interruption_set: bool,
547
548    #[inspect(mut)]
549    private_regs: TdxPrivateRegs,
550
551    /// TDX only TLB flush state.
552    flush_state: TdxFlushState,
553
554    enter_stats: EnterStats,
555    exit_stats: ExitStats,
556}
557
558#[derive(Default)]
559pub struct TdxEmulationCache {
560    segs: [Option<SegmentRegister>; 6],
561    cr0: Option<u64>,
562}
563
564#[derive(Inspect, Default)]
565struct EnterStats {
566    success: Counter,
567    host_routed_async: Counter,
568    l2_exit_pending_intr: Counter,
569    pending_intr: Counter,
570    host_routed_td_vmcall: Counter,
571}
572
573#[derive(Inspect, Default)]
574struct ExitStats {
575    io: Counter,
576    msr_read: Counter,
577    msr_write: Counter,
578    ept_violation: Counter,
579    cpuid: Counter,
580    cr_access: Counter,
581    xsetbv: Counter,
582    tpr_below_threshold: Counter,
583    interrupt_window: Counter,
584    nmi_window: Counter,
585    vmcall: Counter,
586    smi_intr: Counter,
587    wbinvd: Counter,
588    hw_interrupt: Counter,
589    tdcall: Counter,
590    hlt: Counter,
591    pause: Counter,
592    needs_interrupt_reinject: Counter,
593    exception: Counter,
594    descriptor_table: Counter,
595    timer_expired: Counter,
596}
597
598enum UhDirectOverlay {
599    Sipp,
600    Sifp,
601    Count,
602}
603
604impl HardwareIsolatedBacking for TdxBacked {
605    fn cvm_state(&self) -> &UhCvmVpState {
606        &self.cvm
607    }
608
609    fn cvm_state_mut(&mut self) -> &mut UhCvmVpState {
610        &mut self.cvm
611    }
612
613    fn cvm_partition_state(shared: &Self::Shared) -> &UhCvmPartitionState {
614        &shared.cvm
615    }
616
617    fn switch_vtl(this: &mut UhProcessor<'_, Self>, _source_vtl: GuestVtl, target_vtl: GuestVtl) {
618        // The GPs, Fxsave, and CR2 are saved in the shared kernel state. No copying needed.
619        // Debug registers and XFEM are shared architecturally. No copying needed.
620
621        this.backing.cvm_state_mut().exit_vtl = target_vtl;
622    }
623
624    fn translation_registers(
625        &self,
626        this: &UhProcessor<'_, Self>,
627        vtl: GuestVtl,
628    ) -> TranslationRegisters {
629        let cr0 = this.backing.vtls[vtl].cr0.read(&this.runner);
630        let cr4 = this.backing.vtls[vtl].cr4.read(&this.runner);
631        let efer = this.backing.vtls[vtl].efer;
632        let cr3 = this.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR3);
633        let ss = this.read_segment(vtl, TdxSegmentReg::Ss).into();
634        let rflags = this.backing.vtls[vtl].private_regs.rflags;
635
636        TranslationRegisters {
637            cr0,
638            cr4,
639            efer,
640            cr3,
641            ss,
642            rflags,
643            encryption_mode: this.partition.caps.vtom.map_or(
644                virt_support_x86emu::translate::EncryptionMode::None,
645                virt_support_x86emu::translate::EncryptionMode::Vtom,
646            ),
647        }
648    }
649
650    fn tlb_flush_lock_access<'a>(
651        vp_index: Option<VpIndex>,
652        partition: &'a UhPartitionInner,
653        shared: &'a Self::Shared,
654    ) -> impl TlbFlushLockAccess + 'a {
655        TdxTlbLockFlushAccess {
656            vp_index,
657            partition,
658            shared,
659        }
660    }
661
662    fn pending_event_vector(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> Option<u8> {
663        let event_inject = this.backing.vtls[vtl].interruption_information;
664        if event_inject.valid() {
665            Some(event_inject.vector())
666        } else {
667            None
668        }
669    }
670
671    fn set_pending_exception(
672        this: &mut UhProcessor<'_, Self>,
673        vtl: GuestVtl,
674        event: HvX64PendingExceptionEvent,
675    ) {
676        let new_intr = InterruptionInformation::new()
677            .with_valid(true)
678            .with_deliver_error_code(event.deliver_error_code())
679            .with_vector(event.vector().try_into().unwrap())
680            .with_interruption_type(INTERRUPT_TYPE_HARDWARE_EXCEPTION);
681
682        this.backing.vtls[vtl].interruption_information = new_intr;
683        this.backing.vtls[vtl].exception_error_code = event.error_code();
684    }
685
686    fn cr0(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64 {
687        this.read_cr0(vtl)
688    }
689
690    fn cr4(this: &UhProcessor<'_, Self>, vtl: GuestVtl) -> u64 {
691        this.read_cr4(vtl)
692    }
693
694    fn intercept_message_state(
695        this: &UhProcessor<'_, Self>,
696        vtl: GuestVtl,
697        include_optional_state: bool,
698    ) -> super::InterceptMessageState {
699        let exit = TdxExit(this.runner.tdx_vp_enter_exit_info());
700        let backing_vtl = &this.backing.vtls[vtl];
701        let shared_gps = this.runner.tdx_enter_guest_gps();
702
703        super::InterceptMessageState {
704            instruction_length_and_cr8: exit.instr_info().length() as u8,
705            cpl: exit.cpl(),
706            efer_lma: backing_vtl.efer & X64_EFER_LMA != 0,
707            cs: exit.cs().into(),
708            rip: backing_vtl.private_regs.rip,
709            rflags: backing_vtl.private_regs.rflags,
710            rax: shared_gps[TdxGp::RAX],
711            rdx: shared_gps[TdxGp::RDX],
712            optional: if include_optional_state {
713                Some(super::InterceptMessageOptionalState {
714                    ds: this.read_segment(vtl, TdxSegmentReg::Ds).into(),
715                    es: this.read_segment(vtl, TdxSegmentReg::Es).into(),
716                })
717            } else {
718                None
719            },
720            rcx: shared_gps[TdxGp::RCX],
721            rsi: shared_gps[TdxGp::RSI],
722            rdi: shared_gps[TdxGp::RDI],
723        }
724    }
725
726    fn cr_intercept_registration(
727        this: &mut UhProcessor<'_, Self>,
728        intercept_control: hvdef::HvRegisterCrInterceptControl,
729    ) {
730        // Today we only support intercepting VTL 0 on behalf of VTL 1.
731        let vtl = GuestVtl::Vtl0;
732        let intercept_masks = &this
733            .backing
734            .cvm_state()
735            .vtl1
736            .as_ref()
737            .unwrap()
738            .reg_intercept;
739
740        // Update CR0 and CR4 intercept masks in the VMCS.
741        this.runner.write_vmcs64(
742            vtl,
743            VmcsField::VMX_VMCS_CR0_GUEST_HOST_MASK,
744            !0,
745            this.shared.cr_guest_host_mask(ShadowedRegister::Cr0)
746                | if intercept_control.cr0_write() {
747                    intercept_masks.cr0_mask
748                } else {
749                    0
750                },
751        );
752        this.runner.write_vmcs64(
753            vtl,
754            VmcsField::VMX_VMCS_CR4_GUEST_HOST_MASK,
755            !0,
756            this.shared.cr_guest_host_mask(ShadowedRegister::Cr4)
757                | if intercept_control.cr4_write() {
758                    intercept_masks.cr4_mask
759                } else {
760                    0
761                },
762        );
763
764        // Update descriptor table intercepts.
765        let intercept_tables = intercept_control.gdtr_write()
766            | intercept_control.idtr_write()
767            | intercept_control.ldtr_write()
768            | intercept_control.tr_write();
769        this.runner.write_vmcs32(
770            vtl,
771            VmcsField::VMX_VMCS_SECONDARY_PROCESSOR_CONTROLS,
772            SecondaryProcessorControls::new()
773                .with_descriptor_table_exiting(true)
774                .into_bits(),
775            SecondaryProcessorControls::new()
776                .with_descriptor_table_exiting(intercept_tables)
777                .into_bits(),
778        );
779
780        // Update MSR intercepts. We only need to update those that are allowed
781        // to be passed through, as the default otherwise is to always intercept.
782        // See [`MSR_ALLOWED_READ_WRITE`].
783        this.runner.set_msr_bit(
784            vtl,
785            x86defs::X86X_MSR_S_CET,
786            true,
787            intercept_control.msr_scet_write(),
788        );
789        this.runner.set_msr_bit(
790            vtl,
791            x86defs::X86X_MSR_PL0_SSP,
792            true,
793            intercept_control.msr_pls_ssp_write(),
794        );
795        this.runner.set_msr_bit(
796            vtl,
797            x86defs::X86X_MSR_PL1_SSP,
798            true,
799            intercept_control.msr_pls_ssp_write(),
800        );
801        this.runner.set_msr_bit(
802            vtl,
803            x86defs::X86X_MSR_PL2_SSP,
804            true,
805            intercept_control.msr_pls_ssp_write(),
806        );
807        this.runner.set_msr_bit(
808            vtl,
809            x86defs::X86X_MSR_PL3_SSP,
810            true,
811            intercept_control.msr_pls_ssp_write(),
812        );
813        this.runner.set_msr_bit(
814            vtl,
815            x86defs::X86X_MSR_INTERRUPT_SSP_TABLE_ADDR,
816            true,
817            intercept_control.msr_pls_ssp_write(),
818        );
819    }
820
821    fn is_interrupt_pending(
822        this: &mut UhProcessor<'_, Self>,
823        vtl: GuestVtl,
824        check_rflags: bool,
825        dev: &impl CpuIo,
826    ) -> bool {
827        let backing_vtl = &this.backing.vtls[vtl];
828        if backing_vtl.interruption_information.valid()
829            && backing_vtl.interruption_information.interruption_type() == INTERRUPT_TYPE_NMI
830        {
831            return true;
832        }
833
834        let (vector, ppr) = if this.backing.cvm.lapics[vtl].lapic.is_offloaded() {
835            let vector = backing_vtl.private_regs.rvi;
836            let ppr = std::cmp::max(
837                backing_vtl.private_regs.svi.into(),
838                this.runner.tdx_apic_page(vtl).tpr.value,
839            );
840            (vector, ppr)
841        } else {
842            let lapic = &mut this.backing.cvm.lapics[vtl].lapic;
843            let vector = lapic.next_irr().unwrap_or(0);
844            let ppr = lapic
845                .access(&mut TdxApicClient {
846                    partition: this.partition,
847                    apic_page: this.runner.tdx_apic_page_mut(vtl),
848                    dev,
849                    vmtime: &this.vmtime,
850                    vtl,
851                })
852                .get_ppr();
853            (vector, ppr)
854        };
855        let vector_priority = (vector as u32) >> 4;
856        let ppr_priority = ppr >> 4;
857
858        if vector_priority <= ppr_priority {
859            return false;
860        }
861
862        if check_rflags && !RFlags::from_bits(backing_vtl.private_regs.rflags).interrupt_enable() {
863            return false;
864        }
865
866        let interruptibility: Interruptibility = this
867            .runner
868            .read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
869            .into();
870
871        if interruptibility.blocked_by_sti() || interruptibility.blocked_by_movss() {
872            return false;
873        }
874
875        true
876    }
877
878    fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic> {
879        self.untrusted_synic.as_mut()
880    }
881
882    fn update_deadline(this: &mut UhProcessor<'_, Self>, ref_time_now: u64, next_ref_time: u64) {
883        this.shared
884            .guest_timer
885            .update_deadline(this, ref_time_now, next_ref_time);
886    }
887
888    fn clear_deadline(this: &mut UhProcessor<'_, Self>) {
889        this.shared.guest_timer.clear_deadline(this);
890    }
891}
892
893/// Partition-wide shared data for TDX VPs.
894#[derive(Inspect)]
895pub struct TdxBackedShared {
896    #[inspect(flatten)]
897    pub(crate) cvm: UhCvmPartitionState,
898    /// The synic state used for untrusted SINTs, that is, the SINTs for which
899    /// the guest thinks it is interacting directly with the untrusted
900    /// hypervisor via an architecture-specific interface.
901    pub(crate) untrusted_synic: Option<GlobalSynic>,
902    flush_state: VtlArray<TdxPartitionFlushState, 2>,
903    #[inspect(iter_by_index)]
904    active_vtl: Vec<AtomicU8>,
905    /// CR4 bits that the guest is allowed to set to 1.
906    cr4_allowed_bits: u64,
907    /// Accessor for managing lower VTL timer deadlines.
908    #[inspect(skip)]
909    guest_timer: Box<dyn hardware_cvm::HardwareIsolatedGuestTimer<TdxBacked>>,
910}
911
912impl TdxBackedShared {
913    pub(crate) fn new(
914        partition_params: &UhPartitionNewParams<'_>,
915        params: BackingSharedParams<'_>,
916    ) -> Result<Self, crate::Error> {
917        // Create a second synic to fully manage the untrusted SINTs
918        // here. At time of writing, the hypervisor does not support
919        // sharing the untrusted SINTs with the TDX L1. Even if it did,
920        // performance would be poor for cases where the L1 implements
921        // high-performance devices.
922        let untrusted_synic = (partition_params.handle_synic && !partition_params.hide_isolation)
923            .then(|| GlobalSynic::new(partition_params.topology.vp_count()));
924
925        // TODO TDX: Consider just using MSR kernel module instead of explicit ioctl.
926        let cr4_fixed1 = params.hcl.read_vmx_cr4_fixed1();
927        let cr4_allowed_bits =
928            (ShadowedRegister::Cr4.guest_owned_mask() | X64_CR4_MCE) & cr4_fixed1;
929
930        let cvm = params.cvm_state.unwrap();
931
932        // Configure timer interface for lower VTLs.
933        let guest_timer: Box<dyn hardware_cvm::HardwareIsolatedGuestTimer<TdxBacked>> =
934            match params.lower_vtl_timer_virt_available {
935                true => {
936                    // Use TDX L2-VM TSC deadline timer service. Calculate scale factor
937                    // for fixed-point conversion from 100ns to TSC units.
938                    let tsc_frequency = get_tsc_frequency(IsolationType::Tdx).unwrap();
939                    const NUM_100NS_IN_SEC: u128 = 10_000_000;
940                    let tsc_scale_100ns = ((tsc_frequency as u128) << 64) / NUM_100NS_IN_SEC;
941
942                    tracing::info!(CVM_ALLOWED, "enabling TDX L2-VM TSC deadline timer service");
943
944                    Box::new(TdxTscDeadlineService { tsc_scale_100ns })
945                }
946                false => {
947                    // Fall back to [`VmTime`] interface.
948                    Box::new(hardware_cvm::VmTimeGuestTimer)
949                }
950            };
951
952        Ok(Self {
953            untrusted_synic,
954            flush_state: VtlArray::from_fn(|_| TdxPartitionFlushState::new()),
955            cvm,
956            // VPs start in VTL 2.
957            active_vtl: std::iter::repeat_n(2, partition_params.topology.vp_count() as usize)
958                .map(AtomicU8::new)
959                .collect(),
960            cr4_allowed_bits,
961            guest_timer,
962        })
963    }
964
965    /// Get the default guest host mask for the specified register.
966    fn cr_guest_host_mask(&self, reg: ShadowedRegister) -> u64 {
967        match reg {
968            ShadowedRegister::Cr0 => {
969                !ShadowedRegister::Cr0.guest_owned_mask() | X64_CR0_PE | X64_CR0_PG
970            }
971            ShadowedRegister::Cr4 => {
972                !(ShadowedRegister::Cr4.guest_owned_mask() & self.cr4_allowed_bits)
973            }
974        }
975    }
976}
977
978impl TdxBacked {
979    /// Gets the number of pages that will be allocated from the shared page pool
980    /// for each CPU.
981    pub fn shared_pages_required_per_cpu() -> u64 {
982        UhDirectOverlay::Count as u64
983    }
984}
985
986// The memory used to back the untrusted synic is not guest-visible, but rather
987// is allocated from our shared pool. Therefore it does not need to go through
988// the normal memory protections path.
989struct UntrustedSynicVtlProts<'a>(&'a GuestMemory);
990
991impl hv1_emulator::VtlProtectAccess for UntrustedSynicVtlProts<'_> {
992    fn check_modify_and_lock_overlay_page(
993        &mut self,
994        gpn: u64,
995        _check_perms: hvdef::HvMapGpaFlags,
996        _new_perms: Option<hvdef::HvMapGpaFlags>,
997    ) -> Result<guestmem::LockedPages, HvError> {
998        // Overlay pages are written through the returned locked pages, so lock
999        // them for write.
1000        self.0
1001            .lock_gpns(guestmem::AccessType::Write, false, &[gpn])
1002            .map_err(|_| HvError::OperationFailed)
1003    }
1004
1005    fn unlock_overlay_page(&mut self, _gpn: u64) -> Result<(), HvError> {
1006        Ok(())
1007    }
1008}
1009
1010#[expect(private_interfaces)]
1011impl BackingPrivate for TdxBacked {
1012    type HclBacking<'tdx> = Tdx<'tdx>;
1013    type Shared = TdxBackedShared;
1014    type EmulationCache = TdxEmulationCache;
1015
1016    fn shared(shared: &BackingShared) -> &Self::Shared {
1017        let BackingShared::Tdx(shared) = shared else {
1018            unreachable!()
1019        };
1020        shared
1021    }
1022
1023    fn new(
1024        params: super::BackingParams<'_, '_, Self>,
1025        shared: &TdxBackedShared,
1026    ) -> Result<Self, crate::Error> {
1027        // TODO TDX: ssp is for shadow stack
1028        // TODO TDX: direct overlay like snp?
1029        // TODO TDX: lapic / APIC setup?
1030        // TODO TDX: see ValInitializeVplc
1031        // TODO TDX: XCR_XFMEM setup?
1032
1033        // Turn on MBEC for just VTL 0.
1034        params.runner.write_vmcs32(
1035            GuestVtl::Vtl0,
1036            VmcsField::VMX_VMCS_SECONDARY_PROCESSOR_CONTROLS,
1037            SecondaryProcessorControls::new()
1038                .with_mode_based_execute_control(true)
1039                .into(),
1040            SecondaryProcessorControls::new()
1041                .with_mode_based_execute_control(true)
1042                .into(),
1043        );
1044
1045        let controls = TdxL2Ctls::new()
1046            // Configure L2 controls to permit shared memory.
1047            .with_enable_shared_ept(!shared.cvm.hide_isolation)
1048            // If the synic is to be managed by the hypervisor, then enable TDVMCALLs.
1049            .with_enable_tdvmcall(shared.untrusted_synic.is_none() && !shared.cvm.hide_isolation);
1050
1051        params
1052            .runner
1053            .set_l2_ctls(GuestVtl::Vtl0, controls)
1054            .map_err(crate::Error::FailedToSetL2Ctls)?;
1055
1056        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
1057            // Set guest/host masks for CR0 and CR4. These enable shadowing these
1058            // registers since TDX requires certain bits to be set at all times.
1059            let initial_cr0 = params
1060                .runner
1061                .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR0);
1062            assert_eq!(initial_cr0, X64_CR0_PE | X64_CR0_NE);
1063
1064            // N.B. CR0.PE and CR0.PG are guest owned but still intercept when they
1065            // are changed for caching purposes and to ensure EFER is managed
1066            // properly due to the need to change execution state.
1067            params.runner.write_vmcs64(
1068                vtl,
1069                VmcsField::VMX_VMCS_CR0_READ_SHADOW,
1070                !0,
1071                X64_CR0_PE | X64_CR0_NE,
1072            );
1073            params.runner.write_vmcs64(
1074                vtl,
1075                VmcsField::VMX_VMCS_CR0_GUEST_HOST_MASK,
1076                !0,
1077                shared.cr_guest_host_mask(ShadowedRegister::Cr0),
1078            );
1079
1080            let initial_cr4 = params
1081                .runner
1082                .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR4);
1083            assert_eq!(initial_cr4, X64_CR4_MCE | X64_CR4_VMXE);
1084
1085            params
1086                .runner
1087                .write_vmcs64(vtl, VmcsField::VMX_VMCS_CR4_READ_SHADOW, !0, 0);
1088            params.runner.write_vmcs64(
1089                vtl,
1090                VmcsField::VMX_VMCS_CR4_GUEST_HOST_MASK,
1091                !0,
1092                shared.cr_guest_host_mask(ShadowedRegister::Cr4),
1093            );
1094
1095            // Configure the MSR bitmap for this VP. Since the default MSR bitmap
1096            // is set to intercept everything only the MSRs that we want to allow
1097            // to passthrough need to be set.
1098            for msr in MSR_ALLOWED_READ {
1099                params.runner.set_msr_bit(vtl, *msr, false, false);
1100            }
1101            for msr in MSR_ALLOWED_READ_WRITE {
1102                params.runner.set_msr_bit(vtl, *msr, false, false);
1103                params.runner.set_msr_bit(vtl, *msr, true, false);
1104            }
1105
1106            // Set the exception bitmap.
1107            if params.partition.intercept_debug_exceptions {
1108                if cfg!(feature = "gdb") {
1109                    let initial_exception_bitmap = params
1110                        .runner
1111                        .read_vmcs32(vtl, VmcsField::VMX_VMCS_EXCEPTION_BITMAP);
1112
1113                    let exception_bitmap =
1114                        initial_exception_bitmap | (1 << x86defs::Exception::DEBUG.0);
1115
1116                    params.runner.write_vmcs32(
1117                        vtl,
1118                        VmcsField::VMX_VMCS_EXCEPTION_BITMAP,
1119                        !0,
1120                        exception_bitmap,
1121                    );
1122                } else {
1123                    return Err(super::Error::InvalidDebugConfiguration);
1124                }
1125            }
1126        }
1127
1128        let flush_page = shared
1129            .cvm
1130            .private_dma_client
1131            .allocate_dma_buffer(HV_PAGE_SIZE as usize)
1132            .map_err(crate::Error::AllocateTlbFlushPage)?;
1133
1134        let untrusted_synic = shared
1135            .untrusted_synic
1136            .as_ref()
1137            .map(|synic| synic.add_vp(params.vp_info.base.vp_index));
1138
1139        Ok(Self {
1140            vtls: VtlArray::from_fn(|vtl| {
1141                let vtl: GuestVtl = vtl.try_into().unwrap();
1142                TdxVtl {
1143                    efer: params
1144                        .runner
1145                        .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_EFER),
1146                    cr0: VirtualRegister::new(
1147                        ShadowedRegister::Cr0,
1148                        vtl,
1149                        params
1150                            .runner
1151                            .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR0),
1152                        !0,
1153                    ),
1154                    cr4: VirtualRegister::new(
1155                        ShadowedRegister::Cr4,
1156                        vtl,
1157                        params
1158                            .runner
1159                            .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR4),
1160                        shared.cr4_allowed_bits,
1161                    ),
1162                    msr_cstar: 0,
1163                    tpr_threshold: 0,
1164                    processor_controls: params
1165                        .runner
1166                        .read_vmcs32(vtl, VmcsField::VMX_VMCS_PROCESSOR_CONTROLS)
1167                        .into(),
1168                    interruption_information: Default::default(),
1169                    exception_error_code: 0,
1170                    interruption_set: false,
1171                    flush_state: TdxFlushState::new(),
1172                    private_regs: TdxPrivateRegs::new(vtl),
1173                    enter_stats: Default::default(),
1174                    exit_stats: Default::default(),
1175                }
1176            }),
1177            untrusted_synic,
1178            eoi_exit_bitmap: [0; 4],
1179            flush_page,
1180            cvm: UhCvmVpState::new(
1181                &shared.cvm,
1182                params.partition,
1183                params.vp_info,
1184                UhDirectOverlay::Count as usize,
1185            )?,
1186            tsc_deadline_state: shared
1187                .guest_timer
1188                .is_hardware_virtualized()
1189                .then(TdxTscDeadline::default),
1190        })
1191    }
1192
1193    type StateAccess<'p, 'a>
1194        = UhVpStateAccess<'a, 'p, Self>
1195    where
1196        Self: 'a + 'p,
1197        'p: 'a;
1198
1199    fn access_vp_state<'a, 'p>(
1200        this: &'a mut UhProcessor<'p, Self>,
1201        vtl: GuestVtl,
1202    ) -> Self::StateAccess<'p, 'a> {
1203        UhVpStateAccess::new(this, vtl)
1204    }
1205
1206    fn init(this: &mut UhProcessor<'_, Self>) {
1207        // Configure the synic direct overlays.
1208        // So far, only VTL 0 is using these (for VMBus).
1209        let pfns = &this.backing.cvm.direct_overlay_handle.pfns();
1210        let reg = |gpn| {
1211            u64::from(
1212                HvSynicSimpSiefp::new()
1213                    .with_base_gpn(gpn)
1214                    .with_enabled(true),
1215            )
1216        };
1217
1218        let values: &[(HvX64RegisterName, u64); 2] = &[
1219            (
1220                HvX64RegisterName::Sifp,
1221                reg(pfns[UhDirectOverlay::Sifp as usize]),
1222            ),
1223            (
1224                HvX64RegisterName::Sipp,
1225                reg(pfns[UhDirectOverlay::Sipp as usize]),
1226            ),
1227        ];
1228
1229        let reg_count = if let Some(synic) = &mut this.backing.untrusted_synic {
1230            let prot_access = &mut UntrustedSynicVtlProts(&this.partition.gm[GuestVtl::Vtl0]);
1231
1232            synic
1233                .set_simp(reg(pfns[UhDirectOverlay::Sipp as usize]), prot_access)
1234                .unwrap();
1235            synic
1236                .set_siefp(reg(pfns[UhDirectOverlay::Sifp as usize]), prot_access)
1237                .unwrap();
1238            // Set the SIEFP in the hypervisor so that the hypervisor can
1239            // directly signal synic events. Don't set the SIMP, since the
1240            // message page is owned by the paravisor.
1241            1
1242        } else {
1243            2
1244        };
1245
1246        this.runner
1247            .set_vp_registers_hvcall(Vtl::Vtl0, &values[..reg_count])
1248            .expect("set_vp_registers hypercall for direct overlays should succeed");
1249
1250        // Enable APIC offload by default for VTL 0.
1251        this.set_apic_offload(GuestVtl::Vtl0, true);
1252        this.backing.cvm.lapics[GuestVtl::Vtl0]
1253            .lapic
1254            .enable_offload();
1255
1256        // But disable it for VTL 1.
1257        this.set_apic_offload(GuestVtl::Vtl1, false);
1258
1259        // Initialize registers to the reset state, since this may be different
1260        // than what's on the VMCS and is certainly different than what's in the
1261        // VP enter and private register state (which was mostly zero
1262        // initialized).
1263        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
1264            let registers = Registers::at_reset(&this.partition.caps, &this.inner.vp_info);
1265
1266            let mut state = this.access_state(vtl.into());
1267            state
1268                .set_registers(&registers)
1269                .expect("Resetting to architectural state should succeed");
1270
1271            state.commit().expect("committing state should succeed");
1272        }
1273
1274        // FX regs and XMM registers are zero-initialized by the kernel. Set
1275        // them to the arch default.
1276        *this.runner.fx_state_mut() =
1277            vp::Xsave::at_reset(&this.partition.caps, &this.inner.vp_info).fxsave();
1278    }
1279
1280    async fn run_vp(
1281        this: &mut UhProcessor<'_, Self>,
1282        dev: &impl CpuIo,
1283        _stop: &mut virt::StopVp<'_>,
1284    ) -> Result<(), VpHaltReason> {
1285        this.run_vp_tdx(dev).await
1286    }
1287
1288    fn poll_apic(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl, scan_irr: bool) {
1289        if !this.try_poll_apic(vtl, scan_irr) {
1290            tracing::info!(CVM_ALLOWED, "disabling APIC offload due to auto EOI");
1291            let page = this.runner.tdx_apic_page_mut(vtl);
1292            let (irr, isr) = pull_apic_offload(page);
1293
1294            this.backing.cvm.lapics[vtl]
1295                .lapic
1296                .disable_offload(&irr, &isr);
1297            this.set_apic_offload(vtl, false);
1298            this.try_poll_apic(vtl, false);
1299        }
1300    }
1301
1302    fn request_extint_readiness(_this: &mut UhProcessor<'_, Self>) {
1303        unreachable!("extint managed through software apic")
1304    }
1305
1306    fn request_untrusted_sint_readiness(this: &mut UhProcessor<'_, Self>, sints: u16) {
1307        if let Some(synic) = &mut this.backing.untrusted_synic {
1308            synic.request_sint_readiness(sints);
1309        } else {
1310            tracelimit::error_ratelimited!(CVM_ALLOWED, "untrusted synic is not configured");
1311        }
1312    }
1313
1314    fn hv(&self, vtl: GuestVtl) -> Option<&ProcessorVtlHv> {
1315        Some(&self.cvm.hv[vtl])
1316    }
1317
1318    fn hv_mut(&mut self, vtl: GuestVtl) -> Option<&mut ProcessorVtlHv> {
1319        Some(&mut self.cvm.hv[vtl])
1320    }
1321
1322    fn handle_vp_start_enable_vtl_wake(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl) {
1323        this.hcvm_handle_vp_start_enable_vtl(vtl)
1324    }
1325
1326    fn vtl1_inspectable(this: &UhProcessor<'_, Self>) -> bool {
1327        this.hcvm_vtl1_inspectable()
1328    }
1329
1330    fn process_interrupts(
1331        this: &mut UhProcessor<'_, Self>,
1332        scan_irr: VtlArray<bool, 2>,
1333        first_scan_irr: &mut bool,
1334        dev: &impl CpuIo,
1335    ) -> bool {
1336        this.cvm_process_interrupts(scan_irr, first_scan_irr, dev)
1337    }
1338}
1339
1340impl UhProcessor<'_, TdxBacked> {
1341    /// Returns `Ok(false)` if the APIC offload needs to be disabled and the
1342    /// poll retried.
1343    fn try_poll_apic(&mut self, vtl: GuestVtl, scan_irr: bool) -> bool {
1344        let mut scan = TdxApicScanner {
1345            processor_controls: self.backing.vtls[vtl]
1346                .processor_controls
1347                .with_nmi_window_exiting(false)
1348                .with_interrupt_window_exiting(false),
1349            vp: self,
1350            tpr_threshold: 0,
1351        };
1352
1353        // TODO TDX: filter proxy IRRs by setting the `proxy_irr_blocked` field of the run page
1354        hardware_cvm::apic::poll_apic_core(&mut scan, vtl, scan_irr);
1355
1356        let TdxApicScanner {
1357            vp: _,
1358            processor_controls: new_processor_controls,
1359            tpr_threshold: new_tpr_threshold,
1360        } = scan;
1361
1362        // Interrupts are ignored while waiting for SIPI.
1363        if self.backing.cvm.lapics[vtl].activity != MpState::WaitForSipi
1364            && self.backing.vtls[vtl].tpr_threshold != new_tpr_threshold
1365        {
1366            tracing::trace!(new_tpr_threshold, ?vtl, "setting tpr threshold");
1367            self.runner.write_vmcs32(
1368                vtl,
1369                VmcsField::VMX_VMCS_TPR_THRESHOLD,
1370                !0,
1371                new_tpr_threshold.into(),
1372            );
1373            self.backing.vtls[vtl].tpr_threshold = new_tpr_threshold;
1374        }
1375
1376        if self.backing.vtls[vtl].processor_controls != new_processor_controls {
1377            tracing::trace!(?new_processor_controls, ?vtl, "requesting window change");
1378            self.runner.write_vmcs32(
1379                vtl,
1380                VmcsField::VMX_VMCS_PROCESSOR_CONTROLS,
1381                !0,
1382                new_processor_controls.into(),
1383            );
1384            self.backing.vtls[vtl].processor_controls = new_processor_controls;
1385        }
1386
1387        // Offloading and proxying is only done with VTL 0 today.
1388        if vtl == GuestVtl::Vtl0 {
1389            let mut update_rvi = false;
1390            let r: Result<(), OffloadNotSupported> = self.backing.cvm.lapics[vtl]
1391                .lapic
1392                .push_to_offload(|irr, isr, tmr| {
1393                    let apic_page = self.runner.tdx_apic_page_mut(vtl);
1394
1395                    for (((irr, page_irr), isr), page_isr) in irr
1396                        .iter()
1397                        .zip(&mut apic_page.irr)
1398                        .zip(isr)
1399                        .zip(&mut apic_page.isr)
1400                    {
1401                        page_irr.value |= *irr;
1402                        page_isr.value |= *isr;
1403                    }
1404
1405                    // Update SVI and RVI.
1406                    let svi = top_vector(&apic_page.isr);
1407                    self.backing.vtls[vtl].private_regs.svi = svi;
1408                    update_rvi = true;
1409
1410                    // Ensure the EOI exit bitmap is up to date.
1411                    let fields = [
1412                        VmcsField::VMX_VMCS_EOI_EXIT_0,
1413                        VmcsField::VMX_VMCS_EOI_EXIT_1,
1414                        VmcsField::VMX_VMCS_EOI_EXIT_2,
1415                        VmcsField::VMX_VMCS_EOI_EXIT_3,
1416                    ];
1417                    for ((&field, eoi_exit), (i, tmr)) in fields
1418                        .iter()
1419                        .zip(&mut self.backing.eoi_exit_bitmap)
1420                        .zip(tmr.chunks_exact(2).enumerate())
1421                    {
1422                        let tmr = tmr[0] as u64 | ((tmr[1] as u64) << 32);
1423                        if *eoi_exit != tmr {
1424                            self.runner.write_vmcs64(vtl, field, !0, tmr);
1425                            *eoi_exit = tmr;
1426                            // The kernel driver supports some common APIC functionality (ICR writes,
1427                            // interrupt injection). When the kernel driver handles an interrupt, it
1428                            // must know if that interrupt was previously level-triggered. Otherwise,
1429                            // the EOI will be incorrectly treated as level-triggered. We keep a copy
1430                            // of the tmr in the kernel so it knows when this scenario occurs.
1431                            self.runner.proxy_irr_exit_mut_vtl0()[i * 2] = tmr as u32;
1432                            self.runner.proxy_irr_exit_mut_vtl0()[i * 2 + 1] = (tmr >> 32) as u32;
1433                        }
1434                    }
1435                });
1436
1437            if let Err(OffloadNotSupported) = r {
1438                // APIC needs offloading to be disabled to support auto-EOI. The caller
1439                // will disable offload and try again.
1440                return false;
1441            }
1442
1443            if update_rvi {
1444                let page = self.runner.tdx_apic_page_mut(vtl);
1445                let rvi = top_vector(&page.irr);
1446                self.backing.vtls[vtl].private_regs.rvi = rvi;
1447            }
1448        }
1449
1450        // If there is a pending interrupt, clear the halted and idle state.
1451        if (self.backing.cvm.lapics[vtl].activity != MpState::Running)
1452            && self.backing.cvm.lapics[vtl].lapic.is_offloaded()
1453            && self.backing.vtls[vtl].private_regs.rvi != 0
1454        {
1455            // To model a non-virtualized processor, we should only do this if
1456            // TPR and IF and interrupt shadow allow. However, fetching the
1457            // interrupt shadow state is expensive (tdcall). This shouldn't
1458            // matter much, because real guests don't issue hlt while in
1459            // interrupt shadow or with interrupts disabled or with a non-zero
1460            // TPR.
1461            //
1462            // Note that the processor will not actually inject the interrupt
1463            // until conditions hold. So, unless the guest fails to loop around
1464            // and hlt again (which we already treat as a guest bug, since
1465            // Hyper-V in general does not guarantee hlt will stick until an
1466            // interrupt is pending), at worst this will just burn some CPU.
1467            self.backing.cvm.lapics[vtl].activity = MpState::Running;
1468        }
1469
1470        true
1471    }
1472
1473    fn access_apic_without_offload<R>(
1474        &mut self,
1475        vtl: GuestVtl,
1476        f: impl FnOnce(&mut Self) -> R,
1477    ) -> R {
1478        let offloaded = self.backing.cvm.lapics[vtl].lapic.is_offloaded();
1479        if offloaded {
1480            let (irr, isr) = pull_apic_offload(self.runner.tdx_apic_page_mut(vtl));
1481            self.backing.cvm.lapics[vtl]
1482                .lapic
1483                .disable_offload(&irr, &isr);
1484        }
1485        let r = f(self);
1486        if offloaded {
1487            self.backing.cvm.lapics[vtl].lapic.enable_offload();
1488        }
1489        r
1490    }
1491
1492    fn set_apic_offload(&mut self, vtl: GuestVtl, offload: bool) {
1493        // Update the APIC portion of the MSR bitmap.
1494        let offload_bitmap = if offload {
1495            (1 << x86defs::apic::ApicRegister::TPR.0)
1496                | (1 << x86defs::apic::ApicRegister::EOI.0)
1497                | (1 << x86defs::apic::ApicRegister::SELF_IPI.0)
1498        } else {
1499            0
1500        };
1501        // Once for read and once for write.
1502        for offset in [0, 0x100] {
1503            self.runner
1504                .write_msr_bitmap(vtl, offset + X2APIC_MSR_BASE / 64, !0, !offload_bitmap);
1505        }
1506
1507        // Update virtual-interrupt delivery.
1508        self.runner.write_vmcs32(
1509            vtl,
1510            VmcsField::VMX_VMCS_SECONDARY_PROCESSOR_CONTROLS,
1511            SecondaryProcessorControls::new()
1512                .with_virtual_interrupt_delivery(true)
1513                .into(),
1514            SecondaryProcessorControls::new()
1515                .with_virtual_interrupt_delivery(offload)
1516                .into(),
1517        );
1518
1519        // Clear any pending external interrupt when enabling the APIC offload.
1520        if offload
1521            && self.backing.vtls[vtl]
1522                .interruption_information
1523                .interruption_type()
1524                == INTERRUPT_TYPE_EXTERNAL
1525        {
1526            self.backing.vtls[vtl]
1527                .interruption_information
1528                .set_valid(false);
1529        }
1530    }
1531}
1532
1533struct TdxApicScanner<'a, 'b> {
1534    vp: &'a mut UhProcessor<'b, TdxBacked>,
1535    processor_controls: ProcessorControls,
1536    tpr_threshold: u8,
1537}
1538
1539impl<'b> hardware_cvm::apic::ApicBacking<'b, TdxBacked> for TdxApicScanner<'_, 'b> {
1540    fn vp(&mut self) -> &mut UhProcessor<'b, TdxBacked> {
1541        self.vp
1542    }
1543
1544    fn handle_interrupt(&mut self, vtl: GuestVtl, vector: u8) {
1545        // Exit idle when an interrupt is received, regardless of IF
1546        if self.vp.backing.cvm.lapics[vtl].activity == MpState::Idle {
1547            self.vp.backing.cvm.lapics[vtl].activity = MpState::Running;
1548        }
1549        // If there is a higher-priority pending event of some kind, then
1550        // just request an exit after it has resolved, after which we will
1551        // try again.
1552        if self.vp.backing.vtls[vtl].interruption_information.valid()
1553            && self.vp.backing.vtls[vtl]
1554                .interruption_information
1555                .interruption_type()
1556                != INTERRUPT_TYPE_EXTERNAL
1557        {
1558            self.processor_controls.set_interrupt_window_exiting(true);
1559            return;
1560        }
1561
1562        // Ensure the interrupt is not blocked by RFLAGS.IF or interrupt shadow.
1563        let interruptibility: Interruptibility = self
1564            .vp
1565            .runner
1566            .read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
1567            .into();
1568
1569        let rflags = RFlags::from(self.vp.backing.vtls[vtl].private_regs.rflags);
1570        if !rflags.interrupt_enable()
1571            || interruptibility.blocked_by_sti()
1572            || interruptibility.blocked_by_movss()
1573        {
1574            self.processor_controls.set_interrupt_window_exiting(true);
1575            return;
1576        }
1577
1578        let priority = vector >> 4;
1579        let apic = self.vp.runner.tdx_apic_page(vtl);
1580        if (apic.tpr.value as u8 >> 4) >= priority {
1581            self.tpr_threshold = priority;
1582            return;
1583        }
1584
1585        self.vp.backing.vtls[vtl].interruption_information = InterruptionInformation::new()
1586            .with_valid(true)
1587            .with_vector(vector)
1588            .with_interruption_type(INTERRUPT_TYPE_EXTERNAL);
1589
1590        self.vp.backing.cvm.lapics[vtl].activity = MpState::Running;
1591    }
1592
1593    fn handle_nmi(&mut self, vtl: GuestVtl) {
1594        // Exit idle when an interrupt is received, regardless of IF
1595        // TODO: Investigate lifting more activity management into poll_apic_core
1596        if self.vp.backing.cvm.lapics[vtl].activity == MpState::Idle {
1597            self.vp.backing.cvm.lapics[vtl].activity = MpState::Running;
1598        }
1599        // If there is a higher-priority pending event of some kind, then
1600        // just request an exit after it has resolved, after which we will
1601        // try again.
1602        if self.vp.backing.vtls[vtl].interruption_information.valid()
1603            && self.vp.backing.vtls[vtl]
1604                .interruption_information
1605                .interruption_type()
1606                != INTERRUPT_TYPE_EXTERNAL
1607        {
1608            self.processor_controls.set_nmi_window_exiting(true);
1609            return;
1610        }
1611
1612        let interruptibility: Interruptibility = self
1613            .vp
1614            .runner
1615            .read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
1616            .into();
1617
1618        if interruptibility.blocked_by_nmi()
1619            || interruptibility.blocked_by_sti()
1620            || interruptibility.blocked_by_movss()
1621        {
1622            self.processor_controls.set_nmi_window_exiting(true);
1623            return;
1624        }
1625
1626        self.vp.backing.vtls[vtl].interruption_information = InterruptionInformation::new()
1627            .with_valid(true)
1628            .with_vector(2)
1629            .with_interruption_type(INTERRUPT_TYPE_NMI);
1630
1631        self.vp.backing.cvm.lapics[vtl].activity = MpState::Running;
1632    }
1633
1634    fn handle_sipi(&mut self, vtl: GuestVtl, cs: SegmentRegister) {
1635        self.vp.write_segment(vtl, TdxSegmentReg::Cs, cs).unwrap();
1636        self.vp.backing.vtls[vtl].private_regs.rip = 0;
1637        self.vp.backing.cvm.lapics[vtl].activity = MpState::Running;
1638    }
1639}
1640
1641impl UhProcessor<'_, TdxBacked> {
1642    async fn run_vp_tdx(&mut self, dev: &impl CpuIo) -> Result<(), VpHaltReason> {
1643        let next_vtl = self.backing.cvm.exit_vtl;
1644
1645        if self.backing.vtls[next_vtl].interruption_information.valid() {
1646            tracing::trace!(
1647                vector = self.backing.vtls[next_vtl]
1648                    .interruption_information
1649                    .vector(),
1650                vp_index = self.vp_index().index(),
1651                ?next_vtl,
1652                "injecting interrupt"
1653            );
1654
1655            self.runner.write_vmcs32(
1656                next_vtl,
1657                VmcsField::VMX_VMCS_ENTRY_INTERRUPT_INFO,
1658                !0,
1659                self.backing.vtls[next_vtl].interruption_information.into(),
1660            );
1661            if self.backing.vtls[next_vtl]
1662                .interruption_information
1663                .deliver_error_code()
1664            {
1665                self.runner.write_vmcs32(
1666                    next_vtl,
1667                    VmcsField::VMX_VMCS_ENTRY_EXCEPTION_ERROR_CODE,
1668                    !0,
1669                    self.backing.vtls[next_vtl].exception_error_code,
1670                );
1671            }
1672            self.backing.vtls[next_vtl].interruption_set = true;
1673        } else if self.backing.vtls[next_vtl].interruption_set {
1674            self.runner
1675                .write_vmcs32(next_vtl, VmcsField::VMX_VMCS_ENTRY_INTERRUPT_INFO, !0, 0);
1676            self.backing.vtls[next_vtl].interruption_set = false;
1677        }
1678
1679        // We're about to return to a lower VTL, so set active_vtl for other VPs,
1680        // do any pending flushes, unlock our TLB locks, and wait for any others
1681        // we're supposed to.
1682
1683        // active_vtl needs SeqCst ordering here in order to correctly synchronize
1684        // access with the TLB address flush list. We need to ensure that, when
1685        // other VPs are adding entries to the list, they always observe the
1686        // correct lower active VTL. Otherwise they might choose to not send this
1687        // VP a wake, leading to a stall, until this VP happens to exit to VTL 2 again.
1688        //
1689        // This does technically leave open a small window for potential spurious
1690        // wakes, but that's preferable, and will cause no problems besides a
1691        // small amount of time waste.
1692        self.shared.active_vtl[self.vp_index().index() as usize]
1693            .store(next_vtl as u8, Ordering::SeqCst);
1694
1695        self.do_tlb_flush(next_vtl);
1696        self.unlock_tlb_lock(Vtl::Vtl2);
1697        let tlb_halt = self.should_halt_for_tlb_unlock(next_vtl);
1698
1699        // If we are halted in the kernel due to hlt or idle, and we receive an interrupt
1700        // we'd like to unhalt, inject the interrupt, and resume vtl0 without returning to
1701        // user-mode. To enable this, the kernel must know why are are halted
1702        let activity = self.backing.cvm.lapics[next_vtl].activity;
1703        let kernel_known_state =
1704            matches!(activity, MpState::Running | MpState::Halted | MpState::Idle);
1705        let halted_other = tlb_halt || !kernel_known_state;
1706
1707        self.runner
1708            .set_halted(activity != MpState::Running || tlb_halt);
1709
1710        // Turn on kernel interrupt handling if possible. This will cause the
1711        // kernel to handle some exits internally, without returning to user
1712        // mode, to improve performance.
1713        //
1714        // Do not do this if there is a pending interruption, since we need to
1715        // run code on the next exit to clear it. If we miss this opportunity,
1716        // we will probably double-inject the interruption, wreaking havoc.
1717        //
1718        // Also do not do this if there is a pending TLB flush, since we need to
1719        // run code on the next exit to clear it. If we miss this opportunity,
1720        // we could double-inject the TLB flush unnecessarily.
1721        let offload_enabled = self.backing.cvm.lapics[next_vtl].lapic.can_offload_irr()
1722            && !self.backing.vtls[next_vtl].interruption_information.valid()
1723            && self.backing.vtls[next_vtl]
1724                .private_regs
1725                .vp_entry_flags
1726                .invd_translations()
1727                == 0;
1728        let x2apic_enabled = self.backing.cvm.lapics[next_vtl].lapic.x2apic_enabled();
1729
1730        let offload_flags = hcl_intr_offload_flags::new()
1731            .with_offload_intr_inject(offload_enabled)
1732            .with_offload_x2apic(offload_enabled && x2apic_enabled)
1733            .with_halted_other(halted_other)
1734            .with_halted_hlt(activity == MpState::Halted)
1735            .with_halted_idle(activity == MpState::Idle);
1736
1737        *self.runner.offload_flags_mut() = offload_flags;
1738
1739        self.shared.guest_timer.begin_vtl_transition(self, next_vtl);
1740
1741        self.runner
1742            .write_private_regs(&self.backing.vtls[next_vtl].private_regs);
1743
1744        let has_intercept = self
1745            .runner
1746            .run()
1747            .map_err(|e| dev.fatal_error(TdxRunVpError(e).into()))?;
1748
1749        // TLB flushes can only target lower VTLs, so it is fine to use a relaxed
1750        // ordering here. The worst that can happen is some spurious wakes, due
1751        // to another VP observing that this VP is still in a lower VTL.
1752        self.shared.active_vtl[self.vp_index().index() as usize].store(2, Ordering::Relaxed);
1753
1754        let entered_from_vtl = next_vtl;
1755        self.runner
1756            .read_private_regs(&mut self.backing.vtls[entered_from_vtl].private_regs);
1757
1758        self.shared
1759            .guest_timer
1760            .end_vtl_transition(self, entered_from_vtl);
1761
1762        // Kernel offload may have set or cleared the halt/idle states
1763        if offload_enabled && kernel_known_state {
1764            let offload_flags = self.runner.offload_flags_mut();
1765
1766            self.backing.cvm.lapics[entered_from_vtl].activity =
1767                match (offload_flags.halted_hlt(), offload_flags.halted_idle()) {
1768                    (false, false) => MpState::Running,
1769                    (true, false) => MpState::Halted,
1770                    (false, true) => MpState::Idle,
1771                    (true, true) => {
1772                        tracelimit::warn_ratelimited!(
1773                            CVM_ALLOWED,
1774                            "Kernel indicates VP is both halted and idle!"
1775                        );
1776                        activity
1777                    }
1778                };
1779        }
1780
1781        if !has_intercept {
1782            return Ok(());
1783        }
1784
1785        let exit_info = TdxExit(self.runner.tdx_vp_enter_exit_info());
1786
1787        // Result codes above PENDING_INTERRUPT indicate the L2 was never entered.
1788        if exit_info.code().tdx_exit() >= TdCallResultCode::PENDING_INTERRUPT {
1789            self.backing.vtls[entered_from_vtl]
1790                .enter_stats
1791                .pending_intr
1792                .increment();
1793            return Ok(());
1794        }
1795
1796        // Since the L2 was entered we can clear any TLB flush requests
1797        self.backing.vtls[entered_from_vtl]
1798            .private_regs
1799            .vp_entry_flags
1800            .set_invd_translations(0);
1801
1802        // The L2 was entered, so process the exit.
1803        let stat = match exit_info.code().tdx_exit() {
1804            TdCallResultCode::SUCCESS => {
1805                &mut self.backing.vtls[entered_from_vtl].enter_stats.success
1806            }
1807            TdCallResultCode::L2_EXIT_HOST_ROUTED_ASYNC => {
1808                &mut self.backing.vtls[entered_from_vtl]
1809                    .enter_stats
1810                    .host_routed_async
1811            }
1812            TdCallResultCode::L2_EXIT_PENDING_INTERRUPT => {
1813                &mut self.backing.vtls[entered_from_vtl]
1814                    .enter_stats
1815                    .l2_exit_pending_intr
1816            }
1817            TdCallResultCode::L2_EXIT_HOST_ROUTED_TDVMCALL => {
1818                // This is expected, and means that the hypervisor completed a
1819                // TD.VMCALL from the L2 and has requested to resume the L2 to
1820                // the L1.
1821                //
1822                // There is nothing to do here.
1823                assert_eq!(
1824                    exit_info.code().vmx_exit(),
1825                    VmxExit::new().with_basic_reason(VmxExitBasic::TDCALL)
1826                );
1827                &mut self.backing.vtls[entered_from_vtl]
1828                    .enter_stats
1829                    .host_routed_td_vmcall
1830            }
1831            _ => panic!("unexpected tdx exit code {:?}", exit_info.code()),
1832        };
1833
1834        stat.increment();
1835        self.handle_vmx_exit(dev, entered_from_vtl).await?;
1836        Ok(())
1837    }
1838
1839    async fn handle_vmx_exit(
1840        &mut self,
1841        dev: &impl CpuIo,
1842        intercepted_vtl: GuestVtl,
1843    ) -> Result<(), VpHaltReason> {
1844        let exit_info = TdxExit(self.runner.tdx_vp_enter_exit_info());
1845
1846        // First, check that the VM entry was even successful.
1847        let vmx_exit = exit_info.code().vmx_exit();
1848        if vmx_exit.vm_enter_failed() {
1849            return Err(self.handle_vm_enter_failed(dev, intercepted_vtl, vmx_exit));
1850        }
1851
1852        let next_interruption = exit_info.idt_vectoring_info();
1853
1854        // Acknowledge the APIC interrupt/NMI if it was delivered.
1855        if self.backing.vtls[intercepted_vtl]
1856            .interruption_information
1857            .valid()
1858            && (!next_interruption.valid()
1859                || self.backing.vtls[intercepted_vtl]
1860                    .interruption_information
1861                    .interruption_type()
1862                    != next_interruption.interruption_type())
1863        {
1864            match self.backing.vtls[intercepted_vtl]
1865                .interruption_information
1866                .interruption_type()
1867            {
1868                INTERRUPT_TYPE_EXTERNAL
1869                    if !self.backing.cvm.lapics[intercepted_vtl]
1870                        .lapic
1871                        .is_offloaded() =>
1872                {
1873                    // This must be a pending APIC interrupt. Acknowledge it.
1874                    tracing::trace!(
1875                        vector = self.backing.vtls[intercepted_vtl]
1876                            .interruption_information
1877                            .vector(),
1878                        "acknowledging interrupt"
1879                    );
1880                    self.backing.cvm.lapics[intercepted_vtl]
1881                        .lapic
1882                        .acknowledge_interrupt(
1883                            self.backing.vtls[intercepted_vtl]
1884                                .interruption_information
1885                                .vector(),
1886                        );
1887                }
1888                INTERRUPT_TYPE_NMI => {
1889                    // This must be a pending NMI.
1890                    tracing::debug!("acknowledging NMI");
1891                    self.backing.cvm.lapics[intercepted_vtl].nmi_pending = false;
1892                }
1893                _ => {}
1894            }
1895        }
1896
1897        if self.backing.cvm.lapics[intercepted_vtl]
1898            .lapic
1899            .is_offloaded()
1900        {
1901            // It's possible with vAPIC that we take an exit in the window where
1902            // hardware has moved a bit from IRR to ISR, but has not injected
1903            // the interrupt into the guest. In this case, we need to track that
1904            // we must inject the interrupt before we return to the guest,
1905            // otherwise the interrupt will be lost and the guest left in a bad
1906            // state.
1907            //
1908            // TODO TDX: Unclear what kind of exits these would be, but they
1909            // should be spurious EPT exits. Can we validate or assert that
1910            // somehow? If we were to somehow call some other path which would
1911            // set interruption_information before we inject this one, we would
1912            // lose this interrupt.
1913            if next_interruption.valid() {
1914                tracing::debug!(
1915                    ?next_interruption,
1916                    vp_index = self.vp_index().index(),
1917                    "exit requires reinjecting interrupt"
1918                );
1919                self.backing.vtls[intercepted_vtl].interruption_information = next_interruption;
1920                self.backing.vtls[intercepted_vtl].exception_error_code =
1921                    exit_info.idt_vectoring_error_code();
1922                self.backing.vtls[intercepted_vtl]
1923                    .exit_stats
1924                    .needs_interrupt_reinject
1925                    .increment();
1926            } else {
1927                self.backing.vtls[intercepted_vtl].interruption_information = Default::default();
1928            }
1929        } else {
1930            // Ignore (and later recalculate) the next interruption if it is an
1931            // external interrupt or NMI, since it may change if the APIC state
1932            // changes.
1933            if next_interruption.valid()
1934                && !matches!(
1935                    next_interruption.interruption_type(),
1936                    INTERRUPT_TYPE_EXTERNAL | INTERRUPT_TYPE_NMI
1937                )
1938            {
1939                self.backing.vtls[intercepted_vtl].interruption_information = next_interruption;
1940                self.backing.vtls[intercepted_vtl].exception_error_code =
1941                    exit_info.idt_vectoring_error_code();
1942            } else {
1943                self.backing.vtls[intercepted_vtl].interruption_information = Default::default();
1944            }
1945        }
1946
1947        let mut breakpoint_debug_exception = false;
1948        let stat = match vmx_exit.basic_reason() {
1949            VmxExitBasic::IO_INSTRUCTION => {
1950                let io_qual = ExitQualificationIo::from(exit_info.qualification() as u32);
1951
1952                let len = match io_qual.access_size() {
1953                    IO_SIZE_8_BIT => 1,
1954                    IO_SIZE_16_BIT => 2,
1955                    IO_SIZE_32_BIT => 4,
1956                    _ => panic!(
1957                        "tdx module returned invalid io instr size {}",
1958                        io_qual.access_size()
1959                    ),
1960                };
1961
1962                let port_access_protected = self.cvm_try_protect_io_port_access(
1963                    intercepted_vtl,
1964                    io_qual.port(),
1965                    io_qual.is_in(),
1966                    len,
1967                    io_qual.is_string(),
1968                    io_qual.rep_prefix(),
1969                );
1970
1971                if !port_access_protected {
1972                    if io_qual.is_string() || io_qual.rep_prefix() {
1973                        // TODO GUEST VSM: consider changing the emulation path
1974                        // to also check for io port installation, mainly for
1975                        // handling rep instructions.
1976
1977                        self.emulate(
1978                            dev,
1979                            self.backing.vtls[intercepted_vtl]
1980                                .interruption_information
1981                                .valid(),
1982                            intercepted_vtl,
1983                            TdxEmulationCache::default(),
1984                        )
1985                        .await?;
1986                    } else {
1987                        let mut rax = self.runner.tdx_enter_guest_gps()[TdxGp::RAX];
1988                        emulate_io(
1989                            self.inner.vp_info.base.vp_index,
1990                            !io_qual.is_in(),
1991                            io_qual.port(),
1992                            &mut rax,
1993                            len,
1994                            dev,
1995                        )
1996                        .await;
1997                        self.runner.tdx_enter_guest_gps_mut()[TdxGp::RAX] = rax;
1998
1999                        self.advance_to_next_instruction(intercepted_vtl);
2000                    }
2001                }
2002
2003                &mut self.backing.vtls[intercepted_vtl].exit_stats.io
2004            }
2005            VmxExitBasic::MSR_READ => {
2006                let msr = self.runner.tdx_enter_guest_gps()[TdxGp::RCX] as u32;
2007
2008                let result = self.backing.cvm.lapics[intercepted_vtl]
2009                    .lapic
2010                    .access(&mut TdxApicClient {
2011                        partition: self.partition,
2012                        vmtime: &self.vmtime,
2013                        apic_page: self.runner.tdx_apic_page_mut(intercepted_vtl),
2014                        dev,
2015                        vtl: intercepted_vtl,
2016                    })
2017                    .msr_read(msr)
2018                    .or_else_if_unknown(|| self.read_msr_cvm(msr, intercepted_vtl))
2019                    .or_else_if_unknown(|| self.read_msr_tdx(msr, intercepted_vtl));
2020
2021                let value = match result {
2022                    Ok(v) => Some(v),
2023                    Err(MsrError::Unknown) => {
2024                        tracelimit::warn_ratelimited!(CVM_ALLOWED, msr, "unknown tdx vm msr read");
2025                        Some(0)
2026                    }
2027                    Err(MsrError::InvalidAccess) => None,
2028                };
2029
2030                let inject_gp = if let Some(value) = value {
2031                    let gps = self.runner.tdx_enter_guest_gps_mut();
2032                    gps[TdxGp::RAX] = (value as u32).into();
2033                    gps[TdxGp::RDX] = ((value >> 32) as u32).into();
2034                    false
2035                } else {
2036                    true
2037                };
2038
2039                if inject_gp {
2040                    self.inject_gpf(intercepted_vtl);
2041                } else {
2042                    self.advance_to_next_instruction(intercepted_vtl);
2043                }
2044                &mut self.backing.vtls[intercepted_vtl].exit_stats.msr_read
2045            }
2046            VmxExitBasic::MSR_WRITE => {
2047                let gps = self.runner.tdx_enter_guest_gps();
2048                let msr = gps[TdxGp::RCX] as u32;
2049                let value =
2050                    (gps[TdxGp::RAX] as u32 as u64) | ((gps[TdxGp::RDX] as u32 as u64) << 32);
2051
2052                if !self.cvm_try_protect_msr_write(intercepted_vtl, msr) {
2053                    let result = self.backing.cvm.lapics[intercepted_vtl]
2054                        .lapic
2055                        .access(&mut TdxApicClient {
2056                            partition: self.partition,
2057                            vmtime: &self.vmtime,
2058                            apic_page: self.runner.tdx_apic_page_mut(intercepted_vtl),
2059                            dev,
2060                            vtl: intercepted_vtl,
2061                        })
2062                        .msr_write(msr, value)
2063                        .or_else_if_unknown(|| self.write_msr_cvm(msr, value, intercepted_vtl))
2064                        .or_else_if_unknown(|| self.write_msr_tdx(msr, value, intercepted_vtl))
2065                        .or_else_if_unknown(|| {
2066                            // Sanity check
2067                            if MSR_ALLOWED_READ_WRITE.contains(&msr) {
2068                                unreachable!("intercepted a write to MSR {msr}, configured for passthrough by default, that wasn't registered for intercepts by a higher VTL");
2069                            }
2070                            Err(MsrError::Unknown)
2071                        });
2072
2073                    let inject_gp = match result {
2074                        Ok(()) => false,
2075                        Err(MsrError::Unknown) => {
2076                            tracelimit::warn_ratelimited!(
2077                                CVM_ALLOWED,
2078                                msr,
2079                                "unknown tdx vm msr write"
2080                            );
2081                            tracelimit::warn_ratelimited!(
2082                                CVM_CONFIDENTIAL,
2083                                value,
2084                                "unknown tdx vm msr write"
2085                            );
2086                            false
2087                        }
2088                        Err(MsrError::InvalidAccess) => true,
2089                    };
2090
2091                    if inject_gp {
2092                        self.inject_gpf(intercepted_vtl);
2093                    } else {
2094                        self.advance_to_next_instruction(intercepted_vtl);
2095                    }
2096                }
2097                &mut self.backing.vtls[intercepted_vtl].exit_stats.msr_write
2098            }
2099            VmxExitBasic::CPUID => {
2100                let gps = self.runner.tdx_enter_guest_gps();
2101                let leaf = gps[TdxGp::RAX] as u32;
2102                let subleaf = gps[TdxGp::RCX] as u32;
2103                let [eax, ebx, ecx, edx] = self.cvm_cpuid_result(intercepted_vtl, leaf, subleaf);
2104                let gps = self.runner.tdx_enter_guest_gps_mut();
2105                gps[TdxGp::RAX] = eax.into();
2106                gps[TdxGp::RBX] = ebx.into();
2107                gps[TdxGp::RCX] = ecx.into();
2108                gps[TdxGp::RDX] = edx.into();
2109                self.advance_to_next_instruction(intercepted_vtl);
2110                &mut self.backing.vtls[intercepted_vtl].exit_stats.cpuid
2111            }
2112            VmxExitBasic::VMCALL_INSTRUCTION => {
2113                if exit_info.cpl() != 0 {
2114                    self.inject_gpf(intercepted_vtl);
2115                } else {
2116                    let is_64bit = self.long_mode(intercepted_vtl);
2117                    let guest_memory = &self.partition.gm[intercepted_vtl];
2118                    let handler = UhHypercallHandler {
2119                        trusted: !self.cvm_partition().hide_isolation,
2120                        vp: &mut *self,
2121                        intercepted_vtl,
2122                    };
2123
2124                    UhHypercallHandler::TDX_DISPATCHER.dispatch(
2125                        guest_memory,
2126                        hv1_hypercall::X64RegisterIo::new(handler, is_64bit, true),
2127                    );
2128                }
2129                &mut self.backing.vtls[intercepted_vtl].exit_stats.vmcall
2130            }
2131            VmxExitBasic::HLT_INSTRUCTION => {
2132                self.backing.cvm.lapics[intercepted_vtl].activity = MpState::Halted;
2133                self.clear_interrupt_shadow(intercepted_vtl);
2134                self.advance_to_next_instruction(intercepted_vtl);
2135                &mut self.backing.vtls[intercepted_vtl].exit_stats.hlt
2136            }
2137            VmxExitBasic::CR_ACCESS => {
2138                let qual = CrAccessQualification::from(exit_info.qualification());
2139                let cr;
2140                let value;
2141                match qual.access_type() {
2142                    CR_ACCESS_TYPE_MOV_TO_CR => {
2143                        cr = qual.cr();
2144                        value = self.runner.tdx_enter_guest_gps()[qual.gp_register() as usize];
2145                    }
2146                    CR_ACCESS_TYPE_LMSW => {
2147                        cr = 0;
2148                        let cr0 = self.backing.vtls[intercepted_vtl].cr0.read(&self.runner);
2149                        // LMSW updates the low four bits only.
2150                        value = (qual.lmsw_source_data() as u64 & 0xf) | (cr0 & !0xf);
2151                    }
2152                    access_type => unreachable!("not registered for cr access type {access_type}"),
2153                }
2154
2155                let cr = match cr {
2156                    0 => HvX64RegisterName::Cr0,
2157                    4 => HvX64RegisterName::Cr4,
2158                    _ => unreachable!("not registered for cr{cr} accesses"),
2159                };
2160
2161                if !self.cvm_try_protect_secure_register_write(intercepted_vtl, cr, value) {
2162                    let r = match cr {
2163                        HvX64RegisterName::Cr0 => self.backing.vtls[intercepted_vtl]
2164                            .cr0
2165                            .write(value, &mut self.runner),
2166                        HvX64RegisterName::Cr4 => self.backing.vtls[intercepted_vtl]
2167                            .cr4
2168                            .write(value, &mut self.runner),
2169                        _ => unreachable!(),
2170                    };
2171                    if r.is_ok() {
2172                        self.update_execution_mode(intercepted_vtl);
2173                        self.advance_to_next_instruction(intercepted_vtl);
2174                    } else {
2175                        tracelimit::warn_ratelimited!(
2176                            CVM_ALLOWED,
2177                            ?cr,
2178                            value,
2179                            "failed to write cr"
2180                        );
2181                        self.inject_gpf(intercepted_vtl);
2182                    }
2183                }
2184                &mut self.backing.vtls[intercepted_vtl].exit_stats.cr_access
2185            }
2186            VmxExitBasic::XSETBV => {
2187                let gps = self.runner.tdx_enter_guest_gps();
2188                if let Some(value) =
2189                    hardware_cvm::validate_xsetbv_exit(hardware_cvm::XsetbvExitInput {
2190                        rax: gps[TdxGp::RAX],
2191                        rcx: gps[TdxGp::RCX],
2192                        rdx: gps[TdxGp::RDX],
2193                        cr4: self.backing.vtls[intercepted_vtl].cr4.read(&self.runner),
2194                        cpl: exit_info.cpl(),
2195                    })
2196                {
2197                    if !self.cvm_try_protect_secure_register_write(
2198                        intercepted_vtl,
2199                        HvX64RegisterName::Xfem,
2200                        value,
2201                    ) {
2202                        self.runner
2203                            .set_vp_register(intercepted_vtl, HvX64RegisterName::Xfem, value.into())
2204                            .unwrap();
2205                        self.advance_to_next_instruction(intercepted_vtl);
2206                    }
2207                } else {
2208                    self.inject_gpf(intercepted_vtl);
2209                }
2210                &mut self.backing.vtls[intercepted_vtl].exit_stats.xsetbv
2211            }
2212            VmxExitBasic::WBINVD_INSTRUCTION => {
2213                // Ask the kernel to flush the cache before issuing VP.ENTER.
2214                let no_invalidate = exit_info.qualification() != 0;
2215                if no_invalidate {
2216                    self.runner.tdx_vp_state_flags_mut().set_wbnoinvd(true);
2217                } else {
2218                    self.runner.tdx_vp_state_flags_mut().set_wbinvd(true);
2219                }
2220
2221                self.advance_to_next_instruction(intercepted_vtl);
2222                &mut self.backing.vtls[intercepted_vtl].exit_stats.wbinvd
2223            }
2224            VmxExitBasic::EPT_VIOLATION => {
2225                let gpa = exit_info.gpa().expect("is EPT exit");
2226                let ept_info = VmxEptExitQualification::from(exit_info.qualification());
2227                // If this was an EPT violation while handling an iret, and
2228                // that iret cleared the NMI blocking state, restore it.
2229                if !next_interruption.valid() && ept_info.nmi_unmasking_due_to_iret() {
2230                    let mask = Interruptibility::new().with_blocked_by_nmi(true);
2231                    let value = Interruptibility::new().with_blocked_by_nmi(true);
2232                    let old_interruptibility: Interruptibility = self
2233                        .runner
2234                        .write_vmcs32(
2235                            intercepted_vtl,
2236                            VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY,
2237                            mask.into(),
2238                            value.into(),
2239                        )
2240                        .into();
2241                    assert!(!old_interruptibility.blocked_by_nmi());
2242                } else {
2243                    let is_write = ept_info.access_mask() & 0b10 != 0;
2244                    if self.check_mem_fault(intercepted_vtl, gpa, is_write, ept_info) {
2245                        self.emulate(
2246                            dev,
2247                            self.backing.vtls[intercepted_vtl]
2248                                .interruption_information
2249                                .valid(),
2250                            intercepted_vtl,
2251                            TdxEmulationCache::default(),
2252                        )
2253                        .await?;
2254                    }
2255                }
2256
2257                &mut self.backing.vtls[intercepted_vtl].exit_stats.ept_violation
2258            }
2259            VmxExitBasic::TPR_BELOW_THRESHOLD => {
2260                // Loop around to reevaluate the APIC.
2261                &mut self.backing.vtls[intercepted_vtl]
2262                    .exit_stats
2263                    .tpr_below_threshold
2264            }
2265            VmxExitBasic::INTERRUPT_WINDOW => {
2266                // Loop around to reevaluate the APIC.
2267                &mut self.backing.vtls[intercepted_vtl]
2268                    .exit_stats
2269                    .interrupt_window
2270            }
2271            VmxExitBasic::NMI_WINDOW => {
2272                // Loop around to reevaluate pending NMIs.
2273                &mut self.backing.vtls[intercepted_vtl].exit_stats.nmi_window
2274            }
2275            VmxExitBasic::HW_INTERRUPT => {
2276                if cfg!(feature = "gdb") {
2277                    // Check if the interrupt was triggered by a hardware breakpoint.
2278                    let debug_regs = self
2279                        .access_state(intercepted_vtl.into())
2280                        .debug_regs()
2281                        .expect("register query should not fail");
2282                    // The lowest four bits of DR6 indicate which of the
2283                    // four breakpoints triggered.
2284                    breakpoint_debug_exception = debug_regs.dr6.trailing_zeros() < 4;
2285                }
2286                &mut self.backing.vtls[intercepted_vtl].exit_stats.hw_interrupt
2287            }
2288            VmxExitBasic::SMI_INTR => &mut self.backing.vtls[intercepted_vtl].exit_stats.smi_intr,
2289            VmxExitBasic::PAUSE_INSTRUCTION => {
2290                &mut self.backing.vtls[intercepted_vtl].exit_stats.pause
2291            }
2292            VmxExitBasic::TDCALL => {
2293                // If the proxy synic is local, then the host did not get this
2294                // instruction, and we need to handle it.
2295                if self.backing.untrusted_synic.is_some() {
2296                    assert_eq!(intercepted_vtl, GuestVtl::Vtl0);
2297                    self.handle_tdvmcall(dev, intercepted_vtl);
2298                } else if self.cvm_partition().hide_isolation {
2299                    // TDCALL is not valid when hiding isolation. Inject a #UD.
2300                    self.backing.vtls[intercepted_vtl].interruption_information =
2301                        InterruptionInformation::new()
2302                            .with_valid(true)
2303                            .with_vector(x86defs::Exception::INVALID_OPCODE.0)
2304                            .with_interruption_type(INTERRUPT_TYPE_HARDWARE_EXCEPTION);
2305                }
2306                &mut self.backing.vtls[intercepted_vtl].exit_stats.tdcall
2307            }
2308            VmxExitBasic::EXCEPTION => {
2309                tracing::trace!(
2310                    "Caught Exception: {:?}",
2311                    exit_info._exit_interruption_info()
2312                );
2313                if cfg!(feature = "gdb") {
2314                    breakpoint_debug_exception = true;
2315                }
2316                &mut self.backing.vtls[intercepted_vtl].exit_stats.exception
2317            }
2318            VmxExitBasic::TRIPLE_FAULT => {
2319                return Err(VpHaltReason::TripleFault {
2320                    vtl: intercepted_vtl.into(),
2321                });
2322            }
2323            VmxExitBasic::GDTR_OR_IDTR => {
2324                let info = GdtrOrIdtrInstructionInfo::from(exit_info.instr_info().info());
2325                tracing::trace!("Intercepted GDT or IDT instruction: {:?}", info);
2326                let reg = match info.instruction() {
2327                    GdtrOrIdtrInstruction::Sidt | GdtrOrIdtrInstruction::Lidt => {
2328                        HvX64RegisterName::Idtr
2329                    }
2330                    GdtrOrIdtrInstruction::Sgdt | GdtrOrIdtrInstruction::Lgdt => {
2331                        HvX64RegisterName::Gdtr
2332                    }
2333                };
2334                // We only support fowarding intercepts for descriptor table loads today.
2335                if (info.instruction().is_load()
2336                    && !self.cvm_try_protect_secure_register_write(intercepted_vtl, reg, 0))
2337                    || !info.instruction().is_load()
2338                {
2339                    self.emulate_gdtr_or_idtr(intercepted_vtl, dev).await?;
2340                }
2341                &mut self.backing.vtls[intercepted_vtl]
2342                    .exit_stats
2343                    .descriptor_table
2344            }
2345            VmxExitBasic::LDTR_OR_TR => {
2346                let info = LdtrOrTrInstructionInfo::from(exit_info.instr_info().info());
2347                tracing::trace!("Intercepted LDT or TR instruction: {:?}", info);
2348                let reg = match info.instruction() {
2349                    LdtrOrTrInstruction::Sldt | LdtrOrTrInstruction::Lldt => {
2350                        HvX64RegisterName::Ldtr
2351                    }
2352                    LdtrOrTrInstruction::Str | LdtrOrTrInstruction::Ltr => HvX64RegisterName::Tr,
2353                };
2354                // We only support fowarding intercepts for descriptor table loads today.
2355                if (info.instruction().is_load()
2356                    && !self.cvm_try_protect_secure_register_write(intercepted_vtl, reg, 0))
2357                    || !info.instruction().is_load()
2358                {
2359                    self.emulate_ldtr_or_tr(intercepted_vtl, dev).await?;
2360                }
2361                &mut self.backing.vtls[intercepted_vtl]
2362                    .exit_stats
2363                    .descriptor_table
2364            }
2365            VmxExitBasic::TIMER_EXPIRED => {
2366                // Loop around to reevaluate pending interrupts.
2367                &mut self.backing.vtls[intercepted_vtl].exit_stats.timer_expired
2368            }
2369            _ => {
2370                return Err(dev.fatal_error(UnknownVmxExit(exit_info.code().vmx_exit()).into()));
2371            }
2372        };
2373        stat.increment();
2374
2375        // Breakpoint exceptions may return a non-fatal error.
2376        // We dispatch here to correctly increment the counter.
2377        if cfg!(feature = "gdb") && breakpoint_debug_exception {
2378            self.handle_debug_exception(dev, intercepted_vtl)?;
2379        }
2380
2381        Ok(())
2382    }
2383
2384    /// Trace processor state for debugging purposes.
2385    fn trace_processor_state(&self, vtl: GuestVtl) {
2386        let raw_exit = self.runner.tdx_vp_enter_exit_info();
2387        tracing::error!(CVM_CONFIDENTIAL, ?raw_exit, "raw tdx vp enter exit info");
2388
2389        let gprs = self.runner.tdx_enter_guest_gps();
2390        tracing::error!(CVM_CONFIDENTIAL, ?gprs, "guest gpr list");
2391
2392        let TdxPrivateRegs {
2393            rflags,
2394            rip,
2395            rsp,
2396            ssp,
2397            rvi,
2398            svi,
2399            msr_kernel_gs_base,
2400            msr_star,
2401            msr_lstar,
2402            msr_sfmask,
2403            msr_xss,
2404            msr_tsc_aux,
2405            vp_entry_flags,
2406        } = self.backing.vtls[vtl].private_regs;
2407        tracing::error!(
2408            CVM_CONFIDENTIAL,
2409            rflags,
2410            rip,
2411            rsp,
2412            ssp,
2413            rvi,
2414            svi,
2415            msr_kernel_gs_base,
2416            msr_star,
2417            msr_lstar,
2418            msr_sfmask,
2419            msr_xss,
2420            msr_tsc_aux,
2421            ?vp_entry_flags,
2422            "private registers"
2423        );
2424
2425        let physical_cr0 = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR0);
2426        let shadow_cr0 = self
2427            .runner
2428            .read_vmcs64(vtl, VmcsField::VMX_VMCS_CR0_READ_SHADOW);
2429        let cr0_guest_host_mask: u64 = self
2430            .runner
2431            .read_vmcs64(vtl, VmcsField::VMX_VMCS_CR0_GUEST_HOST_MASK);
2432        tracing::error!(
2433            CVM_CONFIDENTIAL,
2434            physical_cr0,
2435            shadow_cr0,
2436            cr0_guest_host_mask,
2437            "cr0 values"
2438        );
2439
2440        let physical_cr4 = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR4);
2441        let shadow_cr4 = self
2442            .runner
2443            .read_vmcs64(vtl, VmcsField::VMX_VMCS_CR4_READ_SHADOW);
2444        let cr4_guest_host_mask = self
2445            .runner
2446            .read_vmcs64(vtl, VmcsField::VMX_VMCS_CR4_GUEST_HOST_MASK);
2447        tracing::error!(
2448            CVM_CONFIDENTIAL,
2449            physical_cr4,
2450            shadow_cr4,
2451            cr4_guest_host_mask,
2452            "cr4 values"
2453        );
2454
2455        let cr3 = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_CR3);
2456        tracing::error!(CVM_CONFIDENTIAL, cr3, "cr3");
2457
2458        let cached_efer = self.backing.vtls[vtl].efer;
2459        let vmcs_efer = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_EFER);
2460        let entry_controls = self
2461            .runner
2462            .read_vmcs32(vtl, VmcsField::VMX_VMCS_ENTRY_CONTROLS);
2463        tracing::error!(CVM_CONFIDENTIAL, cached_efer, vmcs_efer, "efer");
2464        tracing::error!(CVM_CONFIDENTIAL, entry_controls, "entry controls");
2465
2466        let cs = self.read_segment(vtl, TdxSegmentReg::Cs);
2467        let ds = self.read_segment(vtl, TdxSegmentReg::Ds);
2468        let es = self.read_segment(vtl, TdxSegmentReg::Es);
2469        let fs = self.read_segment(vtl, TdxSegmentReg::Fs);
2470        let gs = self.read_segment(vtl, TdxSegmentReg::Gs);
2471        let ss = self.read_segment(vtl, TdxSegmentReg::Ss);
2472        let tr = self.read_segment(vtl, TdxSegmentReg::Tr);
2473        let ldtr = self.read_segment(vtl, TdxSegmentReg::Ldtr);
2474
2475        tracing::error!(
2476            CVM_CONFIDENTIAL,
2477            ?cs,
2478            ?ds,
2479            ?es,
2480            ?fs,
2481            ?gs,
2482            ?ss,
2483            ?tr,
2484            ?ldtr,
2485            "segment values"
2486        );
2487
2488        let exception_bitmap = self
2489            .runner
2490            .read_vmcs32(vtl, VmcsField::VMX_VMCS_EXCEPTION_BITMAP);
2491        tracing::error!(CVM_CONFIDENTIAL, exception_bitmap, "exception bitmap");
2492
2493        let cached_processor_controls = self.backing.vtls[vtl].processor_controls;
2494        let vmcs_processor_controls = ProcessorControls::from(
2495            self.runner
2496                .read_vmcs32(vtl, VmcsField::VMX_VMCS_PROCESSOR_CONTROLS),
2497        );
2498        let vmcs_secondary_processor_controls = SecondaryProcessorControls::from(
2499            self.runner
2500                .read_vmcs32(vtl, VmcsField::VMX_VMCS_SECONDARY_PROCESSOR_CONTROLS),
2501        );
2502        tracing::error!(
2503            CVM_CONFIDENTIAL,
2504            ?cached_processor_controls,
2505            ?vmcs_processor_controls,
2506            ?vmcs_secondary_processor_controls,
2507            "processor controls"
2508        );
2509
2510        if cached_processor_controls != vmcs_processor_controls {
2511            tracing::error!(CVM_ALLOWED, "BUGBUG: processor controls mismatch");
2512        }
2513
2514        let cached_tpr_threshold = self.backing.vtls[vtl].tpr_threshold;
2515        let vmcs_tpr_threshold = self
2516            .runner
2517            .read_vmcs32(vtl, VmcsField::VMX_VMCS_TPR_THRESHOLD);
2518        tracing::error!(
2519            CVM_CONFIDENTIAL,
2520            cached_tpr_threshold,
2521            vmcs_tpr_threshold,
2522            "tpr threshold"
2523        );
2524
2525        let cached_eoi_exit_bitmap = self.backing.eoi_exit_bitmap;
2526        let vmcs_eoi_exit_bitmap = {
2527            let fields = [
2528                VmcsField::VMX_VMCS_EOI_EXIT_0,
2529                VmcsField::VMX_VMCS_EOI_EXIT_1,
2530                VmcsField::VMX_VMCS_EOI_EXIT_2,
2531                VmcsField::VMX_VMCS_EOI_EXIT_3,
2532            ];
2533            fields
2534                .iter()
2535                .map(|field| self.runner.read_vmcs64(vtl, *field))
2536                .collect::<Vec<_>>()
2537        };
2538        tracing::error!(
2539            CVM_CONFIDENTIAL,
2540            ?cached_eoi_exit_bitmap,
2541            ?vmcs_eoi_exit_bitmap,
2542            "eoi exit bitmap"
2543        );
2544
2545        let cached_interrupt_information = self.backing.vtls[vtl].interruption_information;
2546        let cached_interruption_set = self.backing.vtls[vtl].interruption_set;
2547        let vmcs_interrupt_information = self
2548            .runner
2549            .read_vmcs32(vtl, VmcsField::VMX_VMCS_ENTRY_INTERRUPT_INFO);
2550        let vmcs_entry_exception_code = self
2551            .runner
2552            .read_vmcs32(vtl, VmcsField::VMX_VMCS_ENTRY_EXCEPTION_ERROR_CODE);
2553        tracing::error!(
2554            CVM_CONFIDENTIAL,
2555            ?cached_interrupt_information,
2556            cached_interruption_set,
2557            vmcs_interrupt_information,
2558            vmcs_entry_exception_code,
2559            "interrupt information"
2560        );
2561
2562        let guest_interruptibility = self
2563            .runner
2564            .read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY);
2565        tracing::error!(
2566            CVM_CONFIDENTIAL,
2567            guest_interruptibility,
2568            "guest interruptibility"
2569        );
2570
2571        let vmcs_sysenter_cs = self
2572            .runner
2573            .read_vmcs32(vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_CS_MSR);
2574        let vmcs_sysenter_esp = self
2575            .runner
2576            .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_ESP_MSR);
2577        let vmcs_sysenter_eip = self
2578            .runner
2579            .read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_EIP_MSR);
2580        tracing::error!(
2581            CVM_CONFIDENTIAL,
2582            vmcs_sysenter_cs,
2583            vmcs_sysenter_esp,
2584            vmcs_sysenter_eip,
2585            "sysenter values"
2586        );
2587
2588        let vmcs_pat = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_PAT);
2589        tracing::error!(CVM_CONFIDENTIAL, vmcs_pat, "guest PAT");
2590    }
2591
2592    fn handle_vm_enter_failed(
2593        &self,
2594        dev: &impl CpuIo,
2595        vtl: GuestVtl,
2596        vmx_exit: VmxExit,
2597    ) -> VpHaltReason {
2598        assert!(vmx_exit.vm_enter_failed());
2599        match vmx_exit.basic_reason() {
2600            VmxExitBasic::BAD_GUEST_STATE => {
2601                // Log system register state for debugging why we were
2602                // unable to enter the guest. This is a VMM bug.
2603                tracing::error!(CVM_ALLOWED, "VP.ENTER failed with bad guest state");
2604                self.trace_processor_state(vtl);
2605
2606                dev.fatal_error(VmxBadGuestState.into())
2607            }
2608            _ => dev.fatal_error(UnknownVmxExit(vmx_exit).into()),
2609        }
2610    }
2611
2612    fn advance_to_next_instruction(&mut self, vtl: GuestVtl) {
2613        let instr_info = TdxExit(self.runner.tdx_vp_enter_exit_info()).instr_info();
2614        let rip = &mut self.backing.vtls[vtl].private_regs.rip;
2615        *rip = rip.wrapping_add(instr_info.length().into());
2616    }
2617
2618    fn clear_interrupt_shadow(&mut self, vtl: GuestVtl) {
2619        let mask = Interruptibility::new().with_blocked_by_sti(true);
2620        let value = Interruptibility::new().with_blocked_by_sti(false);
2621        self.runner.write_vmcs32(
2622            vtl,
2623            VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY,
2624            mask.into(),
2625            value.into(),
2626        );
2627    }
2628
2629    fn inject_gpf(&mut self, vtl: GuestVtl) {
2630        self.backing.vtls[vtl].interruption_information = InterruptionInformation::new()
2631            .with_valid(true)
2632            .with_vector(x86defs::Exception::GENERAL_PROTECTION_FAULT.0)
2633            .with_interruption_type(INTERRUPT_TYPE_HARDWARE_EXCEPTION)
2634            .with_deliver_error_code(true);
2635        self.backing.vtls[vtl].exception_error_code = 0;
2636    }
2637
2638    fn handle_tdvmcall(&mut self, _dev: &impl CpuIo, intercepted_vtl: GuestVtl) {
2639        let regs = self.runner.tdx_enter_guest_gps();
2640        if regs[TdxGp::R10] == 0 {
2641            // Architectural VMCALL.
2642            let result = match VmxExitBasic(regs[TdxGp::R11] as u16) {
2643                VmxExitBasic::MSR_WRITE => {
2644                    let msr = regs[TdxGp::R12] as u32;
2645                    let value = regs[TdxGp::R13];
2646                    match self.write_tdvmcall_msr(msr, value, intercepted_vtl) {
2647                        Ok(()) => {
2648                            tracing::debug!(msr, value, "tdvmcall msr write");
2649                            TdVmCallR10Result::SUCCESS
2650                        }
2651                        Err(err) => {
2652                            tracelimit::warn_ratelimited!(
2653                                CVM_ALLOWED,
2654                                msr,
2655                                ?err,
2656                                "failed tdvmcall msr write"
2657                            );
2658                            tracelimit::warn_ratelimited!(
2659                                CVM_CONFIDENTIAL,
2660                                value,
2661                                "failed tdvmcall msr write"
2662                            );
2663                            TdVmCallR10Result::OPERAND_INVALID
2664                        }
2665                    }
2666                }
2667                VmxExitBasic::MSR_READ => {
2668                    let msr = regs[TdxGp::R12] as u32;
2669                    match self.read_tdvmcall_msr(msr, intercepted_vtl) {
2670                        Ok(value) => {
2671                            tracing::debug!(msr, value, "tdvmcall msr read");
2672                            self.runner.tdx_enter_guest_gps_mut()[TdxGp::R11] = value;
2673                            TdVmCallR10Result::SUCCESS
2674                        }
2675                        Err(err) => {
2676                            tracelimit::warn_ratelimited!(
2677                                CVM_ALLOWED,
2678                                msr,
2679                                ?err,
2680                                "failed tdvmcall msr read"
2681                            );
2682                            TdVmCallR10Result::OPERAND_INVALID
2683                        }
2684                    }
2685                }
2686                subfunction => {
2687                    tracelimit::warn_ratelimited!(
2688                        CVM_ALLOWED,
2689                        ?subfunction,
2690                        "architectural vmcall not supported"
2691                    );
2692                    TdVmCallR10Result::OPERAND_INVALID
2693                }
2694            };
2695            self.runner.tdx_enter_guest_gps_mut()[TdxGp::R10] = result.0;
2696            self.backing.vtls[intercepted_vtl].private_regs.rip = self.backing.vtls
2697                [intercepted_vtl]
2698                .private_regs
2699                .rip
2700                .wrapping_add(4);
2701        } else {
2702            // This hypercall is normally handled by the hypervisor, so the gpas
2703            // given by the guest should all be shared. The hypervisor allows
2704            // gpas to be set with or without the shared gpa boundary bit, which
2705            // untrusted_dma_memory correctly models. Note that some Linux
2706            // guests will issue hypercalls without the boundary bit set,
2707            // whereas UEFI will issue with the bit set.
2708            let guest_memory = &self.shared.cvm.shared_memory;
2709            let handler = UhHypercallHandler {
2710                vp: &mut *self,
2711                trusted: false,
2712                intercepted_vtl,
2713            };
2714
2715            UhHypercallHandler::TDCALL_DISPATCHER.dispatch(guest_memory, TdHypercall(handler));
2716        }
2717    }
2718
2719    fn read_tdvmcall_msr(&mut self, msr: u32, intercepted_vtl: GuestVtl) -> Result<u64, MsrError> {
2720        match msr {
2721            msr @ (hvdef::HV_X64_MSR_GUEST_OS_ID | hvdef::HV_X64_MSR_VP_INDEX) => {
2722                self.backing.cvm.hv[intercepted_vtl].msr_read(msr)
2723            }
2724            _ => self
2725                .backing
2726                .untrusted_synic
2727                .as_mut()
2728                .unwrap()
2729                .read_nontimer_msr(msr),
2730        }
2731    }
2732
2733    fn write_tdvmcall_msr(
2734        &mut self,
2735        msr: u32,
2736        value: u64,
2737        intercepted_vtl: GuestVtl,
2738    ) -> Result<(), MsrError> {
2739        match msr {
2740            hvdef::HV_X64_MSR_GUEST_OS_ID => {
2741                self.backing.cvm.hv[intercepted_vtl].msr_write_guest_os_id(value)
2742            }
2743            _ => {
2744                // If we get here we must have an untrusted synic, as otherwise
2745                // we wouldn't be handling the TDVMCALL that ends up here. Therefore
2746                // this is fine to unwrap.
2747                self.backing
2748                    .untrusted_synic
2749                    .as_mut()
2750                    .unwrap()
2751                    .write_nontimer_msr(
2752                        msr,
2753                        value,
2754                        &mut UntrustedSynicVtlProts(&self.partition.gm[GuestVtl::Vtl0]),
2755                    )?;
2756                // Propagate sint MSR writes to the hypervisor as well
2757                // so that the hypervisor can directly inject events.
2758                if matches!(msr, hvdef::HV_X64_MSR_SINT0..=hvdef::HV_X64_MSR_SINT15) {
2759                    if let Err(err) = self.runner.set_vp_register(
2760                        intercepted_vtl,
2761                        HvX64RegisterName(
2762                            HvX64RegisterName::Sint0.0 + (msr - hvdef::HV_X64_MSR_SINT0),
2763                        ),
2764                        value.into(),
2765                    ) {
2766                        tracelimit::warn_ratelimited!(
2767                            CVM_ALLOWED,
2768                            error = &err as &dyn std::error::Error,
2769                            "failed to set sint register"
2770                        );
2771                    }
2772                }
2773            }
2774        }
2775
2776        Ok(())
2777    }
2778
2779    fn read_msr_tdx(&mut self, msr: u32, vtl: GuestVtl) -> Result<u64, MsrError> {
2780        // TODO TDX: port remaining tdx and common values
2781        //
2782        // TODO TDX: consider if this can be shared with SnpBacked's
2783        // implementation. For the most part other than Intel/TDX specific
2784        // registers, MSR handling should be the same.
2785
2786        match msr {
2787            // TODO TDX: LIFTED FROM WHP
2788            x86defs::X86X_IA32_MSR_PLATFORM_ID => {
2789                // Windows requires accessing this to boot. WHP
2790                // used to pass this through to the hardware,
2791                // but this regressed. Zero seems to work fine
2792                // for Windows.
2793                //
2794                // TODO: Pass through the host value if it can
2795                //       be retrieved.
2796                Ok(0)
2797            }
2798
2799            x86defs::X86X_MSR_MTRR_CAP => {
2800                // Advertise the absence of MTRR capabilities, but include the availability of write
2801                // combining.
2802                Ok(0x400)
2803            }
2804            x86defs::X86X_MSR_MTRR_DEF_TYPE => {
2805                // Because the MTRR registers are advertised via CPUID, even though no actual ranges
2806                // are supported a guest may choose to write to this MSR. Implement it as read as
2807                // zero/write ignore.
2808                Ok(0)
2809            }
2810            x86defs::X86X_MSR_CSTAR => Ok(self.backing.vtls[vtl].msr_cstar),
2811            x86defs::X86X_MSR_MCG_CAP => Ok(0),
2812            x86defs::X86X_MSR_MCG_STATUS => Ok(0),
2813            x86defs::X86X_MSR_MC_UPDATE_PATCH_LEVEL => Ok(0xFFFFFFFF),
2814            x86defs::X86X_MSR_XSS => Ok(self.backing.vtls[vtl].private_regs.msr_xss),
2815            x86defs::X86X_IA32_MSR_MISC_ENABLE => Ok(hv1_emulator::x86::MISC_ENABLE.into()),
2816            x86defs::X86X_IA32_MSR_FEATURE_CONTROL => {
2817                Ok(u64::from(Ia32FeatureControl::new().with_locked(true)))
2818            }
2819            x86defs::X86X_MSR_CR_PAT => {
2820                let pat = self.runner.read_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_PAT);
2821                Ok(pat)
2822            }
2823
2824            // Following MSRs are unconditionally read by Linux guests.
2825            // These are not virtualized and unsupported for L2-VMs
2826            x86defs::X86X_MSR_MISC_FEATURE_ENABLES
2827            | x86defs::X86X_MSR_PLATFORM_INFO
2828            | x86defs::X86X_MSR_PPIN_CTL
2829            | x86defs::X86X_IA32_MSR_SMI_COUNT
2830            | x86defs::X86X_MSR_UMWAIT_CONTROL
2831            | x86defs::X86X_AMD_MSR_DE_CFG
2832            | x86defs::X86X_IA32_MSR_RAPL_POWER_UNIT
2833            | x86defs::X86X_IA32_MSR_PKG_ENERGY_STATUS
2834            | x86defs::X86X_IA32_MSR_DRAM_ENERGY_STATUS
2835            | x86defs::X86X_IA32_MSR_PP0_ENERGY_STATUS => Ok(0),
2836
2837            hvdef::HV_X64_MSR_GUEST_IDLE => {
2838                self.backing.cvm.lapics[vtl].activity = MpState::Idle;
2839                self.clear_interrupt_shadow(vtl);
2840                Ok(0)
2841            }
2842            X86X_MSR_EFER => Ok(self.backing.vtls[vtl].efer),
2843
2844            _ => Err(MsrError::Unknown),
2845        }
2846    }
2847
2848    fn write_msr_tdx(&mut self, msr: u32, value: u64, vtl: GuestVtl) -> Result<(), MsrError> {
2849        hardware_cvm::validate_cvm_msr_write(msr, value, &self.partition.caps.xsave)?;
2850
2851        let state = &mut self.backing.vtls[vtl].private_regs;
2852
2853        match msr {
2854            X86X_MSR_EFER => {
2855                self.write_efer(vtl, value)
2856                    .map_err(|_| MsrError::InvalidAccess)?;
2857                self.update_execution_mode(vtl);
2858            }
2859            x86defs::X86X_MSR_STAR => state.msr_star = value,
2860            x86defs::X86X_MSR_CSTAR => self.backing.vtls[vtl].msr_cstar = value,
2861            x86defs::X86X_MSR_LSTAR => state.msr_lstar = value,
2862            x86defs::X86X_MSR_SFMASK => state.msr_sfmask = value,
2863            x86defs::X86X_MSR_TSC_AUX => state.msr_tsc_aux = value,
2864            x86defs::X86X_MSR_SYSENTER_CS => {
2865                self.runner.write_vmcs32(
2866                    vtl,
2867                    VmcsField::VMX_VMCS_GUEST_SYSENTER_CS_MSR,
2868                    !0,
2869                    value as u32,
2870                );
2871            }
2872            x86defs::X86X_MSR_SYSENTER_EIP => {
2873                self.runner.write_vmcs64(
2874                    vtl,
2875                    VmcsField::VMX_VMCS_GUEST_SYSENTER_EIP_MSR,
2876                    !0,
2877                    value,
2878                );
2879            }
2880            x86defs::X86X_MSR_SYSENTER_ESP => {
2881                self.runner.write_vmcs64(
2882                    vtl,
2883                    VmcsField::VMX_VMCS_GUEST_SYSENTER_ESP_MSR,
2884                    !0,
2885                    value,
2886                );
2887            }
2888            x86defs::X86X_MSR_XSS => state.msr_xss = value,
2889            x86defs::X86X_MSR_MC_UPDATE_PATCH_LEVEL => {
2890                // Writing zero on intel platforms is allowed and ignored.
2891                if value != 0 {
2892                    return Err(MsrError::InvalidAccess);
2893                }
2894            }
2895            x86defs::X86X_IA32_MSR_MISC_ENABLE => {}
2896            x86defs::X86X_MSR_CR_PAT => {
2897                self.runner
2898                    .write_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_PAT, !0, value);
2899            }
2900
2901            x86defs::X86X_MSR_MCG_STATUS => {
2902                // Writes are swallowed, except for reserved bits violations
2903                if x86defs::X86xMcgStatusRegister::from(value).reserved0() != 0 {
2904                    return Err(MsrError::InvalidAccess);
2905                }
2906            }
2907
2908            // Ignore writes to this MSR
2909            x86defs::X86X_MSR_MTRR_DEF_TYPE => {}
2910
2911            // Following MSRs are sometimes written by Windows guests.
2912            // These are not virtualized and unsupported for L2-VMs
2913            x86defs::X86X_MSR_BIOS_UPDT_TRIG => {}
2914
2915            // Following MSRs are unconditionally written by Linux guests.
2916            // These are not virtualized and unsupported for L2-VMs
2917            x86defs::X86X_MSR_MISC_FEATURE_ENABLES
2918            | x86defs::X86X_MSR_PLATFORM_INFO
2919            | x86defs::X86X_MSR_PPIN_CTL
2920            | x86defs::X86X_IA32_MSR_SMI_COUNT
2921            | x86defs::X86X_MSR_UMWAIT_CONTROL
2922            | x86defs::X86X_AMD_MSR_DE_CFG
2923            | x86defs::X86X_IA32_MSR_RAPL_POWER_UNIT
2924            | x86defs::X86X_IA32_MSR_PKG_ENERGY_STATUS
2925            | x86defs::X86X_IA32_MSR_DRAM_ENERGY_STATUS
2926            | x86defs::X86X_IA32_MSR_PP0_ENERGY_STATUS => {}
2927
2928            _ => return Err(MsrError::Unknown),
2929        }
2930
2931        Ok(())
2932    }
2933
2934    fn write_segment(
2935        &mut self,
2936        vtl: GuestVtl,
2937        seg: TdxSegmentReg,
2938        reg: SegmentRegister,
2939    ) -> Result<(), vp_state::Error> {
2940        // write base, selector, limit
2941        self.runner
2942            .write_vmcs16(vtl, seg.selector(), !0, reg.selector);
2943        self.runner.write_vmcs64(vtl, seg.base(), !0, reg.base);
2944        self.runner.write_vmcs32(vtl, seg.limit(), !0, reg.limit);
2945
2946        // Mark segment not valid if its attributes indicate not present.
2947        let mut attributes = x86defs::vmx::VmxSegmentAttributes::from(reg.attributes as u32);
2948        attributes.set_null(!attributes.present());
2949
2950        self.runner
2951            .write_vmcs32(vtl, seg.attributes(), !0, attributes.into());
2952
2953        Ok(())
2954    }
2955
2956    fn read_segment(&self, vtl: GuestVtl, seg: TdxSegmentReg) -> SegmentRegister {
2957        let selector = self.runner.read_vmcs16(vtl, seg.selector());
2958        let base = self.runner.read_vmcs64(vtl, seg.base());
2959        let limit = self.runner.read_vmcs32(vtl, seg.limit());
2960        let attributes = self.runner.read_vmcs32(vtl, seg.attributes());
2961
2962        SegmentRegister {
2963            selector,
2964            base,
2965            limit,
2966            attributes: attributes as u16,
2967        }
2968    }
2969
2970    fn long_mode(&self, vtl: GuestVtl) -> bool {
2971        let backing = &self.backing.vtls[vtl];
2972        backing.cr0.read(&self.runner) & X64_CR0_PE != 0 && backing.efer & X64_EFER_LMA != 0
2973    }
2974}
2975
2976impl<T: CpuIo> X86EmulatorSupport for UhEmulationState<'_, '_, T, TdxBacked> {
2977    fn vp_index(&self) -> VpIndex {
2978        self.vp.vp_index()
2979    }
2980
2981    fn flush(&mut self) {
2982        // no cached registers are modifiable by the emulator for TDX
2983    }
2984
2985    fn vendor(&self) -> x86defs::cpuid::Vendor {
2986        self.vp.partition.caps.vendor
2987    }
2988
2989    fn gp(&mut self, reg: Gp) -> u64 {
2990        self.vp.runner.tdx_enter_guest_gps()[reg as usize]
2991    }
2992
2993    fn set_gp(&mut self, reg: Gp, v: u64) {
2994        self.vp.runner.tdx_enter_guest_gps_mut()[reg as usize] = v;
2995    }
2996
2997    fn xmm(&mut self, index: usize) -> u128 {
2998        u128::from_ne_bytes(self.vp.runner.fx_state().xmm[index])
2999    }
3000
3001    fn set_xmm(&mut self, index: usize, v: u128) {
3002        self.vp.runner.fx_state_mut().xmm[index] = v.to_ne_bytes();
3003    }
3004
3005    fn rip(&mut self) -> u64 {
3006        self.vp.backing.vtls[self.vtl].private_regs.rip
3007    }
3008
3009    fn set_rip(&mut self, v: u64) {
3010        self.vp.backing.vtls[self.vtl].private_regs.rip = v;
3011    }
3012
3013    fn segment(&mut self, index: Segment) -> x86defs::SegmentRegister {
3014        let tdx_segment_index = match index {
3015            Segment::CS => TdxSegmentReg::Cs,
3016            Segment::ES => TdxSegmentReg::Es,
3017            Segment::SS => TdxSegmentReg::Ss,
3018            Segment::DS => TdxSegmentReg::Ds,
3019            Segment::FS => TdxSegmentReg::Fs,
3020            Segment::GS => TdxSegmentReg::Gs,
3021        };
3022        let reg = match tdx_segment_index {
3023            TdxSegmentReg::Cs => self.cache.segs[index as usize]
3024                .get_or_insert_with(|| TdxExit(self.vp.runner.tdx_vp_enter_exit_info()).cs()),
3025            _ => self.cache.segs[index as usize]
3026                .get_or_insert_with(|| self.vp.read_segment(self.vtl, tdx_segment_index)),
3027        };
3028        (*reg).into()
3029    }
3030
3031    fn efer(&mut self) -> u64 {
3032        self.vp.backing.vtls[self.vtl].efer
3033    }
3034
3035    fn cr0(&mut self) -> u64 {
3036        let reg = self
3037            .cache
3038            .cr0
3039            .get_or_insert_with(|| self.vp.backing.vtls[self.vtl].cr0.read(&self.vp.runner));
3040        *reg
3041    }
3042
3043    fn rflags(&mut self) -> RFlags {
3044        self.vp.backing.vtls[self.vtl].private_regs.rflags.into()
3045    }
3046
3047    fn set_rflags(&mut self, v: RFlags) {
3048        self.vp.backing.vtls[self.vtl].private_regs.rflags = v.into();
3049    }
3050
3051    fn instruction_bytes(&self) -> &[u8] {
3052        &[]
3053    }
3054
3055    fn physical_address(&self) -> Option<u64> {
3056        TdxExit(self.vp.runner.tdx_vp_enter_exit_info()).gpa()
3057    }
3058
3059    fn initial_gva_translation(
3060        &mut self,
3061    ) -> Option<virt_support_x86emu::emulate::InitialTranslation> {
3062        let exit_info = TdxExit(self.vp.runner.tdx_vp_enter_exit_info());
3063        let ept_info = VmxEptExitQualification::from(exit_info.qualification());
3064
3065        if exit_info.code().vmx_exit().basic_reason() == VmxExitBasic::EPT_VIOLATION
3066            && ept_info.gva_valid()
3067        {
3068            Some(virt_support_x86emu::emulate::InitialTranslation {
3069                gva: exit_info.gla().expect("already validated EPT exit"),
3070                gpa: exit_info.gpa().expect("already validated EPT exit"),
3071                translate_mode: match ept_info.access_mask() {
3072                    0x1 => TranslateMode::Read,
3073                    // As defined in "Table 28-7. Exit Qualification for EPT
3074                    // Violations" in the Intel SDM, the processor may set both
3075                    // the read and write bits in certain conditions:
3076                    //
3077                    // If accessed and dirty flags for EPT are enabled,
3078                    // processor accesses to guest paging-structure entries are
3079                    // treated as writes with regard to EPT violations (see
3080                    // Section 29.3.3.2). If such an access causes an EPT
3081                    // violation, the processor sets both bit 0 and bit 1 of the
3082                    // exit qualification.
3083                    //
3084                    // Treat both 0x2 and 0x3 as writes.
3085                    0x2 | 0x3 => TranslateMode::Write,
3086                    0x4 => TranslateMode::Execute,
3087                    _ => panic!("unexpected ept access mask 0x{:x}", ept_info.access_mask()),
3088                },
3089            })
3090        } else {
3091            None
3092        }
3093    }
3094
3095    fn interruption_pending(&self) -> bool {
3096        self.interruption_pending
3097    }
3098
3099    fn check_vtl_access(
3100        &mut self,
3101        _gpa: u64,
3102        _mode: TranslateMode,
3103    ) -> Result<(), virt_support_x86emu::emulate::EmuCheckVtlAccessError> {
3104        // Nothing to do here, the guest memory object will handle the check.
3105        Ok(())
3106    }
3107
3108    fn translate_gva(
3109        &mut self,
3110        gva: u64,
3111        mode: TranslateMode,
3112    ) -> Result<
3113        virt_support_x86emu::emulate::EmuTranslateResult,
3114        virt_support_x86emu::emulate::EmuTranslateError,
3115    > {
3116        emulate_translate_gva(self, gva, mode)
3117    }
3118
3119    fn inject_pending_event(&mut self, event_info: hvdef::HvX64PendingEvent) {
3120        assert!(event_info.reg_0.event_pending());
3121        assert_eq!(
3122            event_info.reg_0.event_type(),
3123            hvdef::HV_X64_PENDING_EVENT_EXCEPTION
3124        );
3125        assert!(!self.interruption_pending);
3126
3127        // There's no interruption pending, so just inject the exception
3128        // directly without checking for double fault.
3129        TdxBacked::set_pending_exception(
3130            self.vp,
3131            self.vtl,
3132            HvX64PendingExceptionEvent::from(event_info.reg_0.into_bits()),
3133        );
3134    }
3135
3136    fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool {
3137        // Ignore the VTOM address bit when checking, since memory is mirrored
3138        // across the VTOM.
3139        let vtom = self.vp.partition.caps.vtom.unwrap_or(0);
3140        debug_assert!(vtom == 0 || vtom.is_power_of_two());
3141        self.vp.partition.is_gpa_mapped(gpa & !vtom, write)
3142    }
3143
3144    fn lapic_base_address(&self) -> Option<u64> {
3145        self.vp.backing.cvm.lapics[self.vtl].lapic.base_address()
3146    }
3147
3148    fn lapic_read(&mut self, address: u64, data: &mut [u8]) {
3149        self.vp.backing.cvm.lapics[self.vtl]
3150            .lapic
3151            .access(&mut TdxApicClient {
3152                partition: self.vp.partition,
3153                dev: self.devices,
3154                vmtime: &self.vp.vmtime,
3155                apic_page: self.vp.runner.tdx_apic_page_mut(self.vtl),
3156                vtl: self.vtl,
3157            })
3158            .mmio_read(address, data);
3159    }
3160
3161    fn lapic_write(&mut self, address: u64, data: &[u8]) {
3162        self.vp.backing.cvm.lapics[self.vtl]
3163            .lapic
3164            .access(&mut TdxApicClient {
3165                partition: self.vp.partition,
3166                dev: self.devices,
3167                vmtime: &self.vp.vmtime,
3168                apic_page: self.vp.runner.tdx_apic_page_mut(self.vtl),
3169                vtl: self.vtl,
3170            })
3171            .mmio_write(address, data);
3172    }
3173
3174    fn monitor_support(&self) -> Option<&dyn EmulatorMonitorSupport> {
3175        Some(self)
3176    }
3177}
3178
3179#[derive(Debug)]
3180enum TdxSegmentReg {
3181    Es,
3182    Cs,
3183    Ss,
3184    Ds,
3185    Fs,
3186    Gs,
3187    Ldtr,
3188    Tr,
3189}
3190
3191impl TdxSegmentReg {
3192    /// The selector vmcs field code.
3193    fn selector(&self) -> VmcsField {
3194        match self {
3195            Self::Es => VmcsField::VMX_VMCS_GUEST_ES_SELECTOR,
3196            Self::Cs => VmcsField::VMX_VMCS_GUEST_CS_SELECTOR,
3197            Self::Ss => VmcsField::VMX_VMCS_GUEST_SS_SELECTOR,
3198            Self::Ds => VmcsField::VMX_VMCS_GUEST_DS_SELECTOR,
3199            Self::Fs => VmcsField::VMX_VMCS_GUEST_FS_SELECTOR,
3200            Self::Gs => VmcsField::VMX_VMCS_GUEST_GS_SELECTOR,
3201            Self::Ldtr => VmcsField::VMX_VMCS_GUEST_LDTR_SELECTOR,
3202            Self::Tr => VmcsField::VMX_VMCS_GUEST_TR_SELECTOR,
3203        }
3204    }
3205
3206    /// The base vmcs field code.
3207    fn base(&self) -> VmcsField {
3208        match self {
3209            Self::Es => VmcsField::VMX_VMCS_GUEST_ES_BASE,
3210            Self::Cs => VmcsField::VMX_VMCS_GUEST_CS_BASE,
3211            Self::Ss => VmcsField::VMX_VMCS_GUEST_SS_BASE,
3212            Self::Ds => VmcsField::VMX_VMCS_GUEST_DS_BASE,
3213            Self::Fs => VmcsField::VMX_VMCS_GUEST_FS_BASE,
3214            Self::Gs => VmcsField::VMX_VMCS_GUEST_GS_BASE,
3215            Self::Ldtr => VmcsField::VMX_VMCS_GUEST_LDTR_BASE,
3216            Self::Tr => VmcsField::VMX_VMCS_GUEST_TR_BASE,
3217        }
3218    }
3219
3220    /// The limit vmcs field code.
3221    fn limit(&self) -> VmcsField {
3222        match self {
3223            Self::Es => VmcsField::VMX_VMCS_GUEST_ES_LIMIT,
3224            Self::Cs => VmcsField::VMX_VMCS_GUEST_CS_LIMIT,
3225            Self::Ss => VmcsField::VMX_VMCS_GUEST_SS_LIMIT,
3226            Self::Ds => VmcsField::VMX_VMCS_GUEST_DS_LIMIT,
3227            Self::Fs => VmcsField::VMX_VMCS_GUEST_FS_LIMIT,
3228            Self::Gs => VmcsField::VMX_VMCS_GUEST_GS_LIMIT,
3229            Self::Ldtr => VmcsField::VMX_VMCS_GUEST_LDTR_LIMIT,
3230            Self::Tr => VmcsField::VMX_VMCS_GUEST_TR_LIMIT,
3231        }
3232    }
3233
3234    // The attributes vmcs field code.
3235    fn attributes(&self) -> VmcsField {
3236        match self {
3237            Self::Es => VmcsField::VMX_VMCS_GUEST_ES_AR,
3238            Self::Cs => VmcsField::VMX_VMCS_GUEST_CS_AR,
3239            Self::Ss => VmcsField::VMX_VMCS_GUEST_SS_AR,
3240            Self::Ds => VmcsField::VMX_VMCS_GUEST_DS_AR,
3241            Self::Fs => VmcsField::VMX_VMCS_GUEST_FS_AR,
3242            Self::Gs => VmcsField::VMX_VMCS_GUEST_GS_AR,
3243            Self::Ldtr => VmcsField::VMX_VMCS_GUEST_LDTR_AR,
3244            Self::Tr => VmcsField::VMX_VMCS_GUEST_TR_AR,
3245        }
3246    }
3247}
3248
3249#[derive(Debug)]
3250enum TdxTableReg {
3251    Idtr,
3252    Gdtr,
3253}
3254
3255impl TdxTableReg {
3256    fn base_code(&self) -> VmcsField {
3257        match self {
3258            Self::Idtr => VmcsField::VMX_VMCS_GUEST_IDTR_BASE,
3259            Self::Gdtr => VmcsField::VMX_VMCS_GUEST_GDTR_BASE,
3260        }
3261    }
3262
3263    fn limit_code(&self) -> VmcsField {
3264        match self {
3265            Self::Idtr => VmcsField::VMX_VMCS_GUEST_IDTR_LIMIT,
3266            Self::Gdtr => VmcsField::VMX_VMCS_GUEST_GDTR_LIMIT,
3267        }
3268    }
3269}
3270
3271impl UhProcessor<'_, TdxBacked> {
3272    /// Handle a write to EFER, which requires special handling on TDX due to
3273    /// required bits and state updates.
3274    ///
3275    /// Note that a caller must also call [`Self::update_execution_mode`] after
3276    /// updating EFER.
3277    fn write_efer(&mut self, vtl: GuestVtl, efer: u64) -> Result<(), vp_state::Error> {
3278        if efer & (X64_EFER_SVME | X64_EFER_FFXSR) != 0 {
3279            return Err(vp_state::Error::InvalidValue(
3280                efer,
3281                "EFER",
3282                "SVME or FFXSR set",
3283            ));
3284        }
3285
3286        // EFER.NXE must be 1.
3287        if efer & X64_EFER_NXE == 0 {
3288            return Err(vp_state::Error::InvalidValue(efer, "EFER", "NXE not set"));
3289        }
3290
3291        // Update the local value of EFER and the VMCS.
3292        if self.backing.vtls[vtl].efer != efer {
3293            self.backing.vtls[vtl].efer = efer;
3294            self.runner
3295                .write_vmcs64(vtl, VmcsField::VMX_VMCS_GUEST_EFER, !0, efer);
3296        }
3297
3298        Ok(())
3299    }
3300
3301    /// Read CR0 that includes guest shadowed bits. This is the value the guest
3302    /// sees.
3303    fn read_cr0(&self, vtl: GuestVtl) -> u64 {
3304        self.backing.vtls[vtl].cr0.read(&self.runner)
3305    }
3306
3307    /// Write to the guest CR0.
3308    fn write_cr0(&mut self, vtl: GuestVtl, value: u64) -> Result<(), vp_state::Error> {
3309        self.backing.vtls[vtl]
3310            .cr0
3311            .write(value | X64_CR0_ET, &mut self.runner)
3312    }
3313
3314    fn read_cr4(&self, vtl: GuestVtl) -> u64 {
3315        self.backing.vtls[vtl].cr4.read(&self.runner)
3316    }
3317
3318    fn write_cr4(&mut self, vtl: GuestVtl, value: u64) -> Result<(), vp_state::Error> {
3319        self.backing.vtls[vtl].cr4.write(value, &mut self.runner)
3320    }
3321
3322    fn write_table_register(&mut self, vtl: GuestVtl, table: TdxTableReg, reg: TableRegister) {
3323        self.runner
3324            .write_vmcs64(vtl, table.base_code(), !0, reg.base);
3325        self.runner
3326            .write_vmcs32(vtl, table.limit_code(), !0, reg.limit.into());
3327    }
3328
3329    fn read_table_register(&self, vtl: GuestVtl, table: TdxTableReg) -> TableRegister {
3330        let base = self.runner.read_vmcs64(vtl, table.base_code());
3331        let limit = self.runner.read_vmcs32(vtl, table.limit_code());
3332
3333        TableRegister {
3334            base,
3335            limit: limit as u16,
3336        }
3337    }
3338
3339    /// Update execution mode when CR0 or EFER is changed.
3340    fn update_execution_mode(&mut self, vtl: GuestVtl) {
3341        let lme = self.backing.vtls[vtl].efer & X64_EFER_LME == X64_EFER_LME;
3342        let pg = self.read_cr0(vtl) & X64_CR0_PG == X64_CR0_PG;
3343        let efer_lma = self.backing.vtls[vtl].efer & X64_EFER_LMA == X64_EFER_LMA;
3344        let lma = lme && pg;
3345
3346        if lma != efer_lma {
3347            // Flip only the LMA bit.
3348            let new_efer = self.backing.vtls[vtl].efer ^ X64_EFER_LMA;
3349            self.write_efer(vtl, new_efer)
3350                .expect("EFER was valid before, it should still be valid");
3351        }
3352
3353        self.runner.write_vmcs32(
3354            vtl,
3355            VmcsField::VMX_VMCS_ENTRY_CONTROLS,
3356            VMX_ENTRY_CONTROL_LONG_MODE_GUEST,
3357            if lma {
3358                VMX_ENTRY_CONTROL_LONG_MODE_GUEST
3359            } else {
3360                0
3361            },
3362        );
3363    }
3364
3365    async fn emulate_gdtr_or_idtr(
3366        &mut self,
3367        vtl: GuestVtl,
3368        dev: &impl CpuIo,
3369    ) -> Result<(), VpHaltReason> {
3370        let exit_info = TdxExit(self.runner.tdx_vp_enter_exit_info());
3371        assert_eq!(
3372            exit_info.code().vmx_exit().basic_reason(),
3373            VmxExitBasic::GDTR_OR_IDTR
3374        );
3375        let instr_info = GdtrOrIdtrInstructionInfo::from(exit_info.instr_info().info());
3376
3377        // Check if load instructions are executed outside of kernel mode.
3378        // Check if store instructions are blocked by UMIP.
3379        if (instr_info.instruction().is_load() && exit_info.cpl() != 0)
3380            || (!instr_info.instruction().is_load()
3381                && exit_info.cpl() > 0
3382                && self.read_cr4(vtl) & X64_CR4_UMIP != 0)
3383        {
3384            self.inject_gpf(vtl);
3385            return Ok(());
3386        }
3387
3388        let (gva, segment) = self.compute_gva_for_table_access_emulation(
3389            exit_info.qualification(),
3390            (!instr_info.base_register_invalid()).then_some(instr_info.base_register()),
3391            (!instr_info.index_register_invalid()).then_some(instr_info.index_register()),
3392            instr_info.scaling(),
3393            instr_info.address_size(),
3394            instr_info.segment_register(),
3395        );
3396
3397        let gm = &self.partition.gm[vtl];
3398        let interruption_pending = self.backing.vtls[vtl].interruption_information.valid();
3399        let len = 2 + if self.long_mode(vtl) { 8 } else { 4 };
3400        let mut buf = [0u8; 10];
3401
3402        match instr_info.instruction() {
3403            GdtrOrIdtrInstruction::Sidt | GdtrOrIdtrInstruction::Sgdt => {
3404                let table = self.read_table_register(
3405                    vtl,
3406                    if matches!(instr_info.instruction(), GdtrOrIdtrInstruction::Sidt) {
3407                        TdxTableReg::Idtr
3408                    } else {
3409                        TdxTableReg::Gdtr
3410                    },
3411                );
3412                buf[..2].copy_from_slice(&table.limit.to_le_bytes());
3413                buf[2..].copy_from_slice(&table.base.to_le_bytes());
3414                let mut emulation_state = UhEmulationState {
3415                    vp: &mut *self,
3416                    interruption_pending,
3417                    devices: dev,
3418                    vtl,
3419                    cache: TdxEmulationCache::default(),
3420                };
3421                emulate_insn_memory_op(
3422                    &mut emulation_state,
3423                    gm,
3424                    dev,
3425                    gva,
3426                    segment,
3427                    x86emu::AlignmentMode::Unaligned,
3428                    EmulatedMemoryOperation::Write(&buf[..len]),
3429                )
3430                .await?;
3431            }
3432
3433            GdtrOrIdtrInstruction::Lgdt | GdtrOrIdtrInstruction::Lidt => {
3434                let mut emulation_state = UhEmulationState {
3435                    vp: &mut *self,
3436                    interruption_pending,
3437                    devices: dev,
3438                    vtl,
3439                    cache: TdxEmulationCache::default(),
3440                };
3441                emulate_insn_memory_op(
3442                    &mut emulation_state,
3443                    gm,
3444                    dev,
3445                    gva,
3446                    segment,
3447                    x86emu::AlignmentMode::Unaligned,
3448                    EmulatedMemoryOperation::Read(&mut buf[..len]),
3449                )
3450                .await?;
3451                let table = TableRegister {
3452                    limit: u16::from_le_bytes(buf[..2].try_into().unwrap()),
3453                    base: u64::from_le_bytes(buf[2..len].try_into().unwrap()),
3454                };
3455                self.write_table_register(
3456                    vtl,
3457                    if matches!(instr_info.instruction(), GdtrOrIdtrInstruction::Lidt) {
3458                        TdxTableReg::Idtr
3459                    } else {
3460                        TdxTableReg::Gdtr
3461                    },
3462                    table,
3463                );
3464            }
3465        }
3466
3467        self.advance_to_next_instruction(vtl);
3468        Ok(())
3469    }
3470
3471    async fn emulate_ldtr_or_tr(
3472        &mut self,
3473        vtl: GuestVtl,
3474        dev: &impl CpuIo,
3475    ) -> Result<(), VpHaltReason> {
3476        let exit_info = TdxExit(self.runner.tdx_vp_enter_exit_info());
3477        assert_eq!(
3478            exit_info.code().vmx_exit().basic_reason(),
3479            VmxExitBasic::LDTR_OR_TR
3480        );
3481        let instr_info = LdtrOrTrInstructionInfo::from(exit_info.instr_info().info());
3482
3483        // Check if load instructions are executed outside of kernel mode.
3484        // Check if store instructions are blocked by UMIP.
3485        if (instr_info.instruction().is_load() && exit_info.cpl() != 0)
3486            || (!instr_info.instruction().is_load()
3487                && exit_info.cpl() > 0
3488                && self.read_cr4(vtl) & X64_CR4_UMIP != 0)
3489        {
3490            self.inject_gpf(vtl);
3491            return Ok(());
3492        }
3493
3494        let gm = &self.partition.gm[vtl];
3495        let interruption_pending = self.backing.vtls[vtl].interruption_information.valid();
3496
3497        match instr_info.instruction() {
3498            LdtrOrTrInstruction::Sldt | LdtrOrTrInstruction::Str => {
3499                let value = self.runner.read_vmcs16(
3500                    vtl,
3501                    if matches!(instr_info.instruction(), LdtrOrTrInstruction::Sldt) {
3502                        TdxSegmentReg::Ldtr
3503                    } else {
3504                        TdxSegmentReg::Tr
3505                    }
3506                    .selector(),
3507                );
3508
3509                if instr_info.memory_or_register() {
3510                    let gps = self.runner.tdx_enter_guest_gps_mut();
3511                    gps[instr_info.register_1() as usize] = value.into();
3512                } else {
3513                    let (gva, segment) = self.compute_gva_for_table_access_emulation(
3514                        exit_info.qualification(),
3515                        (!instr_info.base_register_invalid()).then_some(instr_info.base_register()),
3516                        (!instr_info.index_register_invalid())
3517                            .then_some(instr_info.index_register()),
3518                        instr_info.scaling(),
3519                        instr_info.address_size(),
3520                        instr_info.segment_register(),
3521                    );
3522                    let mut emulation_state = UhEmulationState {
3523                        vp: &mut *self,
3524                        interruption_pending,
3525                        devices: dev,
3526                        vtl,
3527                        cache: TdxEmulationCache::default(),
3528                    };
3529                    emulate_insn_memory_op(
3530                        &mut emulation_state,
3531                        gm,
3532                        dev,
3533                        gva,
3534                        segment,
3535                        x86emu::AlignmentMode::Standard,
3536                        EmulatedMemoryOperation::Write(&value.to_le_bytes()),
3537                    )
3538                    .await?;
3539                }
3540            }
3541
3542            LdtrOrTrInstruction::Lldt | LdtrOrTrInstruction::Ltr => {
3543                let value = if instr_info.memory_or_register() {
3544                    let gps = self.runner.tdx_enter_guest_gps();
3545                    gps[instr_info.register_1() as usize] as u16
3546                } else {
3547                    let (gva, segment) = self.compute_gva_for_table_access_emulation(
3548                        exit_info.qualification(),
3549                        (!instr_info.base_register_invalid()).then_some(instr_info.base_register()),
3550                        (!instr_info.index_register_invalid())
3551                            .then_some(instr_info.index_register()),
3552                        instr_info.scaling(),
3553                        instr_info.address_size(),
3554                        instr_info.segment_register(),
3555                    );
3556                    let mut emulation_state = UhEmulationState {
3557                        vp: &mut *self,
3558                        interruption_pending,
3559                        devices: dev,
3560                        vtl,
3561                        cache: TdxEmulationCache::default(),
3562                    };
3563                    let mut buf = [0u8; 2];
3564                    emulate_insn_memory_op(
3565                        &mut emulation_state,
3566                        gm,
3567                        dev,
3568                        gva,
3569                        segment,
3570                        x86emu::AlignmentMode::Standard,
3571                        EmulatedMemoryOperation::Read(&mut buf),
3572                    )
3573                    .await?;
3574                    u16::from_le_bytes(buf)
3575                };
3576                self.runner.write_vmcs16(
3577                    vtl,
3578                    if matches!(instr_info.instruction(), LdtrOrTrInstruction::Lldt) {
3579                        TdxSegmentReg::Ldtr
3580                    } else {
3581                        TdxSegmentReg::Tr
3582                    }
3583                    .selector(),
3584                    !0,
3585                    value,
3586                );
3587            }
3588        }
3589
3590        self.advance_to_next_instruction(vtl);
3591        Ok(())
3592    }
3593
3594    fn compute_gva_for_table_access_emulation(
3595        &self,
3596        qualification: u64,
3597        base_reg: Option<u8>,
3598        index_reg: Option<u8>,
3599        scaling: u8,
3600        address_size: u8,
3601        segment_register: u8,
3602    ) -> (u64, Segment) {
3603        let gps = self.runner.tdx_enter_guest_gps();
3604
3605        // Displacement is stored in the qualification field for these instructions.
3606        let mut gva = qualification;
3607        if let Some(base_register) = base_reg {
3608            gva += gps[base_register as usize];
3609        }
3610        if let Some(index_register) = index_reg {
3611            gva += gps[index_register as usize] << scaling;
3612        }
3613        match address_size {
3614            // 16-bit address size
3615            0 => gva &= 0xFFFF,
3616            // 32-bit address size
3617            1 => gva &= 0xFFFFFFFF,
3618            // 64-bit address size
3619            2 => {}
3620            _ => unreachable!(),
3621        }
3622
3623        let segment = match segment_register {
3624            0 => Segment::ES,
3625            1 => Segment::CS,
3626            2 => Segment::SS,
3627            3 => Segment::DS,
3628            4 => Segment::FS,
3629            5 => Segment::GS,
3630            _ => unreachable!(),
3631        };
3632
3633        (gva, segment)
3634    }
3635}
3636
3637struct TdxApicClient<'a, T> {
3638    partition: &'a UhPartitionInner,
3639    apic_page: &'a mut VmxApicPage,
3640    dev: &'a T,
3641    vmtime: &'a VmTimeAccess,
3642    vtl: GuestVtl,
3643}
3644
3645impl<T: CpuIo> ApicClient for TdxApicClient<'_, T> {
3646    fn cr8(&mut self) -> u32 {
3647        self.apic_page.tpr.value >> 4
3648    }
3649
3650    fn set_cr8(&mut self, value: u32) {
3651        self.apic_page.tpr.value = value << 4;
3652    }
3653
3654    fn set_apic_base(&mut self, _value: u64) {
3655        // No-op--the APIC base is stored in the APIC itself.
3656    }
3657
3658    fn wake(&mut self, vp_index: VpIndex) {
3659        self.partition.vps[vp_index.index() as usize].wake(self.vtl, WakeReason::INTCON);
3660    }
3661
3662    fn eoi(&mut self, vector: u8) {
3663        self.dev.handle_eoi(vector.into())
3664    }
3665
3666    fn now(&mut self) -> vmcore::vmtime::VmTime {
3667        self.vmtime.now()
3668    }
3669
3670    fn pull_offload(&mut self) -> ([u32; 8], [u32; 8]) {
3671        pull_apic_offload(self.apic_page)
3672    }
3673}
3674
3675fn pull_apic_offload(page: &mut VmxApicPage) -> ([u32; 8], [u32; 8]) {
3676    let mut irr = [0; 8];
3677    let mut isr = [0; 8];
3678    for (((irr, page_irr), isr), page_isr) in irr
3679        .iter_mut()
3680        .zip(page.irr.iter_mut())
3681        .zip(isr.iter_mut())
3682        .zip(page.isr.iter_mut())
3683    {
3684        *irr = std::mem::take(&mut page_irr.value);
3685        *isr = std::mem::take(&mut page_isr.value);
3686    }
3687    (irr, isr)
3688}
3689
3690impl hv1_hypercall::X64RegisterState for UhHypercallHandler<'_, '_, TdxBacked> {
3691    fn rip(&mut self) -> u64 {
3692        self.vp.backing.vtls[self.intercepted_vtl].private_regs.rip
3693    }
3694
3695    fn set_rip(&mut self, rip: u64) {
3696        self.vp.backing.vtls[self.intercepted_vtl].private_regs.rip = rip;
3697    }
3698
3699    fn gp(&mut self, n: hv1_hypercall::X64HypercallRegister) -> u64 {
3700        self.vp.runner.tdx_enter_guest_gps()[n as usize]
3701    }
3702
3703    fn set_gp(&mut self, n: hv1_hypercall::X64HypercallRegister, value: u64) {
3704        let gps = self.vp.runner.tdx_enter_guest_gps_mut();
3705        gps[n as usize] = value;
3706    }
3707
3708    // TODO: cleanup xmm to not use same as mshv
3709    fn xmm(&mut self, n: usize) -> u128 {
3710        u128::from_ne_bytes(self.vp.runner.fx_state().xmm[n])
3711    }
3712
3713    fn set_xmm(&mut self, n: usize, value: u128) {
3714        self.vp.runner.fx_state_mut().xmm[n] = value.to_ne_bytes();
3715    }
3716}
3717
3718impl UhHypercallHandler<'_, '_, TdxBacked> {
3719    const TDX_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
3720        Self,
3721        [
3722            hv1_hypercall::HvModifySparseGpaPageHostVisibility,
3723            hv1_hypercall::HvQuerySparseGpaPageHostVisibility,
3724            hv1_hypercall::HvX64StartVirtualProcessor,
3725            hv1_hypercall::HvGetVpIndexFromApicId,
3726            hv1_hypercall::HvRetargetDeviceInterrupt,
3727            hv1_hypercall::HvFlushVirtualAddressList,
3728            hv1_hypercall::HvFlushVirtualAddressListEx,
3729            hv1_hypercall::HvFlushVirtualAddressSpace,
3730            hv1_hypercall::HvFlushVirtualAddressSpaceEx,
3731            hv1_hypercall::HvPostMessage,
3732            hv1_hypercall::HvSignalEvent,
3733            hv1_hypercall::HvExtQueryCapabilities,
3734            hv1_hypercall::HvGetVpRegisters,
3735            hv1_hypercall::HvSetVpRegisters,
3736            hv1_hypercall::HvEnablePartitionVtl,
3737            hv1_hypercall::HvX64EnableVpVtl,
3738            hv1_hypercall::HvVtlCall,
3739            hv1_hypercall::HvVtlReturn,
3740            hv1_hypercall::HvModifyVtlProtectionMask,
3741            hv1_hypercall::HvX64TranslateVirtualAddress,
3742            hv1_hypercall::HvSendSyntheticClusterIpi,
3743            hv1_hypercall::HvSendSyntheticClusterIpiEx,
3744            hv1_hypercall::HvInstallIntercept,
3745            hv1_hypercall::HvAssertVirtualInterrupt,
3746        ]
3747    );
3748
3749    /// Hypercalls that come through a tdg.vp.vmcall tdcall instruction.
3750    ///
3751    /// This is just to handle the proxy synic.
3752    const TDCALL_DISPATCHER: hv1_hypercall::Dispatcher<Self> = hv1_hypercall::dispatcher!(
3753        Self,
3754        [hv1_hypercall::HvPostMessage, hv1_hypercall::HvSignalEvent],
3755    );
3756}
3757
3758impl AccessVpState for UhVpStateAccess<'_, '_, TdxBacked> {
3759    type Error = vp_state::Error;
3760
3761    fn caps(&self) -> &virt::x86::X86PartitionCapabilities {
3762        &self.vp.partition.caps
3763    }
3764
3765    fn commit(&mut self) -> Result<(), Self::Error> {
3766        Ok(())
3767    }
3768
3769    fn registers(&mut self) -> Result<Registers, Self::Error> {
3770        let gps = self.vp.runner.tdx_enter_guest_gps();
3771
3772        let cs = self.vp.read_segment(self.vtl, TdxSegmentReg::Cs);
3773        let ds = self.vp.read_segment(self.vtl, TdxSegmentReg::Ds);
3774        let es = self.vp.read_segment(self.vtl, TdxSegmentReg::Es);
3775        let fs = self.vp.read_segment(self.vtl, TdxSegmentReg::Fs);
3776        let gs = self.vp.read_segment(self.vtl, TdxSegmentReg::Gs);
3777        let ss = self.vp.read_segment(self.vtl, TdxSegmentReg::Ss);
3778        let tr = self.vp.read_segment(self.vtl, TdxSegmentReg::Tr);
3779        let ldtr = self.vp.read_segment(self.vtl, TdxSegmentReg::Ldtr);
3780
3781        let gdtr = self.vp.read_table_register(self.vtl, TdxTableReg::Gdtr);
3782        let idtr = self.vp.read_table_register(self.vtl, TdxTableReg::Idtr);
3783
3784        let cr0 = self.vp.read_cr0(self.vtl);
3785        let cr2 = self.vp.runner.cr2();
3786        let cr3 = self
3787            .vp
3788            .runner
3789            .read_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_CR3);
3790        let cr4 = self.vp.read_cr4(self.vtl);
3791
3792        let cr8 = self.vp.runner.tdx_apic_page(self.vtl).tpr.value >> 4;
3793
3794        let efer = self.vp.backing.vtls[self.vtl].efer;
3795
3796        Ok(Registers {
3797            rax: gps[TdxGp::RAX],
3798            rcx: gps[TdxGp::RCX],
3799            rdx: gps[TdxGp::RDX],
3800            rbx: gps[TdxGp::RBX],
3801            rsp: self.vp.backing.vtls[self.vtl].private_regs.rsp,
3802            rbp: gps[TdxGp::RBP],
3803            rsi: gps[TdxGp::RSI],
3804            rdi: gps[TdxGp::RDI],
3805            r8: gps[TdxGp::R8],
3806            r9: gps[TdxGp::R9],
3807            r10: gps[TdxGp::R10],
3808            r11: gps[TdxGp::R11],
3809            r12: gps[TdxGp::R12],
3810            r13: gps[TdxGp::R13],
3811            r14: gps[TdxGp::R14],
3812            r15: gps[TdxGp::R15],
3813            rip: self.vp.backing.vtls[self.vtl].private_regs.rip,
3814            rflags: self.vp.backing.vtls[self.vtl].private_regs.rflags,
3815            cs,
3816            ds,
3817            es,
3818            fs,
3819            gs,
3820            ss,
3821            tr,
3822            ldtr,
3823            gdtr,
3824            idtr,
3825            cr0,
3826            cr2,
3827            cr3,
3828            cr4,
3829            cr8: cr8.into(),
3830            efer,
3831        })
3832    }
3833
3834    fn set_registers(&mut self, value: &Registers) -> Result<(), Self::Error> {
3835        let Registers {
3836            rax,
3837            rcx,
3838            rdx,
3839            rbx,
3840            rsp,
3841            rbp,
3842            rsi,
3843            rdi,
3844            r8,
3845            r9,
3846            r10,
3847            r11,
3848            r12,
3849            r13,
3850            r14,
3851            r15,
3852            rip,
3853            rflags,
3854            cs,
3855            ds,
3856            es,
3857            fs,
3858            gs,
3859            ss,
3860            tr,
3861            ldtr,
3862            gdtr,
3863            idtr,
3864            cr0,
3865            cr2,
3866            cr3,
3867            cr4,
3868            cr8,
3869            efer,
3870        } = value;
3871
3872        let gps = self.vp.runner.tdx_enter_guest_gps_mut();
3873        gps[TdxGp::RAX] = *rax;
3874        gps[TdxGp::RCX] = *rcx;
3875        gps[TdxGp::RDX] = *rdx;
3876        gps[TdxGp::RBX] = *rbx;
3877        self.vp.backing.vtls[self.vtl].private_regs.rsp = *rsp;
3878        gps[TdxGp::RBP] = *rbp;
3879        gps[TdxGp::RSI] = *rsi;
3880        gps[TdxGp::RDI] = *rdi;
3881        gps[TdxGp::R8] = *r8;
3882        gps[TdxGp::R9] = *r9;
3883        gps[TdxGp::R10] = *r10;
3884        gps[TdxGp::R11] = *r11;
3885        gps[TdxGp::R12] = *r12;
3886        gps[TdxGp::R13] = *r13;
3887        gps[TdxGp::R14] = *r14;
3888        gps[TdxGp::R15] = *r15;
3889        self.vp.backing.vtls[self.vtl].private_regs.rip = *rip;
3890        // BUGBUG: rflags set also updates interrupts in hcl
3891        self.vp.backing.vtls[self.vtl].private_regs.rflags = *rflags;
3892
3893        // Set segment registers
3894        self.vp.write_segment(self.vtl, TdxSegmentReg::Cs, *cs)?;
3895        self.vp.write_segment(self.vtl, TdxSegmentReg::Ds, *ds)?;
3896        self.vp.write_segment(self.vtl, TdxSegmentReg::Es, *es)?;
3897        self.vp.write_segment(self.vtl, TdxSegmentReg::Fs, *fs)?;
3898        self.vp.write_segment(self.vtl, TdxSegmentReg::Gs, *gs)?;
3899        self.vp.write_segment(self.vtl, TdxSegmentReg::Ss, *ss)?;
3900        self.vp.write_segment(self.vtl, TdxSegmentReg::Tr, *tr)?;
3901        self.vp
3902            .write_segment(self.vtl, TdxSegmentReg::Ldtr, *ldtr)?;
3903
3904        // Set table registers
3905        self.vp
3906            .write_table_register(self.vtl, TdxTableReg::Gdtr, *gdtr);
3907        self.vp
3908            .write_table_register(self.vtl, TdxTableReg::Idtr, *idtr);
3909
3910        self.vp.write_cr0(self.vtl, *cr0)?;
3911
3912        // CR2 is shared with the kernel, so set it in the VP run page which
3913        // will be set before lower VTL entry.
3914        self.vp.runner.set_cr2(*cr2);
3915
3916        self.vp
3917            .runner
3918            .write_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_CR3, !0, *cr3);
3919
3920        self.vp.write_cr4(self.vtl, *cr4)?;
3921
3922        self.vp.runner.tdx_apic_page_mut(self.vtl).tpr.value = (*cr8 << 4) as u32;
3923
3924        self.vp.write_efer(self.vtl, *efer)?;
3925
3926        // Execution mode must be updated after setting EFER and CR0.
3927        self.vp.update_execution_mode(self.vtl);
3928
3929        Ok(())
3930    }
3931
3932    fn activity(&mut self) -> Result<vp::Activity, Self::Error> {
3933        let lapic = &self.vp.backing.cvm.lapics[self.vtl];
3934        let interruptibility: Interruptibility = self
3935            .vp
3936            .runner
3937            .read_vmcs32(self.vtl, VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY)
3938            .into();
3939        Ok(vp::Activity {
3940            mp_state: lapic.activity,
3941            nmi_pending: lapic.nmi_pending,
3942            nmi_masked: interruptibility.blocked_by_nmi(),
3943            interrupt_shadow: interruptibility.blocked_by_sti()
3944                || interruptibility.blocked_by_movss(),
3945            pending_event: None,        // TODO TDX
3946            pending_interruption: None, // TODO TDX
3947        })
3948    }
3949
3950    fn set_activity(&mut self, value: &vp::Activity) -> Result<(), Self::Error> {
3951        let &vp::Activity {
3952            mp_state,
3953            nmi_pending,
3954            nmi_masked,
3955            interrupt_shadow,
3956            pending_event: _,        // TODO TDX
3957            pending_interruption: _, // TODO TDX
3958        } = value;
3959        self.vp.backing.cvm.lapics[self.vtl].activity = mp_state;
3960        self.vp.backing.cvm.lapics[self.vtl].nmi_pending = nmi_pending;
3961        let interruptibility = Interruptibility::new()
3962            .with_blocked_by_movss(interrupt_shadow)
3963            .with_blocked_by_nmi(nmi_masked);
3964        self.vp.runner.write_vmcs32(
3965            self.vtl,
3966            VmcsField::VMX_VMCS_GUEST_INTERRUPTIBILITY,
3967            !0,
3968            interruptibility.into(),
3969        );
3970        Ok(())
3971    }
3972
3973    fn xsave(&mut self) -> Result<vp::Xsave, Self::Error> {
3974        // TODO: needed?
3975        Err(vp_state::Error::Unimplemented("xsave"))
3976    }
3977
3978    fn set_xsave(&mut self, _value: &vp::Xsave) -> Result<(), Self::Error> {
3979        // TODO: needed?
3980        Err(vp_state::Error::Unimplemented("xsave"))
3981    }
3982
3983    fn apic(&mut self) -> Result<vp::Apic, Self::Error> {
3984        self.vp.access_apic_without_offload(self.vtl, |vp| {
3985            Ok(vp.backing.cvm.lapics[self.vtl].lapic.save())
3986        })
3987    }
3988
3989    fn set_apic(&mut self, value: &vp::Apic) -> Result<(), Self::Error> {
3990        self.vp.access_apic_without_offload(self.vtl, |vp| {
3991            vp.backing.cvm.lapics[self.vtl]
3992                .lapic
3993                .restore(value)
3994                .map_err(vp_state::Error::InvalidApicBase)?;
3995
3996            Ok(())
3997        })
3998    }
3999
4000    fn xcr(&mut self) -> Result<vp::Xcr0, Self::Error> {
4001        Ok(vp::Xcr0 {
4002            value: self
4003                .vp
4004                .runner
4005                .get_vp_register(self.vtl, HvX64RegisterName::Xfem)
4006                .unwrap()
4007                .as_u64(),
4008        })
4009    }
4010
4011    fn set_xcr(&mut self, _value: &vp::Xcr0) -> Result<(), Self::Error> {
4012        Err(vp_state::Error::Unimplemented("xcr"))
4013    }
4014
4015    fn xss(&mut self) -> Result<vp::Xss, Self::Error> {
4016        Ok(vp::Xss {
4017            value: self.vp.backing.vtls[self.vtl].private_regs.msr_xss,
4018        })
4019    }
4020
4021    fn set_xss(&mut self, value: &vp::Xss) -> Result<(), Self::Error> {
4022        self.vp.backing.vtls[self.vtl].private_regs.msr_xss = value.value;
4023        Ok(())
4024    }
4025
4026    fn mtrrs(&mut self) -> Result<vp::Mtrrs, Self::Error> {
4027        Ok(vp::Mtrrs {
4028            msr_mtrr_def_type: 0, // TODO TDX: MTRRs
4029            fixed: [0; 11],       // TODO TDX: MTRRs
4030            variable: [0; 16],    // TODO TDX: MTRRs
4031        })
4032    }
4033
4034    fn set_mtrrs(&mut self, _value: &vp::Mtrrs) -> Result<(), Self::Error> {
4035        // TODO TDX: MTRRs
4036        Ok(())
4037    }
4038
4039    fn pat(&mut self) -> Result<vp::Pat, Self::Error> {
4040        let msr_cr_pat = self
4041            .vp
4042            .runner
4043            .read_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_PAT);
4044        Ok(vp::Pat { value: msr_cr_pat })
4045    }
4046
4047    fn set_pat(&mut self, value: &vp::Pat) -> Result<(), Self::Error> {
4048        self.vp
4049            .runner
4050            .write_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_PAT, !0, value.value);
4051        Ok(())
4052    }
4053
4054    fn virtual_msrs(&mut self) -> Result<vp::VirtualMsrs, Self::Error> {
4055        let state = &self.vp.backing.vtls[self.vtl].private_regs;
4056
4057        let sysenter_cs = self
4058            .vp
4059            .runner
4060            .read_vmcs32(self.vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_CS_MSR)
4061            .into();
4062        let sysenter_eip = self
4063            .vp
4064            .runner
4065            .read_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_EIP_MSR);
4066        let sysenter_esp = self
4067            .vp
4068            .runner
4069            .read_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_SYSENTER_ESP_MSR);
4070
4071        Ok(vp::VirtualMsrs {
4072            kernel_gs_base: state.msr_kernel_gs_base,
4073            sysenter_cs,
4074            sysenter_eip,
4075            sysenter_esp,
4076            star: state.msr_star,
4077            lstar: state.msr_lstar,
4078            cstar: self.vp.backing.vtls[self.vtl].msr_cstar,
4079            sfmask: state.msr_sfmask,
4080        })
4081    }
4082
4083    fn set_virtual_msrs(&mut self, value: &vp::VirtualMsrs) -> Result<(), Self::Error> {
4084        let &vp::VirtualMsrs {
4085            kernel_gs_base,
4086            sysenter_cs,
4087            sysenter_eip,
4088            sysenter_esp,
4089            star,
4090            lstar,
4091            cstar,
4092            sfmask,
4093        } = value;
4094
4095        let state = &mut self.vp.backing.vtls[self.vtl].private_regs;
4096        state.msr_kernel_gs_base = kernel_gs_base;
4097        state.msr_star = star;
4098        state.msr_lstar = lstar;
4099        state.msr_sfmask = sfmask;
4100
4101        self.vp.runner.write_vmcs32(
4102            self.vtl,
4103            VmcsField::VMX_VMCS_GUEST_SYSENTER_CS_MSR,
4104            !0,
4105            sysenter_cs as u32,
4106        );
4107        self.vp.runner.write_vmcs64(
4108            self.vtl,
4109            VmcsField::VMX_VMCS_GUEST_SYSENTER_EIP_MSR,
4110            !0,
4111            sysenter_eip,
4112        );
4113        self.vp.runner.write_vmcs64(
4114            self.vtl,
4115            VmcsField::VMX_VMCS_GUEST_SYSENTER_ESP_MSR,
4116            !0,
4117            sysenter_esp,
4118        );
4119
4120        self.vp.backing.vtls[self.vtl].msr_cstar = cstar;
4121
4122        Ok(())
4123    }
4124
4125    fn debug_regs(&mut self) -> Result<vp::DebugRegisters, Self::Error> {
4126        let mut values = [0u64.into(); 5];
4127        self.vp
4128            .runner
4129            .get_vp_registers(
4130                self.vtl,
4131                &[
4132                    HvX64RegisterName::Dr0,
4133                    HvX64RegisterName::Dr1,
4134                    HvX64RegisterName::Dr2,
4135                    HvX64RegisterName::Dr3,
4136                    HvX64RegisterName::Dr6,
4137                ],
4138                &mut values,
4139            )
4140            .map_err(vp_state::Error::GetRegisters)?;
4141
4142        let dr7 = self
4143            .vp
4144            .runner
4145            .read_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_DR7);
4146
4147        Ok(vp::DebugRegisters {
4148            dr0: values[0].as_u64(),
4149            dr1: values[1].as_u64(),
4150            dr2: values[2].as_u64(),
4151            dr3: values[3].as_u64(),
4152            dr6: values[4].as_u64(),
4153            dr7,
4154        })
4155    }
4156
4157    fn set_debug_regs(&mut self, value: &vp::DebugRegisters) -> Result<(), Self::Error> {
4158        let &vp::DebugRegisters {
4159            dr0,
4160            dr1,
4161            dr2,
4162            dr3,
4163            dr6,
4164            dr7,
4165        } = value;
4166        self.vp
4167            .runner
4168            .set_vp_registers(
4169                self.vtl,
4170                [
4171                    (HvX64RegisterName::Dr0, dr0),
4172                    (HvX64RegisterName::Dr1, dr1),
4173                    (HvX64RegisterName::Dr2, dr2),
4174                    (HvX64RegisterName::Dr3, dr3),
4175                    (HvX64RegisterName::Dr6, dr6),
4176                ],
4177            )
4178            .map_err(vp_state::Error::SetRegisters)?;
4179
4180        self.vp
4181            .runner
4182            .write_vmcs64(self.vtl, VmcsField::VMX_VMCS_GUEST_DR7, !0, dr7);
4183
4184        Ok(())
4185    }
4186
4187    fn tsc(&mut self) -> Result<vp::Tsc, Self::Error> {
4188        Err(vp_state::Error::Unimplemented("tsc"))
4189    }
4190
4191    fn set_tsc(&mut self, _value: &vp::Tsc) -> Result<(), Self::Error> {
4192        Err(vp_state::Error::Unimplemented("tsc"))
4193    }
4194
4195    fn tsc_aux(&mut self) -> Result<vp::TscAux, Self::Error> {
4196        Ok(vp::TscAux {
4197            value: self.vp.backing.vtls[self.vtl].private_regs.msr_tsc_aux,
4198        })
4199    }
4200
4201    fn set_tsc_aux(&mut self, value: &vp::TscAux) -> Result<(), Self::Error> {
4202        self.vp.backing.vtls[self.vtl].private_regs.msr_tsc_aux = value.value;
4203        Ok(())
4204    }
4205
4206    fn cet(&mut self) -> Result<vp::Cet, Self::Error> {
4207        Err(vp_state::Error::Unimplemented("cet"))
4208    }
4209
4210    fn set_cet(&mut self, _value: &vp::Cet) -> Result<(), Self::Error> {
4211        Err(vp_state::Error::Unimplemented("cet"))
4212    }
4213
4214    fn cet_ss(&mut self) -> Result<vp::CetSs, Self::Error> {
4215        Err(vp_state::Error::Unimplemented("cet_ss"))
4216    }
4217
4218    fn set_cet_ss(&mut self, _value: &vp::CetSs) -> Result<(), Self::Error> {
4219        Err(vp_state::Error::Unimplemented("cet_ss"))
4220    }
4221
4222    fn synic_msrs(&mut self) -> Result<vp::SyntheticMsrs, Self::Error> {
4223        Err(vp_state::Error::Unimplemented("synic_msrs"))
4224    }
4225
4226    fn set_synic_msrs(&mut self, _value: &vp::SyntheticMsrs) -> Result<(), Self::Error> {
4227        Err(vp_state::Error::Unimplemented("synic_msrs"))
4228    }
4229
4230    fn synic_message_page(&mut self) -> Result<vp::SynicMessagePage, Self::Error> {
4231        Err(vp_state::Error::Unimplemented("synic_message_page"))
4232    }
4233
4234    fn set_synic_message_page(&mut self, _value: &vp::SynicMessagePage) -> Result<(), Self::Error> {
4235        Err(vp_state::Error::Unimplemented("synic_message_page"))
4236    }
4237
4238    fn synic_event_flags_page(&mut self) -> Result<vp::SynicEventFlagsPage, Self::Error> {
4239        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
4240    }
4241
4242    fn set_synic_event_flags_page(
4243        &mut self,
4244        _value: &vp::SynicEventFlagsPage,
4245    ) -> Result<(), Self::Error> {
4246        Err(vp_state::Error::Unimplemented("synic_event_flags_page"))
4247    }
4248
4249    fn synic_message_queues(&mut self) -> Result<vp::SynicMessageQueues, Self::Error> {
4250        Err(vp_state::Error::Unimplemented("synic_message_queues"))
4251    }
4252
4253    fn set_synic_message_queues(
4254        &mut self,
4255        _value: &vp::SynicMessageQueues,
4256    ) -> Result<(), Self::Error> {
4257        Err(vp_state::Error::Unimplemented("synic_message_queues"))
4258    }
4259
4260    fn synic_timers(&mut self) -> Result<vp::SynicTimers, Self::Error> {
4261        Err(vp_state::Error::Unimplemented("synic_timers"))
4262    }
4263
4264    fn set_synic_timers(&mut self, _value: &vp::SynicTimers) -> Result<(), Self::Error> {
4265        Err(vp_state::Error::Unimplemented("synic_timers"))
4266    }
4267
4268    fn nested_state(&mut self) -> Result<vp::NestedState, Self::Error> {
4269        Err(vp_state::Error::Unimplemented("nested_state"))
4270    }
4271
4272    fn set_nested_state(&mut self, _value: &vp::NestedState) -> Result<(), Self::Error> {
4273        Err(vp_state::Error::Unimplemented("nested_state"))
4274    }
4275}
4276
4277/// Compute the index of the highest vector set in IRR/ISR, or 0
4278/// if no vector is set. (Vectors 0-15 are invalid so this is not
4279/// ambiguous.)
4280fn top_vector(reg: &[ApicRegisterValue; 8]) -> u8 {
4281    reg.iter()
4282        .enumerate()
4283        .rev()
4284        .find_map(|(i, r)| {
4285            (r.value != 0).then(|| (i as u32 * 32 + (31 - r.value.leading_zeros())) as u8)
4286        })
4287        .unwrap_or(0)
4288}
4289
4290struct TdHypercall<'a, 'b>(UhHypercallHandler<'a, 'b, TdxBacked>);
4291
4292impl<'a, 'b> AsHandler<UhHypercallHandler<'a, 'b, TdxBacked>> for TdHypercall<'a, 'b> {
4293    fn as_handler(&mut self) -> &mut UhHypercallHandler<'a, 'b, TdxBacked> {
4294        &mut self.0
4295    }
4296}
4297
4298impl HypercallIo for TdHypercall<'_, '_> {
4299    fn advance_ip(&mut self) {
4300        self.0.vp.runner.tdx_enter_guest_gps_mut()[TdxGp::R10] = 0;
4301        self.0.vp.backing.vtls[self.0.intercepted_vtl]
4302            .private_regs
4303            .rip = self.0.vp.backing.vtls[self.0.intercepted_vtl]
4304            .private_regs
4305            .rip
4306            .wrapping_add(4);
4307    }
4308
4309    fn retry(&mut self, control: u64) {
4310        self.0.vp.runner.tdx_enter_guest_gps_mut()[TdxGp::R10] = control;
4311        self.set_result(hvdef::hypercall::HypercallOutput::from(HvError::Timeout).into());
4312    }
4313
4314    fn control(&mut self) -> u64 {
4315        self.0.vp.runner.tdx_enter_guest_gps()[TdxGp::R10]
4316    }
4317
4318    fn input_gpa(&mut self) -> u64 {
4319        self.0.vp.runner.tdx_enter_guest_gps()[TdxGp::RDX]
4320    }
4321
4322    fn output_gpa(&mut self) -> u64 {
4323        self.0.vp.runner.tdx_enter_guest_gps()[TdxGp::R8]
4324    }
4325
4326    fn fast_register_pair_count(&mut self) -> usize {
4327        7
4328    }
4329
4330    fn extended_fast_hypercalls_ok(&mut self) -> bool {
4331        false
4332    }
4333
4334    fn fast_input(&mut self, buf: &mut [[u64; 2]], _output_register_pairs: usize) -> usize {
4335        self.fast_regs(0, buf);
4336        buf.len()
4337    }
4338
4339    fn fast_output(&mut self, _starting_pair_index: usize, buf: &[[u64; 2]]) {
4340        assert!(buf.is_empty());
4341    }
4342
4343    fn vtl_input(&mut self) -> u64 {
4344        unreachable!()
4345    }
4346
4347    fn set_result(&mut self, n: u64) {
4348        self.0.vp.runner.tdx_enter_guest_gps_mut()[TdxGp::R11] = n;
4349    }
4350
4351    fn fast_regs(&mut self, starting_pair_index: usize, buf: &mut [[u64; 2]]) {
4352        let regs = self.0.vp.runner.tdx_enter_guest_gps();
4353        let fx_state = self.0.vp.runner.fx_state();
4354        for (i, [low, high]) in buf.iter_mut().enumerate() {
4355            let index = i + starting_pair_index;
4356            if index == 0 {
4357                *low = regs[TdxGp::RDX];
4358                *high = regs[TdxGp::R8];
4359            } else {
4360                let value = u128::from_ne_bytes(fx_state.xmm[index - 1]);
4361                *low = value as u64;
4362                *high = (value >> 64) as u64;
4363            }
4364        }
4365    }
4366}
4367
4368impl hv1_hypercall::VtlSwitchOps for UhHypercallHandler<'_, '_, TdxBacked> {
4369    fn advance_ip(&mut self) {
4370        let long_mode = self.vp.long_mode(self.intercepted_vtl);
4371        let mut io = hv1_hypercall::X64RegisterIo::new(self, long_mode, true);
4372        io.advance_ip();
4373    }
4374
4375    fn inject_invalid_opcode_fault(&mut self) {
4376        self.vp.backing.vtls[self.intercepted_vtl].interruption_information =
4377            InterruptionInformation::new()
4378                .with_valid(true)
4379                .with_interruption_type(INTERRUPT_TYPE_HARDWARE_EXCEPTION)
4380                .with_vector(x86defs::Exception::INVALID_OPCODE.0);
4381    }
4382}
4383
4384impl hv1_hypercall::FlushVirtualAddressList for UhHypercallHandler<'_, '_, TdxBacked> {
4385    fn flush_virtual_address_list(
4386        &mut self,
4387        processor_set: ProcessorSet<'_>,
4388        flags: HvFlushFlags,
4389        gva_ranges: &[HvGvaRange],
4390    ) -> HvRepResult {
4391        hv1_hypercall::FlushVirtualAddressListEx::flush_virtual_address_list_ex(
4392            self,
4393            processor_set,
4394            flags,
4395            gva_ranges,
4396        )
4397    }
4398}
4399
4400impl hv1_hypercall::FlushVirtualAddressListEx for UhHypercallHandler<'_, '_, TdxBacked> {
4401    fn flush_virtual_address_list_ex(
4402        &mut self,
4403        processor_set: ProcessorSet<'_>,
4404        flags: HvFlushFlags,
4405        gva_ranges: &[HvGvaRange],
4406    ) -> HvRepResult {
4407        self.hcvm_validate_flush_inputs(processor_set, flags, true)
4408            .map_err(|e| (e, 0))?;
4409
4410        let vtl = self.intercepted_vtl;
4411        let flush_state = &self.vp.shared.flush_state[vtl];
4412
4413        // If we fail to add ranges to the list for any reason then promote this request to a flush entire.
4414        if let Err(()) = Self::add_ranges_to_tlb_flush_list(
4415            flush_state,
4416            gva_ranges,
4417            flags.use_extended_range_format(),
4418        ) {
4419            if flags.non_global_mappings_only() {
4420                flush_state
4421                    .flush_entire_non_global_counter
4422                    .fetch_add(1, Ordering::Relaxed);
4423            } else {
4424                flush_state
4425                    .flush_entire_counter
4426                    .fetch_add(1, Ordering::Relaxed);
4427            }
4428        }
4429
4430        // Send flush IPIs to the specified VPs.
4431        TdxTlbLockFlushAccess {
4432            vp_index: Some(self.vp.vp_index()),
4433            partition: self.vp.partition,
4434            shared: self.vp.shared,
4435        }
4436        .wake_processors_for_tlb_flush(vtl, (!flags.all_processors()).then_some(processor_set));
4437
4438        // Mark that this VP needs to wait for all TLB locks to be released before returning.
4439        self.vp.set_wait_for_tlb_locks(vtl);
4440
4441        Ok(())
4442    }
4443}
4444
4445impl hv1_hypercall::FlushVirtualAddressSpace for UhHypercallHandler<'_, '_, TdxBacked> {
4446    fn flush_virtual_address_space(
4447        &mut self,
4448        processor_set: ProcessorSet<'_>,
4449        flags: HvFlushFlags,
4450    ) -> hvdef::HvResult<()> {
4451        hv1_hypercall::FlushVirtualAddressSpaceEx::flush_virtual_address_space_ex(
4452            self,
4453            processor_set,
4454            flags,
4455        )
4456    }
4457}
4458
4459impl hv1_hypercall::FlushVirtualAddressSpaceEx for UhHypercallHandler<'_, '_, TdxBacked> {
4460    fn flush_virtual_address_space_ex(
4461        &mut self,
4462        processor_set: ProcessorSet<'_>,
4463        flags: HvFlushFlags,
4464    ) -> hvdef::HvResult<()> {
4465        self.hcvm_validate_flush_inputs(processor_set, flags, false)?;
4466        let vtl = self.intercepted_vtl;
4467
4468        let flush_state = &self.vp.shared.flush_state[vtl];
4469
4470        // Set flush entire.
4471        if flags.non_global_mappings_only() {
4472            flush_state
4473                .flush_entire_non_global_counter
4474                .fetch_add(1, Ordering::Relaxed);
4475        } else {
4476            flush_state
4477                .flush_entire_counter
4478                .fetch_add(1, Ordering::Relaxed);
4479        }
4480
4481        // Send flush IPIs to the specified VPs.
4482        TdxTlbLockFlushAccess {
4483            vp_index: Some(self.vp.vp_index()),
4484            partition: self.vp.partition,
4485            shared: self.vp.shared,
4486        }
4487        .wake_processors_for_tlb_flush(vtl, (!flags.all_processors()).then_some(processor_set));
4488
4489        // Mark that this VP needs to wait for all TLB locks to be released before returning.
4490        self.vp.set_wait_for_tlb_locks(vtl);
4491
4492        Ok(())
4493    }
4494}
4495
4496impl UhHypercallHandler<'_, '_, TdxBacked> {
4497    fn add_ranges_to_tlb_flush_list(
4498        flush_state: &TdxPartitionFlushState,
4499        gva_ranges: &[HvGvaRange],
4500        use_extended_range_format: bool,
4501    ) -> Result<(), ()> {
4502        // If there are more gvas than the list size there's no point in filling the list.
4503        if gva_ranges.len() > FLUSH_GVA_LIST_SIZE {
4504            return Err(());
4505        }
4506
4507        if use_extended_range_format
4508            && gva_ranges
4509                .iter()
4510                .any(|range| range.as_extended().large_page())
4511        {
4512            // TDX does not provide a way to flush large page ranges,
4513            // we have to promote this request to a flush entire.
4514            return Err(());
4515        }
4516
4517        flush_state
4518            .gva_list
4519            .write()
4520            .extend(gva_ranges.iter().copied());
4521
4522        Ok(())
4523    }
4524}
4525
4526impl TdxTlbLockFlushAccess<'_> {
4527    fn wake_processors_for_tlb_flush(
4528        &mut self,
4529        target_vtl: GuestVtl,
4530        processor_set: Option<ProcessorSet<'_>>,
4531    ) {
4532        match processor_set {
4533            Some(processors) => {
4534                self.wake_processors_for_tlb_flush_inner(target_vtl, processors);
4535            }
4536            None => self.wake_processors_for_tlb_flush_inner(
4537                target_vtl,
4538                0..(self.partition.vps.len() as u32),
4539            ),
4540        }
4541    }
4542
4543    fn wake_processors_for_tlb_flush_inner(
4544        &mut self,
4545        target_vtl: GuestVtl,
4546        processors: impl IntoIterator<Item = u32>,
4547    ) {
4548        // Use SeqCst ordering to ensure that we are observing the most
4549        // up-to-date value from other VPs. Otherwise we might not send a
4550        // wake to a VP in a lower VTL, which could cause TLB lock holders
4551        // to be stuck waiting until the target_vp happens to switch into
4552        // VTL 2.
4553        // We use a single fence to avoid having to take a SeqCst load
4554        // for each VP.
4555        std::sync::atomic::fence(Ordering::SeqCst);
4556        self.partition.hcl.kick_cpus(
4557            processors.into_iter().filter(|&vp| {
4558                self.shared.active_vtl[vp as usize].load(Ordering::Relaxed) == target_vtl as u8
4559            }),
4560            true,
4561            true,
4562        );
4563    }
4564}
4565
4566struct TdxTlbLockFlushAccess<'a> {
4567    vp_index: Option<VpIndex>,
4568    partition: &'a UhPartitionInner,
4569    shared: &'a TdxBackedShared,
4570}
4571
4572impl TlbFlushLockAccess for TdxTlbLockFlushAccess<'_> {
4573    fn flush(&mut self, vtl: GuestVtl) {
4574        self.shared.flush_state[vtl]
4575            .flush_entire_counter
4576            .fetch_add(1, Ordering::Relaxed);
4577
4578        self.wake_processors_for_tlb_flush(vtl, None);
4579        self.set_wait_for_tlb_locks(vtl);
4580    }
4581
4582    fn flush_entire(&mut self) {
4583        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
4584            self.shared.flush_state[vtl]
4585                .flush_entire_counter
4586                .fetch_add(1, Ordering::Relaxed);
4587        }
4588        for vtl in [GuestVtl::Vtl0, GuestVtl::Vtl1] {
4589            self.wake_processors_for_tlb_flush(vtl, None);
4590            self.set_wait_for_tlb_locks(vtl);
4591        }
4592    }
4593
4594    fn set_wait_for_tlb_locks(&mut self, vtl: GuestVtl) {
4595        if let Some(vp_index) = self.vp_index {
4596            hardware_cvm::tlb_lock::TlbLockAccess {
4597                vp_index,
4598                cvm_partition: &self.shared.cvm,
4599            }
4600            .set_wait_for_tlb_locks(vtl);
4601        }
4602    }
4603}
4604
4605mod save_restore {
4606    use super::TdxBacked;
4607    use super::UhProcessor;
4608    use vmcore::save_restore::RestoreError;
4609    use vmcore::save_restore::SaveError;
4610    use vmcore::save_restore::SaveRestore;
4611    use vmcore::save_restore::SavedStateNotSupported;
4612
4613    impl SaveRestore for UhProcessor<'_, TdxBacked> {
4614        type SavedState = SavedStateNotSupported;
4615
4616        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
4617            Err(SaveError::NotSupported)
4618        }
4619
4620        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
4621            match state {}
4622        }
4623    }
4624}