massive restructure

This commit is contained in:
2026-07-30 01:02:17 +02:00
parent 0b0634530f
commit 438ce4c58e
32 changed files with 3558 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
{ stdenv, kernel }:
stdenv.mkDerivation {
pname = "cpuid_fault_emulation";
version = "0.1";
src = ./source;
nativeBuildInputs = kernel.moduleBuildDependencies;
hardeningDisable = [
"pic"
"format"
];
# Patch the hardcoded host paths to point into the Nix store
postPatch = ''
substituteInPlace Makefile \
--replace '/lib/modules/$(KERNEL)/build' '${kernel.dev}/lib/modules/${kernel.modDirVersion}/build' \
--replace '$(shell uname -r)' '${kernel.modDirVersion}'
'';
installPhase = ''
install -D cpuid_fault_emulation.ko \
$out/lib/modules/${kernel.modDirVersion}/extra/cpuid_fault_emulation.ko
'';
meta.platforms = [ "x86_64-linux" ];
}
@@ -0,0 +1,23 @@
obj-m += cpuid_fault_emulation.o
cpuid_fault_emulation-y := src/cpuid_fault_emulation.o src/capture_context.o src/run_vm.o
PWD := $(CURDIR)
ccflags-y += -I$(src)/inc
asflags-y += -I$(src)/inc
NOCLANG := CONFIG_CLANG_VERSION=0
KERNEL := $(if $(KERNEL),$(KERNEL),$(shell uname -r))
ifneq ($(shell grep "CONFIG_CLANG_VERSION" /lib/modules/$(KERNEL)/build/.config), $(NOCLANG))
LLVM := LLVM=1
endif
all:
echo $(MAKE) $(LLVM) -C /lib/modules/$(KERNEL)/build M=$(PWD) modules
$(MAKE) $(LLVM) -C /lib/modules/$(KERNEL)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(KERNEL)/build M=$(PWD) clean
@@ -0,0 +1,7 @@
PACKAGE_NAME="cpuid_fault_emulation"
PACKAGE_VERSION="0.1"
BUILT_MODULE_NAME="cpuid_fault_emulation"
BUILT_MODULE_NAME="cpuid_fault_emulation"
DEST_MODULE_LOCATION="/updates"
AUTOINSTALL="yes"
@@ -0,0 +1,101 @@
#ifndef HOST_STATE_H
#define HOST_STATE_H
#define GUEST_RAX_OFFSET 0x00
#define GUEST_RBX_OFFSET 0x08
#define GUEST_RCX_OFFSET 0x10
#define GUEST_RDX_OFFSET 0x18
#define GUEST_RSI_OFFSET 0x20
#define GUEST_RDI_OFFSET 0x28
#define GUEST_RSP_OFFSET 0x30
#define GUEST_RBP_OFFSET 0x38
#define GUEST_R8_OFFSET 0x40
#define GUEST_R9_OFFSET 0x48
#define GUEST_R10_OFFSET 0x50
#define GUEST_R11_OFFSET 0x58
#define GUEST_R12_OFFSET 0x60
#define GUEST_R13_OFFSET 0x68
#define GUEST_R14_OFFSET 0x70
#define GUEST_R15_OFFSET 0x78
#define GUEST_RIP_OFFSET 0x80
#define GUEST_RFLAGS_OFFSET 0x88
#define HOST_STACK_OFFSET 0x000
#define HOST_STACK_SIZE 0x1000
#define HOST_VMCB_OFFSET 0x1000
#define HOST_HOST_VMCB_OFFSET 0x2000
#define HOST_HOST_SAVE_OFFSET 0x3000
#define HOST_VMCB_PA_OFFSET 0x4000
#define HOST_HOST_VMCB_PA_OFFSET 0x4008
#define HOST_REGISTER_OFFSET 0x4010
#ifndef __ASSEMBLER__
#include "vmcb_layout.h"
struct GuestRegisters {
u64 rax;
u64 rbx;
u64 rcx;
u64 rdx;
u64 rsi;
u64 rdi;
u64 rsp;
u64 rbp;
u64 r8;
u64 r9;
u64 r10;
u64 r11;
u64 r12;
u64 r13;
u64 r14;
u64 r15;
} __attribute__((packed));
static_assert(GUEST_RAX_OFFSET == offsetof(struct GuestRegisters, rax));
static_assert(GUEST_RBX_OFFSET == offsetof(struct GuestRegisters, rbx));
static_assert(GUEST_RCX_OFFSET == offsetof(struct GuestRegisters, rcx));
static_assert(GUEST_RDX_OFFSET == offsetof(struct GuestRegisters, rdx));
static_assert(GUEST_RSI_OFFSET == offsetof(struct GuestRegisters, rsi));
static_assert(GUEST_RDI_OFFSET == offsetof(struct GuestRegisters, rdi));
static_assert(GUEST_RSP_OFFSET == offsetof(struct GuestRegisters, rsp));
static_assert(GUEST_RBP_OFFSET == offsetof(struct GuestRegisters, rbp));
static_assert(GUEST_R8_OFFSET == offsetof(struct GuestRegisters, r8));
static_assert(GUEST_R9_OFFSET == offsetof(struct GuestRegisters, r9));
static_assert(GUEST_R10_OFFSET == offsetof(struct GuestRegisters, r10));
static_assert(GUEST_R11_OFFSET == offsetof(struct GuestRegisters, r11));
static_assert(GUEST_R12_OFFSET == offsetof(struct GuestRegisters, r12));
static_assert(GUEST_R13_OFFSET == offsetof(struct GuestRegisters, r13));
static_assert(GUEST_R14_OFFSET == offsetof(struct GuestRegisters, r14));
static_assert(GUEST_R15_OFFSET == offsetof(struct GuestRegisters, r15));
struct InitialContext {
struct GuestRegisters regs;
u64 rip;
u64 rflags;
} __attribute__((packed));
static_assert(GUEST_RIP_OFFSET == offsetof(struct InitialContext, rip));
static_assert(GUEST_RFLAGS_OFFSET == offsetof(struct InitialContext, rflags));
struct ProcessorStatus {
u8 host_stack[HOST_STACK_SIZE];
struct VMCB vmcb;
struct VMCB host_vmcb;
u8 host_save[0x1000];
u64 vmcb_pa;
u64 host_vmcb_pa;
struct GuestRegisters guest_registers;
void* page_ptr;
u64 prev_hsave_pa;
bool cpuid_fault;
} __attribute__((packed));
static_assert(offsetof(struct ProcessorStatus, host_stack) == HOST_STACK_OFFSET);
static_assert(offsetof(struct ProcessorStatus, vmcb) == HOST_VMCB_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_vmcb) == HOST_HOST_VMCB_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_save) == HOST_HOST_SAVE_OFFSET);
static_assert(offsetof(struct ProcessorStatus, vmcb_pa) == HOST_VMCB_PA_OFFSET);
static_assert(offsetof(struct ProcessorStatus, host_vmcb_pa) == HOST_HOST_VMCB_PA_OFFSET);
static_assert(offsetof(struct ProcessorStatus, guest_registers) == HOST_REGISTER_OFFSET);
#endif
#endif
@@ -0,0 +1,574 @@
#ifndef VMCB_LAYOUT_H
#define VMCB_LAYOUT_H
#include <linux/types.h>
struct InterceptVector0 {
u32 read_cr0 : 1;
u32 read_cr1 : 1;
u32 read_cr2 : 1;
u32 read_cr3 : 1;
u32 read_cr4 : 1;
u32 read_cr5 : 1;
u32 read_cr6 : 1;
u32 read_cr7 : 1;
u32 read_cr8 : 1;
u32 read_cr9 : 1;
u32 read_cr10 : 1;
u32 read_cr11 : 1;
u32 read_cr12 : 1;
u32 read_cr13 : 1;
u32 read_cr14 : 1;
u32 read_cr15 : 1;
u32 write_cr0 : 1;
u32 write_cr1 : 1;
u32 write_cr2 : 1;
u32 write_cr3 : 1;
u32 write_cr4 : 1;
u32 write_cr5 : 1;
u32 write_cr6 : 1;
u32 write_cr7 : 1;
u32 write_cr8 : 1;
u32 write_cr9 : 1;
u32 write_cr10 : 1;
u32 write_cr11 : 1;
u32 write_cr12 : 1;
u32 write_cr13 : 1;
u32 write_cr14 : 1;
u32 write_cr15 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector0) == 4);
struct InterceptVector1 {
u32 read_dr0 : 1;
u32 read_dr1 : 1;
u32 read_dr2 : 1;
u32 read_dr3 : 1;
u32 read_dr4 : 1;
u32 read_dr5 : 1;
u32 read_dr6 : 1;
u32 read_dr7 : 1;
u32 read_dr8 : 1;
u32 read_dr9 : 1;
u32 read_dr10 : 1;
u32 read_dr11 : 1;
u32 read_dr12 : 1;
u32 read_dr13 : 1;
u32 read_dr14 : 1;
u32 read_dr15 : 1;
u32 write_dr0 : 1;
u32 write_dr1 : 1;
u32 write_dr2 : 1;
u32 write_dr3 : 1;
u32 write_dr4 : 1;
u32 write_dr5 : 1;
u32 write_dr6 : 1;
u32 write_dr7 : 1;
u32 write_dr8 : 1;
u32 write_dr9 : 1;
u32 write_dr10 : 1;
u32 write_dr11 : 1;
u32 write_dr12 : 1;
u32 write_dr13 : 1;
u32 write_dr14 : 1;
u32 write_dr15 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector1) == 4);
struct InterceptVector2 {
u32 exception_vector0 : 1;
u32 exception_vector1 : 1;
u32 exception_vector2 : 1;
u32 exception_vector3 : 1;
u32 exception_vector4 : 1;
u32 exception_vector5 : 1;
u32 exception_vector6 : 1;
u32 exception_vector7 : 1;
u32 exception_vector8 : 1;
u32 exception_vector9 : 1;
u32 exception_vector10 : 1;
u32 exception_vector11 : 1;
u32 exception_vector12 : 1;
u32 exception_vector13 : 1;
u32 exception_vector14 : 1;
u32 exception_vector15 : 1;
u32 exception_vector16 : 1;
u32 exception_vector17 : 1;
u32 exception_vector18 : 1;
u32 exception_vector19 : 1;
u32 exception_vector20 : 1;
u32 exception_vector21 : 1;
u32 exception_vector22 : 1;
u32 exception_vector23 : 1;
u32 exception_vector24 : 1;
u32 exception_vector25 : 1;
u32 exception_vector26 : 1;
u32 exception_vector27 : 1;
u32 exception_vector28 : 1;
u32 exception_vector29 : 1;
u32 exception_vector30 : 1;
u32 exception_vector31 : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector2) == 4);
struct InterceptVector3 {
u32 intr : 1;
u32 nmi : 1;
u32 smi : 1;
u32 init : 1;
u32 vintr : 1;
u32 cr0_writes : 1;
u32 idtr_read : 1;
u32 gdtr_read : 1;
u32 ldtr_read : 1;
u32 tr_read : 1;
u32 idtr_write : 1;
u32 gdtr_write : 1;
u32 ldtr_write : 1;
u32 tr_write : 1;
u32 rdtsc_inst : 1;
u32 rdpmc_inst : 1;
u32 pushf_inst : 1;
u32 popf_inst : 1;
u32 cpuid_inst : 1;
u32 rsm_inst : 1;
u32 iret_inst : 1;
u32 intn_inst : 1;
u32 invd_inst : 1;
u32 pause_inst : 1;
u32 hlt_inst : 1;
u32 invlpg_inst : 1;
u32 invlpga_inst : 1;
u32 ioio_prot : 1;
u32 msr_prot : 1;
u32 task_switches : 1;
u32 ferr_freeze : 1;
u32 shutdown : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector3) == 4);
struct InterceptVector4 {
u32 vmrun_inst : 1;
u32 vmcall_inst : 1;
u32 vmload_inst : 1;
u32 vmsave_inst : 1;
u32 stgi_inst : 1;
u32 clgi_inst : 1;
u32 skinit_inst : 1;
u32 rdtscp_inst : 1;
u32 icebp_inst : 1;
u32 wbinvd_inst : 1;
u32 monitor_inst : 1;
u32 mwait_inst_uncond : 1;
u32 mwait_inst_armed : 1;
u32 xsetbv_inst : 1;
u32 rdpru_inst : 1;
u32 efer_write : 1;
u32 write_cr0_after : 1;
u32 write_cr1_after : 1;
u32 write_cr2_after : 1;
u32 write_cr3_after : 1;
u32 write_cr4_after : 1;
u32 write_cr5_after : 1;
u32 write_cr6_after : 1;
u32 write_cr7_after : 1;
u32 write_cr8_after : 1;
u32 write_cr9_after : 1;
u32 write_cr10_after : 1;
u32 write_cr11_after : 1;
u32 write_cr12_after : 1;
u32 write_cr13_after : 1;
u32 write_cr14_after : 1;
u32 write_cr15_after : 1;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector4) == 4);
struct InterceptVector5 {
u32 invlpgb_inst_all : 1;
u32 invlpgb_inst_ill : 1;
u32 invpcid_inst : 1;
u32 mcommit_inst : 1;
u32 tlbsync_inst : 1;
u32 bus_lock_op : 1;
u32 hlt_inst_npen : 1;
u32 reserved : 25;
} __attribute__((packed));
static_assert(sizeof(struct InterceptVector5) == 4);
struct TLBControl {
u32 tlb_control : 8;
u32 allow_larger_rap : 1;
u32 clear_rap : 1;
u32 reserved : 22;
} __attribute__((packed));
static_assert(sizeof(struct TLBControl) == 4);
struct VirtualInterruptControl {
u64 v_tpr : 4;
u64 v_tpr_mbz : 4;
u64 v_irq : 1;
u64 v_gif : 1;
u64 reserved_0 : 1;
u64 v_nmi : 1;
u64 v_nmi_mask : 1;
u64 reserved_1 : 3;
u64 v_intr_prio : 4;
u64 v_ign_tpr : 1;
u64 reserved_2 : 3;
u64 v_intr_masking : 1;
u64 v_gif_enable : 1;
u64 v_nmi_enable : 1;
u64 reserved_3 : 3;
u64 x2avic_enable : 1;
u64 avic_enable : 1;
u64 v_intr_vector : 8;
u64 reserved_4 : 24;
} __attribute__((packed));
static_assert(sizeof(struct VirtualInterruptControl) == 8);
struct InterruptShadow {
u64 interrupt_shadow : 1;
u64 guest_interrupt_mask : 1;
u64 reserved : 62;
} __attribute__((packed));
static_assert(sizeof(struct InterruptShadow) == 8);
struct VirtualizationControl0 {
u64 np_enable : 1;
u64 sev_enable : 1;
u64 sev_es : 1;
u64 guest_mode_execute_trap : 1;
u64 ssscheck_en : 1;
u64 virtual_transparent_encryption : 1;
u64 read_only_guest_page_tables : 1;
u64 invlpg_tlbsync_enable : 1;
u64 reserved : 56;
} __attribute__((packed));
static_assert(sizeof(struct VirtualizationControl0) == 8);
struct AVICAPICBar {
u64 avic_apic_bar : 52;
u64 reserved : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICAPICBar) == 8);
struct VirtualizationControl1 {
u64 lbr_virtualization_enable : 1;
u64 vmsave_vmload_virtualization_enable : 1;
u64 ibs_virtualization_enable : 1;
u64 pmc_virtualization_enable : 1;
u64 ignored : 60;
} __attribute__((packed));
static_assert(sizeof(struct VirtualizationControl1) == 8);
struct GuestInstruction {
u8 bytes_fetched;
u8 guest_instruction[0xF];
} __attribute__((packed));
static_assert(sizeof(struct GuestInstruction) == 0x10);
struct AVICAPICBackingPage {
u64 avic_apic_backing_page_pointer : 52;
u64 reserved : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICAPICBackingPage) == 8);
struct AVICLogicalTablePointer {
u64 reserved_0 : 12;
u64 avic_logical_table_pointer : 40;
u64 reserved_1 : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICLogicalTablePointer) == 8);
struct AVICPhysicalTablePointer {
u64 avic_physical_max_index : 12;
u64 avic_physical_table_pointer : 40;
u64 reserved_0 : 12;
} __attribute__((packed));
static_assert(sizeof(struct AVICPhysicalTablePointer) == 8);
struct VMSAPointer {
u64 reserved_0 : 12;
u64 vmsa_pointer : 40;
u64 reserved_1 : 12;
} __attribute__((packed));
static_assert(sizeof(struct VMSAPointer) == 8);
struct AllowedSEVFeatures {
u64 allowed_sev_features_mask : 62;
u64 reserved_0 : 1;
u64 allowed_sev_features_en : 1;
} __attribute__((packed));
static_assert(sizeof(struct AllowedSEVFeatures) == 8);
struct RequestedIRR {
u64 irr[4];
} __attribute__((packed));
static_assert(sizeof(struct RequestedIRR) == 0x20);
struct VMCBControlArea {
struct InterceptVector0 intercept_vector0;
struct InterceptVector1 intercept_vector1;
struct InterceptVector2 intercept_vector2;
struct InterceptVector3 intercept_vector3;
struct InterceptVector4 intercept_vector4;
struct InterceptVector5 intercept_vector5;
u8 reserved_0[0x24];
u16 pause_filter_threshold;
u16 pause_filter_count;
u64 iopm_base_pa;
u64 msrpm_base_pa;
u64 tsc_offset;
u32 guest_asid;
struct TLBControl tlb_control;
struct VirtualInterruptControl v_intr;
struct InterruptShadow interrupt_shadow;
u64 exitcode;
u64 exitinfo1;
u64 exitinfo2;
u64 exitintinfo;
struct VirtualizationControl0 virtualization_control0;
struct AVICAPICBar avic_apic_bar;
u64 guest_ghcb_pa;
u64 eventinj;
u64 h_cr3;
struct VirtualizationControl1 virtualization_control1;
u32 vmcb_clean_bits;
u8 reserved_1[0x4];
u64 nrip;
struct GuestInstruction guest_instruction;
struct AVICAPICBackingPage avicapic_backing_page;
u8 reserved_2[0x8];
struct AVICLogicalTablePointer avic_logical_table;
struct AVICPhysicalTablePointer avic_physical_table;
u8 reserved_3[0x08];
struct VMSAPointer vmsa_pointer;
u64 vmgexit_rax;
u8 vmgexit_cpl;
u8 reserved_4[0x07];
u16 bus_lock_threshold_counter;
u8 reserved_5[0x12];
u32 update_irr;
struct AllowedSEVFeatures allowed_sev_features;
u64 guest_sev_features;
u8 reserved_6[0x8];
struct RequestedIRR requested_irr;
u8 reserved_7[0x270];
u8 host_reserved[0x20];
} __attribute__((packed));
static_assert(offsetof(struct VMCBControlArea, intercept_vector0) == 0x000);
static_assert(offsetof(struct VMCBControlArea, intercept_vector1) == 0x004);
static_assert(offsetof(struct VMCBControlArea, intercept_vector2) == 0x008);
static_assert(offsetof(struct VMCBControlArea, intercept_vector3) == 0x00C);
static_assert(offsetof(struct VMCBControlArea, intercept_vector4) == 0x010);
static_assert(offsetof(struct VMCBControlArea, intercept_vector5) == 0x014);
static_assert(offsetof(struct VMCBControlArea, pause_filter_threshold) == 0x03C);
static_assert(offsetof(struct VMCBControlArea, pause_filter_count) == 0x03E);
static_assert(offsetof(struct VMCBControlArea, intercept_vector5) == 0x014);
static_assert(offsetof(struct VMCBControlArea, iopm_base_pa) == 0x040);
static_assert(offsetof(struct VMCBControlArea, msrpm_base_pa) == 0x048);
static_assert(offsetof(struct VMCBControlArea, tsc_offset) == 0x050);
static_assert(offsetof(struct VMCBControlArea, guest_asid) == 0x058);
static_assert(offsetof(struct VMCBControlArea, tlb_control) == 0x05C);
static_assert(offsetof(struct VMCBControlArea, v_intr) == 0x060);
static_assert(offsetof(struct VMCBControlArea, interrupt_shadow) == 0x068);
static_assert(offsetof(struct VMCBControlArea, exitcode) == 0x070);
static_assert(offsetof(struct VMCBControlArea, exitinfo1) == 0x078);
static_assert(offsetof(struct VMCBControlArea, exitinfo2) == 0x080);
static_assert(offsetof(struct VMCBControlArea, exitintinfo) == 0x088);
static_assert(offsetof(struct VMCBControlArea, virtualization_control0) == 0x090);
static_assert(offsetof(struct VMCBControlArea, avic_apic_bar) == 0x098);
static_assert(offsetof(struct VMCBControlArea, guest_ghcb_pa) == 0x0A0);
static_assert(offsetof(struct VMCBControlArea, eventinj) == 0x0A8);
static_assert(offsetof(struct VMCBControlArea, h_cr3) == 0x0B0);
static_assert(offsetof(struct VMCBControlArea, virtualization_control1) == 0x0B8);
static_assert(offsetof(struct VMCBControlArea, vmcb_clean_bits) == 0x0C0);
static_assert(offsetof(struct VMCBControlArea, nrip) == 0x0C8);
static_assert(offsetof(struct VMCBControlArea, guest_instruction) == 0x0D0);
static_assert(offsetof(struct VMCBControlArea, avicapic_backing_page) == 0x0E0);
static_assert(offsetof(struct VMCBControlArea, avic_logical_table) == 0x0F0);
static_assert(offsetof(struct VMCBControlArea, avic_physical_table) == 0x0F8);
static_assert(offsetof(struct VMCBControlArea, vmsa_pointer) == 0x108);
static_assert(offsetof(struct VMCBControlArea, vmgexit_rax) == 0x110);
static_assert(offsetof(struct VMCBControlArea, vmgexit_cpl) == 0x118);
static_assert(offsetof(struct VMCBControlArea, bus_lock_threshold_counter) == 0x120);
static_assert(offsetof(struct VMCBControlArea, update_irr) == 0x134);
static_assert(offsetof(struct VMCBControlArea, allowed_sev_features) == 0x138);
static_assert(offsetof(struct VMCBControlArea, guest_sev_features) == 0x140);
static_assert(offsetof(struct VMCBControlArea, requested_irr) == 0x150);
static_assert(offsetof(struct VMCBControlArea, host_reserved) == 0x3E0);
static_assert(sizeof(struct VMCBControlArea) == 0x400);
struct SegmentDescriptor {
u16 selector;
u16 attrib;
u32 limit;
u64 base;
} __attribute__((packed));
static_assert(offsetof(struct SegmentDescriptor, selector) == 0);
static_assert(offsetof(struct SegmentDescriptor, attrib) == 2);
static_assert(offsetof(struct SegmentDescriptor, limit) == 4);
static_assert(offsetof(struct SegmentDescriptor, base) == 8);
static_assert(sizeof(struct SegmentDescriptor) == 0x10);
struct LBRSTACK {
u8 lbr_stack_from_to[256];
} __attribute__((packed));
static_assert(sizeof(struct LBRSTACK) == 0x100);
struct VMCBStateSaveArea {
struct SegmentDescriptor es;
struct SegmentDescriptor cs;
struct SegmentDescriptor ss;
struct SegmentDescriptor ds;
struct SegmentDescriptor fs;
struct SegmentDescriptor gs;
struct SegmentDescriptor gdtr;
struct SegmentDescriptor ldtr;
struct SegmentDescriptor idtr;
struct SegmentDescriptor tr;
u8 reserved_0[0x2B];
u8 cpl;
u8 reserved_1[0x4];
u64 efer;
u8 reserved_2[0x8];
u64 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;
u8 reserved_3[0x8];
u64 cr4;
u64 cr3;
u64 cr0;
u64 dr7;
u64 dr6;
u64 rflags;
u64 rip;
u8 reserved_4[0x40];
u64 instr_retired_ctr;
u64 perf_ctr_global_sts;
u64 perf_ctr_global_ctl;
u64 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;
u8 reserved_5[0x20];
u64 g_pat;
u64 dbgctl;
u64 br_from;
u64 br_to;
u64 lastexcpfrom;
u64 lastexcpto;
u64 dbgextnctl;
u8 reserved_6[0x40];
u64 spec_ctrl;
u8 reserved_7[0x388];
struct LBRSTACK lbr_stack_from_to;
u64 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_ibs_extd_ctl;
u8 reserved_9[0x38];
} __attribute__((packed));
static_assert(offsetof(struct VMCBStateSaveArea, es) == 0x000);
static_assert(offsetof(struct VMCBStateSaveArea, cs) == 0x010);
static_assert(offsetof(struct VMCBStateSaveArea, ss) == 0x020);
static_assert(offsetof(struct VMCBStateSaveArea, ds) == 0x030);
static_assert(offsetof(struct VMCBStateSaveArea, fs) == 0x040);
static_assert(offsetof(struct VMCBStateSaveArea, gs) == 0x050);
static_assert(offsetof(struct VMCBStateSaveArea, gdtr) == 0x060);
static_assert(offsetof(struct VMCBStateSaveArea, ldtr) == 0x070);
static_assert(offsetof(struct VMCBStateSaveArea, idtr) == 0x080);
static_assert(offsetof(struct VMCBStateSaveArea, tr) == 0x090);
static_assert(offsetof(struct VMCBStateSaveArea, cpl) == 0x0CB);
static_assert(offsetof(struct VMCBStateSaveArea, efer) == 0x0D0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl0) == 0x0E0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr0) == 0x0E8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl1) == 0x0F0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr1) == 0x0F8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl2) == 0x100);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr2) == 0x108);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl3) == 0x110);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr3) == 0x118);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl4) == 0x120);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr4) == 0x128);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctl5) == 0x130);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr5) == 0x138);
static_assert(offsetof(struct VMCBStateSaveArea, cr4) == 0x148);
static_assert(offsetof(struct VMCBStateSaveArea, cr3) == 0x150);
static_assert(offsetof(struct VMCBStateSaveArea, cr0) == 0x158);
static_assert(offsetof(struct VMCBStateSaveArea, dr7) == 0x160);
static_assert(offsetof(struct VMCBStateSaveArea, dr6) == 0x168);
static_assert(offsetof(struct VMCBStateSaveArea, rflags) == 0x170);
static_assert(offsetof(struct VMCBStateSaveArea, rip) == 0x178);
static_assert(offsetof(struct VMCBStateSaveArea, instr_retired_ctr) == 0x1C0);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr_global_sts) == 0x1C8);
static_assert(offsetof(struct VMCBStateSaveArea, perf_ctr_global_ctl) == 0x1D0);
static_assert(offsetof(struct VMCBStateSaveArea, rsp) == 0x1D8);
static_assert(offsetof(struct VMCBStateSaveArea, rax) == 0x1F8);
static_assert(offsetof(struct VMCBStateSaveArea, star) == 0x200);
static_assert(offsetof(struct VMCBStateSaveArea, lstar) == 0x208);
static_assert(offsetof(struct VMCBStateSaveArea, cstar) == 0x210);
static_assert(offsetof(struct VMCBStateSaveArea, sfmask) == 0x218);
static_assert(offsetof(struct VMCBStateSaveArea, kernel_gs_base) == 0x220);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_cs) == 0x228);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_esp) == 0x230);
static_assert(offsetof(struct VMCBStateSaveArea, sysenter_eip) == 0x238);
static_assert(offsetof(struct VMCBStateSaveArea, cr2) == 0x240);
static_assert(offsetof(struct VMCBStateSaveArea, g_pat) == 0x268);
static_assert(offsetof(struct VMCBStateSaveArea, dbgctl) == 0x270);
static_assert(offsetof(struct VMCBStateSaveArea, br_from) == 0x278);
static_assert(offsetof(struct VMCBStateSaveArea, br_to) == 0x280);
static_assert(offsetof(struct VMCBStateSaveArea, lastexcpfrom) == 0x288);
static_assert(offsetof(struct VMCBStateSaveArea, lastexcpto) == 0x290);
static_assert(offsetof(struct VMCBStateSaveArea, dbgextnctl) == 0x298);
static_assert(offsetof(struct VMCBStateSaveArea, spec_ctrl) == 0x2E0);
static_assert(offsetof(struct VMCBStateSaveArea, lbr_stack_from_to) == 0x670);
static_assert(offsetof(struct VMCBStateSaveArea, lbr_select) == 0x770);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_fetch_ctl) == 0x778);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_fetch_linaddr) == 0x780);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_ctl) == 0x788);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_rip) == 0x790);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data) == 0x798);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data2) == 0x7A0);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_op_data3) == 0x7A8);
static_assert(offsetof(struct VMCBStateSaveArea, ibs_dc_linaddr) == 0x7B0);
static_assert(offsetof(struct VMCBStateSaveArea, bp_ibstgt_rip) == 0x7B8);
static_assert(offsetof(struct VMCBStateSaveArea, ic_ibs_extd_ctl) == 0x7C0);
static_assert(sizeof(struct VMCBStateSaveArea) == 0x800);
struct VMCB {
struct VMCBControlArea control_area;
struct VMCBStateSaveArea state_area;
u8 reserved_0 [0x400];
} __attribute__((packed));
static_assert(offsetof(struct VMCB, control_area) == 0x000);
static_assert(offsetof(struct VMCB, state_area) == 0x400);
static_assert(sizeof(struct VMCB) == 0x1000);
#endif
@@ -0,0 +1,40 @@
#include <linux/linkage.h>
#include <asm/asm.h>
#include <host_state.h>
#define save_to_offset(val, REG) movq val , GUEST_ ## REG ## _OFFSET (% _ASM_ARG1 )
SYM_FUNC_START(capture_context)
#ifdef ENDBR
ENDBR
#endif
save_to_offset($0, RAX) /* save 0 for guest rax */
save_to_offset(%rbx, RBX)
save_to_offset(%rcx, RCX)
save_to_offset(%rdx, RDX)
save_to_offset(%rsi, RSI)
save_to_offset(%rdi, RDI)
save_to_offset(%rbp, RBP)
leaq 8(%rsp),%rax /* rsp after ret */
save_to_offset(%rax, RSP)
save_to_offset(%r8, R8)
save_to_offset(%r9, R9)
save_to_offset(%r10, R10)
save_to_offset(%r11, R11)
save_to_offset(%r12, R12)
save_to_offset(%r13, R13)
save_to_offset(%r14, R14)
save_to_offset(%r15, R15)
movq (%rsp), %rax /* return addr*/
save_to_offset(%rax, RIP)
pushfq
popq %rax /* rflags */
save_to_offset(%rax, RFLAGS)
mov $1, %rax;
RET
SYM_FUNC_END(capture_context)
@@ -0,0 +1,663 @@
#include <linux/init.h> /* Needed for the macros */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */
#include <linux/string.h> /* for memset() and memcpy() */
#include <linux/bits.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
#include <linux/cpuhplock.h>
#else
#include <linux/cpu.h>
#endif
#include <linux/suspend.h>
#include <linux/types.h>
#include <asm/desc.h>
#include <asm/msr.h>
#include <linux/mm.h>
#include <host_state.h>
#include <vmcb_layout.h>
#define MSR_PM_BASE1 0x00000000
#define MSR_PM_BASE2 0xc0000000
#define MSR_PM_BASE3 0xc0010000
#define SVM_MSR_VM_HSAVE_PA 0xc0010117
#define UNLOAD_HV_MAGIC 0x40067420
#define SVM_SUPPORT_LEAF 0x80000001
#define SVM_SUPPORT_BIT 2
#define SVM_MSR_VM_CR 0xc0010114
#define VM_CR_SVMDIS_BIT 4
#define VMEXIT_CPUID 0x72
#define VMEXIT_MSRPROT 0x7C
/* VECTOR 8 bits = 0x0D (GP) */
/* TYPE 3 bits = 0x3 (Fault) */
/* EV 1 bit = 0x1 (Error Code Valid) */
/* reserved 19 bits */
/* V 1 bit = 0x1 (Valid) */
/* ERRORCODE 32 bits = 0x0 */
#define GP0_EVENTINJ (0x0D | (0x3 << 8) | (0x1 << 11) | (0x1 << 31))
#ifndef page_to_phys
#define page_to_phys(page) PFN_PHYS(page_to_pfn(page))
#endif
/* The hypervisor doesn't handle nested virtualization, so attempting to
* virtualize a kernel currently using virtualization features will probably
* result in an immediate crash. If you know better you can tell the module
* to attempt the virtualization anyway */
static int ignore_svm_enabled = 0;
module_param(ignore_svm_enabled, int, 0);
static struct {
struct page* msrpm_page;
u64 msrpm_pa;
bool prev_cpuid_fault_cap;
bool prev_svm_enabled;
} shared_data;
inline static void vmsave(u64 vmcb_pa) {
asm volatile ("vmsave" : : "a" (vmcb_pa) : "memory");
}
inline static void vmload(u64 vmcb_pa) {
asm volatile ("vmload" : : "a" (vmcb_pa) : "memory");
}
inline static void do_cpuid(u32* eax, u32* ebx, u32* ecx, u32* edx) {
asm volatile ("cpuid"
: "+a"(*eax), "=b"(*ebx), "+c"(*ecx), "=d"(*edx)
:
: "memory");
}
inline static void do_cpuid_leaf(u32 leaf, u32* eax, u32* ebx,
u32* ecx, u32* edx) {
asm volatile ("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(leaf)
: "memory");
}
inline static void do_wrmsr(u32 msr, u64 val) {
asm volatile ("wrmsr" : : "c"(msr), "a"((u32)val), "d" ((u32)(val >> 32)) : "memory");
}
inline static u64 do_rdmsr(u32 msr) {
u32 low, high;
asm volatile ("rdmsr" : "=a"(low), "=d" (high) : "c"(msr));
return low | ((u64)high) << 32;
}
/* Raw writes and reads for cr registers. The kernel has it's own safer
* versions, however, all the writes we do should use values provided by the
* kernel, so these consistent raw versions are cleaner to use. */
#define define_cr_ops(cr) \
inline static u64 do_read_ ## cr (void) {\
u64 val; \
asm volatile ("movq %%" #cr ", %0" : "=r" (val) : :); \
return val; \
} \
inline static void do_write_ ## cr (u64 val) {\
asm volatile ("movq %0, %%" #cr : :"r" (val) :); \
}
define_cr_ops(cr0);
define_cr_ops(cr2);
define_cr_ops(cr3);
define_cr_ops(cr4);
/* Checks the relevant CPUID bits and MSR registers to determine SVM support
* since the hypervisor doesn't use features like NPT or decode assists,
* very few checks are needed */
static bool check_svm_support(void) {
u32 eax, ebx, ecx, edx;
/* check for "AuthenticAMD" on leaf 0 */
do_cpuid_leaf(0, &eax, &ebx, &ecx, &edx);
if (!(ebx == 'htuA' && edx == 'itne' && ecx == 'DMAc'))
return false;
/* check for SVM support */
do_cpuid_leaf(SVM_SUPPORT_LEAF, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1U << SVM_SUPPORT_BIT)))
return false;
/* check if SVM is disabled by the BIOS */
if (do_rdmsr(SVM_MSR_VM_CR) & BIT_ULL(VM_CR_SVMDIS_BIT))
return false;
return true;
}
/* capture_context saves all GPR's, as well as RIP and RFLAGS as they are at
* it's return, except for RAX, which is saved as 0, but returned as 1 */
noinline_for_stack u64 capture_context(struct InitialContext* registers);
/* run_vm repeatedly runs the VMRUN instruction, effectively continuing to
* run the guest (the OS), when an intercepted event happens, it calls the
* handle_vm_exit function with the current state of the guest. If the call
* returns a non-zero value it begins the process of devirtualizing the
* processor */
__attribute__((noreturn)) void run_vm(struct ProcessorStatus* status);
/* called when the guest would execute a CPUID instruction*/
static u64 handle_cpuid(struct ProcessorStatus* status) {
u32 eax = status->guest_registers.rax;
u32 ebx;
u32 ecx = status->guest_registers.rcx;
u32 edx;
if(status->vmcb.state_area.cpl == 0) {
switch (eax) {
case UNLOAD_HV_MAGIC:
status->vmcb.state_area.rip += 2;
return 1; /* tell the hypervisor to devirtualize the processor */
}
} else {
if(status->cpuid_fault) { /* if CPUID should fault */
status->vmcb.control_area.eventinj = GP0_EVENTINJ; /* inject GP */
return 0;
}
}
/* otherwise, run CPUID and increase RIP */
do_cpuid(&eax, &ebx, &ecx, &edx);
status->guest_registers.rax = eax;
status->guest_registers.rbx = ebx;
status->guest_registers.rcx = ecx;
status->guest_registers.rdx = edx;
status->vmcb.state_area.rip += 2;
return 0;
}
/* called when the guest would execute a read or write to a protected MSR */
static u64 handle_msr(struct ProcessorStatus* status) {
/* the msr the guest is trying to access */
u32 msr = status->guest_registers.rcx;
bool write_access = (status->vmcb.control_area.exitinfo1 != 0);
/* to emulate cpuid_fault, writes and reads to the corresponding MSR are
* intercepted */
if (write_access) {
u64 value = (status->guest_registers.rdx << 32) |
(u32)status->guest_registers.rax;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
/* If the MSR to enable / disable cpuid_fault is being written to */
if(msr == MSR_K7_HWCR) {
/* update whether the hypervisor should inject a fault on CPUID */
status->cpuid_fault = value & BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT);
/* write the value to the register, with the cpuid_fault bit clear */
do_wrmsr(msr, value & ~BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT));
}
#else
/* Older kernels will attempt to enable cpuid_fault using the intel
* MSR */
if(msr == MSR_MISC_FEATURES_ENABLES) {
status->cpuid_fault = value & MSR_MISC_FEATURES_ENABLES_CPUID_FAULT;
}
#endif
else {
/* If we don't care about the MSR, do the write directly */
do_wrmsr(msr, value);
}
} else {
u64 value;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
/* If the MSR to enable / disable cpuid_fault is being read */
if(msr == MSR_K7_HWCR) {
value = do_rdmsr(msr);
/* reflect the current cpuid_fault status to the guest */
if(status->cpuid_fault)
value |= BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT);
}
#else
/* Older kernels will attempt to enable cpuid_faulting using the intel
* MSR */
if(msr == MSR_MISC_FEATURES_ENABLES) {
value = 0;
if(status->cpuid_fault)
value |= MSR_MISC_FEATURES_ENABLES_CPUID_FAULT;
}
#endif
else {
/* If we don't care about the MSR, do the read directly */
value = do_rdmsr(msr);
}
status->guest_registers.rax = (u32)value;
status->guest_registers.rdx = (u32)(value >> 32);
}
status->vmcb.state_area.rip += 2;
return 0;
}
/* consistent functions to write segment selectors */
#define define_sel_ops(sel) \
inline static u64 read_ ## sel ## _sel(void) {\
u16 sel; \
asm volatile ("movw %%" #sel ", %0" : "=r" (sel) : :); \
return sel; \
} \
inline static void write_ ## sel ## _sel(u16 sel) {\
asm volatile ("movw %0, %%" #sel : :"r" (sel) :); \
}
define_sel_ops(ds);
define_sel_ops(es);
define_sel_ops(ss);
inline static u64 read_cs_sel(void) {
u16 cs;
asm volatile("movw %%cs, %0" : "=r"(cs) : :);
return cs;
}
/* a direct move into cs is not a valid instruction, so the value is written
* via a far jump */
inline static void write_cs_sel(u16 cs) {
asm volatile("movzwq %0,%%rax\n\t"
"pushq %%rax\n\t"
"leaq curr_ip(%%rip),%%rax\n\t"
"pushq %%rax\n\t"
"lretq\n\t"
"curr_ip:": : "r"(cs) : "rax");
};
/* To end the virtualization the host needs to put itself the guest was just
* in, this effectively does the opposite to setup_guest_state */
static void restore_host_state(struct ProcessorStatus* status) {
struct VMCB* guest_vmcb = &status->vmcb;
struct desc_ptr gdt_ptr;
struct desc_ptr idt_ptr;
/* Load other state that might have changed */
vmload(status->vmcb_pa);
/* Restore the hsave address before the processor was virtualized */
do_wrmsr(SVM_MSR_VM_HSAVE_PA, status->prev_hsave_pa);
do_wrmsr(MSR_EFER, guest_vmcb->state_area.efer);
idt_ptr.address = guest_vmcb->state_area.idtr.base;
idt_ptr.size = guest_vmcb->state_area.idtr.limit;
gdt_ptr.address = guest_vmcb->state_area.gdtr.base;
gdt_ptr.size = guest_vmcb->state_area.gdtr.limit;
/* Restore gdt and idt. Done for correctness */
native_load_gdt(&gdt_ptr);
native_load_idt(&idt_ptr);
/* Load segment selectors from the guest. Also for correctness */
write_es_sel(guest_vmcb->state_area.es.selector);
write_ss_sel(guest_vmcb->state_area.ss.selector);
write_ds_sel(guest_vmcb->state_area.ds.selector);
write_cs_sel(guest_vmcb->state_area.cs.selector);
/* run_vm will resume running the OS by setting this rip and rsp */
status->guest_registers.rax = guest_vmcb->state_area.rip;
status->guest_registers.rsp = guest_vmcb->state_area.rsp;
/* will be used by the OS to cleanup the per processor data */
status->guest_registers.rbx = (u64)status;
}
/* Everytime an intercepted instruction is excecuted (currently only
* wrmsr/rdmsr to the cpuid_fault register, cpuid and runvm (required by
* the processor)), the guest's state is saved and the hypervisor calls
* this function */
u64 handle_vm_exit(struct ProcessorStatus* status) {
status->guest_registers.rsp = status->vmcb.state_area.rsp;
status->guest_registers.rax = status->vmcb.state_area.rax;
u64 exitcode = status->vmcb.control_area.exitcode;
u64 result = 0;
switch(exitcode) {
case VMEXIT_CPUID:
result = handle_cpuid(status);
break;
case VMEXIT_MSRPROT:
result = handle_msr(status);
break;
}
if(result) { /* begin the devirtualization process */
restore_host_state(status);
}
status->vmcb.state_area.rsp = status->guest_registers.rsp;
status->vmcb.state_area.rax = status->guest_registers.rax;
return result; /* tell run_vm if it should keep running the guest */
}
/* Used to initialize the segment registers (cs, ds, es, ss) in the VMCB */
static void set_descriptor_from_gdt(struct desc_ptr* gdtr, u16 selector,
struct SegmentDescriptor* desc) {
desc->selector = selector;
u64 idx = selector & ~0b111; /* Mask out the TI and RPL bits */
if(!idx) {
desc->attrib = 0;
desc->limit = 0;
desc->base = 0;
return;
}
/* Read the 8 bytes of the descriptor from the gdt, there's also 16-byte
* descriptors (e.g. TSS, LDT), but the function is not used for them, so
* they're not handled */
u64 raw = *(u64*) (gdtr->address + idx);
/* Concatenate the attribute bits */
desc->attrib = ((raw & (0xFFL << 40)) >> 40) |
((raw & (0xFL << 52)) >> 44);
/* Concatenate the limit bits */
desc->limit = (raw & 0xFFFF) | ((raw & (0xFL << 48)) >> 32);
/* If the granularity is 4kb, store the effective limit */
if(raw & (0x1L << 55)) desc->limit = (desc->limit << 12) | 0xFFFL;
/* Concatenate the base address bits */
desc->base = ((raw & (0xFFFFL << 16)) >> 16) |
((raw & (0xFFL << 32)) >> 16) |
((raw & (0xFFL << 56)) >> 32);
}
/* This setups all of the state needed to run a vm with the current status
* of the processor */
static void setup_guest_state(struct page* host_status_page,
struct InitialContext* initial_context) {
u64 host_status_pa = page_to_phys(host_status_page);
u64 guest_vmcb_pa = host_status_pa + HOST_VMCB_OFFSET;
u64 host_vmcb_pa = host_status_pa + HOST_HOST_VMCB_OFFSET;
u64 host_save_pa = host_status_pa + HOST_HOST_SAVE_OFFSET;
struct ProcessorStatus* host_status = page_address(host_status_page);
host_status->vmcb_pa = guest_vmcb_pa;
host_status->host_vmcb_pa = host_vmcb_pa;
/* used to cleanup after the processor is devirtualized */
host_status->page_ptr = host_status_page;
host_status->guest_registers = initial_context->regs;
struct VMCB* guest_vmcb = &host_status->vmcb;
struct desc_ptr gdt_ptr;
struct desc_ptr idt_ptr;
native_store_gdt(&gdt_ptr);
store_idt(&idt_ptr);
/* intercept MSRs defined by MSR prot */
guest_vmcb->control_area.intercept_vector3.msr_prot = 1;
guest_vmcb->control_area.msrpm_base_pa = shared_data.msrpm_pa;
/* intercept the CPUID instruction */
guest_vmcb->control_area.intercept_vector3.cpuid_inst = 1;
/* intercept the VMRUN instruction (required to run VMRUN) */
guest_vmcb->control_area.intercept_vector4.vmrun_inst = 1;
/* the guest uses it's own address space, 1 is the safest value to use */
guest_vmcb->control_area.guest_asid = 1;
u32 eax, ebx, ecx, edx;
do_cpuid_leaf(0x80000008, &eax, &ebx, &ecx, &edx);
if(ebx & (1 << 3)) {
do_cpuid_leaf(0x8000000a, &eax, &ebx, &ecx, &edx);
if(edx & (1 << 24)) {
guest_vmcb->control_area.virtualization_control0.invlpg_tlbsync_enable = 1;
}
}
guest_vmcb->state_area.gdtr.base = gdt_ptr.address;
guest_vmcb->state_area.gdtr.limit = gdt_ptr.size;
guest_vmcb->state_area.idtr.base = idt_ptr.address;
guest_vmcb->state_area.idtr.limit = idt_ptr.size;
u16 cs_selector = read_cs_sel();
set_descriptor_from_gdt(&gdt_ptr, cs_selector,
&guest_vmcb->state_area.cs);
u16 ds_selector = read_ds_sel();
set_descriptor_from_gdt(&gdt_ptr, ds_selector,
&guest_vmcb->state_area.ds);
u16 es_selector = read_es_sel();
set_descriptor_from_gdt(&gdt_ptr, es_selector,
&guest_vmcb->state_area.es);
u16 ss_selector = read_ss_sel();
set_descriptor_from_gdt(&gdt_ptr, ss_selector,
&guest_vmcb->state_area.ss);
guest_vmcb->state_area.efer = do_rdmsr(MSR_EFER);
guest_vmcb->state_area.cr0 = do_read_cr0();
guest_vmcb->state_area.cr2 = do_read_cr2();
guest_vmcb->state_area.cr3 = do_read_cr3();
guest_vmcb->state_area.cr4 = do_read_cr4();
guest_vmcb->state_area.rflags = initial_context->rflags;
guest_vmcb->state_area.rsp = initial_context->regs.rsp;
guest_vmcb->state_area.rip = initial_context->rip;
guest_vmcb->state_area.rax = initial_context->regs.rax;
vmsave(guest_vmcb_pa);
/* Save the hsave address before the hypervisor is loaded */
/* (If the kvm_amd kernel module is loaded, this value will already be
* setup and changing it and trying to run a kvm vm will crash the OS) */
host_status->prev_hsave_pa = do_rdmsr(SVM_MSR_VM_HSAVE_PA);
do_wrmsr(SVM_MSR_VM_HSAVE_PA, host_save_pa);
/* save another copy of the host state, used to provide a more consistent
* host state at vmexit */
vmsave(host_vmcb_pa);
}
static int virtualize_processor(void* processor_number) {
struct page* host_status_page;
int order = get_order(sizeof(struct ProcessorStatus));
host_status_page = alloc_pages(GFP_KERNEL, order);
if (!host_status_page)
goto host_status_page_failed;
unsigned long flags;
get_cpu();
local_irq_save(flags);
memset(page_address(host_status_page), 0, PAGE_SIZE * (1 << order));
struct InitialContext context;
/* Capture the context to be used to initialize the guest, since it
* returns 1 but the stored state has 0 as it's return, when the guest
* starts execution at the stored state it will not enter into the if.
* */
if(capture_context(&context)) {
do_wrmsr(MSR_EFER, do_rdmsr(MSR_EFER) | EFER_SVME); /* Enable SVME */
setup_guest_state(host_status_page, &context);
struct ProcessorStatus* host_status = page_address(host_status_page);
/* Kinda ugly, effectively sets a CR3 for the host that will
* always be safe to access the hypervisor's code from
* */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0)
leave_mm();
#else
leave_mm(smp_processor_id());
#endif
run_vm(host_status);
BUG();
}
/* Only the guest should run this code */
put_cpu();
pr_info("Processor %d virtualized\n", (u32)(u64)processor_number);
return 0;
host_status_page_failed:
local_irq_restore(flags);
put_cpu();
return -ENOMEM;
}
static int check_svm_on_cpu(void*) {
return do_rdmsr(MSR_EFER) & EFER_SVME;
}
static bool check_svm_status(void) {
u32 svm_enabled = 0;
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
svm_enabled |= smp_call_on_cpu(cpu, check_svm_on_cpu, NULL, true);
}
cpus_read_unlock();
return svm_enabled;
}
static int devirtualize_processor(void* processor_number) {
int order = get_order(sizeof(struct ProcessorStatus));
unsigned long flags;
get_cpu();
local_irq_save(flags);
u64 rax = UNLOAD_HV_MAGIC;
u64 rbx, rcx, rdx;
asm volatile("cpuid":"+a"(rax),"=b"(rbx),"=c"(rcx),"=d"(rdx)::"memory");
struct ProcessorStatus* status = NULL;
if(rax != UNLOAD_HV_MAGIC) {
status = (struct ProcessorStatus*)rbx;
struct VMCB* guest_vmcb = &status->vmcb;
do_write_cr3(guest_vmcb->state_area.cr3);
do_write_cr4(guest_vmcb->state_area.cr4);
do_write_cr2(guest_vmcb->state_area.cr2);
do_write_cr0(guest_vmcb->state_area.cr0);
}
if(!shared_data.prev_svm_enabled)
do_wrmsr(MSR_EFER, do_rdmsr(MSR_EFER) & ~EFER_SVME);
local_irq_restore(flags);
put_cpu();
if(status)__free_pages(status->page_ptr, order);
pr_info("Processor %d devirtualized\n", (u32)(u64)processor_number);
return 0;
}
/* called when a Power Management (e.g. suspend) event is about to happen */
static int handle_pm_notification(struct notifier_block* nb, unsigned long action, void *data) {
if(action == PM_HIBERNATION_PREPARE || action == PM_SUSPEND_PREPARE) {
/* on suspend or hibernation svm is disabled, so
* all processors need to be devirtualized */
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, devirtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
/* return the cpuid_fault bit capability to it's previous value */
if(!shared_data.prev_cpuid_fault_cap)
clear_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
}
if(action == PM_POST_HIBERNATION || action == PM_POST_SUSPEND) {
/* revirtualize all processors after suspend or hibernation */
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, virtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
shared_data.prev_cpuid_fault_cap = test_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
set_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
}
return 0;
}
static struct notifier_block pm_notifier = {
.notifier_call = handle_pm_notification
};
static int __init init_emulation(void) {
if (!check_svm_support()) {
pr_info("Processor not supported\n");
return -ENODEV;
}
shared_data.prev_svm_enabled = check_svm_status();
if(shared_data.prev_svm_enabled && !ignore_svm_enabled) {
pr_info("SVM already enabled\n");
return -ENODEV;
}
shared_data.msrpm_page = alloc_pages(GFP_KERNEL, 1);
if (!shared_data.msrpm_page)
goto msrpm_failed;
shared_data.msrpm_pa = page_to_phys(shared_data.msrpm_page);
memset(page_address(shared_data.msrpm_page), 0, 0x2000);
unsigned char* section_1_start = (unsigned char*)
page_address(shared_data.msrpm_page);
unsigned char* section_3_start = (unsigned char*)
page_address(shared_data.msrpm_page) + 0x1000;
#ifdef MSR_K7_HWCR_CPUID_USER_DIS_BIT
set_bit((MSR_K7_HWCR - MSR_PM_BASE3) * 2,
(unsigned long*) section_3_start);
set_bit((MSR_K7_HWCR - MSR_PM_BASE3) * 2 + 1,
(unsigned long*) section_3_start);
#else /* on older kernels intercept the intel register instead */
set_bit((MSR_MISC_FEATURES_ENABLES - MSR_PM_BASE1) * 2,
(unsigned long*) section_1_start);
set_bit((MSR_MISC_FEATURES_ENABLES - MSR_PM_BASE1) * 2 + 1,
(unsigned long*) section_1_start);
#endif
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, virtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
shared_data.prev_cpuid_fault_cap = test_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
set_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
register_pm_notifier(&pm_notifier);
return 0;
msrpm_failed:
return -ENOMEM;
}
static void __exit deinit_emulation(void) {
unregister_pm_notifier(&pm_notifier);
if(!shared_data.prev_cpuid_fault_cap)
clear_bit(X86_FEATURE_CPUID_FAULT,
(unsigned long *)(boot_cpu_data.x86_capability));
cpus_read_lock();
u32 cpu;
for_each_online_cpu(cpu) {
smp_call_on_cpu(cpu, devirtualize_processor, (void*)(u64)cpu, true);
}
cpus_read_unlock();
if (shared_data.msrpm_page)
__free_pages(shared_data.msrpm_page, 1);
}
module_init(init_emulation);
module_exit(deinit_emulation);
MODULE_LICENSE("GPL");
@@ -0,0 +1,121 @@
#include <linux/linkage.h>
#include <asm/asm.h>
#include <host_state.h>
#define save_to_offset_from(val, off, from) movq val, off -(HOST_STACK_OFFSET + HOST_STACK_SIZE) (from)
#define mov_from_offset_from(off, reg, from) movq off -(HOST_STACK_OFFSET + HOST_STACK_SIZE) (from), reg
#define save_to_offset(val, off) save_to_offset_from(val, off, %rsp)
#define mov_from_offset(off, reg) mov_from_offset_from(off, reg, %rsp)
#define save_to_reg_from(val, REG, from) save_to_offset_from(val, HOST_REGISTER_OFFSET + GUEST_ ## REG ## _OFFSET, from)
#define mov_from_reg_from(REG, reg, from) mov_from_offset_from(HOST_REGISTER_OFFSET + GUEST_ ## REG ## _OFFSET, reg, from)
#define save_to_reg(val, REG) save_to_reg_from(val, REG, %rsp)
#define mov_from_reg(REG, reg) mov_from_reg_from(REG, reg, %rsp)
SYM_CODE_START(run_vm)
#ifdef ENDBR
ENDBR
#endif
leaq HOST_STACK_OFFSET + HOST_STACK_SIZE(%_ASM_ARG1), %rsp
vmloop:
mov_from_offset(HOST_HOST_VMCB_PA_OFFSET, %rax)
vmsave
mov_from_offset(HOST_VMCB_PA_OFFSET, %rax)
vmload
mov_from_reg(RBX, %rbx)
mov_from_reg(RCX, %rcx)
mov_from_reg(RDX, %rdx)
mov_from_reg(RSI, %rsi)
mov_from_reg(RDI, %rdi)
mov_from_reg(RBP, %rbp)
mov_from_reg(R8, %r8)
mov_from_reg(R9, %r9)
mov_from_reg(R10, %r10)
mov_from_reg(R11, %r11)
mov_from_reg(R12, %r12)
mov_from_reg(R13, %r13)
mov_from_reg(R14, %r14)
mov_from_reg(R15, %r15)
vmrun
vmsave
mov_from_offset(HOST_HOST_VMCB_PA_OFFSET, %rax)
vmload
save_to_reg(%rbx, RBX)
save_to_reg(%rcx, RCX)
save_to_reg(%rdx, RDX)
save_to_reg(%rsi, RSI)
save_to_reg(%rdi, RDI)
save_to_reg(%rbp, RBP)
save_to_reg(%r8, R8)
save_to_reg(%r9, R9)
save_to_reg(%r10, R10)
save_to_reg(%r11, R11)
save_to_reg(%r12, R12)
save_to_reg(%r13, R13)
save_to_reg(%r14, R14)
save_to_reg(%r15, R15)
leaq -HOST_STACK_SIZE(%rsp), %_ASM_ARG1
/* Save volatile XMM registers */
sub $0x80, %rsp
movaps %xmm0, 0x20(%rsp)
movaps %xmm1, 0x30(%rsp)
movaps %xmm2, 0x40(%rsp)
movaps %xmm3, 0x50(%rsp)
movaps %xmm4, 0x60(%rsp)
movaps %xmm5, 0x70(%rsp)
sub $0x8, %rsp
call handle_vm_exit
add $0x8, %rsp
/* Restore volatile XMM registers */
movaps 0x70(%rsp), %xmm5
movaps 0x60(%rsp), %xmm4
movaps 0x50(%rsp), %xmm3
movaps 0x40(%rsp), %xmm2
movaps 0x30(%rsp), %xmm1
movaps 0x20(%rsp), %xmm0
add $0x80, %rsp
test %rax, %rax
jz vmloop
mov_from_reg(RBX, %rbx) /* now holds rflags */
pushq %rbx
popfq
leaq (%rsp), %rax;
mov_from_reg(RBX, %rbx)
mov_from_reg(RCX, %rcx)
mov_from_reg(RDX, %rdx)
mov_from_reg(RSI, %rsi)
mov_from_reg(RDI, %rdi)
mov_from_reg(RBP, %rbp)
mov_from_reg(R8, %r8)
mov_from_reg(R9, %r9)
mov_from_reg(R10, %r10)
mov_from_reg(R11, %r11)
mov_from_reg(R12, %r12)
mov_from_reg(R13, %r13)
mov_from_reg(R14, %r14)
mov_from_reg(R15, %r15)
mov_from_reg(RSP, %rsp)
mov_from_reg_from(RAX, %rax, %rax) /* now holds rip */
stgi
pushq %rax
RET
SYM_CODE_END(run_vm)