hammer2 - Merge Mihai Carabas's VKERNEL/VMM GSOC project into the main tree
[dragonfly.git] / sys / platform / pc64 / vmm / ept.c
1 /*
2  * Copyright (c) 2003-2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Mihai Carabas <mihai.carabas@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/systm.h>
36 #include <sys/sfbuf.h>
37 #include <sys/proc.h>
38 #include <sys/thread.h>
39
40 #include <machine/pmap.h>
41 #include <machine/specialreg.h>
42 #include <machine/cpufunc.h>
43 #include <machine/vmm.h>
44
45 #include <vm/vm_extern.h>
46 #include <vm/vm_map.h>
47
48 #include "vmx.h"
49 #include "ept.h"
50 #include "vmm_utils.h"
51
52 static uint64_t pmap_bits_ept[PG_BITS_SIZE];
53 static pt_entry_t pmap_cache_bits_ept[PAT_INDEX_SIZE];
54 static int ept_protection_codes[PROTECTION_CODES_SIZE];
55 static pt_entry_t pmap_cache_mask_ept;
56
57 static int pmap_pm_flags_ept;
58 static int eptp_bits;
59
60 extern uint64_t vmx_ept_vpid_cap;
61
62 int
63 vmx_ept_init(void)
64 {
65         int prot;
66         /* Chapter 28 VMX SUPPORT FOR ADDRESS TRANSLATION
67          * Intel Manual 3c, page 107
68          */
69         vmx_ept_vpid_cap = rdmsr(IA32_VMX_EPT_VPID_CAP);
70
71         if(!EPT_PWL4(vmx_ept_vpid_cap)||
72             !EPT_MEMORY_TYPE_WB(vmx_ept_vpid_cap)) {
73                 return EINVAL;
74         }
75
76         eptp_bits |= EPTP_CACHE(PAT_WRITE_BACK) |
77             EPTP_PWLEN(EPT_PWLEVELS - 1);
78
79         if (EPT_AD_BITS_SUPPORTED(vmx_ept_vpid_cap)) {
80                 eptp_bits |= EPTP_AD_ENABLE;
81         } else {
82                 pmap_pm_flags_ept = PMAP_EMULATE_AD_BITS;
83         }
84
85         /* Initialize EPT bits
86          * - for PG_V - set READ and EXECUTE to preserve compatibility
87          * - for PG_U and PG_G - set 0 to preserve compatiblity
88          * - for PG_N - set the Uncacheable bit
89          */
90         pmap_bits_ept[TYPE_IDX] = EPT_PMAP;
91         pmap_bits_ept[PG_V_IDX] = EPT_PG_READ | EPT_PG_EXECUTE;
92         pmap_bits_ept[PG_RW_IDX] = EPT_PG_WRITE;
93         pmap_bits_ept[PG_PS_IDX] = EPT_PG_PS;
94         pmap_bits_ept[PG_G_IDX] = 0;
95         pmap_bits_ept[PG_U_IDX] = 0;
96         pmap_bits_ept[PG_A_IDX] = EPT_PG_A;
97         pmap_bits_ept[PG_M_IDX] = EPT_PG_M;
98         pmap_bits_ept[PG_W_IDX] = EPT_PG_AVAIL1;
99         pmap_bits_ept[PG_MANAGED_IDX] = EPT_PG_AVAIL2;
100         pmap_bits_ept[PG_DEVICE_IDX] = EPT_PG_AVAIL3;
101         pmap_bits_ept[PG_N_IDX] = EPT_IGNORE_PAT | EPT_MEM_TYPE_UC;
102
103
104         pmap_cache_mask_ept = EPT_IGNORE_PAT | EPT_MEM_TYPE_MASK;
105
106         pmap_cache_bits_ept[PAT_UNCACHEABLE] = EPT_IGNORE_PAT | EPT_MEM_TYPE_UC;
107         pmap_cache_bits_ept[PAT_WRITE_COMBINING] = EPT_IGNORE_PAT | EPT_MEM_TYPE_WC;
108         pmap_cache_bits_ept[PAT_WRITE_THROUGH] = EPT_IGNORE_PAT | EPT_MEM_TYPE_WT;
109         pmap_cache_bits_ept[PAT_WRITE_PROTECTED] = EPT_IGNORE_PAT | EPT_MEM_TYPE_WP;
110         pmap_cache_bits_ept[PAT_WRITE_BACK] = EPT_IGNORE_PAT | EPT_MEM_TYPE_WB;
111         pmap_cache_bits_ept[PAT_UNCACHED] = EPT_IGNORE_PAT | EPT_MEM_TYPE_UC;
112
113         for (prot = 0; prot < PROTECTION_CODES_SIZE; prot++) {
114                 switch (prot) {
115                 case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE:
116                 case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE:
117                 case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE:
118                 case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE:
119                         ept_protection_codes[prot] = 0;
120                         break;
121                 case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE:
122                 case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE:
123                 case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE:
124                 case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
125                         ept_protection_codes[prot] = pmap_bits_ept[PG_RW_IDX];
126
127                         break;
128                 }
129         }
130
131         return 0;
132 }
133
134 /* Build the VMCS_EPTP pointer
135  * - the ept_address
136  * - the EPTP bits indicating optional features
137  */
138 uint64_t vmx_eptp(uint64_t ept_address)
139 {
140         return (ept_address | eptp_bits);
141 }
142
143 /* Copyin from guest VMM */
144 static int
145 ept_copyin(const void *udaddr, void *kaddr, size_t len)
146 {
147         struct lwbuf *lwb;
148         struct lwbuf lwb_cache;
149         vm_page_t m;
150         register_t gpa;
151         size_t n;
152         int err = 0;
153         struct vmspace *vm = curproc->p_vmspace;
154         struct vmx_thread_info *vti = curthread->td_vmm;
155         register_t guest_cr3 = vti->guest_cr3;
156
157         while (len) {
158                 /* Get the GPA by manually walking the-GUEST page table*/
159                 err = guest_phys_addr(vm, &gpa, guest_cr3, (vm_offset_t)udaddr);
160                 if (err) {
161                         kprintf("%s: could not get guest_phys_addr\n", __func__);
162                         break;
163                 }
164
165                 m = vm_fault_page(&vm->vm_map, trunc_page(gpa),
166                     VM_PROT_READ, VM_FAULT_NORMAL, &err);
167                 if (err) {
168                         kprintf("%s: could not fault in vm map, gpa: %llx\n",
169                             __func__, (unsigned long long) gpa);
170                         break;
171                 }
172
173                 n = PAGE_SIZE - ((vm_offset_t)udaddr & PAGE_MASK);
174                 if (n > len)
175                         n = len;
176
177                 lwb = lwbuf_alloc(m, &lwb_cache);
178                 bcopy((char *)lwbuf_kva(lwb)+((vm_offset_t)udaddr & PAGE_MASK), kaddr, n);
179                 len -= n;
180                 udaddr = (const char *)udaddr + n;
181                 kaddr = (char *)kaddr + n;
182                 lwbuf_free(lwb);
183                 vm_page_unhold(m);
184         }
185         if (err)
186                 err = EFAULT;
187         return (err);
188 }
189
190 /* Copyout from guest VMM */
191 static int
192 ept_copyout(const void *kaddr, void *udaddr, size_t len)
193 {
194         struct lwbuf *lwb;
195         struct lwbuf lwb_cache;
196         vm_page_t m;
197         register_t gpa;
198         size_t n;
199         int err = 0;
200         struct vmspace *vm = curproc->p_vmspace;
201         struct vmx_thread_info *vti = curthread->td_vmm;
202         register_t guest_cr3 = vti->guest_cr3;
203
204         while (len) {
205                 /* Get the GPA by manually walking the-GUEST page table*/
206                 err = guest_phys_addr(vm, &gpa, guest_cr3, (vm_offset_t)udaddr);
207                 if (err) {
208                         kprintf("%s: could not get guest_phys_addr\n", __func__);
209                         break;
210                 }
211
212                 m = vm_fault_page(&vm->vm_map, trunc_page(gpa),
213                     VM_PROT_READ | VM_PROT_WRITE,
214                     VM_FAULT_NORMAL, &err);
215                 if (err) {
216                         kprintf("%s: could not fault in vm map, gpa: %llx\n",
217                             __func__, (unsigned long long) gpa);
218                         break;
219                 }
220
221                 n = PAGE_SIZE - ((vm_offset_t)udaddr & PAGE_MASK);
222                 if (n > len)
223                         n = len;
224
225                 lwb = lwbuf_alloc(m, &lwb_cache);
226                 bcopy(kaddr, (char *)lwbuf_kva(lwb) +
227                              ((vm_offset_t)udaddr & PAGE_MASK), n);
228
229                 len -= n;
230                 udaddr = (char *)udaddr + n;
231                 kaddr = (const char *)kaddr + n;
232                 vm_page_dirty(m);
233 #if 0
234                 /* should not be needed */
235                 cpu_invlpg((char *)lwbuf_kva(lwb) +
236                              ((vm_offset_t)udaddr & PAGE_MASK));
237 #endif
238                 lwbuf_free(lwb);
239                 vm_page_unhold(m);
240         }
241         if (err)
242                 err = EFAULT;
243         return (err);
244 }
245
246 static int
247 ept_copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *res)
248 {
249         int error;
250         size_t n;
251         const char *uptr = udaddr;
252         char *kptr = kaddr;
253
254         if (res)
255                 *res = 0;
256         while (len) {
257                 n = PAGE_SIZE - ((vm_offset_t)uptr & PAGE_MASK);
258                 if (n > 32)
259                         n = 32;
260                 if (n > len)
261                         n = len;
262                 if ((error = ept_copyin(uptr, kptr, n)) != 0)
263                         return(error);
264                 while (n) {
265                         if (res)
266                                 ++*res;
267                         if (*kptr == 0)
268                                 return(0);
269                         ++kptr;
270                         ++uptr;
271                         --n;
272                         --len;
273                 }
274
275         }
276         return(ENAMETOOLONG);
277 }
278
279
280 static int
281 ept_fubyte(const void *base)
282 {
283         unsigned char c = 0;
284
285         if (ept_copyin(base, &c, 1) == 0)
286                 return((int)c);
287         return(-1);
288 }
289
290 static int
291 ept_subyte(void *base, int byte)
292 {
293         unsigned char c = byte;
294
295         if (ept_copyout(&c, base, 1) == 0)
296                 return(0);
297         return(-1);
298 }
299
300 static long
301 ept_fuword(const void *base)
302 {
303         long v;
304
305         if (ept_copyin(base, &v, sizeof(v)) == 0)
306                 return(v);
307         return(-1);
308 }
309
310 static int
311 ept_suword(void *base, long word)
312 {
313         if (ept_copyout(&word, base, sizeof(word)) == 0)
314                 return(0);
315         return(-1);
316 }
317
318 static int
319 ept_suword32(void *base, int word)
320 {
321         if (ept_copyout(&word, base, sizeof(word)) == 0)
322                 return(0);
323         return(-1);
324 }
325
326 void
327 vmx_ept_pmap_pinit(pmap_t pmap)
328 {
329         pmap->pm_flags |= pmap_pm_flags_ept;
330
331         bcopy(pmap_bits_ept, pmap->pmap_bits, sizeof(pmap_bits_ept));
332         bcopy(ept_protection_codes, pmap->protection_codes,
333               sizeof(ept_protection_codes));
334         bcopy(pmap_cache_bits_ept, pmap->pmap_cache_bits,
335               sizeof(pmap_cache_bits_ept));
336         pmap->pmap_cache_mask = pmap_cache_mask_ept;
337         pmap->copyinstr = ept_copyinstr;
338         pmap->copyin = ept_copyin;
339         pmap->copyout = ept_copyout;
340         pmap->fubyte = ept_fubyte;
341         pmap->subyte = ept_subyte;
342         pmap->fuword = ept_fuword;
343         pmap->suword = ept_suword;
344         pmap->suword32 = ept_suword32;
345 }