diff --git a/Cargo.toml b/Cargo.toml index c78d47c..e0ed47b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" # MulanPubL2 is not i [dependencies] log = "0.4.19" cfg-if = "1.0" +bilge = "0.2.0" bitflags = "2.2" bit_field = "0.10" paste = "1.0.15" diff --git a/src/lib.rs b/src/lib.rs index 79d3e6c..6f0526d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,12 @@ cfg_if::cfg_if! { } } +cfg_if::cfg_if! { + if #[cfg(feature = "svm")]{ + pub mod svm; + } +} + pub use ept::GuestPageWalkInfo; pub use regs::GeneralRegisters; pub use vender::has_hardware_support; diff --git a/src/svm/definitions.rs b/src/svm/definitions.rs new file mode 100644 index 0000000..1440d32 --- /dev/null +++ b/src/svm/definitions.rs @@ -0,0 +1,290 @@ +use core::fmt::{Debug, Formatter, Result}; + +pub struct SvmInstructionError(i32); + +impl SvmInstructionError { + pub fn as_str(&self) -> &str { + match self.0 { + 0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xa | 0xb | 0xc | 0xd | 0xe | 0xf => "Cr Read", + 0x10 | 0x11 | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 | 0x1a | 0x1b | 0x1c | 0x1d | 0x1e | 0x1f => "Cr Wirte", + 0x20 | 0x21 | 0x22 | 0x23 | 0x24 | 0x25 | 0x26 | 0x27 | 0x28 | 0x29 | 0x2a | 0x2b | 0x2c | 0x2d | 0x2e | 0x2f => "Dr Read", + 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39 | 0x3a | 0x3b | 0x3c | 0x3d | 0x3e | 0x3f => "Dr Wirte", + 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 | 0x49 | 0x4a | 0x4b | 0x4c | 0x4d | 0x4e | 0x4f => "Exception", + 0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 | 0x58 | 0x59 | 0x5a | 0x5b | 0x5c | 0x5d | 0x5e | 0x5f => "Exception", + 0x60 => "Physical INTR", + 0x61 => "Physical NMI", + 0x62 => "Physical SMI (the EXITINFO1 field provides more information)", + 0x63 => "Physical INIT", + 0x64 => "Virtual INTR", + 0x65 => "Write of CR0 changed bits other than CR0. TS or CR0.MP.", + 0x66 => "Read of IDTR", + 0x67 => "Read of GDTR", + 0x68 => "Read of LDTR", + 0x69 => "Read of TR", + 0x6a => "Write of IDTR", + 0x6b => "Write of GDTR", + 0x6c => "Write of LDTR", + 0x6d => "Write of TR", + 0x6e => "RDTSC instruction", + 0x6f => "RDTSCP instruction", + 0x70 => "Pushf instruction", + 0x71 => "Popf instruction", + 0x72 => "CPUID instruction", + 0x73 => "RSM instruction", + 0x74 => "IRET instruction", + 0x75 => "Software interrupt (INTn instruction)", + 0x76 => "INVD instruction", + 0x77 => "PAUSE instruction", + 0x78 => "HLT instruction", + 0x79 => "INVLPG instruction", + 0x7a => "INVLPGA instruction", + 0x7b => "IN or OOUT accessing protected port (the EXITINF1 field provides more information)", + 0x7c => "RDMSR or WRMSR access to protexted MSR", + 0x7d => "Task switch", + 0x7e => "FP legacy handling enabled, and processor is frozen in x87/mmx instruction waiting for an interrupt", + 0x7f => "Shutdown", + 0x80 => "VMRUN instruction", + 0x81 => "VMMCALL instruction", + 0x82 => "VMLOAD instruction", + 0x83 => "VMSAVE instruction", + 0x84 => "STGI instruction", + 0x85 => "CLGI instruction", + 0x86 => "SKINIT instruction", + 0x87 => "RDTSCP instruction", + 0x88 => "ICEBP instruction", + 0x89 => "WBINVD or WBNINVD instruction", + 0x8a => "MONITOR or MONITORX instruction", + 0x8b => "MWAIT or MWAITX instruction", + 0x8c => "MWAIT or MWAITX instruction, if monitor handware is armed", + 0x8d => "XSETBV instruction", + 0x8e => "RDPRU instruction", + 0x8f => "EFER write trap (occurs after guest instruction is finishes)", + 0x90 | 0x91 | 0x92 | 0x93 | 0x94 | 0x95 | 0x96 | 0x97 | 0x98 | 0x99 | 0x9a | 0x9b | 0x9c | 0x9d | 0x9e | 0x9f => "Write of CR0-15, respectively (occurs after guest instruction is finishes)", + 0xa0 => "INVLPGB instruction", + 0xa1 => "Illegal INVLPGB instruction", + 0xa2 => "INVOCID instruction", + 0xa3 => "MCOMMIT instruction", + 0xa4 => "TLBSYNC instruction", + 0xa5 => "Bus lock while Bus Lock Threshold Counter value is 0.", + 0xa6 => "HLT instructio if a virtual interrupt is not pending", + 0x400 => "Nested paging: host-level page fault occurred (EXITINFO1 contains fault error code; EXITINFO2 contains the guest physical address causing the fault).", + 0x401 => "AVIC—Virtual IPI delivery not completed. See \"AVIC IPI Delivery Not Completed\" on page 580 for EXITINFO1–2 definitions.", + 0x402 => "AVIC—Attempted access by guest to vAPIC register not handled by AVIC hardware. See \"AVIC Access to Unaccelerated vAPIC register\" on page 581 for EXITINFO1–2 definitions.", + 0x403 => "VMGEXIT instruction", + 0xF000_000 => "Reserved for Host", + -1 => "Invalid guest state in VMCB.", + -2 => "BUSY bit was set in the VMSA", + -3 => "The sibling thread is not in an idle state", + -4 => "Invalid PMC state", + _ => "[INVALID]", + } + } +} + +impl From for SvmInstructionError { + fn from(value: i32) -> Self { + Self(value) + } +} + +impl Debug for SvmInstructionError { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "SvmInstructionError({}, {:?})", self.0, self.as_str()) + } +} + +numeric_enum_macro::numeric_enum! { +#[repr(i32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[allow(non_camel_case_types)] +pub enum SvmExitReason { + Cr0Read = 0x0, + Cr1Read = 0x1, + Cr2Read = 0x2, + Cr3Read = 0x3, + Cr4Read = 0x4, + Cr5Read = 0x5, + Cr6Read = 0x6, + Cr7Read = 0x7, + Cr8Read = 0x8, + Cr9Read = 0x9, + Cr10Read = 0xa, + Cr11Read = 0xb, + Cr12Read = 0xc, + Cr13Read = 0xd, + Cr14Read = 0xe, + Cr15Read = 0xf, + Cr0Write = 0x10, + Cr1Write = 0x11, + Cr2Write = 0x12, + Cr3Write = 0x13, + Cr4Write = 0x14, + Cr5Write = 0x15, + Cr6Write = 0x16, + Cr7Write = 0x17, + Cr8Write = 0x18, + Cr9Write = 0x19, + Cr10Write = 0x1a, + Cr11Write = 0x1b, + Cr12Write = 0x1c, + Cr13Write = 0x1d, + Cr14Write = 0x1e, + Cr15Write = 0x1f, + Dr0Read = 0x20, + Dr1Read = 0x21, + Dr2Read = 0x22, + Dr3Read = 0x23, + Dr4Read = 0x24, + Dr5Read = 0x25, + Dr6Read = 0x26, + Dr7Read = 0x27, + Dr8Read = 0x28, + Dr9Read = 0x29, + Dr10Read = 0x2a, + Dr11Read = 0x2b, + Dr12Read = 0x2c, + Dr13Read = 0x2d, + Dr14Read = 0x2e, + Dr15Read = 0x2f, + Dr0Write = 0x30, + Dr1Write = 0x31, + Dr2Write = 0x32, + Dr3Write = 0x33, + Dr4Write = 0x34, + Dr5Write = 0x35, + Dr6Write = 0x36, + Dr7Write = 0x37, + Dr8Write = 0x38, + Dr9Write = 0x39, + Dr10Write = 0x3a, + Dr11Write = 0x3b, + Dr12Write = 0x3c, + Dr13Write = 0x3d, + Dr14Write = 0x3e, + Dr15Write = 0x3f, + Excp_0 = 0x40, + Excp_1 = 0x41, + Excp_2 = 0x42, + Excp_3 = 0x43, + Excp_4 = 0x44, + Excp_5 = 0x45, + Excp_6 = 0x46, + Excp_7 = 0x47, + Excp_8 = 0x48, + Excp_9 = 0x49, + Excp_10 = 0x4a, + Excp_11 = 0x4b, + Excp_12 = 0x4c, + Excp_13 = 0x4d, + Excp_14 = 0x4e, + Excp_15 = 0x4f, + Excp_16 = 0x50, + Excp_17 = 0x51, + Excp_18 = 0x52, + Excp_19 = 0x53, + Excp_20 = 0x54, + Excp_21 = 0x55, + Excp_22 = 0x56, + Excp_23 = 0x57, + Excp_24 = 0x58, + Excp_25 = 0x59, + Excp_26 = 0x5a, + Excp_27 = 0x5b, + Excp_28 = 0x5c, + Excp_29 = 0x5d, + Excp_30 = 0x5e, + Excp_31 = 0x5f, + + Intr = 0x60, + Nmi = 0x61, + Smi = 0x62, + Init = 0x63, + Vintr = 0x64, + Cr0SelWrite = 0x65, + IdtrRead = 0x66, + GdtrRead = 0x67, + LdtrRead = 0x68, + TrRead = 0x69, + IdtrWrite = 0x6a, + GdtrWrite = 0x6b, + LdtrWrite = 0x6c, + TrWrite = 0x6d, + Rdtsc = 0x6e, + Rdpmc = 0x6f, + Pushf = 0x70, + Popf = 0x71, + Cpuid = 0x72, + Rsm = 0x73, + Iret = 0x74, + Swint = 0x75, + Invd = 0x76, + Pause = 0x77, + Hlt = 0x78, + Invlpg = 0x79, + Invlpga = 0x7a, + Ioio = 0x7b, + Msr = 0x7c, + TaskSwitch = 0x7d, + FreeFreeze = 0x7e, + Shutdown = 0x7f, + Vmrun = 0x80, + Vmmcall = 0x81, + Vmload = 0x82, + Vmsave = 0x83, + Stgi = 0x84, + Clgi = 0x85, + Skinit = 0x86, + Rdtscp = 0x87, + Icebp = 0x88, + Wbinvd = 0x89, + Monitor = 0x8a, + Mwait = 0x8b, + MwaitConditioal = 0x8c, + Xsetbv = 0x8d, + Rdpru = 0x8e, + EferWriteTrap = 0x8f, + + Cr0WriteTrap = 0x90, + Cr1WriteTrap = 0x91, + Cr2WriteTrap = 0x92, + Cr3WriteTrap = 0x93, + Cr4WriteTrap = 0x94, + Cr5WriteTrap = 0x95, + Cr6WriteTrap = 0x96, + Cr7WriteTrap = 0x97, + Cr8WriteTrap = 0x98, + Cr9WriteTrap = 0x99, + Cr10WriteTrap = 0x9a, + Cr11WriteTrap = 0x9b, + Cr12WriteTrap = 0x9c, + Cr13WriteTrap = 0x9d, + Cr14WriteTrap = 0x9e, + Cr15WriteTrap = 0x9f, + + Invlpgb = 0xa0, + InvlpgaIllegal = 0xa1, + Invpcid = 0xa2, + Mcommit = 0xa3, + Tlbsync = 0xa4, + Buslock = 0xa5, + IdleHlt = 0xa6, + + Npf = 0x400, + AvicIncompleteIpi = 0x401, + AvicNoaccel = 0x402, + Vmgexit = 0x403, + + Invalid = -1, + Busy = -2, + IdleRequired = -3, + InvalidPmc = -4, +} +} + +#[derive(Debug)] +pub struct SvmExitInfo { + pub entry_failure: bool, + pub exit_reason: SvmExitReason, + pub exit_instruction_length: u32, + pub guest_rip: usize, +} diff --git a/src/svm/instructions.rs b/src/svm/instructions.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/svm/instructions.rs @@ -0,0 +1 @@ + diff --git a/src/svm/mod.rs b/src/svm/mod.rs new file mode 100644 index 0000000..e111dba --- /dev/null +++ b/src/svm/mod.rs @@ -0,0 +1,9 @@ +mod definitions; +mod instructions; +mod percpu; +mod vcpu; +mod vmcb; + +pub fn has_hardware_support() -> bool { + raw_cpuid::CpuId::new().get_svm_info().is_some() +} diff --git a/src/svm/percpu.rs b/src/svm/percpu.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/svm/percpu.rs @@ -0,0 +1 @@ + diff --git a/src/svm/vcpu.rs b/src/svm/vcpu.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/svm/vcpu.rs @@ -0,0 +1 @@ + diff --git a/src/svm/vmcb.rs b/src/svm/vmcb.rs new file mode 100644 index 0000000..b3e903e --- /dev/null +++ b/src/svm/vmcb.rs @@ -0,0 +1,612 @@ +use bilge::prelude::*; + +#[repr(C)] +#[derive(Clone, Copy, PartialEq)] +pub struct Vmcb { + control_area: VmcbControlArea, + state_save_area: VmcbStateSaveArea, +} + +#[repr(C)] +#[derive(Clone, Copy, PartialEq)] +pub struct VmcbControlArea { + // intercept vector 0 + intercept_cr_read: u16, + intercept_cr_write: u16, + // intercept vector 1 + intercept_dr_read: u16, + intercept_dr_write: u16, + // intercept vector 2 + intercept_exception: u32, + // intercept vector 3 + intercept_vector3: VmcbInterceptVector3, + // intercept vector 4 + intercept_vector4: VmcbInterceptVector4, + // intercept vector 5 + intercept_vector5: VmcbInterceptVector5, + + // reserved 0x18 - 0x3b + reserved_0x18_0x3b: [u8; 0x3b - 0x18], + + // PAUSE Filter Threshold + pause_filter_threshold: u16, + // PAUSE Filter Count + pause_filter_count: u16, + + // Physical base address, bits 11:0 are ignored + // Physical base address of IOPM + iopm_base_pa: u64, + msrpm_base_pa: u64, + tsc_offset: u64, + + // Guest state + guest: VmcbGuest, + vm_interrupt_control_flags: VmInterruptControlFlags, + vm_shadow_interrupt: VmShadowInterrupt, + exit_code: u64, + exit_info_1: u64, + exit_info_2: u64, + exit_init_info: u64, + nested_paging_flags: NestedPagingFlags, + avic_control_flags: AvicControlFlags, + + // Guest Physical Address of GHCB + ghcb_pa: u64, + // EVENTINJ—Event injection + event_injection: u64, + // nested paging + n_cr3: u64, + + // LBR Virtualization Flags + lbr_virtualization_flags: LbrVirtualizationFlags, + + // Clean Bits + clean_bits: VmcbCleanBits, + + // Next sequential instruction pointer + n_rip: u64, + + // Instruction Fetch Count + instruction_fetch_count: InstructionFetchCount, + + // AVIC APIC_BACKING_PAGE Pointer + avic_backing_page_pointer: AvicBackingPagePointer, + + // reserved 0xef - 0xe8 + reserved_0xef_0xe8: [u8; 0xef - 0xe8], + + // AVIC Logical Backing Page Pointer + avic_logical_backing_page_pointer: AvicBackingPagePointer, + // AVIC Physical Backing Page Pointer + avic_physical_backing_page_pointer: AvicBackingPagePointer, + + // reserved 0x100 - 0x107 + reserved_0x100_0x107: [u8; 0x107 - 0x100], + + // VMSA Pointer + vmsa_pointer: VmcbStateSaveAreaPointer, + + vmgexit_rax: u64, + vmgrexit_cpl: u8, + + // bus lock threshold counter + bus_lock_threshold_counter: u16, + + // reserved 0x133 - 0x128 + reserved_0x138_0x128: [u8; 0x133 - 0x128], + + // update irr ??? len = 0 + update_irr: [u8; 0x138 - 0x133], + + // SEV + sev_features_mask: SevFeaturesMask, + guest_sev_features: GuestSevFeatures, + + // reserved 0x148 - 0x150 + reserved_0x148_0x150: [u8; 0x150 - 0x148], + + // reserved 0x170 - 0x150 + requested_irr: [u8; 0x170 - 0x150], + + // reserved 0x3df - 0x170 + reserved_0x120_0x3df: [u8; 0x3df - 0x170], + + // reserved for host usage + reserved_0x3e0_0x3ff: [u8; 0x3ff - 0x3e0], +} + +impl VmcbControlArea { + #[allow(unused)] + pub fn tlb_control_description(&self) -> &'static str { + match self.guest.tlb_control() { + 0x00 => "Do nothing", + 0x01 => "Flush entire TLB (all entries, all ASIDs) on VMRUN", + 0x03 => "Flush this guest's TLB entries", + 0x07 => "Flush this guest's non-global TLB entries", + _ => "Reserved or Invalid encoding", + } + } +} + +#[repr(C)] +#[bitsize(32)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbInterceptVector3 { + intercept_intr: u1, + intercept_nmi: u1, + intercept_smi: u1, + intercept_init: u1, + intercept_vintr: u1, + // intercept CR0 writes that change bits other than CR0.TS or CR0.MP + intercept_cr0_writes: u1, + + // descriptor tables intercept + intercept_idtr_reads: u1, + intercept_gdtr_reads: u1, + intercept_ldtr_reads: u1, + intercept_tr_reads: u1, + intercept_idtr_writes: u1, + intercept_gdtr_writes: u1, + intercept_ldtr_writes: u1, + intercept_tr_writes: u1, + + // intercept instruction + intercept_rdtsc: u1, + intercept_rdpmc: u1, + intercept_pushf: u1, + intercept_popf: u1, + intercept_cpuid: u1, + intercept_rsm: u1, + intercept_iret: u1, + intercept_intn: u1, + intercept_invd: u1, + intercept_pause: u1, + intercept_hlt: u1, + intercept_invlpg: u1, + intercept_invlpga: u1, + intercept_ioio_prot: u1, + intercept_msr_prot: u1, + intercept_task_switch: u1, + + // intercept processor "freezing" during legacy FERR handling + intercept_ferr_freeze: u1, + intercept_shutdown_event: u1, +} + +#[repr(C)] +#[bitsize(32)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbInterceptVector4 { + intercept_vmrun: u1, + intercept_vmmcall: u1, + intercept_vmload: u1, + intercept_vmsave: u1, + intercept_stgi: u1, + intercept_clgi: u1, + intercept_skinit: u1, + intercept_rdtscp: u1, + intercept_icebp: u1, + // WBINVD and WBNOINVD + intercept_wbinvd_wbnoinvd: u1, + // MONITOR / MONITORX + intercept_monitor: u1, + // MWAIT / MWAITX instruction unconditional + intercept_mwait: u1, + // MWAIT / MWAITX instruction if monitor hardware is armed + intercept_mwait_hw_armed: u1, + intercept_xsetbv: u1, + intercept_rdpru: u1, + intercept_efer_writes: u1, + intercept_cr0: u1, + intercept_cr1: u1, + intercept_cr2: u1, + intercept_cr3: u1, + intercept_cr4: u1, + intercept_cr5: u1, + intercept_cr6: u1, + intercept_cr7: u1, + intercept_dr8: u1, + intercept_dr9: u1, + intercept_dr10: u1, + intercept_dr11: u1, + intercept_dr12: u1, + intercept_dr13: u1, + intercept_dr14: u1, + intercept_dr15: u1, +} + +#[repr(C)] +#[bitsize(32)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbInterceptVector5 { + intercept_invlpgb: u1, + intercept_illegal_invlpgb: u1, + intercept_invpcid: u1, + intercept_mcommit: u1, + // Intercept TLB invalidation. Presence of this bit + // is indicated by CPUID Fn8000_000A, EDX[24] = 1 + intercept_tlbsync: u1, + // Intercept HLT instruction if a virtual interrupt is not pending + intercept_hlt: u1, + reserve: u26, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbGuest { + // Guest Address Space Identifier (Guest ASID) + guest_asid: u32, + // TLB Control (bits 39:32) + // todo: Parse TLB Control + tlb_control: u8, + // Reserved bits (bits 63:40) + reserved: u24, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmInterruptControlFlags { + // Virtual TPR (bits 7:0) + // 4-bit virtual TPR value in bits 3:0 + // bits 7:4 are reserved (SBZ) + v_tpr: u4, + reserved_tpr: u4, + + // V_IRQ (bit 8) + // Nonzero if virtual INTR is pending + v_irq: u1, + + // VGIF (bit 9) + // Virtual interrupts are masked/unmasked (0: masked, 1: unmasked) + vgif: u1, + + // V_NMI (bit 11) + // Nonzero if virtual NMI is pending + v_nmi: u1, + + // V_NMI_MASK (bit 12) + // Nonzero if virtual NMI is masked + v_nmi_mask: u1, + + // Reserved bits 13:15 + // Reserved (SBZ) + reserved_nmi: u3, + + // V_INTR_PRIO (bits 19:16) + // 4-bit priority for virtual interrupt + v_intr_prio: u4, + + // V_IGN_TPR (bit 20) + // Nonzero if the current virtual interrupt ignores the (virtual) TPR + v_ign_tpr: u1, + + // Reserved bits 21:23 + // Reserved (SBZ) + reserved_intr: u3, + + // V_INTR_MASKING (bit 24) + // Virtualize masking of INTR interrupts + v_intr_masking: u1, + + // AMD Virtual GIF enabled (bit 25) + // Nonzero if AMD Virtual GIF is enabled for this guest + v_gif_enabled: u1, + + // V_NMI_ENABLE (bit 26) + // Nonzero if NMI virtualization is enabled + v_nmi_enable: u1, + + // Reserved bits 27:29 + // Reserved (SBZ) + reserved_nmi_enable: u3, + + // x2AVIC Enable (bit 30) + // Nonzero if x2AVIC is enabled + x2avic_enable: u1, + + // AVIC Enable (bit 31) + // Nonzero if AVIC is enabled + avic_enable: u1, + + // V_INTR_VECTOR (bits 39:32) + // 8-bit vector to use for this interrupt + v_intr_vector: u8, + + // Reserved bits 40:63 + // Reserved (SBZ) + reserved: u25, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmShadowInterrupt { + // INTERRUPT_SHADOW (bit 0) + // Guest is in an interrupt shadow (1: true, 0: false) + interrupt_shadow: u1, + + // GUEST_INTERRUPT_MASK (bit 1) + // Value of RFLAGS.IF for the guest + guest_interrupt_mask: u1, + + // Reserved bits (bits 63:2) + // 62 bits reserved (SBZ) + reserved: u62, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct NestedPagingFlags { + /// Enable nested paging + np_enable: u1, + + /// Enable Secure Encrypted Virtualization (SEV) + enable_sev: u1, + + /// Enable encrypted state for Secure Encrypted Virtualization (SEV) + enable_encrypted_state: u1, + + /// Guest Mode Execute Trap + guest_mode_execute_trap: u1, + + /// Enable supervisor shadow stack restrictions in nested page tables. + /// Support for this feature is indicated by CPUID Fn8000_000A_EDX[19] (SSSCheck). + sss_check_en: u1, + + /// Virtual Transparent Encryption + virtual_transparent_encryption: u1, + + /// Enable Read Only Guest Page Tables. + /// See "Nested Table Walk" for more information. + enable_read_only_guest_page_tables: u1, + + /// Enable INVLPGB/TLBSYNC. + /// + /// 0 - INVLPGB and TLBSYNC will result in #UD. + /// 1 - INVLPGB and TLBSYNC can be executed in guest. + /// + /// Presence of this bit is indicated by CPUID bit 8000_000A, EDX[24] = 1. + /// When in SEV-ES guest or this bit is not present, INVLPGB/TLBSYNC is always enabled in guest + enable_inv_lpgb_tlbsync: u1, + + /// Reserved (SBZ) + reserved: u56, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct AvicControlFlags { + /// Reserved (SBZ) + /// Bits 63:52 are reserved (SBZ) + reserved: u12, + + /// AVIC APIC_BAR + /// + /// Address of the APIC base for the AVIC (AMD Virtual APIC). + /// The APIC_BAR is used by AVIC to access the APIC and other + /// related functionality within the virtualized environment. + /// + /// Bits 51:0 are for AVIC APIC_BAR + apic_bar: u52, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct LbrVirtualizationFlags { + /// LBR Virtualization Enable + /// + /// 0 — Do nothing. + /// 1 — Enable LBR virtualization hardware acceleration. + lbr_virtualization_enable: u1, + + /// Virtualized VMSAVE/VMLOAD Enable + /// + /// Enables virtualized versions of VMSAVE and VMLOAD instructions. + virtualized_vmsave_vmload_enable: u1, + + /// Virtualized Instruction-Based Sampling Enable + /// + /// Enables virtualized instruction-based sampling. + /// See "Instruction-Based Sampling Virtualization" for details. + virtualized_instruction_sampling_enable: u1, + + /// Reserved (SBZ) + /// Reserved bits (63:3) + reserved: u61, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbCleanBits { + /// VMCB Clean Bits + /// + /// Represents the clean bits in the VMCB. + /// These bits are used to track the state of the VMCB, indicating which fields + /// have been modified or need to be written back to memory. + /// Bits 31:0 for VMCB Clean Bits + vmcb_clean_bits: u32, + + /// Reserved (SBZ) + /// Bits 63:32 are reserved (SBZ) + reserved: u32, +} + +#[repr(C)] +#[bitsize(128)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct InstructionFetchCount { + // Number of bytes fetched + number_of_bytes_fetched: u8, + // Guest instruction bytes + guest_instruction_bytes: u120, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct AvicBackingPagePointer { + /// AVIC APIC_BACKING_PAGE Pointer + /// + /// This field holds the pointer to the APIC backing page for AVIC (AMD Virtual APIC). + /// It points to the structure used by AVIC to emulate the APIC and related functionality + /// in a virtualized environment. + /// Bits 51:0 for AVIC APIC_BACKING_PAGE pointer + apic_backing_page_pointer: u52, + apic_backing_page_pointer_reserved: u12, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct AvicLogicalTablePointer { + avic_logical_table_pointer_reserved_1: u12, + avic_logical_table_pointer: u40, + avic_logical_table_pointer_reserved_2: u12, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct AvicPhysicalTablePointer { + avic_physical_table_pointer_reserved_1: u8, + avic_physical_table_pointer: u40, + avic_physical_table_pointer_reserved_2: u8, + avic_physical_max_index: u8, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct SevFeaturesMask { + allowed_sev_features_mask: u62, + reserved: u1, + allowed_sev_features_en: u1, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct GuestSevFeatures { + guest_sev_features: u62, + reserved: u2, +} + +#[repr(C)] +#[bitsize(64)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbStateSaveAreaPointer { + vmsa_pointer_reserved_1: u12, + vmsa_pointer: u40, + vmsa_pointer_reserved_2: u12, +} + +#[repr(C)] +#[derive(Clone, Copy, PartialEq)] +pub struct VmcbStateSaveArea { + // registers + es: VmcbRegister, + cs: VmcbRegister, + ss: VmcbRegister, + ds: VmcbRegister, + fs: VmcbRegister, + gs: VmcbRegister, + gdtr: VmcbRegister, + ldtr: VmcbRegister, + idtr: VmcbRegister, + tr: VmcbRegister, + // reserved 0xa0 - 0xca + reserved_0xa0_0xca: [u8; 0xca - 0xa0], + // If the guest is real-mode then the CPL is forced to 0; + // if the guest is virtual-mode then the CPL is forced to 3. + cpl: u8, + // reserved 0xcc + reserved_0xcc: u32, + efer: u64, + // reserved 0xd8 - 0x147 + reserved_0xd8_0xdf: [u8; 0xdf - 0xd8], + // perf control + perf_ctl0: u64, + perf_ctr0: u64, + perf_ctl1: u64, + perf_ctr1: u64, + perf_ctl2: u64, + perf_ctr2: u64, + perf_ctl3: u64, + perf_ctr3: u64, + perf_ctl4: u64, + perf_ctr4: u64, + perf_ctl5: u64, + perf_ctr5: u64, + // control registers and data registers + cr4: u64, + cr3: u64, + cr0: u64, + dr7: u64, + dr6: u64, + rfalgs: u64, + rip: u64, + // reserved 0x180 - 0x1bf + reserved_0x180_0x1bf: [u8; 0x1bf - 0x180], + instr_retired_ctr: u64, + perf_ctr_global_sts: u64, + perf_ctr_global_ctl: u64, + // reserved 0x1d4 - 1d7 + reserved_0x1d4_0x1d7: [u8; 0x1d7 - 0x1d4], + rsp: u64, + s_cet: u64, + ssp: u64, + isst_addr: u64, + rax: u64, + star: u64, + lstar: u64, + cstar: u64, + sfmask: u64, + kernel_gs_base: u64, + sysenter_cs: u64, + sysenter_esp: u64, + sysenter_eip: u64, + cr2: u64, + // reserved 248h–267h + reserved_0x248_0x267: [u8; 0x267 - 0x248], + g_pat: u64, + dbgctl: u64, + br_from: u64, + br_to: u64, + lastexcpfrom: u64, + lastexcpto: u64, + dbgextnctl: u64, + // reserved 2a0h–2dfh + reserved_0x2a0_0x2df: [u8; 0x2df - 0x2a0], + spec_ctrl: u64, + // reserved 2e8h–66fh + reserved_0x2e8_0x66f: [u8; 0x66f - 0x2e8], + // 670h - 76fh + lbr_stack: [u8; 256], + lbr_select: u64, + ibs_fetch_ctl: u64, + ibs_fetch_linaddr: u64, + ibs_op_ctl: u64, + ibs_op_rip: u64, + ibs_op_data: u64, + ibs_op_data2: u64, + ibs_op_data3: u64, + ibs_dc_linaddr: u64, + bp_ibstgt_rip: u64, + ic_ibst_extd_ctl: u64, +} + +#[repr(C)] +#[bitsize(128)] +#[derive(Clone, Copy, PartialEq, FromBits)] +pub struct VmcbRegister { + selector: u16, + attrib: u16, + limit: u32, + base: u64, +}