From c8d3a770bceaa588be801617645a63f5bc0a6de5 Mon Sep 17 00:00:00 2001 From: KeksNino Date: Sat, 25 Jul 2026 17:39:39 +0200 Subject: [PATCH] update --- configuration.nix | 19 + flake.lock | 54 +- home-manager/home.nix | 18 + packages.nix | 12 +- pkgs/cpuid-fault-emulation/default.nix | 28 + pkgs/cpuid-fault-emulation/source/Makefile | 23 + pkgs/cpuid-fault-emulation/source/dkms.conf | 7 + .../source/inc/host_state.h | 101 +++ .../source/inc/vmcb_layout.h | 574 +++++++++++++++ .../source/src/capture_context.S | 40 ++ .../source/src/cpuid_fault_emulation.c | 663 ++++++++++++++++++ .../cpuid-fault-emulation/source/src/run_vm.S | 121 ++++ 12 files changed, 1630 insertions(+), 30 deletions(-) create mode 100644 pkgs/cpuid-fault-emulation/default.nix create mode 100644 pkgs/cpuid-fault-emulation/source/Makefile create mode 100644 pkgs/cpuid-fault-emulation/source/dkms.conf create mode 100644 pkgs/cpuid-fault-emulation/source/inc/host_state.h create mode 100644 pkgs/cpuid-fault-emulation/source/inc/vmcb_layout.h create mode 100644 pkgs/cpuid-fault-emulation/source/src/capture_context.S create mode 100644 pkgs/cpuid-fault-emulation/source/src/cpuid_fault_emulation.c create mode 100644 pkgs/cpuid-fault-emulation/source/src/run_vm.S diff --git a/configuration.nix b/configuration.nix index 849f020..8f11b9f 100644 --- a/configuration.nix +++ b/configuration.nix @@ -2,8 +2,14 @@ pkgs, sls-steam, options, + config, ... }: +let + cpuid-fault-emulation = pkgs.callPackage ./pkgs/cpuid-fault-emulation { + kernel = config.boot.kernelPackages.kernel; + }; +in { imports = [ @@ -66,6 +72,7 @@ "kvm.ignore_msrs=1" "mitigations=off" "amdgpu.ppfeaturemask=0xffffffff" + "clearcpuid=umip" ]; xdg = { @@ -83,6 +90,18 @@ boot.loader.grub.enable = true; boot.loader.grub.device = "nodev"; boot.loader.grub.efiSupport = true; + boot.extraModulePackages = [ cpuid-fault-emulation ]; + + environment.systemPackages = [ + (pkgs.writeShellScriptBin "enable-hypervisor" '' + sudo modprobe -r kvm_amd kvm + sudo modprobe cpuid_fault_emulation + '') + (pkgs.writeShellScriptBin "disable-hypervisor" '' + sudo modprobe -r cpuid_fault_emulation + sudo modprobe kvm_amd kvm + '') + ]; networking.hostName = "nixos"; # Define your hostname. networking.firewall.enable = false; diff --git a/flake.lock b/flake.lock index 1a4477d..4a2f464 100644 --- a/flake.lock +++ b/flake.lock @@ -18,11 +18,11 @@ "dank-qml-common": { "flake": false, "locked": { - "lastModified": 1784647395, - "narHash": "sha256-kcLSJC8XBzmWoSBc90Zn81qrNsqWMkwQ1m/zZixOw30=", + "lastModified": 1784935106, + "narHash": "sha256-aVgOBRynme6XNKYCZlf/oCjPDZFwddyUQPRRkEupC3c=", "owner": "AvengeMedia", "repo": "dank-qml-common", - "rev": "1919595f0dde4f5bf5ac0baa1902ff6d070971d5", + "rev": "a172b39841d8f42bac46ac133b76cade1fae91b4", "type": "github" }, "original": { @@ -40,11 +40,11 @@ ] }, "locked": { - "lastModified": 1784816066, - "narHash": "sha256-j2jJmxgkcZemmusvQysK+bN/dDhvqYD//IPmcVsfhR0=", + "lastModified": 1784936204, + "narHash": "sha256-XNbDvIaPzqqIpgJynqSADAj7Yf8KlGJTo5S/fQJUBxs=", "owner": "AvengeMedia", "repo": "DankMaterialShell", - "rev": "d818067bee842e432fc8ffd94bf3d0801d190c7f", + "rev": "8bb7396362357650a80dcc8d81407095c91c7dcf", "type": "github" }, "original": { @@ -131,11 +131,11 @@ ] }, "locked": { - "lastModified": 1784064149, - "narHash": "sha256-yJ+YH7ZjNTnU33wnNRs21dGILG+5Ewt2s+B4OAi8SOU=", + "lastModified": 1784926331, + "narHash": "sha256-EiyJny6oq41fes/x+vWmmzlbjZxwy+8TtbaQqKXyRgY=", "owner": "rPlakama", "repo": "gsr-ui-nix", - "rev": "42838d89e48b5c32bcf0e6a670e5daaa084229dc", + "rev": "e463d47ca8724de7a83c394d9e323c6d9c449508", "type": "github" }, "original": { @@ -151,11 +151,11 @@ ] }, "locked": { - "lastModified": 1784789275, - "narHash": "sha256-cgRTWuG+u1tBPhm01JrId2k0Bni09phVJ1Q0BZlRd7M=", + "lastModified": 1784913159, + "narHash": "sha256-JWq0BfjO4ktpH5USfQNQzdvHpIDT8fSKD5K7LvdMRFs=", "owner": "nix-community", "repo": "home-manager", - "rev": "3b0e6bbd65869af1beadf5963a99befc179d209f", + "rev": "079a3b5d1aa6a719920a51316253b7d6dd22738d", "type": "github" }, "original": { @@ -192,11 +192,11 @@ }, "locked": { "dir": "packages/nix", - "lastModified": 1784048235, - "narHash": "sha256-ar1xg5HFbz6XJ/BAOo37ewfnMcsLHj0/088soIvXKIo=", + "lastModified": 1784860807, + "narHash": "sha256-nznQFS9H8er9bTwxKG4LSa2D0lGsxIPUqDuWa1rbJkA=", "owner": "SteamClientHomebrew", "repo": "Millennium", - "rev": "df5a3572fd2366b4aa689abd655ee48296ba1afd", + "rev": "266e41ea8924c41e51145ae986a9bdf68f9415d9", "type": "github" }, "original": { @@ -256,11 +256,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784819053, - "narHash": "sha256-LjxwsNf8fZonvODqPaR6vzAN43ShWji4GaOVUMBv94w=", + "lastModified": 1784992771, + "narHash": "sha256-xEE5I69+WNqcs7Z0v5D9xwcHYRbvC8k/fKBmlUBdCp4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d0f7f5759c3c0ef348c999a0b96eb03071a53f48", + "rev": "37bfb33419c6e367e8bb6f44041eaecf953b5236", "type": "github" }, "original": { @@ -324,11 +324,11 @@ ] }, "locked": { - "lastModified": 1784819646, - "narHash": "sha256-q7etFrDCAZkGwHQDazozduWrxHc9AazJcBNVTBAM94g=", + "lastModified": 1784993591, + "narHash": "sha256-4/evHMKmkO482OWfvaPk4uwyvUJxrJWcxXhNHqMQbDU=", "owner": "noctalia-dev", "repo": "noctalia", - "rev": "6008a66739beb43f8d1509bda459ab5c6e59073f", + "rev": "62975eea9ea64171c35382d904c7564cd3d4077b", "type": "github" }, "original": { @@ -467,11 +467,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784743179, - "narHash": "sha256-zEFky4d+WKVfbWVzcGkECsQgwW0XZWilGA5VQpgtFek=", + "lastModified": 1784975066, + "narHash": "sha256-EoSjKF8AMBbtupTmen2n+CEJnWVW2ynSYIMNHYTTT2s=", "owner": "sxyazi", "repo": "yazi", - "rev": "58f1013348c969538352e3105f58e8d8e870a092", + "rev": "533d1aa3fbb9b388745c72d34d377ff8e8326d84", "type": "github" }, "original": { @@ -486,11 +486,11 @@ "nixpkgs": "nixpkgs_5" }, "locked": { - "lastModified": 1784586513, - "narHash": "sha256-ewIv3xH9bQK7uMkU3xK6NWFEW5oIuAtIny8ifJwcY/E=", + "lastModified": 1784985989, + "narHash": "sha256-Mz4G81HXJlUtHoJtA31D+Ob7uma2BX7E/y7F9QazuFc=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "0ea161e9ac257510acabfb6e9dbf13630298760d", + "rev": "a8f9b6d014c9cfe0615dccb2bc7808713f6e62c2", "type": "github" }, "original": { diff --git a/home-manager/home.nix b/home-manager/home.nix index 97b2adc..d1cf8d4 100644 --- a/home-manager/home.nix +++ b/home-manager/home.nix @@ -16,6 +16,7 @@ home.username = "user"; home.homeDirectory = "/home/user"; + xdg.userDirs = { enable = true; createDirectories = false; @@ -23,6 +24,23 @@ SCREENSHOTS = "${config.home.homeDirectory}/Pictures/Screenshots"; }; }; + + xdg.mimeApps = { + enable = true; + defaultApplications = { + "inode/directory" = [ "nemo.desktop" ]; + }; + }; + + dconf = { + settings = { + "org/cinnamon/desktop/applications/terminal" = { + exec = "kitty"; + # exec-arg = ""; # argument + }; + }; + }; + home.pointerCursor = { enable = true; gtk.enable = true; diff --git a/packages.nix b/packages.nix index f979a79..4140870 100644 --- a/packages.nix +++ b/packages.nix @@ -24,18 +24,16 @@ pwvucontrol lsof picard - klassy exiftool tlrc libnotify trash-cli - nicotine-plus spek ffmpeg vlc # wine ripgrep - nemo + nemo-with-extensions unrar papirus-icon-theme rust-analyzer @@ -55,6 +53,14 @@ playerctl musicpresence nixd + libsForQt5.qt5ct + qt6Packages.qt6ct + kdePackages.breeze + kdePackages.breeze.qt5 + hexchat + gnumake + samrewritten + gale ]; fonts.packages = with pkgs; [ diff --git a/pkgs/cpuid-fault-emulation/default.nix b/pkgs/cpuid-fault-emulation/default.nix new file mode 100644 index 0000000..b0e46d1 --- /dev/null +++ b/pkgs/cpuid-fault-emulation/default.nix @@ -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" ]; +} diff --git a/pkgs/cpuid-fault-emulation/source/Makefile b/pkgs/cpuid-fault-emulation/source/Makefile new file mode 100644 index 0000000..c63d31a --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/Makefile @@ -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 diff --git a/pkgs/cpuid-fault-emulation/source/dkms.conf b/pkgs/cpuid-fault-emulation/source/dkms.conf new file mode 100644 index 0000000..3e7b733 --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/dkms.conf @@ -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" diff --git a/pkgs/cpuid-fault-emulation/source/inc/host_state.h b/pkgs/cpuid-fault-emulation/source/inc/host_state.h new file mode 100644 index 0000000..40fb9db --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/inc/host_state.h @@ -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 + diff --git a/pkgs/cpuid-fault-emulation/source/inc/vmcb_layout.h b/pkgs/cpuid-fault-emulation/source/inc/vmcb_layout.h new file mode 100644 index 0000000..f171f77 --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/inc/vmcb_layout.h @@ -0,0 +1,574 @@ +#ifndef VMCB_LAYOUT_H +#define VMCB_LAYOUT_H +#include + +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 diff --git a/pkgs/cpuid-fault-emulation/source/src/capture_context.S b/pkgs/cpuid-fault-emulation/source/src/capture_context.S new file mode 100644 index 0000000..84061d6 --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/src/capture_context.S @@ -0,0 +1,40 @@ +#include +#include +#include + +#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) diff --git a/pkgs/cpuid-fault-emulation/source/src/cpuid_fault_emulation.c b/pkgs/cpuid-fault-emulation/source/src/cpuid_fault_emulation.c new file mode 100644 index 0000000..e7f3655 --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/src/cpuid_fault_emulation.c @@ -0,0 +1,663 @@ +#include /* Needed for the macros */ +#include /* Needed by all modules */ +#include /* Needed for pr_info() */ +#include /* for memset() and memcpy() */ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0) + #include +#else + #include +#endif +#include + +#include +#include +#include +#include + +#include +#include + +#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"); diff --git a/pkgs/cpuid-fault-emulation/source/src/run_vm.S b/pkgs/cpuid-fault-emulation/source/src/run_vm.S new file mode 100644 index 0000000..1fea9ef --- /dev/null +++ b/pkgs/cpuid-fault-emulation/source/src/run_vm.S @@ -0,0 +1,121 @@ +#include +#include +#include + +#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)