1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test that KVM_SET_BOOT_CPU_ID works as intended 4 * 5 * Copyright (C) 2020, Red Hat, Inc. 6 */ 7 #define _GNU_SOURCE /* for program_invocation_name */ 8 #include <fcntl.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/ioctl.h> 13 14 #include "test_util.h" 15 #include "kvm_util.h" 16 #include "processor.h" 17 18 #define N_VCPU 2 19 #define VCPU_ID0 0 20 #define VCPU_ID1 1 21 22 static uint32_t get_bsp_flag(void) 23 { 24 return rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_BSP; 25 } 26 27 static void guest_bsp_vcpu(void *arg) 28 { 29 GUEST_SYNC(1); 30 31 GUEST_ASSERT(get_bsp_flag() != 0); 32 33 GUEST_DONE(); 34 } 35 36 static void guest_not_bsp_vcpu(void *arg) 37 { 38 GUEST_SYNC(1); 39 40 GUEST_ASSERT(get_bsp_flag() == 0); 41 42 GUEST_DONE(); 43 } 44 45 static void test_set_boot_busy(struct kvm_vm *vm) 46 { 47 int res; 48 49 res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID0); 50 TEST_ASSERT(res == -1 && errno == EBUSY, 51 "KVM_SET_BOOT_CPU_ID set while running vm"); 52 } 53 54 static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid) 55 { 56 struct ucall uc; 57 int stage; 58 59 for (stage = 0; stage < 2; stage++) { 60 61 vcpu_run(vm, vcpuid); 62 63 switch (get_ucall(vm, vcpuid, &uc)) { 64 case UCALL_SYNC: 65 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && 66 uc.args[1] == stage + 1, 67 "Stage %d: Unexpected register values vmexit, got %lx", 68 stage + 1, (ulong)uc.args[1]); 69 test_set_boot_busy(vm); 70 break; 71 case UCALL_DONE: 72 TEST_ASSERT(stage == 1, 73 "Expected GUEST_DONE in stage 2, got stage %d", 74 stage); 75 break; 76 case UCALL_ABORT: 77 TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx", 78 (const char *)uc.args[0], __FILE__, 79 uc.args[1], uc.args[2], uc.args[3]); 80 default: 81 TEST_ASSERT(false, "Unexpected exit: %s", 82 exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); 83 } 84 } 85 } 86 87 static struct kvm_vm *create_vm(void) 88 { 89 struct kvm_vm *vm; 90 uint64_t vcpu_pages = (DEFAULT_STACK_PGS) * 2; 91 uint64_t extra_pg_pages = vcpu_pages / PTES_PER_MIN_PAGE * N_VCPU; 92 uint64_t pages = DEFAULT_GUEST_PHY_PAGES + vcpu_pages + extra_pg_pages; 93 94 pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, pages); 95 vm = vm_create(VM_MODE_DEFAULT, pages, O_RDWR); 96 97 kvm_vm_elf_load(vm, program_invocation_name, 0, 0); 98 vm_create_irqchip(vm); 99 100 return vm; 101 } 102 103 static void add_x86_vcpu(struct kvm_vm *vm, uint32_t vcpuid, bool bsp_code) 104 { 105 if (bsp_code) 106 vm_vcpu_add_default(vm, vcpuid, guest_bsp_vcpu); 107 else 108 vm_vcpu_add_default(vm, vcpuid, guest_not_bsp_vcpu); 109 110 vcpu_set_cpuid(vm, vcpuid, kvm_get_supported_cpuid()); 111 } 112 113 static void run_vm_bsp(uint32_t bsp_vcpu) 114 { 115 struct kvm_vm *vm; 116 bool is_bsp_vcpu1 = bsp_vcpu == VCPU_ID1; 117 118 vm = create_vm(); 119 120 if (is_bsp_vcpu1) 121 vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); 122 123 add_x86_vcpu(vm, VCPU_ID0, !is_bsp_vcpu1); 124 add_x86_vcpu(vm, VCPU_ID1, is_bsp_vcpu1); 125 126 run_vcpu(vm, VCPU_ID0); 127 run_vcpu(vm, VCPU_ID1); 128 129 kvm_vm_free(vm); 130 } 131 132 static void check_set_bsp_busy(void) 133 { 134 struct kvm_vm *vm; 135 int res; 136 137 vm = create_vm(); 138 139 add_x86_vcpu(vm, VCPU_ID0, true); 140 add_x86_vcpu(vm, VCPU_ID1, false); 141 142 res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); 143 TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set after adding vcpu"); 144 145 run_vcpu(vm, VCPU_ID0); 146 run_vcpu(vm, VCPU_ID1); 147 148 res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); 149 TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set to a terminated vcpu"); 150 151 kvm_vm_free(vm); 152 } 153 154 int main(int argc, char *argv[]) 155 { 156 if (!kvm_check_cap(KVM_CAP_SET_BOOT_CPU_ID)) { 157 print_skip("set_boot_cpu_id not available"); 158 return 0; 159 } 160 161 run_vm_bsp(VCPU_ID0); 162 run_vm_bsp(VCPU_ID1); 163 run_vm_bsp(VCPU_ID0); 164 165 check_set_bsp_busy(); 166 } 167