afc063178c6af181af37e3370fbf9eee7b1b6e1b
[linux-2.6-microblaze.git] / tools / testing / selftests / kvm / x86_64 / set_boot_cpu_id.c
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                         TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx",
69                                                 (const char *)uc.args[0], __FILE__,
70                                                 uc.args[1], uc.args[2], uc.args[3]);
71                 default:
72                         TEST_ASSERT(false, "Unexpected exit: %s",
73                                     exit_reason_str(vcpu->run->exit_reason));
74                 }
75         }
76 }
77
78 static struct kvm_vm *create_vm(uint32_t nr_vcpus, uint32_t bsp_vcpu_id,
79                                 struct kvm_vcpu *vcpus[])
80 {
81         uint64_t vcpu_pages = (DEFAULT_STACK_PGS) * nr_vcpus;
82         uint64_t extra_pg_pages = vcpu_pages / PTES_PER_MIN_PAGE * nr_vcpus;
83         uint64_t pages = DEFAULT_GUEST_PHY_PAGES + vcpu_pages + extra_pg_pages;
84         struct kvm_vm *vm;
85         uint32_t i;
86
87         vm = vm_create(pages);
88
89         vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *)(unsigned long)bsp_vcpu_id);
90
91         for (i = 0; i < nr_vcpus; i++)
92                 vcpus[i] = vm_vcpu_add(vm, i, i == bsp_vcpu_id ? guest_bsp_vcpu :
93                                                                  guest_not_bsp_vcpu);
94         return vm;
95 }
96
97 static void run_vm_bsp(uint32_t bsp_vcpu_id)
98 {
99         struct kvm_vcpu *vcpus[2];
100         struct kvm_vm *vm;
101
102         vm = create_vm(ARRAY_SIZE(vcpus), bsp_vcpu_id, vcpus);
103
104         run_vcpu(vcpus[0]);
105         run_vcpu(vcpus[1]);
106
107         kvm_vm_free(vm);
108 }
109
110 static void check_set_bsp_busy(void)
111 {
112         struct kvm_vcpu *vcpus[2];
113         struct kvm_vm *vm;
114
115         vm = create_vm(ARRAY_SIZE(vcpus), 0, vcpus);
116
117         test_set_bsp_busy(vcpus[1], "after adding vcpu");
118
119         run_vcpu(vcpus[0]);
120         run_vcpu(vcpus[1]);
121
122         test_set_bsp_busy(vcpus[1], "to a terminated vcpu");
123
124         kvm_vm_free(vm);
125 }
126
127 int main(int argc, char *argv[])
128 {
129         if (!kvm_check_cap(KVM_CAP_SET_BOOT_CPU_ID)) {
130                 print_skip("set_boot_cpu_id not available");
131                 return 0;
132         }
133
134         run_vm_bsp(0);
135         run_vm_bsp(1);
136         run_vm_bsp(0);
137
138         check_set_bsp_busy();
139 }