master
c 361 lines 10 KB
Raw
1 /*
2 * Copyright (C) 2016 Veertu Inc,
3 * Copyright (C) 2017 Google Inc,
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "qemu/osdep.h"
20 #include "panic.h"
21 #include "cpu.h"
22 #include "system/address-spaces.h"
23 #include "system/memory.h"
24 #include "emulate/x86.h"
25 #include "emulate/x86_emu.h"
26 #include "emulate/x86_mmu.h"
27
28 #define pte_present(pte) (pte & PT_PRESENT)
29 #define pte_write_access(pte) (pte & PT_WRITE)
30 #define pte_user_access(pte) (pte & PT_USER)
31 #define pte_exec_access(pte) (!(pte & PT_NX))
32
33 #define pte_large_page(pte) (pte & PT_PS)
34 #define pte_global_access(pte) (pte & PT_GLOBAL)
35
36 #define mmu_validate_write(flags) (flags & MMU_TRANSLATE_VALIDATE_WRITE)
37 #define mmu_validate_execute(flags) (flags & MMU_TRANSLATE_VALIDATE_EXECUTE)
38 #define mmu_priv_checks_exempt(flags) (flags & MMU_TRANSLATE_PRIV_CHECKS_EXEMPT)
39
40
41 #define PAE_CR3_MASK (~0x1fllu)
42 #define LEGACY_CR3_MASK (0xffffffff)
43
44 #define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12)
45 #define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1))
46 #define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1))
47 #define PAE_PTE_SUPER_PAGE_MASK ((-1llu << (30)) & ((1llu << 52) - 1))
48
49 static bool is_user(CPUState *cpu)
50 {
51 if (emul_ops->is_user_mode) {
52 return emul_ops->is_user_mode(cpu);
53 }
54 return false;
55 }
56
57
58 struct gpt_translation {
59 target_ulong gva;
60 uint64_t gpa;
61 uint64_t pte[6];
62 };
63
64 static int gpt_top_level(CPUState *cpu, bool pae)
65 {
66 if (!pae) {
67 return 2;
68 }
69 if (x86_is_long_mode(cpu)) {
70 if (x86_is_la57(cpu)) {
71 return 5;
72 }
73 return 4;
74 }
75
76 return 3;
77 }
78
79 static inline int gpt_entry(target_ulong addr, int level, bool pae)
80 {
81 int level_shift = pae ? 9 : 10;
82 return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
83 }
84
85 static inline int pte_size(bool pae)
86 {
87 return pae ? 8 : 4;
88 }
89
90
91 static bool get_pt_entry(CPUState *cpu, struct gpt_translation *pt,
92 int level, bool pae)
93 {
94 int index;
95 uint64_t pte = 0;
96 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
97 uint64_t gpa = pt->pte[level] & page_mask;
98
99 if (level == 3 && !x86_is_long_mode(cpu)) {
100 gpa = pt->pte[level];
101 }
102
103 index = gpt_entry(pt->gva, level, pae);
104 address_space_read(&address_space_memory, gpa + index * pte_size(pae),
105 MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
106
107 pt->pte[level - 1] = pte;
108
109 return true;
110 }
111
112 /* test page table entry */
113 static MMUTranslateResult test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
114 int level, int *largeness, bool pae, MMUTranslateFlags flags)
115 {
116 uint64_t pte = pt->pte[level];
117
118 if (!pte_present(pte)) {
119 return MMU_TRANSLATE_PAGE_NOT_MAPPED;
120 }
121
122 if (pae && !x86_is_long_mode(cpu) && 2 == level) {
123 goto exit;
124 }
125
126 if (level && pte_large_page(pte)) {
127 *largeness = level;
128 }
129
130 uint32_t cr0 = x86_read_cr(cpu, 0);
131 /* check protection */
132 if (cr0 & CR0_WP_MASK) {
133 if (mmu_validate_write(flags) && !pte_write_access(pte)) {
134 return MMU_TRANSLATE_PRIV_VIOLATION;
135 }
136 }
137
138 if (!mmu_priv_checks_exempt(flags)) {
139 if (is_user(cpu) && !pte_user_access(pte)) {
140 return MMU_TRANSLATE_PRIV_VIOLATION;
141 }
142 }
143
144 if (pae && mmu_validate_execute(flags) && !pte_exec_access(pte)) {
145 return MMU_TRANSLATE_PRIV_VIOLATION;
146 }
147
148 exit:
149 /* TODO: check reserved bits */
150 return MMU_TRANSLATE_SUCCESS;
151 }
152
153 static inline uint64_t pse_pte_to_page(uint64_t pte)
154 {
155 return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
156 }
157
158 static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae,
159 int largeness)
160 {
161 VM_PANIC_ON(!pte_large_page(pt->pte[largeness]))
162
163 /* 1Gib large page */
164 if (pae && largeness == 2) {
165 return (pt->pte[2] & PAE_PTE_SUPER_PAGE_MASK) | (pt->gva & 0x3fffffff);
166 }
167
168 VM_PANIC_ON(largeness != 1)
169
170 /* 2Mb large page */
171 if (pae) {
172 return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
173 }
174
175 /* 4Mb large page */
176 return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
177 }
178
179
180
181 static MMUTranslateResult walk_gpt(CPUState *cpu, target_ulong addr, MMUTranslateFlags flags,
182 struct gpt_translation *pt, bool pae)
183 {
184 int top_level, level;
185 int largeness = 0;
186 target_ulong cr3 = x86_read_cr(cpu, 3);
187 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
188 MMUTranslateResult res;
189
190 memset(pt, 0, sizeof(*pt));
191 top_level = gpt_top_level(cpu, pae);
192
193 pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
194 pt->gva = addr;
195
196 for (level = top_level; level > 0; level--) {
197 get_pt_entry(cpu, pt, level, pae);
198 res = test_pt_entry(cpu, pt, level - 1, &largeness, pae, flags);
199
200 if (res) {
201 return res;
202 }
203
204 if (largeness) {
205 break;
206 }
207 }
208
209 if (!largeness) {
210 pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
211 } else {
212 pt->gpa = large_page_gpa(pt, pae, largeness);
213 }
214
215 return res;
216 }
217
218
219 MMUTranslateResult mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa, MMUTranslateFlags flags)
220 {
221 if (emul_ops->mmu_gva_to_gpa) {
222 return emul_ops->mmu_gva_to_gpa(cpu, gva, gpa, flags);
223 }
224
225 bool res;
226 struct gpt_translation pt;
227
228 if (!x86_is_paging_mode(cpu)) {
229 *gpa = gva;
230 return MMU_TRANSLATE_SUCCESS;
231 }
232
233 res = walk_gpt(cpu, gva, flags, &pt, x86_is_pae_enabled(cpu));
234 if (res == MMU_TRANSLATE_SUCCESS) {
235 *gpa = pt.gpa;
236 }
237
238 return res;
239 }
240
241 static int translate_res_to_error_code(MMUTranslateResult res, bool is_write, bool is_user)
242 {
243 int error_code = 0;
244 if (is_user) {
245 error_code |= PG_ERROR_U_MASK;
246 }
247 if (!(res & MMU_TRANSLATE_PAGE_NOT_MAPPED)) {
248 error_code |= PG_ERROR_P_MASK;
249 }
250 if (is_write) {
251 error_code |= PG_ERROR_W_MASK;
252 }
253 if (res & MMU_TRANSLATE_INVALID_PT_FLAGS) {
254 error_code |= PG_ERROR_RSVD_MASK;
255 }
256 return error_code;
257 }
258
259 static MMUTranslateResult x86_write_mem_ex(CPUState *cpu, void *data, target_ulong gva, int bytes, bool priv_check_exempt)
260 {
261 X86CPU *x86_cpu = X86_CPU(cpu);
262 CPUX86State *env = &x86_cpu->env;
263
264 MMUTranslateResult translate_res = MMU_TRANSLATE_SUCCESS;
265 MMUTranslateFlags translate_flags = MMU_TRANSLATE_VALIDATE_WRITE;
266 MemTxResult mem_tx_res;
267 uint64_t gpa;
268
269 if (priv_check_exempt) {
270 translate_flags |= MMU_TRANSLATE_PRIV_CHECKS_EXEMPT;
271 }
272
273 while (bytes > 0) {
274 /* copy page */
275 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
276
277 translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, translate_flags);
278 if (translate_res) {
279 int error_code = translate_res_to_error_code(translate_res, true, is_user(cpu));
280 env->exception_has_payload = 1;
281 env->exception_payload = gva;
282 x86_emul_raise_exception(env, EXCP0E_PAGE, error_code);
283 return translate_res;
284 }
285
286 mem_tx_res = address_space_write(&address_space_memory, gpa,
287 MEMTXATTRS_UNSPECIFIED, data, copy);
288
289 if (mem_tx_res == MEMTX_DECODE_ERROR) {
290 return MMU_TRANSLATE_GPA_UNMAPPED;
291 } else if (mem_tx_res == MEMTX_ACCESS_ERROR) {
292 return MMU_TRANSLATE_GPA_NO_WRITE_ACCESS;
293 }
294
295 bytes -= copy;
296 gva += copy;
297 data += copy;
298 }
299 return translate_res;
300 }
301
302 MMUTranslateResult x86_write_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
303 {
304 return x86_write_mem_ex(cpu, data, gva, bytes, false);
305 }
306
307 MMUTranslateResult x86_write_mem_priv(CPUState *cpu, void *data, target_ulong gva, int bytes)
308 {
309 return x86_write_mem_ex(cpu, data, gva, bytes, true);
310 }
311
312 static MMUTranslateResult x86_read_mem_ex(CPUState *cpu, void *data, target_ulong gva, int bytes, bool priv_check_exempt)
313 {
314 X86CPU *x86_cpu = X86_CPU(cpu);
315 CPUX86State *env = &x86_cpu->env;
316
317 MMUTranslateResult translate_res = MMU_TRANSLATE_SUCCESS;
318 MMUTranslateFlags translate_flags = 0;
319 MemTxResult mem_tx_res;
320 uint64_t gpa;
321
322 if (priv_check_exempt) {
323 translate_flags |= MMU_TRANSLATE_PRIV_CHECKS_EXEMPT;
324 }
325
326 while (bytes > 0) {
327 /* copy page */
328 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
329
330 translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, translate_flags);
331 if (translate_res) {
332 int error_code = translate_res_to_error_code(translate_res, false, is_user(cpu));
333 env->cr[2] = gva;
334 x86_emul_raise_exception(env, EXCP0E_PAGE, error_code);
335 return translate_res;
336 }
337 mem_tx_res = address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
338 data, copy);
339
340 if (mem_tx_res == MEMTX_DECODE_ERROR) {
341 return MMU_TRANSLATE_GPA_UNMAPPED;
342 } else if (mem_tx_res == MEMTX_ACCESS_ERROR) {
343 return MMU_TRANSLATE_GPA_NO_READ_ACCESS;
344 }
345
346 bytes -= copy;
347 gva += copy;
348 data += copy;
349 }
350 return translate_res;
351 }
352
353 MMUTranslateResult x86_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
354 {
355 return x86_read_mem_ex(cpu, data, gva, bytes, false);
356 }
357
358 MMUTranslateResult x86_read_mem_priv(CPUState *cpu, void *data, target_ulong gva, int bytes)
359 {
360 return x86_read_mem_ex(cpu, data, gva, bytes, true);
361 }