@@ -152,10 +152,12 @@ enum Encoding {
GUEST_DEBUGCTL = 0x2802ul,
GUEST_DEBUGCTL_HI = 0x2803ul,
GUEST_EFER = 0x2806ul,
+ GUEST_PAT = 0x2804ul,
GUEST_PERF_GLOBAL_CTRL = 0x2808ul,
GUEST_PDPTE = 0x280aul,
/* 64-Bit Host State */
+ HOST_PAT = 0x2c00ul,
HOST_EFER = 0x2c02ul,
HOST_PERF_GLOBAL_CTRL = 0x2c04ul,
@@ -330,11 +332,15 @@ enum Ctrl_exi {
EXI_HOST_64 = 1UL << 9,
EXI_LOAD_PERF = 1UL << 12,
EXI_INTA = 1UL << 15,
+ EXI_SAVE_PAT = 1UL << 18,
+ EXI_LOAD_PAT = 1UL << 19,
+ EXI_SAVE_EFER = 1UL << 20,
EXI_LOAD_EFER = 1UL << 21,
};
enum Ctrl_ent {
ENT_GUEST_64 = 1UL << 9,
+ ENT_LOAD_PAT = 1UL << 14,
ENT_LOAD_EFER = 1UL << 15,
};
@@ -354,6 +360,7 @@ enum Ctrl0 {
CPU_NMI_WINDOW = 1ul << 22,
CPU_IO = 1ul << 24,
CPU_IO_BITMAP = 1ul << 25,
+ CPU_MSR_BITMAP = 1ul << 28,
CPU_SECONDARY = 1ul << 31,
};
@@ -1,4 +1,15 @@
#include "vmx.h"
+#include "msr.h"
+#include "processor.h"
+#include "vm.h"
+
+u64 ia32_pat;
+u64 ia32_efer;
+
+static inline void vmcall()
+{
+ asm volatile("vmcall");
+}
void basic_init()
{
@@ -76,6 +87,176 @@ int vmenter_exit_handler()
return VMX_TEST_VMEXIT;
}
+void msr_bmp_init()
+{
+ void *msr_bitmap;
+ u32 ctrl_cpu0;
+
+ msr_bitmap = alloc_page();
+ memset(msr_bitmap, 0x0, PAGE_SIZE);
+ ctrl_cpu0 = vmcs_read(CPU_EXEC_CTRL0);
+ ctrl_cpu0 |= CPU_MSR_BITMAP;
+ vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu0);
+ vmcs_write(MSR_BITMAP, (u64)msr_bitmap);
+}
+
+static void test_ctrl_pat_init()
+{
+ u64 ctrl_ent;
+ u64 ctrl_exi;
+
+ msr_bmp_init();
+ ctrl_ent = vmcs_read(ENT_CONTROLS);
+ ctrl_exi = vmcs_read(EXI_CONTROLS);
+ vmcs_write(ENT_CONTROLS, ctrl_ent | ENT_LOAD_PAT);
+ vmcs_write(EXI_CONTROLS, ctrl_exi | (EXI_SAVE_PAT | EXI_LOAD_PAT));
+ ia32_pat = rdmsr(MSR_IA32_CR_PAT);
+ vmcs_write(GUEST_PAT, 0x0);
+ vmcs_write(HOST_PAT, ia32_pat);
+}
+
+static void test_ctrl_pat_main()
+{
+ u64 guest_ia32_pat;
+
+ guest_ia32_pat = rdmsr(MSR_IA32_CR_PAT);
+ if (!(ctrl_enter_rev.clr & ENT_LOAD_PAT))
+ printf("\tENT_LOAD_PAT is not supported.\n");
+ else {
+ if (guest_ia32_pat != 0) {
+ report("Entry load PAT", 0);
+ return;
+ }
+ }
+ wrmsr(MSR_IA32_CR_PAT, 0x6);
+ vmcall();
+ guest_ia32_pat = rdmsr(MSR_IA32_CR_PAT);
+ if (ctrl_enter_rev.clr & ENT_LOAD_PAT) {
+ if (guest_ia32_pat != ia32_pat) {
+ report("Entry load PAT", 0);
+ return;
+ }
+ report("Entry load PAT", 1);
+ }
+}
+
+static int test_ctrl_pat_exit_handler()
+{
+ u64 guest_rip;
+ ulong reason;
+ u64 guest_pat;
+
+ guest_rip = vmcs_read(GUEST_RIP);
+ reason = vmcs_read(EXI_REASON) & 0xff;
+ switch (reason) {
+ case VMX_VMCALL:
+ guest_pat = vmcs_read(GUEST_PAT);
+ if (!(ctrl_exit_rev.clr & EXI_SAVE_PAT)) {
+ printf("\tEXI_SAVE_PAT is not supported\n");
+ vmcs_write(GUEST_PAT, 0x6);
+ } else {
+ if (guest_pat == 0x6)
+ report("Exit save PAT", 1);
+ else
+ report("Exit save PAT", 0);
+ }
+ if (!(ctrl_exit_rev.clr & EXI_LOAD_PAT))
+ printf("\tEXI_LOAD_PAT is not supported\n");
+ else {
+ if (rdmsr(MSR_IA32_CR_PAT) == ia32_pat)
+ report("Exit load PAT", 1);
+ else
+ report("Exit load PAT", 0);
+ }
+ vmcs_write(GUEST_PAT, ia32_pat);
+ vmcs_write(GUEST_RIP, guest_rip + 3);
+ return VMX_TEST_RESUME;
+ default:
+ printf("ERROR : Undefined exit reason, reason = %d.\n", reason);
+ break;
+ }
+ return VMX_TEST_VMEXIT;
+}
+
+static void test_ctrl_efer_init()
+{
+ u64 ctrl_ent;
+ u64 ctrl_exi;
+
+ msr_bmp_init();
+ ctrl_ent = vmcs_read(ENT_CONTROLS) | ENT_LOAD_EFER;
+ ctrl_exi = vmcs_read(EXI_CONTROLS) | EXI_SAVE_EFER | EXI_LOAD_EFER;
+ vmcs_write(ENT_CONTROLS, ctrl_ent & ctrl_enter_rev.clr);
+ vmcs_write(EXI_CONTROLS, ctrl_exi & ctrl_exit_rev.clr);
+ ia32_efer = rdmsr(MSR_EFER);
+ vmcs_write(GUEST_EFER, ia32_efer ^ EFER_NX);
+ vmcs_write(HOST_EFER, ia32_efer ^ EFER_NX);
+}
+
+static void test_ctrl_efer_main()
+{
+ u64 guest_ia32_efer;
+
+ guest_ia32_efer = rdmsr(MSR_EFER);
+ if (!(ctrl_enter_rev.clr & ENT_LOAD_EFER))
+ printf("\tENT_LOAD_EFER is not supported.\n");
+ else {
+ if (guest_ia32_efer != (ia32_efer ^ EFER_NX)) {
+ report("Entry load EFER", 0);
+ return;
+ }
+ }
+ wrmsr(MSR_EFER, ia32_efer);
+ vmcall();
+ guest_ia32_efer = rdmsr(MSR_EFER);
+ if (ctrl_enter_rev.clr & ENT_LOAD_EFER) {
+ if (guest_ia32_efer != ia32_efer) {
+ report("Entry load EFER", 0);
+ return;
+ }
+ report("Entry load EFER", 1);
+ }
+}
+
+static int test_ctrl_efer_exit_handler()
+{
+ u64 guest_rip;
+ ulong reason;
+ u64 guest_efer;
+
+ guest_rip = vmcs_read(GUEST_RIP);
+ reason = vmcs_read(EXI_REASON) & 0xff;
+ switch (reason) {
+ case VMX_VMCALL:
+ guest_efer = vmcs_read(GUEST_EFER);
+ if (!(ctrl_exit_rev.clr & EXI_SAVE_EFER)) {
+ printf("\tEXI_SAVE_EFER is not supported\n");
+ vmcs_write(GUEST_EFER, ia32_efer);
+ } else {
+ if (guest_efer == ia32_efer)
+ report("Exit save EFER", 1);
+ else
+ report("Exit save EFER", 0);
+ }
+ if (!(ctrl_exit_rev.clr & EXI_LOAD_EFER)) {
+ printf("\tEXI_LOAD_EFER is not supported\n");
+ wrmsr(MSR_EFER, ia32_efer ^ EFER_NX);
+ } else {
+ if (rdmsr(MSR_EFER) == (ia32_efer ^ EFER_NX))
+ report("Exit load EFER", 1);
+ else
+ report("Exit load EFER", 0);
+ }
+ vmcs_write(GUEST_PAT, ia32_efer);
+ vmcs_write(GUEST_RIP, guest_rip + 3);
+ return VMX_TEST_RESUME;
+ default:
+ printf("ERROR : Undefined exit reason, reason = %d.\n", reason);
+ break;
+ }
+ return VMX_TEST_VMEXIT;
+}
+
/* name/init/guest_main/exit_handler/syscall_handler/guest_regs
basic_* just implement some basic functions */
struct vmx_test vmx_tests[] = {
@@ -83,5 +264,9 @@ struct vmx_test vmx_tests[] = {
basic_syscall_handler, {0} },
{ "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
basic_syscall_handler, {0} },
+ { "control field PAT", test_ctrl_pat_init, test_ctrl_pat_main,
+ test_ctrl_pat_exit_handler, basic_syscall_handler, {0} },
+ { "control field EFER", test_ctrl_efer_init, test_ctrl_efer_main,
+ test_ctrl_efer_exit_handler, basic_syscall_handler, {0} },
{ NULL, NULL, NULL, NULL, NULL, {0} },
};
Add test cases for ENT_LOAD_PAT, ENT_LOAD_EFER, EXI_LOAD_PAT, EXI_SAVE_PAT, EXI_LOAD_EFER, EXI_SAVE_PAT flags in enter/exit control fields. Signed-off-by: Arthur Chunqi Li <yzt356@gmail.com> --- x86/vmx.h | 7 +++ x86/vmx_tests.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+)