1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Test for KVM_X86_DISABLE_EXITS_APERFMPERF 4 * 5 * Copyright (C) 2025, Google LLC. 6 * 7 * Test the ability to disable VM-exits for rdmsr of IA32_APERF and 8 * IA32_MPERF. When these VM-exits are disabled, reads of these MSRs 9 * return the host's values. 10 * 11 * Note: Requires read access to /dev/cpu/<lpu>/msr to read host MSRs. 12 */ 13 14 #include <fcntl.h> 15 #include <limits.h> 16 #include <stdbool.h> 17 #include <stdio.h> 18 #include <stdint.h> 19 #include <unistd.h> 20 #include <asm/msr-index.h> 21 22 #include "kvm_util.h" 23 #include "processor.h" 24 #include "svm_util.h" 25 #include "test_util.h" 26 #include "vmx.h" 27 28 #define NUM_ITERATIONS 10000 29 30 static int open_dev_msr(int cpu) 31 { 32 char path[PATH_MAX]; 33 34 snprintf(path, sizeof(path), "/dev/cpu/%d/msr", cpu); 35 return open_path_or_exit(path, O_RDONLY); 36 } 37 38 static u64 read_dev_msr(int msr_fd, u32 msr) 39 { 40 u64 data; 41 ssize_t rc; 42 43 rc = pread(msr_fd, &data, sizeof(data), msr); 44 TEST_ASSERT(rc == sizeof(data), "Read of MSR 0x%x failed", msr); 45 46 return data; 47 } 48 49 static void guest_read_aperf_mperf(void) 50 { 51 int i; 52 53 for (i = 0; i < NUM_ITERATIONS; i++) 54 GUEST_SYNC2(rdmsr(MSR_IA32_APERF), rdmsr(MSR_IA32_MPERF)); 55 } 56 57 static void l2_guest_code(void) 58 { 59 guest_read_aperf_mperf(); 60 GUEST_DONE(); 61 } 62 63 static void l1_svm_code(struct svm_test_data *svm) 64 { 65 struct vmcb *vmcb = svm->vmcb; 66 67 generic_svm_setup(svm, l2_guest_code); 68 run_guest(vmcb, svm->vmcb_gpa); 69 } 70 71 static void l1_vmx_code(struct vmx_pages *vmx) 72 { 73 GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); 74 GUEST_ASSERT_EQ(load_vmcs(vmx), true); 75 76 prepare_vmcs(vmx, NULL); 77 78 /* 79 * Enable MSR bitmaps (the bitmap itself is allocated, zeroed, and set 80 * in the VMCS by prepare_vmcs()), as MSR exiting mandatory on Intel. 81 */ 82 vmwrite(CPU_BASED_VM_EXEC_CONTROL, 83 vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); 84 85 GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); 86 GUEST_ASSERT(!vmlaunch()); 87 } 88 89 static void guest_code(void *nested_test_data) 90 { 91 guest_read_aperf_mperf(); 92 93 if (this_cpu_has(X86_FEATURE_SVM)) 94 l1_svm_code(nested_test_data); 95 else if (this_cpu_has(X86_FEATURE_VMX)) 96 l1_vmx_code(nested_test_data); 97 else 98 GUEST_DONE(); 99 100 TEST_FAIL("L2 should have signaled 'done'"); 101 } 102 103 static void guest_no_aperfmperf(void) 104 { 105 u64 msr_val; 106 u8 vector; 107 108 vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); 109 GUEST_ASSERT(vector == GP_VECTOR); 110 111 vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); 112 GUEST_ASSERT(vector == GP_VECTOR); 113 114 GUEST_DONE(); 115 } 116 117 int main(int argc, char *argv[]) 118 { 119 const bool has_nested = kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX); 120 u64 host_aperf_before, host_mperf_before; 121 gva_t nested_test_data_gva; 122 struct kvm_vcpu *vcpu; 123 struct kvm_vm *vm; 124 int msr_fd, cpu, i; 125 126 /* Sanity check that APERF/MPERF are unsupported by default. */ 127 vm = vm_create_with_one_vcpu(&vcpu, guest_no_aperfmperf); 128 vcpu_run(vcpu); 129 TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); 130 kvm_vm_free(vm); 131 132 cpu = pin_self_to_any_cpu(); 133 134 msr_fd = open_dev_msr(cpu); 135 136 /* 137 * This test requires a non-standard VM initialization, because 138 * KVM_ENABLE_CAP cannot be used on a VM file descriptor after 139 * a VCPU has been created. 140 */ 141 vm = vm_create(1); 142 143 TEST_REQUIRE(vm_check_cap(vm, KVM_CAP_X86_DISABLE_EXITS) & 144 KVM_X86_DISABLE_EXITS_APERFMPERF); 145 146 vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, 147 KVM_X86_DISABLE_EXITS_APERFMPERF); 148 149 vcpu = vm_vcpu_add(vm, 0, guest_code); 150 151 if (!has_nested) 152 nested_test_data_gva = NONCANONICAL; 153 else if (kvm_cpu_has(X86_FEATURE_SVM)) 154 vcpu_alloc_svm(vm, &nested_test_data_gva); 155 else 156 vcpu_alloc_vmx(vm, &nested_test_data_gva); 157 158 vcpu_args_set(vcpu, 1, nested_test_data_gva); 159 160 host_aperf_before = read_dev_msr(msr_fd, MSR_IA32_APERF); 161 host_mperf_before = read_dev_msr(msr_fd, MSR_IA32_MPERF); 162 163 for (i = 0; i <= NUM_ITERATIONS * (1 + has_nested); i++) { 164 u64 host_aperf_after, host_mperf_after; 165 u64 guest_aperf, guest_mperf; 166 struct ucall uc; 167 168 vcpu_run(vcpu); 169 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 170 171 switch (get_ucall(vcpu, &uc)) { 172 case UCALL_DONE: 173 goto done; 174 case UCALL_ABORT: 175 REPORT_GUEST_ASSERT(uc); 176 case UCALL_SYNC: 177 guest_aperf = uc.args[0]; 178 guest_mperf = uc.args[1]; 179 180 host_aperf_after = read_dev_msr(msr_fd, MSR_IA32_APERF); 181 host_mperf_after = read_dev_msr(msr_fd, MSR_IA32_MPERF); 182 183 TEST_ASSERT(host_aperf_before < guest_aperf, 184 "APERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", 185 host_aperf_before, guest_aperf); 186 TEST_ASSERT(guest_aperf < host_aperf_after, 187 "APERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", 188 guest_aperf, host_aperf_after); 189 TEST_ASSERT(host_mperf_before < guest_mperf, 190 "MPERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", 191 host_mperf_before, guest_mperf); 192 TEST_ASSERT(guest_mperf < host_mperf_after, 193 "MPERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", 194 guest_mperf, host_mperf_after); 195 196 host_aperf_before = host_aperf_after; 197 host_mperf_before = host_mperf_after; 198 199 break; 200 } 201 } 202 TEST_FAIL("Didn't receive UCALL_DONE\n"); 203 done: 204 kvm_vm_free(vm); 205 close(msr_fd); 206 207 return 0; 208 } 209