Unmap pages mapped during a failed fill_vmr()
[akaros.git] / kern / arch / x86 / vmm / ept.h
1 /* Copyright (c) 2015 Google Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * 64 bit EPT helpers */
6
7 #pragma once
8
9 #include <arch/vmm/intel/vmx.h> /* for sync/flush helpers */
10 #include <smp.h>                                /* for current */
11
12 /* Some EPTE PTE flags are only valid for the last PTEs in a walk */
13 #define EPTE_R                  (1ULL << 0)     /* Readable */
14 #define EPTE_W                  (1ULL << 1)     /* Writeable */
15 #define EPTE_X                  (1ULL << 2)     /* Executable */
16 #define EPTE_MEM_BITS           (7ULL << 3)     /* Memory type specifier */
17 #define EPTE_IGN_PAT            (1ULL << 6)     /* Ignore PAT */
18 #define EPTE_PS                 (1ULL << 7)     /* Jumbo Page Size */
19 #define EPTE_A                  (1ULL << 8)     /* Accessed */
20 #define EPTE_D                  (1ULL << 9)     /* Dirty */
21 #define EPTE_SUP_VE             (1ULL << 63)    /* Suppress virt exceptions */
22 #define EPTE_P (EPTE_R | EPTE_W | EPTE_X)
23
24 /* Types available for the EPTE_MEM_TYPE */
25 #define EPT_MEM_TYPE_UC         0
26 #define EPT_MEM_TYPE_WC         1
27 #define EPT_MEM_TYPE_WT         4
28 #define EPT_MEM_TYPE_WP         5
29 #define EPT_MEM_TYPE_WB         6
30 /* Helper to align the type to its location in the PTE */
31 #define EPT_MEM_TYPE(type) ((type) << 3)
32
33 /* Some machines don't support A and D EPTE bits.  We'll |= 1 in those cases. */
34 extern int x86_ept_pte_fix_ups;
35
36 static inline epte_t *kpte_to_epte(kpte_t *kpte)
37 {
38         return (epte_t*)(((uintptr_t)kpte) + PGSIZE);
39 }
40
41 static inline bool epte_is_present(epte_t *epte)
42 {
43         /* Actually, certain combos, like W but not R could be
44          * misconfigurations. */
45         return *epte & EPTE_P ? TRUE : FALSE;
46 }
47
48 static inline bool epte_is_unmapped(epte_t *epte)
49 {
50         return *epte == 0;
51 }
52
53 static inline bool epte_is_mapped(epte_t *epte)
54 {
55         return *epte != 0;
56 }
57
58 static inline bool epte_is_paged_out(epte_t *epte)
59 {
60         return *epte != 0;
61 }
62
63 /* Some Intel machines don't support A or D.  In these cases, we must assume
64  * the pages have been accessed or dirtied... */
65 static inline bool epte_is_dirty(epte_t *epte)
66 {
67         return (*epte | x86_ept_pte_fix_ups) & EPTE_D ? TRUE : FALSE;
68 }
69
70 static inline bool epte_is_accessed(epte_t *epte)
71 {
72         return (*epte | x86_ept_pte_fix_ups) & EPTE_A ? TRUE : FALSE;
73 }
74
75 static inline bool epte_is_jumbo(epte_t *epte)
76 {
77         return *epte & EPTE_PS ? TRUE : FALSE;
78 }
79
80 static inline physaddr_t epte_get_paddr(epte_t *epte)
81 {
82         /* 63:52 are ignored/flags.  51:12 are the addr.  Technically 51:N must
83          * be 0, where N is the physical addr width */
84         return *epte & 0x000ffffffffff000;
85 }
86
87 static inline int __pte_to_epte_perm(int perm)
88 {
89         switch (perm) {
90         /* Since we keep the EPT in lockstep with the KPT, we might get
91          * some mapping requests for the kernel (e.g. vmap_pmem).  */
92         case PTE_KERN_RW:
93         case PTE_KERN_RO:
94         case PTE_NONE:
95                 return 0;
96         case PTE_USER_RW:
97                 return EPTE_W | EPTE_R | EPTE_X;
98         case PTE_USER_RO:
99                 return EPTE_R | EPTE_X;
100         default:
101                 panic("Bad PTE type 0x%x\n", perm);
102         }
103 }
104
105 static inline void epte_write(epte_t *epte, physaddr_t pa, int settings)
106 {
107         /* Could put in a check against the max physaddr len */
108         epte_t temp = pa;
109         temp |= __pte_to_epte_perm(settings & PTE_PERM);
110         temp |= settings & PTE_PS ? EPTE_PS : 0;
111         /* All memory is WB by default, but the guest can override that with
112          * their PAT on the first page walk (guest KPT/cr3) */
113         temp |= EPT_MEM_TYPE(EPT_MEM_TYPE_WB);
114         *epte = temp;
115 }
116
117 static inline void epte_clear_present(epte_t *epte)
118 {
119         *epte &= ~EPTE_P;
120 }
121
122 static inline void epte_clear_dirty(epte_t *epte)
123 {
124         *epte &= ~EPTE_D;
125 }
126
127 static inline void epte_clear(epte_t *epte)
128 {
129         *epte = 0;
130 }
131
132 static inline bool epte_has_perm_ur(epte_t *epte)
133 {
134         return (*epte & (EPTE_R | EPTE_X)) == (EPTE_R | EPTE_X);
135 }
136
137 static inline bool epte_has_perm_urw(epte_t *epte)
138 {
139         return (*epte & (EPTE_R | EPTE_W | EPTE_X)) == (EPTE_R | EPTE_W | EPTE_X);
140 }
141
142 static inline int epte_get_settings(epte_t *epte)
143 {
144         int settings = 0;
145         if (*epte & EPTE_P) {
146                 /* We want to know User and Writable, in the 'PTE' sense.  All
147                  * present epte entries are User PTEs. */
148                 settings |= PTE_P | PTE_U;
149                 settings |= *epte & EPTE_W ? PTE_W : 0;
150         }
151         settings |= *epte & EPTE_PS ? PTE_PS : 0;
152         settings |= *epte & EPTE_A ? PTE_A : 0;
153         settings |= *epte & EPTE_D ? PTE_D : 0;
154         return settings;
155 }
156
157 /* Again, we're replacing the old perms with U and/or W.  Any non-U are ignored,
158  * as with epte_write.  */
159 static inline void epte_replace_perm(epte_t *epte, int perm)
160 {
161         *epte = (*epte & ~EPTE_P) | __pte_to_epte_perm(perm & PTE_PERM);
162 }
163
164 /* These ops might be the same for AMD as Intel; in which case we can move the
165  * body of these ept_sync_* funcs into here */
166 static inline void ept_inval_addr(unsigned long addr)
167 {
168         if (current && current->vmm.vmmcp)
169                 ept_sync_individual_addr(current->env_pgdir.eptp, addr);
170 }
171
172 static inline void ept_inval_context(void)
173 {
174         if (current && current->vmm.vmmcp)
175                 ept_sync_context(current->env_pgdir.eptp);
176 }
177
178 static inline void ept_inval_global(void)
179 {
180         ept_sync_global();
181 }