VMM: moves various helpers to vmx.h
[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 "cpufeature.h"
45
46 #define EPT_LEVELS      4       /* 0 through 3 */
47 #define HUGE_PAGE_SIZE  2097152
48 #define PageHuge(x) (0)
49
50 #define VMX_EPT_FAULT_READ      0x01
51 #define VMX_EPT_FAULT_WRITE     0x02
52 #define VMX_EPT_FAULT_INS       0x04
53
54 typedef unsigned long epte_t;
55
56 #define __EPTE_READ     0x01
57 #define __EPTE_WRITE    0x02
58 #define __EPTE_EXEC     0x04
59 #define __EPTE_IPAT     0x40
60 #define __EPTE_SZ       0x80
61 #define __EPTE_TYPE(n)  (((n) & 0x7) << 3)
62
63 enum {
64         EPTE_TYPE_UC = 0, /* uncachable */
65         EPTE_TYPE_WC = 1, /* write combining */
66         EPTE_TYPE_WT = 4, /* write through */
67         EPTE_TYPE_WP = 5, /* write protected */
68         EPTE_TYPE_WB = 6, /* write back */
69 };
70
71 #define __EPTE_NONE     0
72 #define __EPTE_FULL     (__EPTE_READ | __EPTE_WRITE | __EPTE_EXEC)
73
74 #define EPTE_ADDR       (~(PAGE_SIZE - 1))
75 #define EPTE_FLAGS      (PAGE_SIZE - 1)
76
77 static inline uintptr_t epte_addr(epte_t epte)
78 {
79         return (epte & EPTE_ADDR);
80 }
81
82 static inline uintptr_t epte_page_vaddr(epte_t epte)
83 {
84         return (uintptr_t) KADDR(epte_addr(epte));
85 }
86
87 static inline epte_t epte_flags(epte_t epte)
88 {
89         return (epte & EPTE_FLAGS);
90 }
91
92 static inline int epte_present(epte_t epte)
93 {
94         return (epte & __EPTE_FULL) > 0;
95 }
96
97 static inline int epte_big(epte_t epte)
98 {
99         return (epte & __EPTE_SZ) > 0;
100 }
101
102 #define ADDR_TO_IDX(la, n) \
103         ((((unsigned long) (la)) >> (12 + 9 * (n))) & ((1 << 9) - 1))
104
105 /* for now we assume in 'current' */
106 static int
107 ept_lookup_gpa(epte_t *dir, void *gpa, int level, int create, epte_t **epte_out)
108 {
109         int i;
110
111         for (i = EPT_LEVELS - 1; i > level; i--) {
112                 int idx = ADDR_TO_IDX(gpa, i);
113                 printk("%d: gpa %p, idx %p\n", i, gpa, idx);
114                 if (!epte_present(dir[idx])) {
115                         printk("not present\n");
116                         void *page;
117
118                         if (!create)
119                                 return -ENOENT;
120
121                         page = (void *) kpage_zalloc_addr();
122                         if (!page)
123                                 return -ENOMEM;
124                         printk("page %p\n", page);
125                         dir[idx] = epte_addr(PADDR(page)) |
126                                    __EPTE_FULL;
127                         printk("Set %p[%p] to %p\n", dir, idx, dir[idx]);
128                 }
129
130                 if (epte_big(dir[idx])) {
131                         if (i != 1)
132                                 return -EINVAL;
133                         level = i;
134                         break;
135                 }
136
137                 dir = (epte_t *) epte_page_vaddr(dir[idx]);
138                 printk("Dir for next pass: %p\n", dir);
139         }
140
141         *epte_out = &dir[ADDR_TO_IDX(gpa, level)];
142         printk("Final ept is %p\n", *epte_out);
143         return 0;
144 }
145
146 static void free_ept_page(epte_t epte)
147 {
148         // TODO: clean this up. 
149         void *page = KADDR(epte & ~0xfff);
150         //struct page *page = pfn_to_page(epte_addr(epte) >> PAGE_SHIFT);
151
152         kfree(page);
153 }
154
155 static void vmx_free_ept(unsigned long ept_root)
156 {
157         epte_t *pgd = (epte_t *) KADDR(ept_root);
158         int i, j, k, l;
159
160         // TODO: change all instances of 512 to something.
161         for (i = 0; i < 512; i++) {
162                 epte_t *pud = (epte_t *) epte_page_vaddr(pgd[i]);
163                 if (!epte_present(pgd[i]))
164                         continue;
165
166                 for (j = 0; j < 512; j++) {
167                         epte_t *pmd = (epte_t *) epte_page_vaddr(pud[j]);
168                         if (!epte_present(pud[j]))
169                                 continue;
170                         if (epte_flags(pud[j]) & __EPTE_SZ)
171                                 continue;
172
173                         for (k = 0; k < 512; k++) {
174                                 epte_t *pte = (epte_t *) epte_page_vaddr(pmd[k]);
175                                 if (!epte_present(pmd[k]))
176                                         continue;
177                                 if (epte_flags(pmd[k]) & __EPTE_SZ) {
178                                         free_ept_page(pmd[k]);
179                                         continue;
180                                 }
181
182                                 for (l = 0; l < 512; l++) {
183                                         if (!epte_present(pte[l]))
184                                                 continue;
185
186                                         free_ept_page(pte[l]);
187                                 }
188
189                                 kfree(pte);
190                         }
191
192                         kfree(pmd);
193                 }
194
195                 kfree(pud);
196         }
197
198         kfree(pgd);
199 }
200
201 static int ept_clear_epte(epte_t *epte)
202 {
203         if (*epte == __EPTE_NONE)
204                 return 0;
205
206         free_ept_page(*epte);
207         *epte = __EPTE_NONE;
208
209         return 1;
210 }
211
212 /* We're given a guest physical and a host physical. */
213 static int ept_set_epte(epte_t *dir, int make_write, unsigned long gpa, unsigned long hpa)
214 {
215         int ret = -1;
216         epte_t *epte, flags;
217         struct page *page = NULL;
218
219         // We're going to assume locking is done by this point.
220         // TODO: PageHuge
221
222         ret = ept_lookup_gpa(dir, (void *) gpa, PageHuge(page) ? 1 : 0, 1, &epte);
223         if (ret) {
224                 printk("ept: failed to lookup EPT entry\n");
225                 return ret;
226         }
227
228         printk("=====================> epte %p is %p\n", epte, *epte);
229         if (epte_present(*epte) && (epte_big(*epte) || !PageHuge(page))) {
230                 printk("PRESENT? WTF? OK ...\n");
231                 monitor(NULL);
232                 //ept_clear_epte(epte);
233         } else {
234                 flags = __EPTE_READ | __EPTE_EXEC | __EPTE_WRITE |
235                         __EPTE_TYPE(EPTE_TYPE_WB) | __EPTE_IPAT;
236                 if (make_write)
237                         flags |= __EPTE_WRITE;
238                 
239                 /* TODO: fix thishuge page shit.*/
240                 if (PageHuge(page)) {
241                         flags |= __EPTE_SZ;
242                         if (epte_present(*epte) && !epte_big(*epte)){
243                                 panic("free huge page?");
244                                 //free_page(epte_page_vaddr(*epte));
245                         }
246                         /* FIXME: free L0 entries too */
247                         *epte = epte_addr(PADDR(page) & ~((1 << 21) - 1)) |
248                                 flags;
249                 } else {
250                         *epte = epte_addr(hpa) | flags;
251                         printk("Set epte to %p\n", *epte);
252                 }
253         }
254         return 0;
255 }
256
257 // TODO: kill this? 
258 // NOTE: guest physical is 1:1 mapped to host virtual. This is NOT 
259 // like dune at all.
260 int vmx_do_ept_fault(void *dir, unsigned long gpa, unsigned long hpa, int fault_flags)
261 {
262         int ret;
263         int make_write = (fault_flags & VMX_EPT_FAULT_WRITE) ? 1 : 0;
264
265         printk("ept: GPA: 0x%lx, GVA: 0x%lx, flags: %x\n",
266                  gpa, hpa, fault_flags);
267
268         ret = ept_set_epte((epte_t *)dir, make_write, gpa, hpa);
269
270         return ret;
271 }
272
273 /*
274  * ept_fault_pages pre-faults pages in the range start..end
275  */
276 int ept_fault_pages(void *dir, uint32_t start, uint32_t end)
277 {
278         uint64_t i;
279         int ret;
280         for(i = start; i < end; i++) {
281                 uint64_t addr = i << 12;
282                 ret = vmx_do_ept_fault((epte_t*)dir, i, i, VMX_EPT_FAULT_WRITE);
283                 if (ret) {
284                         return ret;
285                 }
286         }
287         return 0;
288 }
289 /**
290  * ept_invalidate_page - removes a page from the EPT
291  * @vcpu: the vcpu
292  * @mm: the process's mm_struct
293  * @addr: the address of the page
294  * 
295  * Returns 1 if the page was removed, 0 otherwise
296  */
297 static int ept_invalidate_page(epte_t *dir, unsigned long addr)
298 {
299         int ret;
300         epte_t *epte;
301         void *gpa = (void *) addr;
302
303         ret = ept_lookup_gpa(dir, (void *) gpa, 0, 0, &epte);
304         if (ret) {
305                 return 0;
306         }
307
308         ret = ept_clear_epte(epte);
309
310         /* TODO: sync individual?
311         if (ret)
312                 vmx_ept_sync_individual_addr(vcpu, (gpa_t) gpa);
313         */
314
315         return ret;
316 }
317
318 /**
319  * ept_check_page - determines if a page is mapped in the ept
320  * @vcpu: the vcpu
321  * @mm: the process's mm_struct
322  * @addr: the address of the page
323  * 
324  * Returns 1 if the page is mapped, 0 otherwise
325  */
326 int ept_check_page(void *dir, unsigned long addr)
327 {
328         int ret;
329         epte_t *epte;
330         void *gpa = (void *) addr;
331
332         ret = ept_lookup_gpa((epte_t *)dir, gpa, 0, 0, &epte);
333
334         return ret;
335 }