1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2026, Google LLC. 4 * 5 * Test that KVM correctly virtualizes the PAT MSR and VMCB g_pat field 6 * for nested SVM guests: 7 * 8 * o With nested NPT disabled: 9 * - L1 and L2 share the same PAT 10 * - The vmcb12.g_pat is ignored 11 * o With nested NPT enabled: 12 * - Invalid g_pat in vmcb12 should cause VMEXIT_INVALID 13 * - L2 should see vmcb12.g_pat via RDMSR, not L1's PAT 14 * - L2's writes to PAT should be saved to vmcb12 on exit 15 * - L1's PAT should be restored after #VMEXIT from L2 16 * - State save/restore should preserve both L1's and L2's PAT values 17 */ 18 #include <fcntl.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include "test_util.h" 24 #include "kvm_util.h" 25 #include "processor.h" 26 #include "svm_util.h" 27 28 #define L2_GUEST_STACK_SIZE 256 29 30 #define PAT_DEFAULT 0x0007040600070406ULL 31 #define L1_PAT_VALUE 0x0007040600070404ULL /* Change PA0 to WT */ 32 #define L2_VMCB12_PAT 0x0606060606060606ULL /* All WB */ 33 #define L2_PAT_MODIFIED 0x0606060606060604ULL /* Change PA0 to WT */ 34 #define INVALID_PAT_VALUE 0x0808080808080808ULL /* 8 is reserved */ 35 36 bool npt_enabled; 37 int nr_iterations; 38 39 static void l2_guest_code(void) 40 { 41 u64 expected_pat = npt_enabled ? L2_VMCB12_PAT : L1_PAT_VALUE; 42 int i; 43 44 for (i = 0; i < nr_iterations; i++) { 45 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), expected_pat); 46 GUEST_SYNC(1); 47 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), expected_pat); 48 49 wrmsr(MSR_IA32_CR_PAT, L2_PAT_MODIFIED); 50 expected_pat = L2_PAT_MODIFIED; 51 52 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); 53 GUEST_SYNC(2); 54 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); 55 56 vmmcall(); 57 } 58 } 59 60 static void l1_guest_code(struct svm_test_data *svm) 61 { 62 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; 63 struct vmcb *vmcb = svm->vmcb; 64 int i; 65 66 wrmsr(MSR_IA32_CR_PAT, L1_PAT_VALUE); 67 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L1_PAT_VALUE); 68 69 generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); 70 71 vmcb->save.g_pat = L2_VMCB12_PAT; 72 vmcb->control.intercept &= ~(1ULL << INTERCEPT_MSR_PROT); 73 74 for (i = 0; i < nr_iterations; i++) { 75 run_guest(vmcb, svm->vmcb_gpa); 76 77 GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL); 78 79 /* 80 * If NPT is enabled by L1, L2 has a unique PAT and L1's PAT is 81 * unchanged. Otherwise, PAT is shared between L1 and L2. 82 */ 83 if (npt_enabled) { 84 GUEST_ASSERT_EQ(vmcb->save.g_pat, L2_PAT_MODIFIED); 85 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L1_PAT_VALUE); 86 } else { 87 GUEST_ASSERT_EQ(rdmsr(MSR_IA32_CR_PAT), L2_PAT_MODIFIED); 88 } 89 vmcb->save.rip += 3; /* skip over VMMCALL */ 90 } 91 92 GUEST_DONE(); 93 } 94 95 static void l1_guest_code_invalid_gpat(struct svm_test_data *svm) 96 { 97 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; 98 struct vmcb *vmcb = svm->vmcb; 99 100 /* VMRUN should fail without running L2 */ 101 generic_svm_setup(svm, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]); 102 103 vmcb->save.g_pat = INVALID_PAT_VALUE; 104 run_guest(vmcb, svm->vmcb_gpa); 105 106 GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_ERR); 107 GUEST_DONE(); 108 } 109 110 static void run_test(void *guest_code, bool do_save_restore, int nr_iters) 111 { 112 struct kvm_x86_state *state; 113 struct kvm_vcpu *vcpu; 114 struct kvm_vm *vm; 115 struct ucall uc; 116 gva_t svm_gva; 117 118 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 119 vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, 120 KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); 121 122 if (npt_enabled) 123 vm_enable_npt(vm); 124 125 vcpu_alloc_svm(vm, &svm_gva); 126 127 if (npt_enabled) 128 tdp_identity_map_default_memslots(vm); 129 130 vcpu_args_set(vcpu, 1, svm_gva); 131 132 nr_iterations = nr_iters; 133 sync_global_to_guest(vm, npt_enabled); 134 sync_global_to_guest(vm, nr_iterations); 135 136 for (;;) { 137 vcpu_run(vcpu); 138 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 139 140 switch (get_ucall(vcpu, &uc)) { 141 case UCALL_ABORT: 142 REPORT_GUEST_ASSERT(uc); 143 /* NOT REACHED */ 144 case UCALL_SYNC: 145 if (do_save_restore) { 146 state = vcpu_save_state(vcpu); 147 kvm_vm_release(vm); 148 vcpu = vm_recreate_with_one_vcpu(vm); 149 vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, 150 KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); 151 vcpu_load_state(vcpu, state); 152 kvm_x86_state_cleanup(state); 153 } 154 break; 155 case UCALL_DONE: 156 kvm_vm_free(vm); 157 return; 158 default: 159 TEST_FAIL("Unknown ucall %lu", uc.cmd); 160 } 161 } 162 } 163 164 #define gpat_test(test_name, guest_code, npt_setting) \ 165 do { \ 166 npt_setting; \ 167 \ 168 if (npt_enabled && !kvm_cpu_has(X86_FEATURE_NPT)) { \ 169 pr_info("Skipping: " test_name " (no NPT support)\n"); \ 170 break; \ 171 } \ 172 \ 173 pr_info("Testing: " test_name "\n"); \ 174 run_test(guest_code, false, 1); \ 175 \ 176 if (guest_code == l1_guest_code) { \ 177 pr_info("Testing: " test_name " Save/Restore\n"); \ 178 run_test(guest_code, true, 1); \ 179 \ 180 pr_info("Testing: " test_name " Multiple VMRUNs\n"); \ 181 run_test(guest_code, false, 10); \ 182 } \ 183 } while (0) 184 185 int main(int argc, char *argv[]) 186 { 187 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); 188 TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); 189 TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & 190 KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT); 191 192 gpat_test("Invalid gPAT", l1_guest_code_invalid_gpat, npt_enabled = true); 193 gpat_test("Nested NPT enabled", l1_guest_code, npt_enabled = true); 194 gpat_test("Nested NPT disabled", l1_guest_code, npt_enabled = false); 195 return 0; 196 } 197