KVM: selftests: add Enlightened VMCS test
Modify test library and add eVMCS test. This includes nVMX save/restore testing. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Родитель
c939989d74
Коммит
18178ff862
|
@ -13,6 +13,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
|
|||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/state_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
|
||||
TEST_GEN_PROGS_x86_64 += dirty_log_test
|
||||
|
||||
TEST_GEN_PROGS_aarch64 += dirty_log_test
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -339,6 +339,8 @@ struct vmx_msr_entry {
|
|||
uint64_t value;
|
||||
} __attribute__ ((aligned(16)));
|
||||
|
||||
#include "evmcs.h"
|
||||
|
||||
static inline int vmxon(uint64_t phys)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
@ -372,6 +374,9 @@ static inline int vmptrld(uint64_t vmcs_pa)
|
|||
{
|
||||
uint8_t ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return -1;
|
||||
|
||||
__asm__ __volatile__ ("vmptrld %[pa]; setna %[ret]"
|
||||
: [ret]"=rm"(ret)
|
||||
: [pa]"m"(vmcs_pa)
|
||||
|
@ -385,6 +390,9 @@ static inline int vmptrst(uint64_t *value)
|
|||
uint64_t tmp;
|
||||
uint8_t ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmptrst(value);
|
||||
|
||||
__asm__ __volatile__("vmptrst %[value]; setna %[ret]"
|
||||
: [value]"=m"(tmp), [ret]"=rm"(ret)
|
||||
: : "cc", "memory");
|
||||
|
@ -411,6 +419,9 @@ static inline int vmlaunch(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmlaunch();
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
|
@ -443,6 +454,9 @@ static inline int vmresume(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmresume();
|
||||
|
||||
__asm__ __volatile__("push %%rbp;"
|
||||
"push %%rcx;"
|
||||
"push %%rdx;"
|
||||
|
@ -482,6 +496,9 @@ static inline int vmread(uint64_t encoding, uint64_t *value)
|
|||
uint64_t tmp;
|
||||
uint8_t ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmread(encoding, value);
|
||||
|
||||
__asm__ __volatile__("vmread %[encoding], %[value]; setna %[ret]"
|
||||
: [value]"=rm"(tmp), [ret]"=rm"(ret)
|
||||
: [encoding]"r"(encoding)
|
||||
|
@ -506,6 +523,9 @@ static inline int vmwrite(uint64_t encoding, uint64_t value)
|
|||
{
|
||||
uint8_t ret;
|
||||
|
||||
if (enable_evmcs)
|
||||
return evmcs_vmwrite(encoding, value);
|
||||
|
||||
__asm__ __volatile__ ("vmwrite %[value], %[encoding]; setna %[ret]"
|
||||
: [ret]"=rm"(ret)
|
||||
: [value]"rm"(value), [encoding]"r"(encoding)
|
||||
|
@ -543,6 +563,14 @@ struct vmx_pages {
|
|||
void *vmwrite_hva;
|
||||
uint64_t vmwrite_gpa;
|
||||
void *vmwrite;
|
||||
|
||||
void *vp_assist_hva;
|
||||
uint64_t vp_assist_gpa;
|
||||
void *vp_assist;
|
||||
|
||||
void *enlightened_vmcs_hva;
|
||||
uint64_t enlightened_vmcs_gpa;
|
||||
void *enlightened_vmcs;
|
||||
};
|
||||
|
||||
struct vmx_pages *vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "processor.h"
|
||||
#include "vmx.h"
|
||||
|
||||
bool enable_evmcs;
|
||||
|
||||
/* Allocate memory regions for nested VMX tests.
|
||||
*
|
||||
* Input Args:
|
||||
|
@ -62,6 +64,20 @@ vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva)
|
|||
vmx->vmwrite_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vmwrite);
|
||||
memset(vmx->vmwrite_hva, 0, getpagesize());
|
||||
|
||||
/* Setup of a region of guest memory for the VP Assist page. */
|
||||
vmx->vp_assist = (void *)vm_vaddr_alloc(vm, getpagesize(),
|
||||
0x10000, 0, 0);
|
||||
vmx->vp_assist_hva = addr_gva2hva(vm, (uintptr_t)vmx->vp_assist);
|
||||
vmx->vp_assist_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->vp_assist);
|
||||
|
||||
/* Setup of a region of guest memory for the enlightened VMCS. */
|
||||
vmx->enlightened_vmcs = (void *)vm_vaddr_alloc(vm, getpagesize(),
|
||||
0x10000, 0, 0);
|
||||
vmx->enlightened_vmcs_hva =
|
||||
addr_gva2hva(vm, (uintptr_t)vmx->enlightened_vmcs);
|
||||
vmx->enlightened_vmcs_gpa =
|
||||
addr_gva2gpa(vm, (uintptr_t)vmx->enlightened_vmcs);
|
||||
|
||||
*p_vmx_gva = vmx_gva;
|
||||
return vmx;
|
||||
}
|
||||
|
@ -112,18 +128,26 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx)
|
|||
|
||||
bool load_vmcs(struct vmx_pages *vmx)
|
||||
{
|
||||
/* Load a VMCS. */
|
||||
*(uint32_t *)(vmx->vmcs) = vmcs_revision();
|
||||
if (vmclear(vmx->vmcs_gpa))
|
||||
return false;
|
||||
if (!enable_evmcs) {
|
||||
/* Load a VMCS. */
|
||||
*(uint32_t *)(vmx->vmcs) = vmcs_revision();
|
||||
if (vmclear(vmx->vmcs_gpa))
|
||||
return false;
|
||||
|
||||
if (vmptrld(vmx->vmcs_gpa))
|
||||
return false;
|
||||
if (vmptrld(vmx->vmcs_gpa))
|
||||
return false;
|
||||
|
||||
/* Setup shadow VMCS, do not load it yet. */
|
||||
*(uint32_t *)(vmx->shadow_vmcs) = vmcs_revision() | 0x80000000ul;
|
||||
if (vmclear(vmx->shadow_vmcs_gpa))
|
||||
return false;
|
||||
/* Setup shadow VMCS, do not load it yet. */
|
||||
*(uint32_t *)(vmx->shadow_vmcs) =
|
||||
vmcs_revision() | 0x80000000ul;
|
||||
if (vmclear(vmx->shadow_vmcs_gpa))
|
||||
return false;
|
||||
} else {
|
||||
if (evmcs_vmptrld(vmx->enlightened_vmcs_gpa,
|
||||
vmx->enlightened_vmcs))
|
||||
return false;
|
||||
current_evmcs->revision_id = vmcs_revision();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018, Red Hat, Inc.
|
||||
*
|
||||
* Tests for Enlightened VMCS, including nested guest state.
|
||||
*/
|
||||
#define _GNU_SOURCE /* for program_invocation_short_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 "vmx.h"
|
||||
|
||||
#define VCPU_ID 5
|
||||
|
||||
static bool have_nested_state;
|
||||
|
||||
void l2_guest_code(void)
|
||||
{
|
||||
GUEST_SYNC(6);
|
||||
|
||||
GUEST_SYNC(7);
|
||||
|
||||
/* Done, exit to L1 and never come back. */
|
||||
vmcall();
|
||||
}
|
||||
|
||||
void l1_guest_code(struct vmx_pages *vmx_pages)
|
||||
{
|
||||
#define L2_GUEST_STACK_SIZE 64
|
||||
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
|
||||
|
||||
enable_vp_assist(vmx_pages->vp_assist_gpa, vmx_pages->vp_assist);
|
||||
|
||||
GUEST_ASSERT(vmx_pages->vmcs_gpa);
|
||||
GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
|
||||
GUEST_SYNC(3);
|
||||
GUEST_ASSERT(load_vmcs(vmx_pages));
|
||||
GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
|
||||
|
||||
GUEST_SYNC(4);
|
||||
GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
|
||||
|
||||
prepare_vmcs(vmx_pages, l2_guest_code,
|
||||
&l2_guest_stack[L2_GUEST_STACK_SIZE]);
|
||||
|
||||
GUEST_SYNC(5);
|
||||
GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
|
||||
GUEST_ASSERT(!vmlaunch());
|
||||
GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
|
||||
GUEST_SYNC(8);
|
||||
GUEST_ASSERT(!vmresume());
|
||||
GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
|
||||
GUEST_SYNC(9);
|
||||
}
|
||||
|
||||
void guest_code(struct vmx_pages *vmx_pages)
|
||||
{
|
||||
GUEST_SYNC(1);
|
||||
GUEST_SYNC(2);
|
||||
|
||||
if (vmx_pages)
|
||||
l1_guest_code(vmx_pages);
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct vmx_pages *vmx_pages = NULL;
|
||||
vm_vaddr_t vmx_pages_gva = 0;
|
||||
|
||||
struct kvm_regs regs1, regs2;
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_run *run;
|
||||
struct kvm_x86_state *state;
|
||||
struct ucall uc;
|
||||
int stage;
|
||||
uint16_t evmcs_ver;
|
||||
struct kvm_enable_cap enable_evmcs_cap = {
|
||||
.cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
|
||||
.args[0] = (unsigned long)&evmcs_ver
|
||||
};
|
||||
|
||||
struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
|
||||
|
||||
/* Create VM */
|
||||
vm = vm_create_default(VCPU_ID, 0, guest_code);
|
||||
|
||||
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
|
||||
|
||||
if (!kvm_check_cap(KVM_CAP_NESTED_STATE) ||
|
||||
!kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
|
||||
printf("capabilities not available, skipping test\n");
|
||||
exit(KSFT_SKIP);
|
||||
}
|
||||
|
||||
vcpu_ioctl(vm, VCPU_ID, KVM_ENABLE_CAP, &enable_evmcs_cap);
|
||||
|
||||
run = vcpu_state(vm, VCPU_ID);
|
||||
|
||||
vcpu_regs_get(vm, VCPU_ID, ®s1);
|
||||
|
||||
vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
|
||||
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
|
||||
|
||||
for (stage = 1;; stage++) {
|
||||
_vcpu_run(vm, VCPU_ID);
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"Unexpected exit reason: %u (%s),\n",
|
||||
run->exit_reason,
|
||||
exit_reason_str(run->exit_reason));
|
||||
|
||||
memset(®s1, 0, sizeof(regs1));
|
||||
vcpu_regs_get(vm, VCPU_ID, ®s1);
|
||||
switch (get_ucall(vm, VCPU_ID, &uc)) {
|
||||
case UCALL_ABORT:
|
||||
TEST_ASSERT(false, "%s at %s:%d", (const char *)uc.args[0],
|
||||
__FILE__, uc.args[1]);
|
||||
/* NOT REACHED */
|
||||
case UCALL_SYNC:
|
||||
break;
|
||||
case UCALL_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd);
|
||||
}
|
||||
|
||||
/* UCALL_SYNC is handled here. */
|
||||
TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
|
||||
uc.args[1] == stage, "Unexpected register values vmexit #%lx, got %lx",
|
||||
stage, (ulong)uc.args[1]);
|
||||
|
||||
state = vcpu_save_state(vm, VCPU_ID);
|
||||
kvm_vm_release(vm);
|
||||
|
||||
/* Restore state in a new VM. */
|
||||
kvm_vm_restart(vm, O_RDWR);
|
||||
vm_vcpu_add(vm, VCPU_ID, 0, 0);
|
||||
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
|
||||
vcpu_load_state(vm, VCPU_ID, state);
|
||||
run = vcpu_state(vm, VCPU_ID);
|
||||
free(state);
|
||||
|
||||
memset(®s2, 0, sizeof(regs2));
|
||||
vcpu_regs_get(vm, VCPU_ID, ®s2);
|
||||
TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)),
|
||||
"Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx",
|
||||
(ulong) regs2.rdi, (ulong) regs2.rsi);
|
||||
}
|
||||
|
||||
done:
|
||||
kvm_vm_free(vm);
|
||||
}
|
Загрузка…
Ссылка в новой задаче