1 /* 2 * KVM_GET/SET_* tests 3 * 4 * Copyright (C) 2018, Red Hat, Inc. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2. 7 * 8 * Tests for vCPU state save/restore, including nested guest state. 9 */ 10 #define _GNU_SOURCE /* for program_invocation_short_name */ 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 17 #include "test_util.h" 18 19 #include "kvm_util.h" 20 #include "processor.h" 21 #include "vmx.h" 22 23 #define VCPU_ID 5 24 25 void l2_guest_code(void) 26 { 27 GUEST_SYNC(6); 28 29 /* Exit to L1 */ 30 vmcall(); 31 32 /* L1 has now set up a shadow VMCS for us. */ 33 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); 34 GUEST_SYNC(10); 35 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); 36 GUEST_ASSERT(!vmwrite(GUEST_RIP, 0xc0fffee)); 37 GUEST_SYNC(11); 38 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0fffee); 39 GUEST_ASSERT(!vmwrite(GUEST_RIP, 0xc0ffffee)); 40 GUEST_SYNC(12); 41 42 /* Done, exit to L1 and never come back. */ 43 vmcall(); 44 } 45 46 void l1_guest_code(struct vmx_pages *vmx_pages) 47 { 48 #define L2_GUEST_STACK_SIZE 64 49 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; 50 51 GUEST_ASSERT(vmx_pages->vmcs_gpa); 52 GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); 53 GUEST_SYNC(3); 54 GUEST_ASSERT(load_vmcs(vmx_pages)); 55 GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); 56 57 GUEST_SYNC(4); 58 GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); 59 60 prepare_vmcs(vmx_pages, l2_guest_code, 61 &l2_guest_stack[L2_GUEST_STACK_SIZE]); 62 63 GUEST_SYNC(5); 64 GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); 65 GUEST_ASSERT(!vmlaunch()); 66 GUEST_ASSERT(vmptrstz() == vmx_pages->vmcs_gpa); 67 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 68 69 /* Check that the launched state is preserved. */ 70 GUEST_ASSERT(vmlaunch()); 71 72 GUEST_ASSERT(!vmresume()); 73 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 74 75 GUEST_SYNC(7); 76 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 77 78 GUEST_ASSERT(!vmresume()); 79 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 80 81 vmwrite(GUEST_RIP, vmreadz(GUEST_RIP) + 3); 82 83 vmwrite(SECONDARY_VM_EXEC_CONTROL, SECONDARY_EXEC_SHADOW_VMCS); 84 vmwrite(VMCS_LINK_POINTER, vmx_pages->shadow_vmcs_gpa); 85 86 GUEST_ASSERT(!vmptrld(vmx_pages->shadow_vmcs_gpa)); 87 GUEST_ASSERT(vmlaunch()); 88 GUEST_SYNC(8); 89 GUEST_ASSERT(vmlaunch()); 90 GUEST_ASSERT(vmresume()); 91 92 vmwrite(GUEST_RIP, 0xc0ffee); 93 GUEST_SYNC(9); 94 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffee); 95 96 GUEST_ASSERT(!vmptrld(vmx_pages->vmcs_gpa)); 97 GUEST_ASSERT(!vmresume()); 98 GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL); 99 100 GUEST_ASSERT(!vmptrld(vmx_pages->shadow_vmcs_gpa)); 101 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); 102 GUEST_ASSERT(vmlaunch()); 103 GUEST_ASSERT(vmresume()); 104 GUEST_SYNC(13); 105 GUEST_ASSERT(vmreadz(GUEST_RIP) == 0xc0ffffee); 106 GUEST_ASSERT(vmlaunch()); 107 GUEST_ASSERT(vmresume()); 108 } 109 110 void guest_code(struct vmx_pages *vmx_pages) 111 { 112 GUEST_SYNC(1); 113 GUEST_SYNC(2); 114 115 if (vmx_pages) 116 l1_guest_code(vmx_pages); 117 118 GUEST_DONE(); 119 } 120 121 int main(int argc, char *argv[]) 122 { 123 vm_vaddr_t vmx_pages_gva = 0; 124 125 struct kvm_regs regs1, regs2; 126 struct kvm_vm *vm; 127 struct kvm_run *run; 128 struct kvm_x86_state *state; 129 struct ucall uc; 130 int stage; 131 132 /* Create VM */ 133 vm = vm_create_default(VCPU_ID, 0, guest_code); 134 vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); 135 run = vcpu_state(vm, VCPU_ID); 136 137 vcpu_regs_get(vm, VCPU_ID, ®s1); 138 139 if (kvm_check_cap(KVM_CAP_NESTED_STATE)) { 140 vcpu_alloc_vmx(vm, &vmx_pages_gva); 141 vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); 142 } else { 143 printf("will skip nested state checks\n"); 144 vcpu_args_set(vm, VCPU_ID, 1, 0); 145 } 146 147 for (stage = 1;; stage++) { 148 _vcpu_run(vm, VCPU_ID); 149 TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, 150 "Stage %d: unexpected exit reason: %u (%s),\n", 151 stage, run->exit_reason, 152 exit_reason_str(run->exit_reason)); 153 154 switch (get_ucall(vm, VCPU_ID, &uc)) { 155 case UCALL_ABORT: 156 TEST_ASSERT(false, "%s at %s:%d", (const char *)uc.args[0], 157 __FILE__, uc.args[1]); 158 /* NOT REACHED */ 159 case UCALL_SYNC: 160 break; 161 case UCALL_DONE: 162 goto done; 163 default: 164 TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd); 165 } 166 167 /* UCALL_SYNC is handled here. */ 168 TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && 169 uc.args[1] == stage, "Unexpected register values vmexit #%lx, got %lx", 170 stage, (ulong)uc.args[1]); 171 172 state = vcpu_save_state(vm, VCPU_ID); 173 memset(®s1, 0, sizeof(regs1)); 174 vcpu_regs_get(vm, VCPU_ID, ®s1); 175 176 kvm_vm_release(vm); 177 178 /* Restore state in a new VM. */ 179 kvm_vm_restart(vm, O_RDWR); 180 vm_vcpu_add(vm, VCPU_ID, 0, 0); 181 vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); 182 vcpu_load_state(vm, VCPU_ID, state); 183 run = vcpu_state(vm, VCPU_ID); 184 free(state); 185 186 memset(®s2, 0, sizeof(regs2)); 187 vcpu_regs_get(vm, VCPU_ID, ®s2); 188 TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), 189 "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", 190 (ulong) regs2.rdi, (ulong) regs2.rsi); 191 } 192 193 done: 194 kvm_vm_free(vm); 195 } 196