1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * CR4 and CPUID sync test 4 * 5 * Copyright 2018, Red Hat, Inc. and/or its affiliates. 6 * 7 * Author: 8 * Wei Huang <wei@redhat.com> 9 */ 10 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 17 #include "test_util.h" 18 19 #include "kvm_util.h" 20 #include "processor.h" 21 22 #define MAGIC_HYPERCALL_PORT 0x80 23 24 static void guest_code(void) 25 { 26 u32 regs[4] = { 27 [KVM_CPUID_EAX] = X86_FEATURE_OSXSAVE.function, 28 [KVM_CPUID_ECX] = X86_FEATURE_OSXSAVE.index, 29 }; 30 31 /* CR4.OSXSAVE should be enabled by default (for selftests vCPUs). */ 32 GUEST_ASSERT(get_cr4() & X86_CR4_OSXSAVE); 33 34 /* verify CR4.OSXSAVE == CPUID.OSXSAVE */ 35 GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE)); 36 37 /* 38 * Notify hypervisor to clear CR4.0SXSAVE, do CPUID and save output, 39 * and then restore CR4. Do this all in assembly to ensure no AVX 40 * instructions are executed while OSXSAVE=0. 41 */ 42 asm volatile ( 43 "out %%al, $" __stringify(MAGIC_HYPERCALL_PORT) "\n\t" 44 "cpuid\n\t" 45 "mov %%rdi, %%cr4\n\t" 46 : "+a" (regs[KVM_CPUID_EAX]), 47 "=b" (regs[KVM_CPUID_EBX]), 48 "+c" (regs[KVM_CPUID_ECX]), 49 "=d" (regs[KVM_CPUID_EDX]) 50 : "D" (get_cr4()) 51 ); 52 53 /* Verify KVM cleared OSXSAVE in CPUID when it was cleared in CR4. */ 54 GUEST_ASSERT(!(regs[X86_FEATURE_OSXSAVE.reg] & BIT(X86_FEATURE_OSXSAVE.bit))); 55 56 /* Verify restoring CR4 also restored OSXSAVE in CPUID. */ 57 GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE)); 58 59 GUEST_DONE(); 60 } 61 62 int main(int argc, char *argv[]) 63 { 64 struct kvm_vcpu *vcpu; 65 struct kvm_vm *vm; 66 struct kvm_sregs sregs; 67 struct ucall uc; 68 69 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE)); 70 71 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 72 73 while (1) { 74 vcpu_run(vcpu); 75 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 76 77 if (vcpu->run->io.port == MAGIC_HYPERCALL_PORT && 78 vcpu->run->io.direction == KVM_EXIT_IO_OUT) { 79 /* emulate hypervisor clearing CR4.OSXSAVE */ 80 vcpu_sregs_get(vcpu, &sregs); 81 sregs.cr4 &= ~X86_CR4_OSXSAVE; 82 vcpu_sregs_set(vcpu, &sregs); 83 continue; 84 } 85 86 switch (get_ucall(vcpu, &uc)) { 87 case UCALL_ABORT: 88 REPORT_GUEST_ASSERT(uc); 89 break; 90 case UCALL_DONE: 91 goto done; 92 default: 93 TEST_FAIL("Unknown ucall %lu", uc.cmd); 94 } 95 } 96 97 done: 98 kvm_vm_free(vm); 99 return 0; 100 } 101