Skip to content

Commit

Permalink
selftests: kvm: add set_boot_cpu_id test
Browse files Browse the repository at this point in the history
Test for the KVM_SET_BOOT_CPU_ID ioctl.
Check that it correctly allows to change the BSP vcpu.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Message-Id: <20210318151624.490861-2-eesposit@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Emanuele Giuseppe Esposito authored and Paolo Bonzini committed Mar 18, 2021
1 parent e2c1290 commit 3df2252
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
1 change: 1 addition & 0 deletions tools/testing/selftests/kvm/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/x86_64/hyperv_cpuid
/x86_64/mmio_warning_test
/x86_64/platform_info_test
/x86_64/set_boot_cpu_id
/x86_64/set_sregs_test
/x86_64/smm_test
/x86_64/state_test
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/kvm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test
TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
TEST_GEN_PROGS_x86_64 += x86_64/smm_test
TEST_GEN_PROGS_x86_64 += x86_64/state_test
Expand Down
166 changes: 166 additions & 0 deletions tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test that KVM_SET_BOOT_CPU_ID works as intended
*
* Copyright (C) 2020, Red Hat, Inc.
*/
#define _GNU_SOURCE /* for program_invocation_name */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>

#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"

#define N_VCPU 2
#define VCPU_ID0 0
#define VCPU_ID1 1

static uint32_t get_bsp_flag(void)
{
return rdmsr(MSR_IA32_APICBASE) & MSR_IA32_APICBASE_BSP;
}

static void guest_bsp_vcpu(void *arg)
{
GUEST_SYNC(1);

GUEST_ASSERT(get_bsp_flag() != 0);

GUEST_DONE();
}

static void guest_not_bsp_vcpu(void *arg)
{
GUEST_SYNC(1);

GUEST_ASSERT(get_bsp_flag() == 0);

GUEST_DONE();
}

static void test_set_boot_busy(struct kvm_vm *vm)
{
int res;

res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID0);
TEST_ASSERT(res == -1 && errno == EBUSY,
"KVM_SET_BOOT_CPU_ID set while running vm");
}

static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid)
{
struct ucall uc;
int stage;

for (stage = 0; stage < 2; stage++) {

vcpu_run(vm, vcpuid);

switch (get_ucall(vm, vcpuid, &uc)) {
case UCALL_SYNC:
TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
uc.args[1] == stage + 1,
"Stage %d: Unexpected register values vmexit, got %lx",
stage + 1, (ulong)uc.args[1]);
test_set_boot_busy(vm);
break;
case UCALL_DONE:
TEST_ASSERT(stage == 1,
"Expected GUEST_DONE in stage 2, got stage %d",
stage);
break;
case UCALL_ABORT:
TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx",
(const char *)uc.args[0], __FILE__,
uc.args[1], uc.args[2], uc.args[3]);
default:
TEST_ASSERT(false, "Unexpected exit: %s",
exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason));
}
}
}

static struct kvm_vm *create_vm(void)
{
struct kvm_vm *vm;
uint64_t vcpu_pages = (DEFAULT_STACK_PGS) * 2;
uint64_t extra_pg_pages = vcpu_pages / PTES_PER_MIN_PAGE * N_VCPU;
uint64_t pages = DEFAULT_GUEST_PHY_PAGES + vcpu_pages + extra_pg_pages;

pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, pages);
vm = vm_create(VM_MODE_DEFAULT, pages, O_RDWR);

kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
vm_create_irqchip(vm);

return vm;
}

static void add_x86_vcpu(struct kvm_vm *vm, uint32_t vcpuid, bool bsp_code)
{
if (bsp_code)
vm_vcpu_add_default(vm, vcpuid, guest_bsp_vcpu);
else
vm_vcpu_add_default(vm, vcpuid, guest_not_bsp_vcpu);

vcpu_set_cpuid(vm, vcpuid, kvm_get_supported_cpuid());
}

static void run_vm_bsp(uint32_t bsp_vcpu)
{
struct kvm_vm *vm;
bool is_bsp_vcpu1 = bsp_vcpu == VCPU_ID1;

vm = create_vm();

if (is_bsp_vcpu1)
vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1);

add_x86_vcpu(vm, VCPU_ID0, !is_bsp_vcpu1);
add_x86_vcpu(vm, VCPU_ID1, is_bsp_vcpu1);

run_vcpu(vm, VCPU_ID0);
run_vcpu(vm, VCPU_ID1);

kvm_vm_free(vm);
}

static void check_set_bsp_busy(void)
{
struct kvm_vm *vm;
int res;

vm = create_vm();

add_x86_vcpu(vm, VCPU_ID0, true);
add_x86_vcpu(vm, VCPU_ID1, false);

res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1);
TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set after adding vcpu");

run_vcpu(vm, VCPU_ID0);
run_vcpu(vm, VCPU_ID1);

res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1);
TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set to a terminated vcpu");

kvm_vm_free(vm);
}

int main(int argc, char *argv[])
{
if (!kvm_check_cap(KVM_CAP_SET_BOOT_CPU_ID)) {
print_skip("set_boot_cpu_id not available");
return 0;
}

run_vm_bsp(VCPU_ID0);
run_vm_bsp(VCPU_ID1);
run_vm_bsp(VCPU_ID0);

check_set_bsp_busy();
}

0 comments on commit 3df2252

Please sign in to comment.