Skip to main content

x86defs/
snp.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! AMD SEV-SNP specific definitions.
5
6use crate::ApicRegisterValue;
7use bitfield_struct::bitfield;
8use static_assertions::const_assert_eq;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14// Interruption Information Field
15pub const SEV_INTR_TYPE_EXT: u32 = 0;
16pub const SEV_INTR_TYPE_NMI: u32 = 2;
17pub const SEV_INTR_TYPE_EXCEPT: u32 = 3;
18pub const SEV_INTR_TYPE_SW: u32 = 4;
19
20// Secrets page layout.
21pub const REG_TWEAK_BITMAP_OFFSET: usize = 0x100;
22pub const REG_TWEAK_BITMAP_SIZE: usize = 0x40;
23
24/// Value for the `msg_version` member in [`SNP_GUEST_REQ_MSG_VERSION`].
25/// Use 1 for now.
26pub const SNP_GUEST_REQ_MSG_VERSION: u32 = 1;
27
28#[bitfield(u64)]
29#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
30pub struct SevEventInjectInfo {
31    pub vector: u8,
32    #[bits(3)]
33    pub interruption_type: u32,
34    pub deliver_error_code: bool,
35    #[bits(19)]
36    _rsvd1: u64,
37    pub valid: bool,
38    pub error_code: u32,
39}
40
41#[repr(u8)]
42#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
43pub enum Vmpl {
44    Vmpl0 = 0,
45    Vmpl1 = 1,
46    Vmpl2 = 2,
47    Vmpl3 = 3,
48}
49
50impl From<Vmpl> for u8 {
51    fn from(value: Vmpl) -> Self {
52        value as _
53    }
54}
55
56/// A X64 selector register.
57#[repr(C)]
58#[derive(Debug, Clone, Copy, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
59pub struct SevSelector {
60    pub selector: u16,
61    pub attrib: u16,
62    pub limit: u32,
63    pub base: u64,
64}
65
66impl SevSelector {
67    pub fn as_u128(&self) -> u128 {
68        ((self.base as u128) << 64)
69            | ((self.limit as u128) << 32)
70            | ((self.attrib as u128) << 16)
71            | self.selector as u128
72    }
73}
74
75impl From<u128> for SevSelector {
76    fn from(val: u128) -> Self {
77        SevSelector {
78            selector: val as u16,
79            attrib: (val >> 16) as u16,
80            limit: (val >> 32) as u32,
81            base: (val >> 64) as u64,
82        }
83    }
84}
85
86/// An X64 XMM register.
87#[repr(C)]
88#[derive(Debug, Clone, Copy, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
89pub struct SevXmmRegister {
90    low: u64,
91    high: u64,
92}
93
94impl SevXmmRegister {
95    pub fn as_u128(&self) -> u128 {
96        ((self.high as u128) << 64) | self.low as u128
97    }
98}
99
100impl From<u128> for SevXmmRegister {
101    fn from(val: u128) -> Self {
102        SevXmmRegister {
103            low: val as u64,
104            high: (val >> 64) as u64,
105        }
106    }
107}
108
109#[bitfield(u64)]
110#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
111pub struct SevFeatures {
112    pub snp: bool,
113    pub vtom: bool,
114    pub reflect_vc: bool,
115    pub restrict_injection: bool,
116    pub alternate_injection: bool,
117    pub debug_swap: bool,
118    pub prevent_host_ibs: bool,
119    pub snp_btb_isolation: bool,
120    pub vmpl_isss: bool,
121    pub secure_tsc: bool,
122    pub vmgexit_param: bool,
123    pub pmc_virt: bool,
124    pub ibs_virt: bool,
125    pub guest_intercept_control: bool,
126    pub vmsa_reg_prot: bool,
127    pub smt_prot: bool,
128    pub secure_avic: bool,
129    #[bits(4)]
130    _reserved0: u64,
131    pub ibpb_on_entry: bool,
132    #[bits(41)]
133    _reserved1: u64,
134    pub allowed_sev_features_enable: bool,
135}
136
137#[bitfield(u64)]
138#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
139pub struct SevVirtualInterruptControl {
140    pub tpr: u8,
141    pub irq: bool,
142    pub gif: bool,
143    pub intr_shadow: bool,
144    pub nmi: bool,
145    pub nmi_mask: bool,
146    #[bits(3)]
147    _rsvd1: u64,
148    #[bits(4)]
149    pub priority: u64,
150    pub ignore_tpr: bool,
151    #[bits(5)]
152    _rsvd2: u64,
153    pub nmi_enable: bool,
154    #[bits(5)]
155    _rsvd3: u64,
156    pub vector: u8,
157    #[bits(23)]
158    _rsvd4: u64,
159    pub guest_busy: bool,
160}
161
162#[bitfield(u64)]
163#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
164pub struct SevRmpAdjust {
165    pub target_vmpl: u8,
166    pub enable_read: bool,
167    pub enable_write: bool,
168    pub enable_user_execute: bool,
169    pub enable_kernel_execute: bool,
170    #[bits(4)]
171    _rsvd1: u64,
172    pub vmsa: bool,
173    #[bits(47)]
174    _rsvd2: u64,
175}
176
177#[bitfield(u32)]
178#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
179pub struct SevIoAccessInfo {
180    pub read_access: bool,
181    #[bits(1)]
182    reserved1: u32,
183    pub string_access: bool,
184    pub rep_access: bool,
185    pub access_size8: bool,
186    pub access_size16: bool,
187    pub access_size32: bool,
188    pub address_size8: bool,
189    pub address_size16: bool,
190    pub address_size32: bool,
191    #[bits(3)]
192    pub effective_segment: u32,
193    #[bits(3)]
194    rsvd2: u32,
195    pub port: u16,
196}
197
198#[bitfield(u64)]
199#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
200pub struct SevNpfInfo {
201    pub present: bool,
202    pub is_write: bool,
203    pub user: bool,
204    pub reserved_bit_set: bool,
205    pub fetch: bool,
206    #[bits(1)]
207    rsvd5: u64,
208    pub shadow_stack: bool,
209    #[bits(24)]
210    rsvd7_31: u64,
211    pub rmp_failure: bool,
212    pub caused_by_gpa_access: bool,
213    pub caused_by_page_table_access: bool,
214    pub encrypted_access: bool,
215    pub rmp_size_mismatch: bool,
216    pub vmpl_violation: bool,
217    pub npt_supervisor_shadow_stack: bool,
218    #[bits(26)]
219    rsvd38_63: u64,
220}
221
222/// SEV secure AVIC control register
223#[bitfield(u64)]
224#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
225pub struct SecureAvicControl {
226    pub secure_avic_en: bool,
227    pub allowed_nmi: bool,
228    #[bits(10)]
229    _rsvd: u64,
230    #[bits(52)]
231    pub guest_apic_backing_page_ptr: u64,
232}
233
234/// AVIC exit info1 for the incomplete IPI exit
235#[bitfield(u64)]
236pub struct SevAvicIncompleteIpiInfo1 {
237    pub icr_low: u32,
238    pub icr_high: u32,
239}
240
241open_enum::open_enum! {
242    pub enum SevAvicIpiFailure: u32 {
243        INVALID_TYPE = 0,
244        NOT_RUNNING = 1,
245        INVALID_TARGET = 2,
246        INVALID_BACKING_PAGE = 3,
247        INVALID_VECTOR = 4,
248        UNACCELERATED_IPI = 5,
249    }
250}
251
252impl SevAvicIpiFailure {
253    const fn into_bits(self) -> u32 {
254        self.0
255    }
256
257    const fn from_bits(bits: u32) -> Self {
258        Self(bits)
259    }
260}
261
262/// AVIC exit info2 for the incomplete IPI exit
263#[bitfield(u64)]
264pub struct SevAvicIncompleteIpiInfo2 {
265    #[bits(8)]
266    pub index: u32,
267    #[bits(24)]
268    _mbz: u32,
269    #[bits(32)]
270    pub failure: SevAvicIpiFailure,
271}
272
273open_enum::open_enum! {
274    pub enum SevAvicRegisterNumber: u32 {
275        /// APIC ID Register.
276        APIC_ID = 0x2,
277        /// APIC Version Register.
278        VERSION = 0x3,
279        /// Task Priority Register
280        TPR = 0x8,
281        /// Arbitration Priority Register.
282        APR = 0x9,
283        /// Processor Priority Register.
284        PPR = 0xA,
285        /// End Of Interrupt Register.
286        EOI = 0xB,
287        /// Remote Read Register
288        REMOTE_READ = 0xC,
289        /// Logical Destination Register.
290        LDR = 0xD,
291        /// Destination Format Register.
292        DFR = 0xE,
293        /// Spurious Interrupt Vector.
294        SPURIOUS = 0xF,
295        /// In-Service Registers.
296        ISR0 = 0x10,
297        ISR1 = 0x11,
298        ISR2 = 0x12,
299        ISR3 = 0x13,
300        ISR4 = 0x14,
301        ISR5 = 0x15,
302        ISR6 = 0x16,
303        ISR7 = 0x17,
304        /// Trigger Mode Registers.
305        TMR0 = 0x18,
306        TMR1 = 0x19,
307        TMR2 = 0x1A,
308        TMR3 = 0x1B,
309        TMR4 = 0x1C,
310        TMR5 = 0x1D,
311        TMR6 = 0x1E,
312        TMR7 = 0x1F,
313        /// Interrupt Request Registers.
314        IRR0 = 0x20,
315        IRR1 = 0x21,
316        IRR2 = 0x22,
317        IRR3 = 0x23,
318        IRR4 = 0x24,
319        IRR5 = 0x25,
320        IRR6 = 0x26,
321        IRR7 = 0x27,
322        /// Error Status Register.
323        ERROR = 0x28,
324        /// ICR Low.
325        ICR_LOW = 0x30,
326        /// ICR High.
327        ICR_HIGH = 0x31,
328        /// LVT Timer Register.
329        TIMER_LVT = 0x32,
330        /// LVT Thermal Register.
331        THERMAL_LVT = 0x33,
332        /// LVT Performance Monitor Register.
333        PERFMON_LVT = 0x34,
334        /// LVT Local Int0 Register.
335        LINT0_LVT = 0x35,
336        /// LVT Local Int1 Register.
337        LINT1_LVT = 0x36,
338        /// LVT Error Register.
339        ERROR_LVT = 0x37,
340        /// Initial count Register.
341        INITIAL_COUNT = 0x38,
342        /// R/O Current count Register.
343        CURRENT_COUNT = 0x39,
344        /// Divide configuration Register.
345        DIVIDER = 0x3e,
346        /// Self IPI register, only present in x2APIC.
347        SELF_IPI = 0x3f,
348    }
349}
350
351impl SevAvicRegisterNumber {
352    const fn into_bits(self) -> u32 {
353        self.0
354    }
355
356    const fn from_bits(bits: u32) -> Self {
357        Self(bits)
358    }
359}
360
361/// AVIC SEV exit info1 for the no acceleration exit
362#[bitfield(u64)]
363pub struct SevAvicNoAccelInfo {
364    #[bits(4)]
365    _rsvd1: u64,
366    #[bits(8)]
367    pub apic_register_number: SevAvicRegisterNumber,
368    #[bits(20)]
369    _rsvd2: u64,
370    #[bits(1)]
371    pub write_access: bool,
372    #[bits(31)]
373    _rsvd3: u64,
374}
375
376/// SEV VMSA structure representing CPU state
377#[repr(C)]
378#[derive(Debug, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
379pub struct SevVmsa {
380    // Selector Info
381    pub es: SevSelector,
382    pub cs: SevSelector,
383    pub ss: SevSelector,
384    pub ds: SevSelector,
385    pub fs: SevSelector,
386    pub gs: SevSelector,
387
388    // Descriptor Table Info
389    pub gdtr: SevSelector,
390    pub ldtr: SevSelector,
391    pub idtr: SevSelector,
392    pub tr: SevSelector,
393
394    // CET
395    pub pl0_ssp: u64,
396    pub pl1_ssp: u64,
397    pub pl2_ssp: u64,
398    pub pl3_ssp: u64,
399    pub u_cet: u64,
400
401    // Reserved, MBZ
402    pub vmsa_reserved1: [u8; 2],
403
404    // Virtual Machine Privilege Level
405    pub vmpl: u8,
406
407    // CPL
408    pub cpl: u8,
409
410    // Reserved, MBZ
411    pub vmsa_reserved2: u32,
412
413    // EFER
414    pub efer: u64,
415
416    // Reserved, MBZ
417    pub vmsa_reserved3: [u32; 26],
418
419    // XSS (offset 0x140)
420    pub xss: u64,
421
422    // Control registers
423    pub cr4: u64,
424    pub cr3: u64,
425    pub cr0: u64,
426
427    // Debug registers
428    pub dr7: u64,
429    pub dr6: u64,
430
431    // RFLAGS
432    pub rflags: u64,
433
434    // RIP
435    pub rip: u64,
436
437    // Additional saved debug registers
438    pub dr0: u64,
439    pub dr1: u64,
440    pub dr2: u64,
441    pub dr3: u64,
442
443    // Debug register address masks
444    pub dr0_addr_mask: u64,
445    pub dr1_addr_mask: u64,
446    pub dr2_addr_mask: u64,
447    pub dr3_addr_mask: u64,
448
449    // Reserved, MBZ
450    pub vmsa_reserved4: [u64; 3],
451
452    // RSP
453    pub rsp: u64,
454
455    // CET
456    pub s_cet: u64,
457    pub ssp: u64,
458    pub interrupt_ssp_table_addr: u64,
459
460    // RAX
461    pub rax: u64,
462
463    // SYSCALL config registers
464    pub star: u64,
465    pub lstar: u64,
466    pub cstar: u64,
467    pub sfmask: u64,
468
469    // KernelGsBase
470    pub kernel_gs_base: u64,
471
472    // SYSENTER config registers
473    pub sysenter_cs: u64,
474    pub sysenter_esp: u64,
475    pub sysenter_eip: u64,
476
477    // CR2
478    pub cr2: u64,
479
480    // Reserved, MBZ
481    pub vmsa_reserved5: [u64; 4],
482
483    // PAT
484    pub pat: u64,
485
486    // LBR MSRs
487    pub dbgctl: u64,
488    pub last_branch_from_ip: u64,
489    pub last_branch_to_ip: u64,
490    pub last_excp_from_ip: u64,
491    pub last_excp_to_ip: u64,
492
493    // Reserved, MBZ
494    pub vmsa_reserved6: [u64; 9],
495
496    // Speculation control MSR
497    pub spec_ctrl: u64,
498
499    // PKRU
500    pub pkru: u32,
501
502    // TSC_AUX
503    pub tsc_aux: u32,
504
505    // Reserved, MBZ
506    pub vmsa_reserved7: [u32; 4],
507
508    pub register_protection_nonce: u64,
509
510    // GPRs
511    pub rcx: u64,
512    pub rdx: u64,
513    pub rbx: u64,
514    pub secure_avic_control: SecureAvicControl,
515    pub rbp: u64,
516    pub rsi: u64,
517    pub rdi: u64,
518    pub r8: u64,
519    pub r9: u64,
520    pub r10: u64,
521    pub r11: u64,
522    pub r12: u64,
523    pub r13: u64,
524    pub r14: u64,
525    pub r15: u64,
526
527    // Reserved, MBZ
528    pub vmsa_reserved9: [u64; 2],
529
530    // Exit information following an automatic #VMEXIT
531    pub exit_info1: u64,
532    pub exit_info2: u64,
533    pub exit_int_info: u64,
534
535    // Software scratch register
536    pub next_rip: u64,
537
538    // SEV feature information
539    pub sev_features: SevFeatures,
540
541    // Virtual interrupt control
542    pub v_intr_cntrl: SevVirtualInterruptControl,
543
544    // Guest exiting error code
545    pub guest_error_code: u64,
546
547    // Virtual top of memory
548    pub virtual_tom: u64,
549
550    // TLB control.  Writing a zero to PCPU_ID will force a full TLB
551    // invalidation upon the next entry.
552    pub tlb_id: u64,
553    pub pcpu_id: u64,
554
555    // Event injection
556    pub event_inject: SevEventInjectInfo,
557
558    // XCR0
559    pub xcr0: u64,
560
561    // X87 state save valid bitmap
562    pub xsave_valid_bitmap: [u8; 16],
563
564    // X87 save state
565    pub x87dp: u64,
566    pub mxcsr: u32,
567    pub x87_ftw: u16,
568    pub x87_fsw: u16,
569    pub x87_fcw: u16,
570    pub x87_op: u16,
571    pub x87_ds: u16,
572    pub x87_cs: u16,
573    pub x87_rip: u64,
574
575    // NOTE: Should be 80 bytes. Making it 10 u64 because no code uses it on a
576    // byte-level yet.
577    pub x87_registers: [u64; 10],
578
579    // XMM registers
580    pub xmm_registers: [SevXmmRegister; 16],
581
582    // YMM high registers
583    pub ymm_registers: [SevXmmRegister; 16],
584}
585
586#[repr(C)]
587#[derive(Debug, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
588/// Structure representing the SEV-ES AVIC IRR register.
589///
590/// If the UpdateIRR bit is set in the VMCB, the guest-controlled AllowedIRR mask
591/// is logically AND-ed with the host-controlled RequestedIRR and then is logically
592/// OR-ed into the IRR field in the Guest APIC Backing page.
593pub struct SevAvicIrrRegister {
594    pub value: u32,
595    pub allowed: u32,
596    _reserved: [u32; 2],
597}
598
599#[repr(C)]
600#[derive(Debug, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
601/// Structure representing the SEV-ES AVIC backing page.
602/// Specification: "AMD64 PPR Vol3 System Programming", 15.29.3  AVIC Backing Page.
603pub struct SevAvicPage {
604    pub reserved_0: [ApicRegisterValue; 2],
605    pub id: ApicRegisterValue,
606    pub version: ApicRegisterValue,
607    pub reserved_4: [ApicRegisterValue; 4],
608    pub tpr: ApicRegisterValue,
609    pub apr: ApicRegisterValue,
610    pub ppr: ApicRegisterValue,
611    pub eoi: ApicRegisterValue,
612    pub rrd: ApicRegisterValue,
613    pub ldr: ApicRegisterValue,
614    pub dfr: ApicRegisterValue,
615    pub svr: ApicRegisterValue,
616    pub isr: [ApicRegisterValue; 8],
617    pub tmr: [ApicRegisterValue; 8],
618    pub irr: [SevAvicIrrRegister; 8],
619    pub esr: ApicRegisterValue,
620    pub reserved_29: [ApicRegisterValue; 6],
621    pub lvt_cmci: ApicRegisterValue,
622    pub icr: [ApicRegisterValue; 2],
623    pub lvt_timer: ApicRegisterValue,
624    pub lvt_thermal: ApicRegisterValue,
625    pub lvt_pmc: ApicRegisterValue,
626    pub lvt_lint0: ApicRegisterValue,
627    pub lvt_lint1: ApicRegisterValue,
628    pub lvt_error: ApicRegisterValue,
629    pub timer_icr: ApicRegisterValue,
630    pub timer_ccr: ApicRegisterValue,
631    pub reserved_3a: [ApicRegisterValue; 4],
632    pub timer_dcr: ApicRegisterValue,
633    pub self_ipi: ApicRegisterValue,
634    pub eafr: ApicRegisterValue,
635    pub eacr: ApicRegisterValue,
636    pub seoi: ApicRegisterValue,
637    pub reserved_44: [ApicRegisterValue; 0x5],
638    pub ier: [ApicRegisterValue; 8],
639    pub ei_lv_tr: [ApicRegisterValue; 3],
640    pub reserved_54: [ApicRegisterValue; 0xad],
641}
642
643const_assert_eq!(size_of::<SevAvicPage>(), 4096);
644
645// Info codes for the GHCB MSR protocol.
646open_enum::open_enum! {
647    pub enum GhcbInfo: u64 {
648        NORMAL = 0x000,
649        SEV_INFO_RESPONSE = 0x001,
650        SEV_INFO_REQUEST = 0x002,
651        AP_JUMP_TABLE = 0x003,
652        CPUID_REQUEST = 0x004,
653        CPUID_RESPONSE = 0x005,
654        PREFERRED_REQUEST = 0x010,
655        PREFERRED_RESPONSE = 0x011,
656        REGISTER_REQUEST = 0x012,
657        REGISTER_RESPONSE = 0x013,
658        PAGE_STATE_CHANGE = 0x014,
659        PAGE_STATE_UPDATED = 0x015,
660        HYP_FEATURE_REQUEST = 0x080,
661        HYP_FEATURE_RESPONSE = 0x081,
662        SPECIAL_HYPERCALL = 0xF00,
663        SPECIAL_FAST_CALL = 0xF01,
664        HYPERCALL_OUTPUT = 0xF02,
665        SPECIAL_DBGPRINT = 0xF03,
666        SHUTDOWN_REQUEST = 0x100,
667    }
668}
669
670pub const GHCB_DATA_PAGE_STATE_PRIVATE: u64 = 0x001;
671pub const GHCB_DATA_PAGE_STATE_SHARED: u64 = 0x002;
672pub const GHCB_DATA_PAGE_STATE_PSMASH: u64 = 0x003;
673pub const GHCB_DATA_PAGE_STATE_UNSMASH: u64 = 0x004;
674pub const GHCB_DATA_PAGE_STATE_MASK: u64 = 0x00F;
675pub const GHCB_DATA_PAGE_STATE_LARGE_PAGE: u64 = 0x010;
676
677open_enum::open_enum! {
678    pub enum GhcbUsage: u32 {
679        BASE = 0,
680        HYPERCALL = 1,
681        VTL_RETURN = 2,
682    }
683}
684
685/// Struct representing GHCB hypercall parameters. These are located at the GHCB
686/// page starting at [`GHCB_PAGE_HYPERCALL_PARAMETERS_OFFSET`].
687#[repr(C)]
688#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
689pub struct GhcbHypercallParameters {
690    pub output_gpa: u64,
691    pub input_control: u64,
692}
693
694pub const GHCB_PAGE_HYPERCALL_PARAMETERS_OFFSET: usize = 4072;
695pub const GHCB_PAGE_HYPERCALL_OUTPUT_OFFSET: usize = 4080;
696
697// Exit Codes.
698open_enum::open_enum! {
699    pub enum SevExitCode: u64 {
700        CR0_READ = 0x0,
701        CR1_READ = 0x1,
702        CR2_READ = 0x2,
703        CR3_READ = 0x3,
704        CR4_READ = 0x4,
705        CR5_READ = 0x5,
706        CR6_READ = 0x6,
707        CR7_READ = 0x7,
708        CR8_READ = 0x8,
709        CR9_READ = 0x9,
710        CR10_READ = 0xa,
711        CR11_READ = 0xb,
712        CR12_READ = 0xc,
713        CR13_READ = 0xd,
714        CR14_READ = 0xe,
715        CR15_READ = 0xf,
716        CR0_WRITE = 0x10,
717        CR1_WRITE = 0x11,
718        CR2_WRITE = 0x12,
719        CR3_WRITE = 0x13,
720        CR4_WRITE = 0x14,
721        CR5_WRITE = 0x15,
722        CR6_WRITE = 0x16,
723        CR7_WRITE = 0x17,
724        CR8_WRITE = 0x18,
725        CR9_WRITE = 0x19,
726        CR10_WRITE = 0x1a,
727        CR11_WRITE = 0x1b,
728        CR12_WRITE = 0x1c,
729        CR13_WRITE = 0x1d,
730        CR14_WRITE = 0x1e,
731        CR15_WRITE = 0x1f,
732        DR0_READ = 0x20,
733        DR1_READ = 0x21,
734        DR2_READ = 0x22,
735        DR3_READ = 0x23,
736        DR4_READ = 0x24,
737        DR5_READ = 0x25,
738        DR6_READ = 0x26,
739        DR7_READ = 0x27,
740        DR8_READ = 0x28,
741        DR9_READ = 0x29,
742        DR10_READ = 0x2a,
743        DR11_READ = 0x2b,
744        DR12_READ = 0x2c,
745        DR13_READ = 0x2d,
746        DR14_READ = 0x2e,
747        DR15_READ = 0x2f,
748        DR0_WRITE = 0x30,
749        DR1_WRITE = 0x31,
750        DR2_WRITE = 0x32,
751        DR3_WRITE = 0x33,
752        DR4_WRITE = 0x34,
753        DR5_WRITE = 0x35,
754        DR6_WRITE = 0x36,
755        DR7_WRITE = 0x37,
756        DR8_WRITE = 0x38,
757        DR9_WRITE = 0x39,
758        DR10_WRITE = 0x3a,
759        DR11_WRITE = 0x3b,
760        DR12_WRITE = 0x3c,
761        DR13_WRITE = 0x3d,
762        DR14_WRITE = 0x3e,
763        DR15_WRITE = 0x3f,
764        EXCP0 = 0x40,
765        EXCP_DB = 0x41,
766        EXCP2 = 0x42,
767        EXCP3 = 0x43,
768        EXCP4 = 0x44,
769        EXCP5 = 0x45,
770        EXCP6 = 0x46,
771        EXCP7 = 0x47,
772        EXCP8 = 0x48,
773        EXCP9 = 0x49,
774        EXCP10 = 0x4a,
775        EXCP11 = 0x4b,
776        EXCP12 = 0x4c,
777        EXCP13 = 0x4d,
778        EXCP14 = 0x4e,
779        EXCP15 = 0x4f,
780        EXCP16 = 0x50,
781        EXCP17 = 0x51,
782        EXCP18 = 0x52,
783        EXCP19 = 0x53,
784        EXCP20 = 0x54,
785        EXCP21 = 0x55,
786        EXCP22 = 0x56,
787        EXCP23 = 0x57,
788        EXCP24 = 0x58,
789        EXCP25 = 0x59,
790        EXCP26 = 0x5a,
791        EXCP27 = 0x5b,
792        EXCP28 = 0x5c,
793        EXCP29 = 0x5d,
794        EXCP30 = 0x5e,
795        EXCP31 = 0x5f,
796        INTR = 0x60,
797        NMI = 0x61,
798        SMI = 0x62,
799        INIT = 0x63,
800        VINTR = 0x64,
801        CR0_SEL_WRITE = 0x65,
802        IDTR_READ = 0x66,
803        GDTR_READ = 0x67,
804        LDTR_READ = 0x68,
805        TR_READ = 0x69,
806        IDTR_WRITE = 0x6a,
807        GDTR_WRITE = 0x6b,
808        LDTR_WRITE = 0x6c,
809        TR_WRITE = 0x6d,
810        RDTSC = 0x6e,
811        RDPMC = 0x6f,
812        PUSHF = 0x70,
813        POPF = 0x71,
814        CPUID = 0x72,
815        RSM = 0x73,
816        IRET = 0x74,
817        SWINT = 0x75,
818        INVD = 0x76,
819        PAUSE = 0x77,
820        HLT = 0x78,
821        INVLPG = 0x79,
822        INVLPGA = 0x7a,
823        IOIO = 0x7b,
824        MSR = 0x7c,
825        TASK_SWITCH = 0x7d,
826        FERR_FREEZE = 0x7e,
827        SHUTDOWN = 0x7f,
828        VMRUN = 0x80,
829        VMMCALL = 0x81,
830        VMLOAD = 0x82,
831        VMSAVE = 0x83,
832        STGI = 0x84,
833        CLGI = 0x85,
834        SKINIT = 0x86,
835        RDTSCP = 0x87,
836        ICEBP = 0x88,
837        WBINVD = 0x89,
838        MONITOR = 0x8a,
839        MWAIT = 0x8b,
840        MWAIT_CONDITIONAL = 0x8c,
841        XSETBV = 0x8d,
842        RDPRU = 0x8e,
843        EFER_WRITE_TRAP = 0x8f,
844        CR0_WRITE_TRAP = 0x90,
845        CR1_WRITE_TRAP = 0x91,
846        CR2_WRITE_TRAP = 0x92,
847        CR3_WRITE_TRAP = 0x93,
848        CR4_WRITE_TRAP = 0x94,
849        CR5_WRITE_TRAP = 0x95,
850        CR6_WRITE_TRAP = 0x96,
851        CR7_WRITE_TRAP = 0x97,
852        CR8_WRITE_TRAP = 0x98,
853        CR9_WRITE_TRAP = 0x99,
854        CR10_WRITE_TRAP = 0x9a,
855        CR11_WRITE_TRAP = 0x9b,
856        CR12_WRITE_TRAP = 0x9c,
857        CR13_WRITE_TRAP = 0x9d,
858        CR14_WRITE_TRAP = 0x9e,
859        CR15_WRITE_TRAP = 0x9f,
860        INVLPGB = 0xa0,
861        ILLEGAL_INVLPGB = 0xa1,
862        INVPCID = 0xa2,
863        BUSLOCK = 0xa5,
864        IDLE_HLT = 0xa6,
865        NPF = 0x400,
866        AVIC_INCOMPLETE_IPI = 0x401,
867        AVIC_NOACCEL = 0x402,
868        VMGEXIT = 0x403,
869        PAGE_NOT_VALIDATED = 0x404,
870
871        // SEV-ES software-defined exit codes
872        SNP_GUEST_REQUEST = 0x80000011,
873        SNP_EXTENDED_GUEST_REQUEST = 0x80000012,
874        HV_DOORBELL_PAGE = 0x80000014,
875
876        // SEV-SNP hardware error codes
877        INVALID_VMCB = 0xffff_ffff_ffff_ffff,
878        VMSA_BUSY = 0xffff_ffff_ffff_fffe,
879        IDLE_REQUIRED = 0xffff_ffff_ffff_fffd,
880        INVALID_PMC = 0xffff_ffff_ffff_fffc,
881    }
882}
883
884#[bitfield(u64)]
885#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
886pub struct GhcbMsr {
887    #[bits(12)]
888    pub info: u64,
889    #[bits(40)]
890    pub pfn: u64,
891    #[bits(12)]
892    pub extra_data: u64,
893}
894
895/// PSP data structures.
896#[repr(C)]
897#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Clone, Copy)]
898pub struct HvPspCpuidLeaf {
899    pub eax_in: u32,
900    pub ecx_in: u32,
901    pub xfem_in: u64,
902    pub xss_in: u64,
903    pub eax_out: u32,
904    pub ebx_out: u32,
905    pub ecx_out: u32,
906    pub edx_out: u32,
907    pub reserved_z: u64,
908}
909
910pub const HV_PSP_CPUID_LEAF_COUNT_MAX: usize = 64;
911
912#[repr(C)]
913#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes, Clone, Copy)]
914pub struct HvPspCpuidPage {
915    pub count: u32,
916    pub reserved_z1: u32,
917    pub reserved_z2: u64,
918    pub cpuid_leaf_info: [HvPspCpuidLeaf; HV_PSP_CPUID_LEAF_COUNT_MAX],
919    pub reserved_z3: [u64; 126],
920}
921
922/// Structure describing the pages being read during SNP ID block measurement.
923/// Each structure is hashed with the previous structures digest to create a final
924/// measurement
925#[repr(C)]
926#[derive(Debug, Clone, Copy, IntoBytes, Immutable, KnownLayout, FromBytes)]
927pub struct SnpPageInfo {
928    /// Set to the value of the previous page's launch digest
929    pub digest_current: [u8; 48],
930    /// Hash of page contents, if measured
931    pub contents: [u8; 48],
932    /// Size of the SnpPageInfo struct
933    pub length: u16,
934    /// type of page being measured, described by [`SnpPageType`]
935    pub page_type: SnpPageType,
936    /// imi_page_bit must match IMI_PAGE flag
937    pub imi_page_bit: u8,
938    /// All lower VMPL permissions are denied for SNP
939    pub lower_vmpl_permissions: u32,
940    /// The guest physical address at which this page data should be loaded; it
941    /// must be aligned to a page size boundary.
942    pub gpa: u64,
943}
944
945open_enum::open_enum! {
946    /// The type of page described by [`SnpPageInfo`]
947    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
948    pub enum SnpPageType: u8 {
949        /// Reserved
950        RESERVED = 0x0,
951        /// Normal data page
952        NORMAL = 0x1,
953        /// VMSA page
954        VMSA = 0x2,
955        /// Zero page
956        ZERO = 0x3,
957        /// Page encrypted, but not measured
958        UNMEASURED = 0x4,
959        /// Page storing guest secrets
960        SECRETS = 0x5,
961        /// Page to provide CPUID function values
962        CPUID = 0x6,
963    }
964}
965
966/// Structure containing the completed SNP measurement of the IGVM file.
967/// The signature of the hash of this struct is the id_key_signature for
968/// `igvm_defs::IGVM_VHS_SNP_ID_BLOCK`.
969#[repr(C)]
970#[derive(Debug, Clone, Copy, IntoBytes, Immutable, KnownLayout, FromBytes)]
971pub struct SnpPspIdBlock {
972    /// completed launch digest of IGVM file
973    pub ld: [u8; 48],
974    /// family id of the guest
975    pub family_id: [u8; 16],
976    /// image id of the guest
977    pub image_id: [u8; 16],
978    /// Version of the ID block format, must be 0x1
979    pub version: u32,
980    /// Software version of the guest
981    pub guest_svn: u32,
982    /// SNP Policy of the guest
983    pub policy: u64,
984}
985
986#[bitfield(u64)]
987#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
988pub struct SevStatusMsr {
989    pub sev_enabled: bool,
990    pub es_enabled: bool,
991    pub snp_enabled: bool,
992    pub vtom: bool,
993    pub reflect_vc: bool,
994    pub restrict_injection: bool,
995    pub alternate_injection: bool,
996    pub debug_swap: bool,
997    pub prevent_host_ibs: bool,
998    pub snp_btb_isolation: bool,
999    pub vmpl_sss: bool,
1000    pub secure_tsc: bool,
1001    pub vmgexit_param: bool,
1002    _rsvd3: bool,
1003    pub ibs_virt: bool,
1004    _rsvd5: bool,
1005    pub vmsa_reg_prot: bool,
1006    pub smt_prot: bool,
1007    pub secure_avic: bool,
1008    #[bits(4)]
1009    _reserved: u64,
1010    pub ibpb_on_entry: bool,
1011    #[bits(40)]
1012    _unused: u64,
1013}
1014
1015#[bitfield(u64)]
1016#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
1017pub struct SevInvlpgbRax {
1018    pub va_valid: bool,
1019    pub pcid_valid: bool,
1020    pub asid_valid: bool,
1021    pub global: bool,
1022    pub final_only: bool,
1023    pub nested: bool,
1024    #[bits(6)]
1025    reserved: u64,
1026    #[bits(52)]
1027    pub virtual_page_number: u64,
1028}
1029
1030#[bitfield(u32)]
1031#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
1032pub struct SevInvlpgbEdx {
1033    #[bits(16)]
1034    pub asid: u64,
1035    #[bits(12)]
1036    pub pcid: u64,
1037    #[bits(4)]
1038    reserved: u32,
1039}
1040
1041#[bitfield(u32)]
1042#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq, Eq)]
1043pub struct SevInvlpgbEcx {
1044    #[bits(16)]
1045    pub additional_count: u64,
1046    #[bits(15)]
1047    reserved: u64,
1048    pub large_page: bool,
1049}
1050
1051#[bitfield(u64)]
1052pub struct MovCrxDrxInfo {
1053    #[bits(4)]
1054    pub gpr_number: u64,
1055    #[bits(59)]
1056    pub reserved: u64,
1057    pub mov_crx: bool,
1058}
1059
1060/// Request structure for the `SNP_GET_REPORT` request.
1061/// See `MSG_REPORT_REQ` in Table 21, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1062#[repr(C)]
1063#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1064pub struct SnpReportReq {
1065    /// Guest-provided data to be included in the attestation report.
1066    pub user_data: [u8; 64],
1067    /// The VMPL to put in the attestation report. Must be greater than
1068    /// or equal to the current VMPL and, at most, three.
1069    pub vmpl: u32,
1070    /// Reserved
1071    // TODO SNP: Support VLEK feature if needed
1072    pub rsvd: [u8; 28],
1073}
1074
1075pub const SNP_REPORT_RESP_DATA_SIZE: usize =
1076    size_of::<u32>() + size_of::<u32>() + 24 + size_of::<SnpReport>();
1077
1078/// Response structure for the `SNP_GET_REPORT` request.
1079/// See `MSG_REPORT_RSP` in Table 24, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1080#[repr(C)]
1081#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1082pub struct SnpReportResp {
1083    /// The status of key derivation operation.
1084    /// 0h: Success.
1085    /// 16h: Invalid parameters.
1086    /// 27h: Invalid key selection.
1087    pub status: u32,
1088    /// Size in bytes of the report.
1089    pub report_size: u32,
1090    /// Reserved
1091    pub _reserved0: [u8; 24],
1092    /// The attestation report generated by the firmware.
1093    pub report: SnpReport,
1094}
1095
1096/// Size of the [`SnpReport`].
1097pub const SNP_REPORT_SIZE: usize = 0x4a0;
1098
1099/// Size of `report_data` member in [`SnpReport`].
1100pub const SNP_REPORT_DATA_SIZE: usize = 64;
1101
1102/// Report structure.
1103/// See `ATTESTATION_REPORT` in Table 22, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1104#[repr(C)]
1105#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1106pub struct SnpReport {
1107    /// Version number of this attestation report.
1108    /// Set to 2h for this specification.
1109    pub version: u32,
1110    /// The guest SVN.
1111    pub guest_svn: u32,
1112    /// The guest policy.
1113    pub policy: u64,
1114    /// The family ID provided at launch.
1115    pub family: u128,
1116    /// The image ID provided at launch.
1117    pub image_id: u128,
1118    /// The request VMPL for the attestation
1119    /// report.
1120    pub vmpl: u32,
1121    /// The signature algorithm used to sign
1122    /// this report.
1123    pub signature_algo: u32,
1124    /// CurrentTcb.
1125    pub current_tcb: u64,
1126    /// Information about the platform.
1127    pub platform_info: u64,
1128    /// Flags
1129    pub flags: u32,
1130    /// Reserved
1131    pub _reserved0: u32,
1132    /// Guest-provided data.
1133    pub report_data: [u8; SNP_REPORT_DATA_SIZE],
1134    /// The measurement calculated at
1135    /// launch.
1136    pub measurement: [u8; 48],
1137    /// Data provided by the hypervisor at
1138    /// launch.
1139    pub host_data: [u8; 32],
1140    /// SHA-384 digest of the ID public key
1141    /// that signed the ID block provided in
1142    /// SNP_LAUNCH_FINISH.
1143    pub id_key_digest: [u8; 48],
1144    /// SHA-384 digest of the Author public
1145    /// key that certified the ID key, if
1146    /// provided in SNP_LAUNCH_FINISH.
1147    pub author_key_digest: [u8; 48],
1148    /// Report ID of this guest.
1149    pub report_id: [u8; 32],
1150    /// Report ID of this guest’s migration
1151    /// agent
1152    pub report_id_ma: [u8; 32],
1153    /// Reported TCB version used to derive
1154    /// the VCEK that signed this report.
1155    pub reported_tcb: u64,
1156    /// Reserved
1157    pub _reserved1: [u8; 24],
1158    /// If MaskChipId is set to 0, Identifier
1159    /// unique to the chip as output by
1160    /// GET_ID. Otherwise, set to 0h.
1161    pub chip_id: [u8; 64],
1162    /// CommittedTcb.
1163    pub committed_tcb: u64,
1164    /// The build number of CurrentVersion.
1165    pub current_build: u8,
1166    /// The minor number of CurrentVersion.
1167    pub current_minor: u8,
1168    /// The major number of CurrentVersion.
1169    pub current_major: u8,
1170    /// Reserved
1171    pub _reserved2: u8,
1172    /// The build number of CommittedVersion.
1173    pub committed_build: u8,
1174    /// The minor version of CommittedVersion.
1175    pub committed_minor: u8,
1176    /// The major version of CommittedVersion.
1177    pub committed_major: u8,
1178    /// Reserved
1179    pub _reserved3: u8,
1180    /// The CurrentTcb at the time the guest
1181    /// was launched or imported.
1182    pub launch_tcb: u64,
1183    /// Reserved
1184    pub _reserved4: [u8; 168],
1185    /// Signature of bytes inclusive of this report.
1186    pub signature: [u8; 512],
1187}
1188
1189static_assertions::const_assert_eq!(SNP_REPORT_SIZE, size_of::<SnpReport>());
1190
1191/// Request structure for the `SNP_GET_DERIVED_KEY` request.
1192/// See `MSG_KEY_REQ` in Table 18, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1193#[repr(C)]
1194#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1195pub struct SnpDerivedKeyReq {
1196    /// Selects the root key from which to derive the key.
1197    /// 0 indicates VCEK
1198    /// 1 indicates VMRK
1199    // TODO: Support VLEK feature if needed
1200    pub root_key_select: u32,
1201    /// Reserved
1202    pub rsvd: u32,
1203    /// Bitmask indicating which data will be mixed into the
1204    /// derived key.
1205    pub guest_field_select: u64,
1206    /// The VMPL to mix into the derived key. Must be greater
1207    /// than or equal to the current VMPL.
1208    pub vmpl: u32,
1209    /// The guest SVN to mix into the key. Must not exceed the
1210    /// guest SVN provided at launch in the ID block.
1211    pub guest_svn: u32,
1212    /// The TCB version to mix into the derived key. Must not
1213    /// exceed CommittedTcb.
1214    pub tcb_version: u64,
1215}
1216
1217/// Indicate which guest-selectable fields will be mixed into the key.
1218/// See `GUEST_FIELD_SELECT` in Table 19, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1219#[bitfield(u64)]
1220pub struct GuestFieldSelect {
1221    /// Indicate that the guest policy will be mixed into the key.
1222    pub guest_policy: bool,
1223    /// Indicate that the image ID of the guest will be mixed into the key.
1224    pub image_id: bool,
1225    /// Indicate the family ID of the guest will be mixed into the key.
1226    pub family_id: bool,
1227    /// Indicate the measurement of the guest during launch will be mixed into the key.
1228    pub measurement: bool,
1229    /// Indicate that the guest-provided SVN will be mixed into the key.
1230    pub guest_svn: bool,
1231    /// Indicate that the guest-provided TCB_VERSION will be mixed into the key.
1232    pub tcb_version: bool,
1233    /// Reserved
1234    #[bits(58)]
1235    pub _reserved: u64,
1236}
1237
1238/// See `DERIVED_KEY` in Table 20, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1239pub const SNP_DERIVED_KEY_SIZE: usize = 32;
1240
1241/// Response structure for the `SNP_GET_DERIVED_KEY` request.
1242/// See `MSG_KEY_RSP` in Table 20, "SEV Secure Nested Paging Firmware ABI specification", Revision 1.55.
1243#[repr(C)]
1244#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
1245pub struct SnpDerivedKeyResp {
1246    /// The status of key derivation operation.
1247    /// 0h: Success.
1248    /// 16h: Invalid parameters.
1249    /// 27h: Invalid key selection.
1250    pub status: u32,
1251    /// Reserved
1252    pub _reserved: [u8; 28],
1253    /// The requested derived key.
1254    pub derived_key: [u8; SNP_DERIVED_KEY_SIZE],
1255}
1256
1257static_assertions::const_assert_eq!(
1258    // The size of the response data defined by the SNP specification.
1259    64,
1260    size_of::<SnpDerivedKeyResp>()
1261);
1262
1263#[cfg(test)]
1264mod tests {
1265    use super::*;
1266    use zerocopy::FromZeros;
1267
1268    // ---- SecureAvicControl bitfield tests ----
1269
1270    #[test]
1271    fn secure_avic_control_default_is_zero() {
1272        let ctrl = SecureAvicControl::new();
1273        assert_eq!(ctrl.into_bits(), 0);
1274        assert_eq!(ctrl.secure_avic_en(), false);
1275        assert_eq!(ctrl.allowed_nmi(), false);
1276        assert_eq!(ctrl.guest_apic_backing_page_ptr(), 0);
1277    }
1278
1279    #[test]
1280    fn secure_avic_control_enable_bit() {
1281        let ctrl = SecureAvicControl::new().with_secure_avic_en(true);
1282        assert_eq!(ctrl.secure_avic_en(), true);
1283        assert_eq!(ctrl.into_bits() & 1, 1);
1284    }
1285
1286    #[test]
1287    fn secure_avic_control_allowed_nmi_bit() {
1288        let ctrl = SecureAvicControl::new().with_allowed_nmi(true);
1289        assert_eq!(ctrl.allowed_nmi(), true);
1290        assert_eq!(ctrl.into_bits() & 0b10, 0b10);
1291    }
1292
1293    #[test]
1294    fn secure_avic_control_page_ptr() {
1295        // The page pointer is in bits [63:12], representing a PFN.
1296        let pfn = 0xDEAD_BEEF_u64;
1297        let ctrl = SecureAvicControl::new()
1298            .with_secure_avic_en(true)
1299            .with_guest_apic_backing_page_ptr(pfn);
1300        assert_eq!(ctrl.guest_apic_backing_page_ptr(), pfn);
1301        assert_eq!(ctrl.secure_avic_en(), true);
1302        // The PFN should be in bits [63:12]
1303        assert_eq!(ctrl.into_bits() >> 12, pfn);
1304    }
1305
1306    #[test]
1307    fn secure_avic_control_roundtrip() {
1308        let raw = 0xABCD_1234_5678_9001_u64;
1309        let ctrl = SecureAvicControl::from(raw);
1310        assert_eq!(ctrl.into_bits(), raw);
1311    }
1312
1313    // ---- SevAvicNoAccelInfo bitfield tests ----
1314
1315    #[test]
1316    fn no_accel_info_register_number_extraction() {
1317        // Register number is in bits [11:4].
1318        let info = SevAvicNoAccelInfo::new().with_apic_register_number(SevAvicRegisterNumber::EOI);
1319        assert_eq!(info.apic_register_number(), SevAvicRegisterNumber::EOI);
1320        // EOI = 0xB, stored in bits [11:4]
1321        assert_eq!((info.into_bits() >> 4) & 0xFF, 0xB);
1322    }
1323
1324    #[test]
1325    fn no_accel_info_write_access_bit() {
1326        // Write access is bit 32.
1327        let info = SevAvicNoAccelInfo::new().with_write_access(true);
1328        assert!(info.write_access());
1329        assert_eq!(info.into_bits() & (1 << 32), 1 << 32);
1330
1331        let info_read = SevAvicNoAccelInfo::new().with_write_access(false);
1332        assert!(!info_read.write_access());
1333    }
1334
1335    #[test]
1336    fn no_accel_info_combined() {
1337        let info = SevAvicNoAccelInfo::new()
1338            .with_apic_register_number(SevAvicRegisterNumber::ICR_LOW)
1339            .with_write_access(true);
1340        assert_eq!(info.apic_register_number(), SevAvicRegisterNumber::ICR_LOW);
1341        assert!(info.write_access());
1342    }
1343
1344    #[test]
1345    fn no_accel_info_all_register_numbers_roundtrip() {
1346        let registers = [
1347            SevAvicRegisterNumber::APIC_ID,
1348            SevAvicRegisterNumber::VERSION,
1349            SevAvicRegisterNumber::TPR,
1350            SevAvicRegisterNumber::APR,
1351            SevAvicRegisterNumber::PPR,
1352            SevAvicRegisterNumber::EOI,
1353            SevAvicRegisterNumber::LDR,
1354            SevAvicRegisterNumber::DFR,
1355            SevAvicRegisterNumber::SPURIOUS,
1356            SevAvicRegisterNumber::ISR0,
1357            SevAvicRegisterNumber::ISR7,
1358            SevAvicRegisterNumber::TMR0,
1359            SevAvicRegisterNumber::TMR7,
1360            SevAvicRegisterNumber::IRR0,
1361            SevAvicRegisterNumber::IRR7,
1362            SevAvicRegisterNumber::ERROR,
1363            SevAvicRegisterNumber::ICR_LOW,
1364            SevAvicRegisterNumber::ICR_HIGH,
1365            SevAvicRegisterNumber::TIMER_LVT,
1366            SevAvicRegisterNumber::INITIAL_COUNT,
1367            SevAvicRegisterNumber::CURRENT_COUNT,
1368            SevAvicRegisterNumber::DIVIDER,
1369            SevAvicRegisterNumber::SELF_IPI,
1370        ];
1371        for reg in registers {
1372            let info = SevAvicNoAccelInfo::new().with_apic_register_number(reg);
1373            assert_eq!(
1374                info.apic_register_number(),
1375                reg,
1376                "register number roundtrip failed for {reg:#x?}"
1377            );
1378        }
1379    }
1380
1381    // ---- SevAvicIncompleteIpiInfo1/2 bitfield tests ----
1382
1383    #[test]
1384    fn incomplete_ipi_info1_icr_fields() {
1385        let icr_low = 0x0004_10FFu32;
1386        let icr_high = 0x0200_0000u32;
1387        let info = SevAvicIncompleteIpiInfo1::new()
1388            .with_icr_low(icr_low)
1389            .with_icr_high(icr_high);
1390        assert_eq!(info.icr_low(), icr_low);
1391        assert_eq!(info.icr_high(), icr_high);
1392        assert_eq!(info.into_bits(), icr_low as u64 | ((icr_high as u64) << 32));
1393    }
1394
1395    #[test]
1396    fn incomplete_ipi_info2_fields() {
1397        let info = SevAvicIncompleteIpiInfo2::new()
1398            .with_index(42)
1399            .with_failure(SevAvicIpiFailure::NOT_RUNNING);
1400        assert_eq!(info.index(), 42);
1401        assert_eq!(info.failure(), SevAvicIpiFailure::NOT_RUNNING);
1402    }
1403
1404    #[test]
1405    fn incomplete_ipi_info2_all_failure_codes() {
1406        let failures = [
1407            SevAvicIpiFailure::INVALID_TYPE,
1408            SevAvicIpiFailure::NOT_RUNNING,
1409            SevAvicIpiFailure::INVALID_TARGET,
1410            SevAvicIpiFailure::INVALID_BACKING_PAGE,
1411            SevAvicIpiFailure::INVALID_VECTOR,
1412            SevAvicIpiFailure::UNACCELERATED_IPI,
1413        ];
1414        for failure in failures {
1415            let info = SevAvicIncompleteIpiInfo2::new().with_failure(failure);
1416            assert_eq!(
1417                info.failure(),
1418                failure,
1419                "failure code roundtrip failed for {failure:#x?}"
1420            );
1421        }
1422    }
1423
1424    // ---- SevFeatures secure AVIC fields ----
1425
1426    #[test]
1427    fn sev_features_secure_avic_bit() {
1428        let features = SevFeatures::new().with_secure_avic(true);
1429        assert!(features.secure_avic());
1430        // secure_avic is bit 16 (0-indexed).
1431        assert_ne!(features.into_bits() & (1 << 16), 0);
1432    }
1433
1434    #[test]
1435    fn sev_features_guest_intercept_control_bit() {
1436        let features = SevFeatures::new().with_guest_intercept_control(true);
1437        assert!(features.guest_intercept_control());
1438        // guest_intercept_control is bit 13.
1439        assert_ne!(features.into_bits() & (1 << 13), 0);
1440    }
1441
1442    #[test]
1443    fn sev_features_secure_avic_with_no_alternate_injection() {
1444        // Secure AVIC and alternate injection are mutually exclusive per the
1445        // init_vmsa logic.
1446        let features = SevFeatures::new()
1447            .with_secure_avic(true)
1448            .with_guest_intercept_control(true)
1449            .with_alternate_injection(false);
1450        assert!(features.secure_avic());
1451        assert!(features.guest_intercept_control());
1452        assert!(!features.alternate_injection());
1453    }
1454
1455    #[test]
1456    fn sev_features_alternate_injection_without_secure_avic() {
1457        let features = SevFeatures::new()
1458            .with_alternate_injection(true)
1459            .with_secure_avic(false);
1460        assert!(features.alternate_injection());
1461        assert!(!features.secure_avic());
1462    }
1463
1464    // ---- SevStatusMsr secure AVIC field ----
1465
1466    #[test]
1467    fn sev_status_msr_secure_avic_bit() {
1468        let status = SevStatusMsr::new().with_secure_avic(true);
1469        assert!(status.secure_avic());
1470        // secure_avic is bit 18 in SevStatusMsr (after sev_enabled, es_enabled,
1471        // snp_enabled, vtom, reflect_vc, restrict_injection, alternate_injection,
1472        // debug_swap, prevent_host_ibs, snp_btb_isolation, vmpl_sss, secure_tsc,
1473        // vmgexit_param, _rsvd3, ibs_virt, _rsvd5, vmsa_reg_prot, smt_prot).
1474        assert_ne!(status.into_bits() & (1 << 18), 0);
1475    }
1476
1477    // ---- SevVirtualInterruptControl NMI fields ----
1478
1479    #[test]
1480    fn v_intr_cntrl_nmi_fields() {
1481        let ctrl = SevVirtualInterruptControl::new()
1482            .with_nmi(true)
1483            .with_nmi_mask(true)
1484            .with_nmi_enable(true);
1485        assert!(ctrl.nmi());
1486        assert!(ctrl.nmi_mask());
1487        assert!(ctrl.nmi_enable());
1488    }
1489
1490    // ---- SevAvicPage layout tests ----
1491
1492    #[test]
1493    fn sev_avic_page_size_is_4096() {
1494        // Already asserted at compile time, but verify at runtime too.
1495        assert_eq!(size_of::<SevAvicPage>(), 4096);
1496    }
1497
1498    #[test]
1499    fn sev_avic_irr_register_size() {
1500        // Each IRR register has value + allowed + reserved = 16 bytes,
1501        // same as a standard ApicRegisterValue.
1502        assert_eq!(
1503            size_of::<SevAvicIrrRegister>(),
1504            size_of::<ApicRegisterValue>()
1505        );
1506    }
1507
1508    #[test]
1509    fn sev_avic_page_field_offsets() {
1510        // Verify key field offsets match the APIC register map.
1511        // Each "register" is 16 bytes (128 bits per the AMD spec).
1512        let page = SevAvicPage::new_zeroed();
1513        let base = core::ptr::from_ref(&page) as usize;
1514
1515        // id is at register index 2 (offset 0x20)
1516        let id_offset = core::ptr::from_ref(&page.id) as usize - base;
1517        assert_eq!(id_offset, 2 * 16, "APIC ID offset");
1518
1519        // version is at register index 3 (offset 0x30)
1520        let version_offset = core::ptr::from_ref(&page.version) as usize - base;
1521        assert_eq!(version_offset, 3 * 16, "version offset");
1522
1523        // TPR is at register index 8 (offset 0x80)
1524        let tpr_offset = core::ptr::from_ref(&page.tpr) as usize - base;
1525        assert_eq!(tpr_offset, 8 * 16, "TPR offset");
1526
1527        // ISR starts at register index 0x10 (offset 0x100)
1528        let isr_offset = core::ptr::from_ref(&page.isr) as usize - base;
1529        assert_eq!(isr_offset, 0x10 * 16, "ISR offset");
1530
1531        // TMR starts at register index 0x18 (offset 0x180)
1532        let tmr_offset = core::ptr::from_ref(&page.tmr) as usize - base;
1533        assert_eq!(tmr_offset, 0x18 * 16, "TMR offset");
1534
1535        // IRR starts at register index 0x20 (offset 0x200)
1536        let irr_offset = core::ptr::from_ref(&page.irr) as usize - base;
1537        assert_eq!(irr_offset, 0x20 * 16, "IRR offset");
1538
1539        // ICR is at register index 0x30 (offset 0x300)
1540        let icr_offset = core::ptr::from_ref(&page.icr) as usize - base;
1541        assert_eq!(icr_offset, 0x30 * 16, "ICR offset");
1542    }
1543
1544    // ---- VMSA SecureAvicControl field offset test ----
1545
1546    #[test]
1547    fn vmsa_secure_avic_control_at_rsp_offset() {
1548        // In the VMSA, secure_avic_control occupies the RSP slot (between RBX and RBP).
1549        // Verify it's at the expected offset by checking it doesn't overlap GPRs.
1550        let vmsa = SevVmsa::new_zeroed();
1551        let base = core::ptr::from_ref(&vmsa) as usize;
1552        let rbx_offset = core::ptr::from_ref(&vmsa.rbx) as usize - base;
1553        let savic_offset = core::ptr::from_ref(&vmsa.secure_avic_control) as usize - base;
1554        let rbp_offset = core::ptr::from_ref(&vmsa.rbp) as usize - base;
1555
1556        // secure_avic_control should be right after rbx and before rbp.
1557        assert_eq!(savic_offset, rbx_offset + 8);
1558        assert_eq!(rbp_offset, savic_offset + 8);
1559    }
1560
1561    // ---- SevAvicRegisterNumber to x2APIC MSR mapping ----
1562
1563    #[test]
1564    fn avic_register_number_matches_apic_register_enum() {
1565        // The SevAvicRegisterNumber values should match the corresponding
1566        // x86defs::apic::ApicRegisterValue values, ensuring correct MSR computation.
1567        use crate::apic::ApicRegister;
1568        assert_eq!(SevAvicRegisterNumber::APIC_ID.0, ApicRegister::ID.0 as u32);
1569        assert_eq!(
1570            SevAvicRegisterNumber::VERSION.0,
1571            ApicRegister::VERSION.0 as u32
1572        );
1573        assert_eq!(SevAvicRegisterNumber::TPR.0, ApicRegister::TPR.0 as u32);
1574        assert_eq!(SevAvicRegisterNumber::EOI.0, ApicRegister::EOI.0 as u32);
1575        assert_eq!(SevAvicRegisterNumber::LDR.0, ApicRegister::LDR.0 as u32);
1576        assert_eq!(
1577            SevAvicRegisterNumber::SPURIOUS.0,
1578            ApicRegister::SVR.0 as u32
1579        );
1580        assert_eq!(SevAvicRegisterNumber::ISR0.0, ApicRegister::ISR0.0 as u32);
1581        assert_eq!(SevAvicRegisterNumber::TMR0.0, ApicRegister::TMR0.0 as u32);
1582        assert_eq!(SevAvicRegisterNumber::IRR0.0, ApicRegister::IRR0.0 as u32);
1583        assert_eq!(SevAvicRegisterNumber::ERROR.0, ApicRegister::ESR.0 as u32);
1584        assert_eq!(
1585            SevAvicRegisterNumber::ICR_LOW.0,
1586            ApicRegister::ICR0.0 as u32
1587        );
1588        assert_eq!(
1589            SevAvicRegisterNumber::ICR_HIGH.0,
1590            ApicRegister::ICR1.0 as u32
1591        );
1592        assert_eq!(
1593            SevAvicRegisterNumber::TIMER_LVT.0,
1594            ApicRegister::LVT_TIMER.0 as u32
1595        );
1596        assert_eq!(
1597            SevAvicRegisterNumber::INITIAL_COUNT.0,
1598            ApicRegister::TIMER_ICR.0 as u32
1599        );
1600        assert_eq!(
1601            SevAvicRegisterNumber::CURRENT_COUNT.0,
1602            ApicRegister::TIMER_CCR.0 as u32
1603        );
1604        assert_eq!(
1605            SevAvicRegisterNumber::DIVIDER.0,
1606            ApicRegister::TIMER_DCR.0 as u32
1607        );
1608        assert_eq!(
1609            SevAvicRegisterNumber::SELF_IPI.0,
1610            ApicRegister::SELF_IPI.0 as u32
1611        );
1612    }
1613
1614    #[test]
1615    fn avic_register_to_x2apic_msr() {
1616        // Verify the MSR computation: X2APIC_MSR_BASE + register_number.
1617        use crate::apic::X2APIC_MSR_BASE;
1618        let msr = X2APIC_MSR_BASE + SevAvicRegisterNumber::EOI.0;
1619        assert_eq!(msr, 0x80B); // EOI is register 0xB
1620
1621        let msr = X2APIC_MSR_BASE + SevAvicRegisterNumber::ICR_LOW.0;
1622        assert_eq!(msr, 0x830); // ICR_LOW is register 0x30
1623    }
1624
1625    // ---- SevExitCode AVIC constants ----
1626
1627    #[test]
1628    fn sev_exit_code_avic_values() {
1629        assert_eq!(SevExitCode::AVIC_INCOMPLETE_IPI.0, 0x401);
1630        assert_eq!(SevExitCode::AVIC_NOACCEL.0, 0x402);
1631    }
1632}