Files
linux/tools/testing/selftests/kvm/x86/vmx_apic_access_test.c
Sean Christopherson 67730e6c53 KVM: selftests: Use canonical $(ARCH) paths for KVM selftests directories
Use the kernel's canonical $(ARCH) paths instead of the raw target triple
for KVM selftests directories.  KVM selftests are quite nearly the only
place in the entire kernel that using the target triple for directories,
tools/testing/selftests/drivers/s390x being the lone holdout.

Using the kernel's preferred nomenclature eliminates the minor, but
annoying, friction of having to translate to KVM's selftests directories,
e.g. for pattern matching, opening files, running selftests, etc.

Opportunsitically delete file comments that reference the full path of the
file, as they are obviously prone to becoming stale, and serve no known
purpose.

Reviewed-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Acked-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Acked-by: Andrew Jones <ajones@ventanamicro.com>
Link: https://lore.kernel.org/r/20241128005547.4077116-16-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
2024-12-18 14:15:04 -08:00

125 lines
3.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* vmx_apic_access_test
*
* Copyright (C) 2020, Google LLC.
*
* This work is licensed under the terms of the GNU GPL, version 2.
*
* The first subtest simply checks to see that an L2 guest can be
* launched with a valid APIC-access address that is backed by a
* page of L1 physical memory.
*
* The second subtest sets the APIC-access address to a (valid) L1
* physical address that is not backed by memory. KVM can't handle
* this situation, so resuming L2 should result in a KVM exit for
* internal error (emulation). This is not an architectural
* requirement. It is just a shortcoming of KVM. The internal error
* is unfortunate, but it's better than what used to happen!
*/
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
#include "vmx.h"
#include <string.h>
#include <sys/ioctl.h>
#include "kselftest.h"
static void l2_guest_code(void)
{
/* Exit to L1 */
__asm__ __volatile__("vmcall");
}
static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa)
{
#define L2_GUEST_STACK_SIZE 64
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
uint32_t control;
GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
GUEST_ASSERT(load_vmcs(vmx_pages));
/* Prepare the VMCS for L2 execution. */
prepare_vmcs(vmx_pages, l2_guest_code,
&l2_guest_stack[L2_GUEST_STACK_SIZE]);
control = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
control |= CPU_BASED_ACTIVATE_SECONDARY_CONTROLS;
vmwrite(CPU_BASED_VM_EXEC_CONTROL, control);
control = vmreadz(SECONDARY_VM_EXEC_CONTROL);
control |= SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
vmwrite(SECONDARY_VM_EXEC_CONTROL, control);
vmwrite(APIC_ACCESS_ADDR, vmx_pages->apic_access_gpa);
/* Try to launch L2 with the memory-backed APIC-access address. */
GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR));
GUEST_ASSERT(!vmlaunch());
GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
vmwrite(APIC_ACCESS_ADDR, high_gpa);
/* Try to resume L2 with the unbacked APIC-access address. */
GUEST_SYNC(vmreadz(APIC_ACCESS_ADDR));
GUEST_ASSERT(!vmresume());
GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
GUEST_DONE();
}
int main(int argc, char *argv[])
{
unsigned long apic_access_addr = ~0ul;
vm_vaddr_t vmx_pages_gva;
unsigned long high_gpa;
struct vmx_pages *vmx;
bool done = false;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
high_gpa = (vm->max_gfn - 1) << vm->page_shift;
vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva);
prepare_virtualize_apic_accesses(vmx, vm);
vcpu_args_set(vcpu, 2, vmx_pages_gva, high_gpa);
while (!done) {
volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
if (apic_access_addr == high_gpa) {
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR);
TEST_ASSERT(run->internal.suberror ==
KVM_INTERNAL_ERROR_EMULATION,
"Got internal suberror other than KVM_INTERNAL_ERROR_EMULATION: %u",
run->internal.suberror);
break;
}
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);
/* NOT REACHED */
case UCALL_SYNC:
apic_access_addr = uc.args[1];
break;
case UCALL_DONE:
done = true;
break;
default:
TEST_ASSERT(false, "Unknown ucall %lu", uc.cmd);
}
}
kvm_vm_free(vm);
return 0;
}