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 23 static int ud_count; 24 25 static void guest_ud_handler(struct ex_regs *regs) 26 { 27 ud_count++; 28 regs->rip += 3; /* VMLAUNCH */ 29 } 30 31 static void guest_nmi_handler(struct ex_regs *regs) 32 { 33 } 34 35 void 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 48 void 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 103 void 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 115 static 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(®s1, 0, sizeof(regs1)); 122 vcpu_regs_get(vm, VCPU_ID, ®s1); 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(®s2, 0, sizeof(regs2)); 135 vcpu_regs_get(vm, VCPU_ID, ®s2); 136 TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), 137 "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", 138 (ulong) regs2.rdi, (ulong) regs2.rsi); 139 } 140 141 int 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 218 done: 219 kvm_vm_free(vm); 220 } 221