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