VMM: EPT checks, init, and basic usage
[akaros.git] / kern / arch / x86 / vmm / intel / ept.c
1 /**
2  * ept.c - Support for Intel's Extended Page Tables
3  *
4  * Authors:
5  *   Adam Belay <abelay@stanford.edu>
6  *
7  * Right now we support EPT by making a sort of 'shadow' copy of the Linux
8  * process page table. In the future, a more invasive architecture port
9  * to VMX x86 could provide better performance by eliminating the need for
10  * two copies of each page table entry, relying instead on only the EPT
11  * format.
12  * 
13  * This code is only a prototype and could benefit from a more comprehensive
14  * review in terms of performance and correctness. Also, the implications
15  * of threaded processes haven't been fully considered.
16  *
17  * Some of the low-level EPT functions are based on KVM.
18  * Original Authors:
19  *   Avi Kivity   <avi@qumranet.com>
20  *   Yaniv Kamay  <yaniv@qumranet.com>
21  */
22
23 #include <kmalloc.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <assert.h>
27 #include <error.h>
28 #include <pmap.h>
29 #include <sys/queue.h>
30 #include <smp.h>
31 #include <kref.h>
32 #include <atomic.h>
33 #include <alarm.h>
34 #include <event.h>
35 #include <umem.h>
36 #include <bitops.h>
37 #include <arch/types.h>
38 #include <syscall.h>
39 #include <monitor.h>
40
41 #include "vmx.h"
42 #include "../vmm.h"
43
44 #include "compat.h"
45 #include "cpufeature.h"
46
47 #define EPT_LEVELS      4       /* 0 through 3 */
48 #define HUGE_PAGE_SIZE  2097152
49 #define PageHuge(x) (0)
50
51 #define VMX_EPT_FAULT_READ      0x01
52 #define VMX_EPT_FAULT_WRITE     0x02
53 #define VMX_EPT_FAULT_INS       0x04
54
55 typedef unsigned long epte_t;
56
57 #define __EPTE_READ     0x01
58 #define __EPTE_WRITE    0x02
59 #define __EPTE_EXEC     0x04
60 #define __EPTE_IPAT     0x40
61 #define __EPTE_SZ       0x80
62 #define __EPTE_TYPE(n)  (((n) & 0x7) << 3)
63
64 enum {
65         EPTE_TYPE_UC = 0, /* uncachable */
66         EPTE_TYPE_WC = 1, /* write combining */
67         EPTE_TYPE_WT = 4, /* write through */
68         EPTE_TYPE_WP = 5, /* write protected */
69         EPTE_TYPE_WB = 6, /* write back */
70 };
71
72 #define __EPTE_NONE     0
73 #define __EPTE_FULL     (__EPTE_READ | __EPTE_WRITE | __EPTE_EXEC)
74
75 #define EPTE_ADDR       (~(PAGE_SIZE - 1))
76 #define EPTE_FLAGS      (PAGE_SIZE - 1)
77
78 static inline uintptr_t epte_addr(epte_t epte)
79 {
80         return (epte & EPTE_ADDR);
81 }
82
83 static inline uintptr_t epte_page_vaddr(epte_t epte)
84 {
85         return (uintptr_t) KADDR(epte_addr(epte));
86 }
87
88 static inline epte_t epte_flags(epte_t epte)
89 {
90         return (epte & EPTE_FLAGS);
91 }
92
93 static inline int epte_present(epte_t epte)
94 {
95         return (epte & __EPTE_FULL) > 0;
96 }
97
98 static inline int epte_big(epte_t epte)
99 {
100         return (epte & __EPTE_SZ) > 0;
101 }
102
103 #define ADDR_TO_IDX(la, n) \
104         ((((unsigned long) (la)) >> (12 + 9 * (n))) & ((1 << 9) - 1))
105
106 /* for now we assume in 'current' */
107 static int
108 ept_lookup_gpa(epte_t *dir, void *gpa, int level, int create, epte_t **epte_out)
109 {
110         int i;
111
112         for (i = EPT_LEVELS - 1; i > level; i--) {
113                 int idx = ADDR_TO_IDX(gpa, i);
114                 printk("%d: gpa %p, idx %p\n", i, gpa, idx);
115                 if (!epte_present(dir[idx])) {
116                         printk("not present\n");
117                         void *page;
118
119                         if (!create)
120                                 return -ENOENT;
121
122                         page = (void *) kpage_zalloc_addr();
123                         if (!page)
124                                 return -ENOMEM;
125                         printk("page %p\n", page);
126                         dir[idx] = epte_addr(PADDR(page)) |
127                                    __EPTE_FULL;
128                         printk("Set %p[%p] to %p\n", dir, idx, dir[idx]);
129                 }
130
131                 if (epte_big(dir[idx])) {
132                         if (i != 1)
133                                 return -EINVAL;
134                         level = i;
135                         break;
136                 }
137
138                 dir = (epte_t *) epte_page_vaddr(dir[idx]);
139                 printk("Dir for next pass: %p\n", dir);
140         }
141
142         *epte_out = &dir[ADDR_TO_IDX(gpa, level)];
143         printk("Final ept is %p\n", *epte_out);
144         return 0;
145 }
146
147 static void free_ept_page(epte_t epte)
148 {
149         // TODO: clean this up. 
150         void *page = KADDR(epte & ~0xfff);
151         //struct page *page = pfn_to_page(epte_addr(epte) >> PAGE_SHIFT);
152
153         kfree(page);
154 }
155
156 static void vmx_free_ept(unsigned long ept_root)
157 {
158         epte_t *pgd = (epte_t *) KADDR(ept_root);
159         int i, j, k, l;
160
161         // TODO: change all instances of 512 to something.
162         for (i = 0; i < 512; i++) {
163                 epte_t *pud = (epte_t *) epte_page_vaddr(pgd[i]);
164                 if (!epte_present(pgd[i]))
165                         continue;
166
167                 for (j = 0; j < 512; j++) {
168                         epte_t *pmd = (epte_t *) epte_page_vaddr(pud[j]);
169                         if (!epte_present(pud[j]))
170                                 continue;
171                         if (epte_flags(pud[j]) & __EPTE_SZ)
172                                 continue;
173
174                         for (k = 0; k < 512; k++) {
175                                 epte_t *pte = (epte_t *) epte_page_vaddr(pmd[k]);
176                                 if (!epte_present(pmd[k]))
177                                         continue;
178                                 if (epte_flags(pmd[k]) & __EPTE_SZ) {
179                                         free_ept_page(pmd[k]);
180                                         continue;
181                                 }
182
183                                 for (l = 0; l < 512; l++) {
184                                         if (!epte_present(pte[l]))
185                                                 continue;
186
187                                         free_ept_page(pte[l]);
188                                 }
189
190                                 kfree(pte);
191                         }
192
193                         kfree(pmd);
194                 }
195
196                 kfree(pud);
197         }
198
199         kfree(pgd);
200 }
201
202 static int ept_clear_epte(epte_t *epte)
203 {
204         if (*epte == __EPTE_NONE)
205                 return 0;
206
207         free_ept_page(*epte);
208         *epte = __EPTE_NONE;
209
210         return 1;
211 }
212
213 /* We're given a guest physical and a host physical. */
214 static int ept_set_epte(epte_t *dir, int make_write, unsigned long gpa, unsigned long hpa)
215 {
216         int ret = -1;
217         epte_t *epte, flags;
218         struct page *page = NULL;
219
220         // We're going to assume locking is done by this point.
221         // TODO: PageHuge
222
223         ret = ept_lookup_gpa(dir, (void *) gpa, PageHuge(page) ? 1 : 0, 1, &epte);
224         if (ret) {
225                 printk("ept: failed to lookup EPT entry\n");
226                 return ret;
227         }
228
229         printk("=====================> epte %p is %p\n", epte, *epte);
230         if (epte_present(*epte) && (epte_big(*epte) || !PageHuge(page))) {
231                 printk("PRESENT? WTF? OK ...\n");
232                 monitor(NULL);
233                 //ept_clear_epte(epte);
234         } else {
235                 flags = __EPTE_READ | __EPTE_EXEC | __EPTE_WRITE |
236                         __EPTE_TYPE(EPTE_TYPE_WB) | __EPTE_IPAT;
237                 if (make_write)
238                         flags |= __EPTE_WRITE;
239                 
240                 /* TODO: fix thishuge page shit.*/
241                 if (PageHuge(page)) {
242                         flags |= __EPTE_SZ;
243                         if (epte_present(*epte) && !epte_big(*epte)){
244                                 panic("free huge page?");
245                                 //free_page(epte_page_vaddr(*epte));
246                         }
247                         /* FIXME: free L0 entries too */
248                         *epte = epte_addr(PADDR(page) & ~((1 << 21) - 1)) |
249                                 flags;
250                 } else {
251                         *epte = epte_addr(hpa) | flags;
252                         printk("Set epte to %p\n", *epte);
253                 }
254         }
255         return 0;
256 }
257
258 // TODO: kill this? 
259 // NOTE: guest physical is 1:1 mapped to host virtual. This is NOT 
260 // like dune at all.
261 int vmx_do_ept_fault(void *dir, unsigned long gpa, unsigned long hpa, int fault_flags)
262 {
263         int ret;
264         int make_write = (fault_flags & VMX_EPT_FAULT_WRITE) ? 1 : 0;
265
266         printk("ept: GPA: 0x%lx, GVA: 0x%lx, flags: %x\n",
267                  gpa, hpa, fault_flags);
268
269         ret = ept_set_epte((epte_t *)dir, make_write, gpa, hpa);
270
271         return ret;
272 }
273
274 /*
275  * ept_fault_pages pre-faults pages in the range start..end
276  */
277 int ept_fault_pages(void *dir, uint32_t start, uint32_t end)
278 {
279         uint64_t i;
280         int ret;
281         for(i = start; i < end; i++) {
282                 uint64_t addr = i << 12;
283                 ret = vmx_do_ept_fault((epte_t*)dir, i, i, VMX_EPT_FAULT_WRITE);
284                 if (ret) {
285                         return ret;
286                 }
287         }
288         return 0;
289 }
290 /**
291  * ept_invalidate_page - removes a page from the EPT
292  * @vcpu: the vcpu
293  * @mm: the process's mm_struct
294  * @addr: the address of the page
295  * 
296  * Returns 1 if the page was removed, 0 otherwise
297  */
298 static int ept_invalidate_page(epte_t *dir, unsigned long addr)
299 {
300         int ret;
301         epte_t *epte;
302         void *gpa = (void *) addr;
303
304         ret = ept_lookup_gpa(dir, (void *) gpa, 0, 0, &epte);
305         if (ret) {
306                 return 0;
307         }
308
309         ret = ept_clear_epte(epte);
310
311         /* TODO: sync individual?
312         if (ret)
313                 vmx_ept_sync_individual_addr(vcpu, (gpa_t) gpa);
314         */
315
316         return ret;
317 }
318
319 /**
320  * ept_check_page - determines if a page is mapped in the ept
321  * @vcpu: the vcpu
322  * @mm: the process's mm_struct
323  * @addr: the address of the page
324  * 
325  * Returns 1 if the page is mapped, 0 otherwise
326  */
327 int ept_check_page(void *dir, unsigned long addr)
328 {
329         int ret;
330         epte_t *epte;
331         void *gpa = (void *) addr;
332
333         ret = ept_lookup_gpa((epte_t *)dir, gpa, 0, 0, &epte);
334
335         return ret;
336 }