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#define NMI_VECTOR      2
  22
  23static int ud_count;
  24
  25static void guest_ud_handler(struct ex_regs *regs)
  26{
  27        ud_count++;
  28        regs->rip += 3; /* VMLAUNCH */
  29}
  30
  31static void guest_nmi_handler(struct ex_regs *regs)
  32{
  33}
  34
  35void l2_guest_code(void)
  36{
  37        GUEST_SYNC(7);
  38
  39        GUEST_SYNC(8);
  40
  41        /* Forced exit to L1 upon restore */
  42        GUEST_SYNC(9);
  43
  44        /* Done, exit to L1 and never come back.  */
  45        vmcall();
  46}
  47
  48void guest_code(struct vmx_pages *vmx_pages)
  49{
  50#define L2_GUEST_STACK_SIZE 64
  51        unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
  52
  53        x2apic_enable();
  54
  55        GUEST_SYNC(1);
  56        GUEST_SYNC(2);
  57
  58        enable_vp_assist(vmx_pages->vp_assist_gpa, vmx_pages->vp_assist);
  59
  60        GUEST_ASSERT(vmx_pages->vmcs_gpa);
  61        GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
  62        GUEST_SYNC(3);
  63        GUEST_ASSERT(load_vmcs(vmx_pages));
  64        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  65
  66        GUEST_SYNC(4);
  67        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  68
  69        prepare_vmcs(vmx_pages, l2_guest_code,
  70                     &l2_guest_stack[L2_GUEST_STACK_SIZE]);
  71
  72        GUEST_SYNC(5);
  73        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  74        current_evmcs->revision_id = -1u;
  75        GUEST_ASSERT(vmlaunch());
  76        current_evmcs->revision_id = EVMCS_VERSION;
  77        GUEST_SYNC(6);
  78
  79        current_evmcs->pin_based_vm_exec_control |=
  80                PIN_BASED_NMI_EXITING;
  81        GUEST_ASSERT(!vmlaunch());
  82        GUEST_ASSERT(vmptrstz() == vmx_pages->enlightened_vmcs_gpa);
  83
  84        /*
  85         * NMI forces L2->L1 exit, resuming L2 and hope that EVMCS is
  86         * up-to-date (RIP points where it should and not at the beginning
  87         * of l2_guest_code(). GUEST_SYNC(9) checkes that.
  88         */
  89        GUEST_ASSERT(!vmresume());
  90
  91        GUEST_SYNC(10);
  92
  93        GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
  94        GUEST_SYNC(11);
  95
  96        /* Try enlightened vmptrld with an incorrect GPA */
  97        evmcs_vmptrld(0xdeadbeef, vmx_pages->enlightened_vmcs);
  98        GUEST_ASSERT(vmlaunch());
  99        GUEST_ASSERT(ud_count == 1);
 100        GUEST_DONE();
 101}
 102
 103void inject_nmi(struct kvm_vm *vm)
 104{
 105        struct kvm_vcpu_events events;
 106
 107        vcpu_events_get(vm, VCPU_ID, &events);
 108
 109        events.nmi.pending = 1;
 110        events.flags |= KVM_VCPUEVENT_VALID_NMI_PENDING;
 111
 112        vcpu_events_set(vm, VCPU_ID, &events);
 113}
 114
 115static void save_restore_vm(struct kvm_vm *vm)
 116{
 117        struct kvm_regs regs1, regs2;
 118        struct kvm_x86_state *state;
 119
 120        state = vcpu_save_state(vm, VCPU_ID);
 121        memset(&regs1, 0, sizeof(regs1));
 122        vcpu_regs_get(vm, VCPU_ID, &regs1);
 123
 124        kvm_vm_release(vm);
 125
 126        /* Restore state in a new VM.  */
 127        kvm_vm_restart(vm, O_RDWR);
 128        vm_vcpu_add(vm, VCPU_ID);
 129        vcpu_set_hv_cpuid(vm, VCPU_ID);
 130        vcpu_enable_evmcs(vm, VCPU_ID);
 131        vcpu_load_state(vm, VCPU_ID, state);
 132        free(state);
 133
 134        memset(&regs2, 0, sizeof(regs2));
 135        vcpu_regs_get(vm, VCPU_ID, &regs2);
 136        TEST_ASSERT(!memcmp(&regs1, &regs2, sizeof(regs2)),
 137                    "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx",
 138                    (ulong) regs2.rdi, (ulong) regs2.rsi);
 139}
 140
 141int main(int argc, char *argv[])
 142{
 143        vm_vaddr_t vmx_pages_gva = 0;
 144
 145        struct kvm_vm *vm;
 146        struct kvm_run *run;
 147        struct ucall uc;
 148        int stage;
 149
 150        /* Create VM */
 151        vm = vm_create_default(VCPU_ID, 0, guest_code);
 152
 153        if (!nested_vmx_supported() ||
 154            !kvm_check_cap(KVM_CAP_NESTED_STATE) ||
 155            !kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
 156                print_skip("Enlightened VMCS is unsupported");
 157                exit(KSFT_SKIP);
 158        }
 159
 160        vcpu_set_hv_cpuid(vm, VCPU_ID);
 161        vcpu_enable_evmcs(vm, VCPU_ID);
 162
 163        vcpu_alloc_vmx(vm, &vmx_pages_gva);
 164        vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
 165
 166        vm_init_descriptor_tables(vm);
 167        vcpu_init_descriptor_tables(vm, VCPU_ID);
 168        vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler);
 169        vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler);
 170
 171        pr_info("Running L1 which uses EVMCS to run L2\n");
 172
 173        for (stage = 1;; stage++) {
 174                run = vcpu_state(vm, VCPU_ID);
 175                _vcpu_run(vm, VCPU_ID);
 176                TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
 177                            "Stage %d: unexpected exit reason: %u (%s),\n",
 178                            stage, run->exit_reason,
 179                            exit_reason_str(run->exit_reason));
 180
 181                switch (get_ucall(vm, VCPU_ID, &uc)) {
 182                case UCALL_ABORT:
 183                        TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0],
 184                                  __FILE__, uc.args[1]);
 185                        /* NOT REACHED */
 186                case UCALL_SYNC:
 187                        break;
 188                case UCALL_DONE:
 189                        goto done;
 190                default:
 191                        TEST_FAIL("Unknown ucall %lu", uc.cmd);
 192                }
 193
 194                /* UCALL_SYNC is handled here.  */
 195                TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
 196                            uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx",
 197                            stage, (ulong)uc.args[1]);
 198
 199                save_restore_vm(vm);
 200
 201                /* Force immediate L2->L1 exit before resuming */
 202                if (stage == 8) {
 203                        pr_info("Injecting NMI into L1 before L2 had a chance to run after restore\n");
 204                        inject_nmi(vm);
 205                }
 206
 207                /*
 208                 * Do KVM_GET_NESTED_STATE/KVM_SET_NESTED_STATE for a freshly
 209                 * restored VM (before the first KVM_RUN) to check that
 210                 * KVM_STATE_NESTED_EVMCS is not lost.
 211                 */
 212                if (stage == 9) {
 213                        pr_info("Trying extra KVM_GET_NESTED_STATE/KVM_SET_NESTED_STATE cycle\n");
 214                        save_restore_vm(vm);
 215                }
 216        }
 217
 218done:
 219        kvm_vm_free(vm);
 220}
 221