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 #include "apic.h" 18 19 static void guest_bsp_vcpu(void *arg) 20 { 21 GUEST_SYNC(1); 22 23 GUEST_ASSERT(get_bsp_flag() != 0); 24 25 GUEST_DONE(); 26 } 27 28 static void guest_not_bsp_vcpu(void *arg) 29 { 30 GUEST_SYNC(1); 31 32 GUEST_ASSERT(get_bsp_flag() == 0); 33 34 GUEST_DONE(); 35 } 36 37 static void test_set_bsp_busy(struct kvm_vcpu *vcpu, const char *msg) 38 { 39 int r = __vm_ioctl(vcpu->vm, KVM_SET_BOOT_CPU_ID, 40 (void *)(unsigned long)vcpu->id); 41 42 TEST_ASSERT(r == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set %s", msg); 43 } 44 45 static void run_vcpu(struct kvm_vcpu *vcpu) 46 { 47 struct ucall uc; 48 int stage; 49 50 for (stage = 0; stage < 2; stage++) { 51 52 vcpu_run(vcpu); 53 54 switch (get_ucall(vcpu, &uc)) { 55 case UCALL_SYNC: 56 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && 57 uc.args[1] == stage + 1, 58 "Stage %d: Unexpected register values vmexit, got %lx", 59 stage + 1, (ulong)uc.args[1]); 60 test_set_bsp_busy(vcpu, "while running vm"); 61 break; 62 case UCALL_DONE: 63 TEST_ASSERT(stage == 1, 64 "Expected GUEST_DONE in stage 2, got stage %d", 65 stage); 66 break; 67 case UCALL_ABORT: 68 REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); 69 default: 70 TEST_ASSERT(false, "Unexpected exit: %s", 71 exit_reason_str(vcpu->run->exit_reason)); 72 } 73 } 74 } 75 76 static struct kvm_vm *create_vm(uint32_t nr_vcpus, uint32_t bsp_vcpu_id, 77 struct kvm_vcpu *vcpus[]) 78 { 79 struct kvm_vm *vm; 80 uint32_t i; 81 82 vm = vm_create(nr_vcpus); 83 84 vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *)(unsigned long)bsp_vcpu_id); 85 86 for (i = 0; i < nr_vcpus; i++) 87 vcpus[i] = vm_vcpu_add(vm, i, i == bsp_vcpu_id ? guest_bsp_vcpu : 88 guest_not_bsp_vcpu); 89 return vm; 90 } 91 92 static void run_vm_bsp(uint32_t bsp_vcpu_id) 93 { 94 struct kvm_vcpu *vcpus[2]; 95 struct kvm_vm *vm; 96 97 vm = create_vm(ARRAY_SIZE(vcpus), bsp_vcpu_id, vcpus); 98 99 run_vcpu(vcpus[0]); 100 run_vcpu(vcpus[1]); 101 102 kvm_vm_free(vm); 103 } 104 105 static void check_set_bsp_busy(void) 106 { 107 struct kvm_vcpu *vcpus[2]; 108 struct kvm_vm *vm; 109 110 vm = create_vm(ARRAY_SIZE(vcpus), 0, vcpus); 111 112 test_set_bsp_busy(vcpus[1], "after adding vcpu"); 113 114 run_vcpu(vcpus[0]); 115 run_vcpu(vcpus[1]); 116 117 test_set_bsp_busy(vcpus[1], "to a terminated vcpu"); 118 119 kvm_vm_free(vm); 120 } 121 122 int main(int argc, char *argv[]) 123 { 124 TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_BOOT_CPU_ID)); 125 126 run_vm_bsp(0); 127 run_vm_bsp(1); 128 run_vm_bsp(0); 129 130 check_set_bsp_busy(); 131 } 132