mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
Merge tag 'kvm-x86-selftests-6.12' of https://github.com/kvm-x86/linux into HEAD
KVM selftests changes for 6.12: - Fix a goof that caused some Hyper-V tests to be skipped when run on bare metal, i.e. NOT in a VM. - Add a regression test for KVM's handling of SHUTDOWN for an SEV-ES guest. - Explicitly include one-off assets in .gitignore. Past Sean was completely wrong about not being able to detect missing .gitignore entries. - Verify userspace single-stepping works when KVM happens to handle a VM-Exit in its fastpath. - Misc cleanups
This commit is contained in:
commit
c345344e83
3
tools/testing/selftests/kvm/.gitignore
vendored
3
tools/testing/selftests/kvm/.gitignore
vendored
|
|
@ -5,4 +5,7 @@
|
|||
!*.h
|
||||
!*.S
|
||||
!*.sh
|
||||
!.gitignore
|
||||
!config
|
||||
!settings
|
||||
!Makefile
|
||||
|
|
|
|||
|
|
@ -428,8 +428,6 @@ const char *vm_guest_mode_string(uint32_t i);
|
|||
void kvm_vm_free(struct kvm_vm *vmp);
|
||||
void kvm_vm_restart(struct kvm_vm *vmp);
|
||||
void kvm_vm_release(struct kvm_vm *vmp);
|
||||
int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, const vm_vaddr_t gva,
|
||||
size_t len);
|
||||
void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename);
|
||||
int kvm_memfd_alloc(size_t size, bool hugepages);
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,18 @@
|
|||
#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 14)
|
||||
|
||||
/* HYPERV_CPUID_NESTED_FEATURES.EAX */
|
||||
#define HV_X64_NESTED_DIRECT_FLUSH \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_NESTED_FEATURES, 0, EAX, 17)
|
||||
#define HV_X64_NESTED_GUEST_MAPPING_FLUSH \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_NESTED_FEATURES, 0, EAX, 18)
|
||||
#define HV_X64_NESTED_MSR_BITMAP \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_NESTED_FEATURES, 0, EAX, 19)
|
||||
|
||||
/* HYPERV_CPUID_NESTED_FEATURES.EBX */
|
||||
#define HV_X64_NESTED_EVMCS1_PERF_GLOBAL_CTRL \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_NESTED_FEATURES, 0, EBX, 0)
|
||||
|
||||
/* HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX */
|
||||
#define HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING \
|
||||
KVM_X86_CPU_FEATURE(HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES, 0, EAX, 1)
|
||||
|
|
@ -343,4 +355,10 @@ struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm,
|
|||
/* HV_X64_MSR_TSC_INVARIANT_CONTROL bits */
|
||||
#define HV_INVARIANT_TSC_EXPOSED BIT_ULL(0)
|
||||
|
||||
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void);
|
||||
const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu);
|
||||
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu);
|
||||
|
||||
bool kvm_hv_cpu_has(struct kvm_x86_cpu_feature feature);
|
||||
|
||||
#endif /* !SELFTEST_KVM_HYPERV_H */
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ extern bool host_cpu_is_intel;
|
|||
extern bool host_cpu_is_amd;
|
||||
extern uint64_t guest_tsc_khz;
|
||||
|
||||
#ifndef MAX_NR_CPUID_ENTRIES
|
||||
#define MAX_NR_CPUID_ENTRIES 100
|
||||
#endif
|
||||
|
||||
/* Forced emulation prefix, used to invoke the emulator unconditionally. */
|
||||
#define KVM_FEP "ud2; .byte 'k', 'v', 'm';"
|
||||
|
||||
|
|
@ -908,8 +912,6 @@ static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs)
|
|||
const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,
|
||||
uint32_t function, uint32_t index);
|
||||
const struct kvm_cpuid2 *kvm_get_supported_cpuid(void);
|
||||
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void);
|
||||
const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline uint32_t kvm_cpu_fms(void)
|
||||
{
|
||||
|
|
@ -1009,7 +1011,6 @@ static inline struct kvm_cpuid2 *allocate_kvm_cpuid2(int nr_entries)
|
|||
}
|
||||
|
||||
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid);
|
||||
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline struct kvm_cpuid_entry2 *__vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu,
|
||||
uint32_t function,
|
||||
|
|
|
|||
|
|
@ -712,16 +712,13 @@ void kvm_vm_release(struct kvm_vm *vmp)
|
|||
}
|
||||
|
||||
static void __vm_mem_region_delete(struct kvm_vm *vm,
|
||||
struct userspace_mem_region *region,
|
||||
bool unlink)
|
||||
struct userspace_mem_region *region)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlink) {
|
||||
rb_erase(®ion->gpa_node, &vm->regions.gpa_tree);
|
||||
rb_erase(®ion->hva_node, &vm->regions.hva_tree);
|
||||
hash_del(®ion->slot_node);
|
||||
}
|
||||
rb_erase(®ion->gpa_node, &vm->regions.gpa_tree);
|
||||
rb_erase(®ion->hva_node, &vm->regions.hva_tree);
|
||||
hash_del(®ion->slot_node);
|
||||
|
||||
region->region.memory_size = 0;
|
||||
vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION2, ®ion->region);
|
||||
|
|
@ -762,7 +759,7 @@ void kvm_vm_free(struct kvm_vm *vmp)
|
|||
|
||||
/* Free userspace_mem_regions. */
|
||||
hash_for_each_safe(vmp->regions.slot_hash, ctr, node, region, slot_node)
|
||||
__vm_mem_region_delete(vmp, region, false);
|
||||
__vm_mem_region_delete(vmp, region);
|
||||
|
||||
/* Free sparsebit arrays. */
|
||||
sparsebit_free(&vmp->vpages_valid);
|
||||
|
|
@ -794,76 +791,6 @@ int kvm_memfd_alloc(size_t size, bool hugepages)
|
|||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory Compare, host virtual to guest virtual
|
||||
*
|
||||
* Input Args:
|
||||
* hva - Starting host virtual address
|
||||
* vm - Virtual Machine
|
||||
* gva - Starting guest virtual address
|
||||
* len - number of bytes to compare
|
||||
*
|
||||
* Output Args: None
|
||||
*
|
||||
* Input/Output Args: None
|
||||
*
|
||||
* Return:
|
||||
* Returns 0 if the bytes starting at hva for a length of len
|
||||
* are equal the guest virtual bytes starting at gva. Returns
|
||||
* a value < 0, if bytes at hva are less than those at gva.
|
||||
* Otherwise a value > 0 is returned.
|
||||
*
|
||||
* Compares the bytes starting at the host virtual address hva, for
|
||||
* a length of len, to the guest bytes starting at the guest virtual
|
||||
* address given by gva.
|
||||
*/
|
||||
int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, vm_vaddr_t gva, size_t len)
|
||||
{
|
||||
size_t amt;
|
||||
|
||||
/*
|
||||
* Compare a batch of bytes until either a match is found
|
||||
* or all the bytes have been compared.
|
||||
*/
|
||||
for (uintptr_t offset = 0; offset < len; offset += amt) {
|
||||
uintptr_t ptr1 = (uintptr_t)hva + offset;
|
||||
|
||||
/*
|
||||
* Determine host address for guest virtual address
|
||||
* at offset.
|
||||
*/
|
||||
uintptr_t ptr2 = (uintptr_t)addr_gva2hva(vm, gva + offset);
|
||||
|
||||
/*
|
||||
* Determine amount to compare on this pass.
|
||||
* Don't allow the comparsion to cross a page boundary.
|
||||
*/
|
||||
amt = len - offset;
|
||||
if ((ptr1 >> vm->page_shift) != ((ptr1 + amt) >> vm->page_shift))
|
||||
amt = vm->page_size - (ptr1 % vm->page_size);
|
||||
if ((ptr2 >> vm->page_shift) != ((ptr2 + amt) >> vm->page_shift))
|
||||
amt = vm->page_size - (ptr2 % vm->page_size);
|
||||
|
||||
assert((ptr1 >> vm->page_shift) == ((ptr1 + amt - 1) >> vm->page_shift));
|
||||
assert((ptr2 >> vm->page_shift) == ((ptr2 + amt - 1) >> vm->page_shift));
|
||||
|
||||
/*
|
||||
* Perform the comparison. If there is a difference
|
||||
* return that result to the caller, otherwise need
|
||||
* to continue on looking for a mismatch.
|
||||
*/
|
||||
int ret = memcmp((void *)ptr1, (void *)ptr2, amt);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* No mismatch found. Let the caller know the two memory
|
||||
* areas are equal.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vm_userspace_mem_region_gpa_insert(struct rb_root *gpa_tree,
|
||||
struct userspace_mem_region *region)
|
||||
{
|
||||
|
|
@ -1270,7 +1197,7 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa)
|
|||
*/
|
||||
void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot)
|
||||
{
|
||||
__vm_mem_region_delete(vm, memslot2region(vm, slot), true);
|
||||
__vm_mem_region_delete(vm, memslot2region(vm, slot));
|
||||
}
|
||||
|
||||
void vm_guest_mem_fallocate(struct kvm_vm *vm, uint64_t base, uint64_t size,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,73 @@
|
|||
#include "processor.h"
|
||||
#include "hyperv.h"
|
||||
|
||||
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void)
|
||||
{
|
||||
static struct kvm_cpuid2 *cpuid;
|
||||
int kvm_fd;
|
||||
|
||||
if (cpuid)
|
||||
return cpuid;
|
||||
|
||||
cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
kvm_ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
|
||||
|
||||
close(kvm_fd);
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
static struct kvm_cpuid2 *cpuid_full;
|
||||
const struct kvm_cpuid2 *cpuid_sys, *cpuid_hv;
|
||||
int i, nent = 0;
|
||||
|
||||
if (!cpuid_full) {
|
||||
cpuid_sys = kvm_get_supported_cpuid();
|
||||
cpuid_hv = kvm_get_supported_hv_cpuid();
|
||||
|
||||
cpuid_full = allocate_kvm_cpuid2(cpuid_sys->nent + cpuid_hv->nent);
|
||||
if (!cpuid_full) {
|
||||
perror("malloc");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Need to skip KVM CPUID leaves 0x400000xx */
|
||||
for (i = 0; i < cpuid_sys->nent; i++) {
|
||||
if (cpuid_sys->entries[i].function >= 0x40000000 &&
|
||||
cpuid_sys->entries[i].function < 0x40000100)
|
||||
continue;
|
||||
cpuid_full->entries[nent] = cpuid_sys->entries[i];
|
||||
nent++;
|
||||
}
|
||||
|
||||
memcpy(&cpuid_full->entries[nent], cpuid_hv->entries,
|
||||
cpuid_hv->nent * sizeof(struct kvm_cpuid_entry2));
|
||||
cpuid_full->nent = nent + cpuid_hv->nent;
|
||||
}
|
||||
|
||||
vcpu_init_cpuid(vcpu, cpuid_full);
|
||||
}
|
||||
|
||||
const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
|
||||
|
||||
vcpu_ioctl(vcpu, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
|
||||
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
bool kvm_hv_cpu_has(struct kvm_x86_cpu_feature feature)
|
||||
{
|
||||
if (!kvm_has_cap(KVM_CAP_SYS_HYPERV_CPUID))
|
||||
return false;
|
||||
|
||||
return kvm_cpuid_has(kvm_get_supported_hv_cpuid(), feature);
|
||||
}
|
||||
|
||||
struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm,
|
||||
vm_vaddr_t *p_hv_pages_gva)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@
|
|||
#define KERNEL_DS 0x10
|
||||
#define KERNEL_TSS 0x18
|
||||
|
||||
#define MAX_NR_CPUID_ENTRIES 100
|
||||
|
||||
vm_vaddr_t exception_handlers;
|
||||
bool host_cpu_is_amd;
|
||||
bool host_cpu_is_intel;
|
||||
|
|
@ -1193,65 +1191,6 @@ void xen_hypercall(uint64_t nr, uint64_t a0, void *a1)
|
|||
GUEST_ASSERT(!__xen_hypercall(nr, a0, a1));
|
||||
}
|
||||
|
||||
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void)
|
||||
{
|
||||
static struct kvm_cpuid2 *cpuid;
|
||||
int kvm_fd;
|
||||
|
||||
if (cpuid)
|
||||
return cpuid;
|
||||
|
||||
cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
|
||||
kvm_ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
|
||||
|
||||
close(kvm_fd);
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
static struct kvm_cpuid2 *cpuid_full;
|
||||
const struct kvm_cpuid2 *cpuid_sys, *cpuid_hv;
|
||||
int i, nent = 0;
|
||||
|
||||
if (!cpuid_full) {
|
||||
cpuid_sys = kvm_get_supported_cpuid();
|
||||
cpuid_hv = kvm_get_supported_hv_cpuid();
|
||||
|
||||
cpuid_full = allocate_kvm_cpuid2(cpuid_sys->nent + cpuid_hv->nent);
|
||||
if (!cpuid_full) {
|
||||
perror("malloc");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Need to skip KVM CPUID leaves 0x400000xx */
|
||||
for (i = 0; i < cpuid_sys->nent; i++) {
|
||||
if (cpuid_sys->entries[i].function >= 0x40000000 &&
|
||||
cpuid_sys->entries[i].function < 0x40000100)
|
||||
continue;
|
||||
cpuid_full->entries[nent] = cpuid_sys->entries[i];
|
||||
nent++;
|
||||
}
|
||||
|
||||
memcpy(&cpuid_full->entries[nent], cpuid_hv->entries,
|
||||
cpuid_hv->nent * sizeof(struct kvm_cpuid_entry2));
|
||||
cpuid_full->nent = nent + cpuid_hv->nent;
|
||||
}
|
||||
|
||||
vcpu_init_cpuid(vcpu, cpuid_full);
|
||||
}
|
||||
|
||||
const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES);
|
||||
|
||||
vcpu_ioctl(vcpu, KVM_GET_SUPPORTED_HV_CPUID, cpuid);
|
||||
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
|
||||
{
|
||||
const unsigned long num_ht_pages = 12 << (30 - vm->page_shift); /* 12 GiB */
|
||||
|
|
|
|||
|
|
@ -47,15 +47,18 @@ static void guest_code(void)
|
|||
/*
|
||||
* Single step test, covers 2 basic instructions and 2 emulated
|
||||
*
|
||||
* Enable interrupts during the single stepping to see that
|
||||
* pending interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ
|
||||
* Enable interrupts during the single stepping to see that pending
|
||||
* interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ.
|
||||
*
|
||||
* Write MSR_IA32_TSC_DEADLINE to verify that KVM's fastpath handler
|
||||
* exits to userspace due to single-step being enabled.
|
||||
*/
|
||||
asm volatile("ss_start: "
|
||||
"sti\n\t"
|
||||
"xor %%eax,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"movl $0x1a0,%%ecx\n\t"
|
||||
"rdmsr\n\t"
|
||||
"movl $" __stringify(MSR_IA32_TSC_DEADLINE) ", %%ecx\n\t"
|
||||
"wrmsr\n\t"
|
||||
"cli\n\t"
|
||||
: : : "eax", "ebx", "ecx", "edx");
|
||||
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ int main(int argc, char *argv[])
|
|||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_DIRECT_TLBFLUSH));
|
||||
TEST_REQUIRE(kvm_hv_cpu_has(HV_X64_NESTED_DIRECT_FLUSH));
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ int main(int argc, char *argv[])
|
|||
int stage;
|
||||
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_DIRECT_TLBFLUSH));
|
||||
TEST_REQUIRE(kvm_hv_cpu_has(HV_X64_NESTED_DIRECT_FLUSH));
|
||||
|
||||
/* Create VM */
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
|
|
|
|||
|
|
@ -160,6 +160,36 @@ static void test_sev(void *guest_code, uint64_t policy)
|
|||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void guest_shutdown_code(void)
|
||||
{
|
||||
struct desc_ptr idt;
|
||||
|
||||
/* Clobber the IDT so that #UD is guaranteed to trigger SHUTDOWN. */
|
||||
memset(&idt, 0, sizeof(idt));
|
||||
__asm__ __volatile__("lidt %0" :: "m"(idt));
|
||||
|
||||
__asm__ __volatile__("ud2");
|
||||
}
|
||||
|
||||
static void test_sev_es_shutdown(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
uint32_t type = KVM_X86_SEV_ES_VM;
|
||||
|
||||
vm = vm_sev_create_with_one_vcpu(type, guest_shutdown_code, &vcpu);
|
||||
|
||||
vm_sev_launch(vm, SEV_POLICY_ES, NULL);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SHUTDOWN,
|
||||
"Wanted SHUTDOWN, got %s",
|
||||
exit_reason_str(vcpu->run->exit_reason));
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));
|
||||
|
|
@ -171,6 +201,8 @@ int main(int argc, char *argv[])
|
|||
test_sev(guest_sev_es_code, SEV_POLICY_ES | SEV_POLICY_NO_DBG);
|
||||
test_sev(guest_sev_es_code, SEV_POLICY_ES);
|
||||
|
||||
test_sev_es_shutdown();
|
||||
|
||||
if (kvm_has_cap(KVM_CAP_XCRS) &&
|
||||
(xgetbv(0) & XFEATURE_MASK_X87_AVX) == XFEATURE_MASK_X87_AVX) {
|
||||
test_sync_vmsa(0);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "hyperv.h"
|
||||
|
||||
#define HCALL_REGION_GPA 0xc0000000ULL
|
||||
#define HCALL_REGION_SLOT 10
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user