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 uint64_t read_dev_msr(int msr_fd, uint32_t msr) 39 { 40 uint64_t 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 #define L2_GUEST_STACK_SIZE 64 58 59 static void l2_guest_code(void) 60 { 61 guest_read_aperf_mperf(); 62 GUEST_DONE(); 63 } 64 65 static void l1_svm_code(struct svm_test_data *svm) 66 { 67 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; 68 struct vmcb *vmcb = svm->vmcb; 69 70 generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); 71 run_guest(vmcb, svm->vmcb_gpa); 72 } 73 74 static void l1_vmx_code(struct vmx_pages *vmx) 75 { 76 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; 77 78 GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); 79 GUEST_ASSERT_EQ(load_vmcs(vmx), true); 80 81 prepare_vmcs(vmx, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]); 82 83 /* 84 * Enable MSR bitmaps (the bitmap itself is allocated, zeroed, and set 85 * in the VMCS by prepare_vmcs()), as MSR exiting mandatory on Intel. 86 */ 87 vmwrite(CPU_BASED_VM_EXEC_CONTROL, 88 vmreadz(CPU_BASED_VM_EXEC_CONTROL) | CPU_BASED_USE_MSR_BITMAPS); 89 90 GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code)); 91 GUEST_ASSERT(!vmlaunch()); 92 } 93 94 static void guest_code(void *nested_test_data) 95 { 96 guest_read_aperf_mperf(); 97 98 if (this_cpu_has(X86_FEATURE_SVM)) 99 l1_svm_code(nested_test_data); 100 else if (this_cpu_has(X86_FEATURE_VMX)) 101 l1_vmx_code(nested_test_data); 102 else 103 GUEST_DONE(); 104 105 TEST_FAIL("L2 should have signaled 'done'"); 106 } 107 108 static void guest_no_aperfmperf(void) 109 { 110 uint64_t msr_val; 111 uint8_t vector; 112 113 vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); 114 GUEST_ASSERT(vector == GP_VECTOR); 115 116 vector = rdmsr_safe(MSR_IA32_APERF, &msr_val); 117 GUEST_ASSERT(vector == GP_VECTOR); 118 119 GUEST_DONE(); 120 } 121 122 int main(int argc, char *argv[]) 123 { 124 const bool has_nested = kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX); 125 uint64_t host_aperf_before, host_mperf_before; 126 vm_vaddr_t nested_test_data_gva; 127 struct kvm_vcpu *vcpu; 128 struct kvm_vm *vm; 129 int msr_fd, cpu, i; 130 131 /* Sanity check that APERF/MPERF are unsupported by default. */ 132 vm = vm_create_with_one_vcpu(&vcpu, guest_no_aperfmperf); 133 vcpu_run(vcpu); 134 TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); 135 kvm_vm_free(vm); 136 137 cpu = pin_self_to_any_cpu(); 138 139 msr_fd = open_dev_msr(cpu); 140 141 /* 142 * This test requires a non-standard VM initialization, because 143 * KVM_ENABLE_CAP cannot be used on a VM file descriptor after 144 * a VCPU has been created. 145 */ 146 vm = vm_create(1); 147 148 TEST_REQUIRE(vm_check_cap(vm, KVM_CAP_X86_DISABLE_EXITS) & 149 KVM_X86_DISABLE_EXITS_APERFMPERF); 150 151 vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, 152 KVM_X86_DISABLE_EXITS_APERFMPERF); 153 154 vcpu = vm_vcpu_add(vm, 0, guest_code); 155 156 if (!has_nested) 157 nested_test_data_gva = NONCANONICAL; 158 else if (kvm_cpu_has(X86_FEATURE_SVM)) 159 vcpu_alloc_svm(vm, &nested_test_data_gva); 160 else 161 vcpu_alloc_vmx(vm, &nested_test_data_gva); 162 163 vcpu_args_set(vcpu, 1, nested_test_data_gva); 164 165 host_aperf_before = read_dev_msr(msr_fd, MSR_IA32_APERF); 166 host_mperf_before = read_dev_msr(msr_fd, MSR_IA32_MPERF); 167 168 for (i = 0; i <= NUM_ITERATIONS * (1 + has_nested); i++) { 169 uint64_t host_aperf_after, host_mperf_after; 170 uint64_t guest_aperf, guest_mperf; 171 struct ucall uc; 172 173 vcpu_run(vcpu); 174 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 175 176 switch (get_ucall(vcpu, &uc)) { 177 case UCALL_DONE: 178 goto done; 179 case UCALL_ABORT: 180 REPORT_GUEST_ASSERT(uc); 181 case UCALL_SYNC: 182 guest_aperf = uc.args[0]; 183 guest_mperf = uc.args[1]; 184 185 host_aperf_after = read_dev_msr(msr_fd, MSR_IA32_APERF); 186 host_mperf_after = read_dev_msr(msr_fd, MSR_IA32_MPERF); 187 188 TEST_ASSERT(host_aperf_before < guest_aperf, 189 "APERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", 190 host_aperf_before, guest_aperf); 191 TEST_ASSERT(guest_aperf < host_aperf_after, 192 "APERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", 193 guest_aperf, host_aperf_after); 194 TEST_ASSERT(host_mperf_before < guest_mperf, 195 "MPERF: host_before (0x%" PRIx64 ") >= guest (0x%" PRIx64 ")", 196 host_mperf_before, guest_mperf); 197 TEST_ASSERT(guest_mperf < host_mperf_after, 198 "MPERF: guest (0x%" PRIx64 ") >= host_after (0x%" PRIx64 ")", 199 guest_mperf, host_mperf_after); 200 201 host_aperf_before = host_aperf_after; 202 host_mperf_before = host_mperf_after; 203 204 break; 205 } 206 } 207 TEST_FAIL("Didn't receive UCALL_DONE\n"); 208 done: 209 kvm_vm_free(vm); 210 close(msr_fd); 211 212 return 0; 213 } 214