1/*- 2 * Copyright (c) 2011 NetApp, Inc. 3 * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30#include <machine/asmacros.h> 31 32#include "vmx_assym.s" 33 34#ifdef SMP 35#define LK lock ; 36#else 37#define LK 38#endif 39 40/* 41 * Assumes that %rdi holds a pointer to the 'vmxctx'. 42 * 43 * On "return" all registers are updated to reflect guest state. The two 44 * exceptions are %rip and %rsp. These registers are atomically switched 45 * by hardware from the guest area of the vmcs. 46 * 47 * We modify %rsp to point to the 'vmxctx' so we can use it to restore 48 * host context in case of an error with 'vmlaunch' or 'vmresume'. 49 */ 50#define VMX_GUEST_RESTORE \ 51 movq %rdi,%rsp; \ 52 movq VMXCTX_GUEST_CR2(%rdi),%rsi; \ 53 movq %rsi,%cr2; \ 54 movq VMXCTX_GUEST_RSI(%rdi),%rsi; \ 55 movq VMXCTX_GUEST_RDX(%rdi),%rdx; \ 56 movq VMXCTX_GUEST_RCX(%rdi),%rcx; \ 57 movq VMXCTX_GUEST_R8(%rdi),%r8; \ 58 movq VMXCTX_GUEST_R9(%rdi),%r9; \ 59 movq VMXCTX_GUEST_RAX(%rdi),%rax; \ 60 movq VMXCTX_GUEST_RBX(%rdi),%rbx; \ 61 movq VMXCTX_GUEST_RBP(%rdi),%rbp; \ 62 movq VMXCTX_GUEST_R10(%rdi),%r10; \ 63 movq VMXCTX_GUEST_R11(%rdi),%r11; \ 64 movq VMXCTX_GUEST_R12(%rdi),%r12; \ 65 movq VMXCTX_GUEST_R13(%rdi),%r13; \ 66 movq VMXCTX_GUEST_R14(%rdi),%r14; \ 67 movq VMXCTX_GUEST_R15(%rdi),%r15; \ 68 movq VMXCTX_GUEST_RDI(%rdi),%rdi; /* restore rdi the last */ 69 70/* 71 * Save and restore the host context. 72 * 73 * Assumes that %rdi holds a pointer to the 'vmxctx'. 74 */ 75#define VMX_HOST_SAVE(tmpreg) \ 76 movq (%rsp), tmpreg; /* return address */ \ 77 movq %r15, VMXCTX_HOST_R15(%rdi); \ 78 movq %r14, VMXCTX_HOST_R14(%rdi); \ 79 movq %r13, VMXCTX_HOST_R13(%rdi); \ 80 movq %r12, VMXCTX_HOST_R12(%rdi); \ 81 movq %rbp, VMXCTX_HOST_RBP(%rdi); \ 82 movq %rsp, VMXCTX_HOST_RSP(%rdi); \ 83 movq %rbx, VMXCTX_HOST_RBX(%rdi); \ 84 movq tmpreg, VMXCTX_HOST_RIP(%rdi) 85 86#define VMX_HOST_RESTORE(tmpreg) \ 87 movq VMXCTX_HOST_R15(%rdi), %r15; \ 88 movq VMXCTX_HOST_R14(%rdi), %r14; \ 89 movq VMXCTX_HOST_R13(%rdi), %r13; \ 90 movq VMXCTX_HOST_R12(%rdi), %r12; \ 91 movq VMXCTX_HOST_RBP(%rdi), %rbp; \ 92 movq VMXCTX_HOST_RSP(%rdi), %rsp; \ 93 movq VMXCTX_HOST_RBX(%rdi), %rbx; \ 94 movq VMXCTX_HOST_RIP(%rdi), tmpreg; \ 95 movq tmpreg, (%rsp) /* return address */ 96 97/* 98 * vmx_enter_guest(struct vmxctx *vmxctx, int launched) 99 * %rdi: pointer to the 'vmxctx' 100 * %esi: launch state of the VMCS 101 * Interrupts must be disabled on entry. 102 */ 103ENTRY(vmx_enter_guest) 104 /* 105 * Save host state before doing anything else. 106 */ 107 VMX_HOST_SAVE(%r10) 108 109 /* 110 * Activate guest pmap on this cpu. 111 */ 112 movq VMXCTX_PMAP(%rdi), %r11 113 movl PCPU(CPUID), %eax 114 LK btsl %eax, PM_ACTIVE(%r11) 115 116 /* 117 * If 'vmxctx->eptgen[curcpu]' is not identical to 'pmap->pm_eptgen' 118 * then we must invalidate all mappings associated with this EPTP. 119 */ 120 movq PM_EPTGEN(%r11), %r10 121 cmpq %r10, VMXCTX_EPTGEN(%rdi, %rax, 8) 122 je guest_restore 123 124 /* Refresh 'vmxctx->eptgen[curcpu]' */ 125 movq %r10, VMXCTX_EPTGEN(%rdi, %rax, 8) 126 127 /* Setup the invept descriptor on the host stack */ 128 mov %rsp, %r11 129 movq VMXCTX_EPTP(%rdi), %rax 130 movq %rax, -16(%r11) 131 movq $0x0, -8(%r11) 132 mov $0x1, %eax /* Single context invalidate */ 133 invept -16(%r11), %rax 134 jbe invept_error /* Check invept instruction error */ 135 136guest_restore: 137 cmpl $0, %esi 138 je do_launch 139 140 VMX_GUEST_RESTORE 141 vmresume 142 /* 143 * In the common case 'vmresume' returns back to the host through 144 * 'vmx_exit_guest' with %rsp pointing to 'vmxctx'. 145 * 146 * If there is an error we return VMX_VMRESUME_ERROR to the caller. 147 */ 148 movq %rsp, %rdi /* point %rdi back to 'vmxctx' */ 149 movl $VMX_VMRESUME_ERROR, %eax 150 jmp decode_inst_error 151 152do_launch: 153 VMX_GUEST_RESTORE 154 vmlaunch 155 /* 156 * In the common case 'vmlaunch' returns back to the host through 157 * 'vmx_exit_guest' with %rsp pointing to 'vmxctx'. 158 * 159 * If there is an error we return VMX_VMLAUNCH_ERROR to the caller. 160 */ 161 movq %rsp, %rdi /* point %rdi back to 'vmxctx' */ 162 movl $VMX_VMLAUNCH_ERROR, %eax 163 jmp decode_inst_error 164 165invept_error: 166 movl $VMX_INVEPT_ERROR, %eax 167 jmp decode_inst_error 168 169decode_inst_error: 170 movl $VM_FAIL_VALID, %r11d 171 jz inst_error 172 movl $VM_FAIL_INVALID, %r11d 173inst_error: 174 movl %r11d, VMXCTX_INST_FAIL_STATUS(%rdi) 175 176 /* 177 * The return value is already populated in %eax so we cannot use 178 * it as a scratch register beyond this point. 179 */ 180 181 /* 182 * Deactivate guest pmap from this cpu. 183 */ 184 movq VMXCTX_PMAP(%rdi), %r11 185 movl PCPU(CPUID), %r10d 186 LK btrl %r10d, PM_ACTIVE(%r11) 187 188 VMX_HOST_RESTORE(%r10) 189 ret 190END(vmx_enter_guest) 191 192/* 193 * void vmx_exit_guest(void) 194 * %rsp points to the struct vmxctx 195 */ 196ENTRY(vmx_exit_guest) 197 /* 198 * Save guest state that is not automatically saved in the vmcs. 199 */ 200 movq %rdi,VMXCTX_GUEST_RDI(%rsp) 201 movq %rsi,VMXCTX_GUEST_RSI(%rsp) 202 movq %rdx,VMXCTX_GUEST_RDX(%rsp) 203 movq %rcx,VMXCTX_GUEST_RCX(%rsp) 204 movq %r8,VMXCTX_GUEST_R8(%rsp) 205 movq %r9,VMXCTX_GUEST_R9(%rsp) 206 movq %rax,VMXCTX_GUEST_RAX(%rsp) 207 movq %rbx,VMXCTX_GUEST_RBX(%rsp) 208 movq %rbp,VMXCTX_GUEST_RBP(%rsp) 209 movq %r10,VMXCTX_GUEST_R10(%rsp) 210 movq %r11,VMXCTX_GUEST_R11(%rsp) 211 movq %r12,VMXCTX_GUEST_R12(%rsp) 212 movq %r13,VMXCTX_GUEST_R13(%rsp) 213 movq %r14,VMXCTX_GUEST_R14(%rsp) 214 movq %r15,VMXCTX_GUEST_R15(%rsp) 215 216 movq %cr2,%rdi 217 movq %rdi,VMXCTX_GUEST_CR2(%rsp) 218 219 movq %rsp,%rdi 220 221 /* 222 * Deactivate guest pmap from this cpu. 223 */ 224 movq VMXCTX_PMAP(%rdi), %r11 225 movl PCPU(CPUID), %r10d 226 LK btrl %r10d, PM_ACTIVE(%r11) 227 228 VMX_HOST_RESTORE(%r10) 229 230 /* 231 * This will return to the caller of 'vmx_enter_guest()' with a return 232 * value of VMX_GUEST_VMEXIT. 233 */ 234 movl $VMX_GUEST_VMEXIT, %eax 235 ret 236END(vmx_exit_guest) 237