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