1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * KVM_SET_SREGS tests 4 * 5 * Copyright (C) 2018, Google LLC. 6 * 7 * This is a regression test for the bug fixed by the following commit: 8 * d3802286fa0f ("kvm: x86: Disallow illegal IA32_APIC_BASE MSR values") 9 * 10 * That bug allowed a user-mode program that called the KVM_SET_SREGS 11 * ioctl to put a VCPU's local APIC into an invalid state. 12 */ 13 #define _GNU_SOURCE /* for program_invocation_short_name */ 14 #include <fcntl.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/ioctl.h> 19 20 #include "test_util.h" 21 22 #include "kvm_util.h" 23 #include "processor.h" 24 25 #define VCPU_ID 5 26 27 static void test_cr4_feature_bit(struct kvm_vm *vm, struct kvm_sregs *orig, 28 uint64_t feature_bit) 29 { 30 struct kvm_sregs sregs; 31 int rc; 32 33 /* Skip the sub-test, the feature is supported. */ 34 if (orig->cr4 & feature_bit) 35 return; 36 37 memcpy(&sregs, orig, sizeof(sregs)); 38 sregs.cr4 |= feature_bit; 39 40 rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); 41 TEST_ASSERT(rc, "KVM allowed unsupported CR4 bit (0x%lx)", feature_bit); 42 43 /* Sanity check that KVM didn't change anything. */ 44 vcpu_sregs_get(vm, VCPU_ID, &sregs); 45 TEST_ASSERT(!memcmp(&sregs, orig, sizeof(sregs)), "KVM modified sregs"); 46 } 47 48 static uint64_t calc_cr4_feature_bits(struct kvm_vm *vm) 49 { 50 struct kvm_cpuid_entry2 *cpuid_1, *cpuid_7; 51 uint64_t cr4; 52 53 cpuid_1 = kvm_get_supported_cpuid_entry(1); 54 cpuid_7 = kvm_get_supported_cpuid_entry(7); 55 56 cr4 = X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_DE | 57 X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | 58 X86_CR4_PCE | X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT; 59 if (cpuid_7->ecx & CPUID_UMIP) 60 cr4 |= X86_CR4_UMIP; 61 if (cpuid_7->ecx & CPUID_LA57) 62 cr4 |= X86_CR4_LA57; 63 if (cpuid_1->ecx & CPUID_VMX) 64 cr4 |= X86_CR4_VMXE; 65 if (cpuid_1->ecx & CPUID_SMX) 66 cr4 |= X86_CR4_SMXE; 67 if (cpuid_7->ebx & CPUID_FSGSBASE) 68 cr4 |= X86_CR4_FSGSBASE; 69 if (cpuid_1->ecx & CPUID_PCID) 70 cr4 |= X86_CR4_PCIDE; 71 if (cpuid_1->ecx & CPUID_XSAVE) 72 cr4 |= X86_CR4_OSXSAVE; 73 if (cpuid_7->ebx & CPUID_SMEP) 74 cr4 |= X86_CR4_SMEP; 75 if (cpuid_7->ebx & CPUID_SMAP) 76 cr4 |= X86_CR4_SMAP; 77 if (cpuid_7->ecx & CPUID_PKU) 78 cr4 |= X86_CR4_PKE; 79 80 return cr4; 81 } 82 83 int main(int argc, char *argv[]) 84 { 85 struct kvm_sregs sregs; 86 struct kvm_vm *vm; 87 uint64_t cr4; 88 int rc; 89 90 /* Tell stdout not to buffer its content */ 91 setbuf(stdout, NULL); 92 93 /* 94 * Create a dummy VM, specifically to avoid doing KVM_SET_CPUID2, and 95 * use it to verify all supported CR4 bits can be set prior to defining 96 * the vCPU model, i.e. without doing KVM_SET_CPUID2. 97 */ 98 vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); 99 vm_vcpu_add(vm, VCPU_ID); 100 101 vcpu_sregs_get(vm, VCPU_ID, &sregs); 102 103 sregs.cr4 |= calc_cr4_feature_bits(vm); 104 cr4 = sregs.cr4; 105 106 rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); 107 TEST_ASSERT(!rc, "Failed to set supported CR4 bits (0x%lx)", cr4); 108 109 vcpu_sregs_get(vm, VCPU_ID, &sregs); 110 TEST_ASSERT(sregs.cr4 == cr4, "sregs.CR4 (0x%llx) != CR4 (0x%lx)", 111 sregs.cr4, cr4); 112 113 /* Verify all unsupported features are rejected by KVM. */ 114 test_cr4_feature_bit(vm, &sregs, X86_CR4_UMIP); 115 test_cr4_feature_bit(vm, &sregs, X86_CR4_LA57); 116 test_cr4_feature_bit(vm, &sregs, X86_CR4_VMXE); 117 test_cr4_feature_bit(vm, &sregs, X86_CR4_SMXE); 118 test_cr4_feature_bit(vm, &sregs, X86_CR4_FSGSBASE); 119 test_cr4_feature_bit(vm, &sregs, X86_CR4_PCIDE); 120 test_cr4_feature_bit(vm, &sregs, X86_CR4_OSXSAVE); 121 test_cr4_feature_bit(vm, &sregs, X86_CR4_SMEP); 122 test_cr4_feature_bit(vm, &sregs, X86_CR4_SMAP); 123 test_cr4_feature_bit(vm, &sregs, X86_CR4_PKE); 124 kvm_vm_free(vm); 125 126 /* Create a "real" VM and verify APIC_BASE can be set. */ 127 vm = vm_create_default(VCPU_ID, 0, NULL); 128 129 vcpu_sregs_get(vm, VCPU_ID, &sregs); 130 sregs.apic_base = 1 << 10; 131 rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); 132 TEST_ASSERT(rc, "Set IA32_APIC_BASE to %llx (invalid)", 133 sregs.apic_base); 134 sregs.apic_base = 1 << 11; 135 rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); 136 TEST_ASSERT(!rc, "Couldn't set IA32_APIC_BASE to %llx (valid)", 137 sregs.apic_base); 138 139 kvm_vm_free(vm); 140 141 return 0; 142 } 143