linux/tools/testing/selftests/kvm/x86_64/evmcs_test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2018, Red Hat, Inc.
   4 *
   5 * Tests for Enlightened VMCS, including nested guest state.
   6 */
   7#define _GNU_SOURCE /* for program_invocation_short_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
  16#include "kvm_util.h"
  17
  18#include "vmx.h"
  19
  20#define VCPU_ID         5
  21
  22static bool have_nested_state;
  23
  24void l2_guest_code(void)
  25{
  26        GUEST_SYNC(6);
  27
  28        GUEST_SYNC(7);
  29
  30        /* Done, exit to L1 and never come back.  */
  31        vmcall();
  32}
  33
  34void l1_guest_code(struct vmx_pages *vmx_pages)
  35{
  36#define L2_GUEST_STACK_SIZE 64
  37        unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
  38
  39        enable_vp_assist(vmx_pages->vp_assist_gpa, vmx_pages->vp_assist);
  40
  41        GUEST_ASSERT(vmx_pages->vmcs_gpa);
  42        GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
  43        GUEST_SYNC(3);
  44        GUEST_ASSERT(load_vmcs(vmx_pages));
  45        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  46
  47        GUEST_SYNC(4);
  48        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  49
  50        prepare_vmcs(vmx_pages, l2_guest_code,
  51                     &l2_guest_stack[L2_GUEST_STACK_SIZE]);
  52
  53        GUEST_SYNC(5);
  54        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  55        GUEST_ASSERT(!vmlaunch());
  56        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  57        GUEST_SYNC(8);
  58        GUEST_ASSERT(!vmresume());
  59        GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
  60        GUEST_SYNC(9);
  61}
  62
  63void guest_code(struct vmx_pages *vmx_pages)
  64{
  65        GUEST_SYNC(1);
  66        GUEST_SYNC(2);
  67
  68        if (vmx_pages)
  69                l1_guest_code(vmx_pages);
  70
  71        GUEST_DONE();
  72}
  73
  74int main(int argc, char *argv[])
  75{
  76        struct vmx_pages *vmx_pages = NULL;
  77        vm_vaddr_t vmx_pages_gva = 0;
  78
  79        struct kvm_regs regs1, regs2;
  80        struct kvm_vm *vm;
  81        struct kvm_run *run;
  82        struct kvm_x86_state *state;
  83        struct ucall uc;
  84        int stage;
  85        uint16_t evmcs_ver;
  86        struct kvm_enable_cap enable_evmcs_cap = {
  87                .cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
  88                 .args[0] = (unsigned long)&evmcs_ver
  89        };
  90
  91        struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
  92
  93        /* Create VM */
  94        vm = vm_create_default(VCPU_ID, 0, guest_code);
  95
  96        vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
  97
  98        if (!kvm_check_cap(KVM_CAP_NESTED_STATE) ||
  99            !kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
 100                printf("capabilities not available, skipping test\n");
 101                exit(KSFT_SKIP);
 102        }
 103
 104        vcpu_ioctl(vm, VCPU_ID, KVM_ENABLE_CAP, &enable_evmcs_cap);
 105
 106        /* KVM should return supported EVMCS version range */
 107        TEST_ASSERT(((evmcs_ver >> 8) >= (evmcs_ver & 0xff)) &&
 108                    (evmcs_ver & 0xff) > 0,
 109                    "Incorrect EVMCS version range: %x:%x\n",
 110                    evmcs_ver & 0xff, evmcs_ver >> 8);
 111
 112        run = vcpu_state(vm, VCPU_ID);
 113
 114        vcpu_regs_get(vm, VCPU_ID, &regs1);
 115
 116        vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
 117        vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
 118
 119        for (stage = 1;; stage++) {
 120                _vcpu_run(vm, VCPU_ID);
 121                TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
 122                            "Stage %d: unexpected exit reason: %u (%s),\n",
 123                            stage, run->exit_reason,
 124                            exit_reason_str(run->exit_reason));
 125
 126                switch (get_ucall(vm, VCPU_ID, &uc)) {
 127                case UCALL_ABORT:
 128                        TEST_ASSERT(false, "%s at %s:%d", (const char *)uc.args[0],
 129                                    __FILE__, uc.args[1]);
 130                        /* NOT REACHED */
 131                case UCALL_SYNC:
 132                        break;
 133                case UCALL_DONE:
 134                        goto done;
 135                default:
 136                        TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd);
 137                }
 138
 139                /* UCALL_SYNC is handled here.  */
 140                TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
 141                            uc.args[1] == stage, "Unexpected register values vmexit #%lx, got %lx",
 142                            stage, (ulong)uc.args[1]);
 143
 144                state = vcpu_save_state(vm, VCPU_ID);
 145                memset(&regs1, 0, sizeof(regs1));
 146                vcpu_regs_get(vm, VCPU_ID, &regs1);
 147
 148                kvm_vm_release(vm);
 149
 150                /* Restore state in a new VM.  */
 151                kvm_vm_restart(vm, O_RDWR);
 152                vm_vcpu_add(vm, VCPU_ID, 0, 0);
 153                vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
 154                vcpu_ioctl(vm, VCPU_ID, KVM_ENABLE_CAP, &enable_evmcs_cap);
 155                vcpu_load_state(vm, VCPU_ID, state);
 156                run = vcpu_state(vm, VCPU_ID);
 157                free(state);
 158
 159                memset(&regs2, 0, sizeof(regs2));
 160                vcpu_regs_get(vm, VCPU_ID, &regs2);
 161                TEST_ASSERT(!memcmp(&regs1, &regs2, sizeof(regs2)),
 162                            "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx",
 163                            (ulong) regs2.rdi, (ulong) regs2.rsi);
 164        }
 165
 166done:
 167        kvm_vm_free(vm);
 168}
 169