21
#include "cpu.h"
22
#include "system/address-spaces.h"
23
#include "system/memory.h"
24
+#include "qemu/error-report.h"
25
#include "emulate/x86.h"
26
+#include "emulate/x86_emu.h"
27
#include "emulate/x86_mmu.h"
28
29
#define pte_present(pte) (pte & PT_PRESENT)
34
#define pte_large_page(pte) (pte & PT_PS)
35
#define pte_global_access(pte) (pte & PT_GLOBAL)
36
37
+#define mmu_validate_write(flags) (flags & MMU_TRANSLATE_VALIDATE_WRITE)
38
+#define mmu_validate_execute(flags) (flags & MMU_TRANSLATE_VALIDATE_EXECUTE)
39
+#define mmu_priv_checks_exempt(flags) (flags & MMU_TRANSLATE_PRIV_CHECKS_EXEMPT)
40
+
41
+
42
#define PAE_CR3_MASK (~0x1fllu)
43
#define LEGACY_CR3_MASK (0xffffffff)
44
47
#define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1))
48
#define PAE_PTE_SUPER_PAGE_MASK ((-1llu << (30)) & ((1llu << 52) - 1))
49
50
+static bool is_user(CPUState *cpu)
51
+{
52
+ return false;
53
+}
54
+
55
+
56
struct gpt_translation {
57
target_ulong gva;
58
uint64_t gpa;
46
- int err_code;
59
uint64_t pte[5];
48
- bool write_access;
49
- bool user_access;
50
- bool exec_access;
60
};
61
62
static int gpt_top_level(CPUState *cpu, bool pae)
108
}
109
110
/* test page table entry */
102
-static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
103
- int level, int *largeness, bool pae)
111
+static MMUTranslateResult test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
112
+ int level, int *largeness, bool pae, MMUTranslateFlags flags)
113
{
114
X86CPU *x86_cpu = X86_CPU(cpu);
115
CPUX86State *env = &x86_cpu->env;
116
uint64_t pte = pt->pte[level];
117
109
- if (pt->write_access) {
110
- pt->err_code |= MMU_PAGE_WT;
111
- }
112
- if (pt->user_access) {
113
- pt->err_code |= MMU_PAGE_US;
114
- }
115
- if (pt->exec_access) {
116
- pt->err_code |= MMU_PAGE_NX;
117
- }
118
-
118
if (!pte_present(pte)) {
120
- return false;
119
+ return MMU_TRANSLATE_PAGE_NOT_MAPPED;
120
}
121
122
if (pae && !x86_is_long_mode(cpu) && 2 == level) {
124
}
125
126
if (level && pte_large_page(pte)) {
128
- pt->err_code |= MMU_PAGE_PT;
127
*largeness = level;
128
}
131
- if (!level) {
132
- pt->err_code |= MMU_PAGE_PT;
133
- }
129
130
uint32_t cr0 = env->cr[0];
131
/* check protection */
132
if (cr0 & CR0_WP_MASK) {
138
- if (pt->write_access && !pte_write_access(pte)) {
139
- return false;
133
+ if (mmu_validate_write(flags) && !pte_write_access(pte)) {
134
+ return MMU_TRANSLATE_PRIV_VIOLATION;
135
}
136
}
137
143
- if (pt->user_access && !pte_user_access(pte)) {
144
- return false;
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
147
- if (pae && pt->exec_access && !pte_exec_access(pte)) {
148
- return false;
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 */
153
- return true;
150
+ return MMU_TRANSLATE_SUCCESS;
151
}
152
153
static inline uint64_t pse_pte_to_page(uint64_t pte)
178
179
180
184
-static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code,
181
+static MMUTranslateResult walk_gpt(CPUState *cpu, target_ulong addr, MMUTranslateFlags flags,
182
struct gpt_translation *pt, bool pae)
183
{
184
X86CPU *x86_cpu = X86_CPU(cpu);
187
int largeness = 0;
188
target_ulong cr3 = env->cr[3];
189
uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
190
+ MMUTranslateResult res;
191
192
memset(pt, 0, sizeof(*pt));
193
top_level = gpt_top_level(cpu, pae);
194
195
pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
196
pt->gva = addr;
199
- pt->user_access = (err_code & MMU_PAGE_US);
200
- pt->write_access = (err_code & MMU_PAGE_WT);
201
- pt->exec_access = (err_code & MMU_PAGE_NX);
197
198
for (level = top_level; level > 0; level--) {
199
get_pt_entry(cpu, pt, level, pae);
200
+ res = test_pt_entry(cpu, pt, level - 1, &largeness, pae, flags);
201
206
- if (!test_pt_entry(cpu, pt, level - 1, &largeness, pae)) {
207
- return false;
202
+ if (res) {
203
+ return res;
204
}
205
206
if (largeness) {
214
pt->gpa = large_page_gpa(pt, pae, largeness);
215
}
216
221
- return true;
217
+ return res;
218
}
219
220
225
-bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa)
221
+MMUTranslateResult mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa, MMUTranslateFlags flags)
222
{
223
+ if (emul_ops->mmu_gva_to_gpa) {
224
+ return emul_ops->mmu_gva_to_gpa(cpu, gva, gpa, flags);
225
+ }
226
+
227
bool res;
228
struct gpt_translation pt;
229
- int err_code = 0;
229
230
if (!x86_is_paging_mode(cpu)) {
231
*gpa = gva;
233
- return true;
232
+ return MMU_TRANSLATE_SUCCESS;
233
}
234
236
- res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
237
- if (res) {
235
+ res = walk_gpt(cpu, gva, flags, &pt, x86_is_pae_enabled(cpu));
236
+ if (res == MMU_TRANSLATE_SUCCESS) {
237
*gpa = pt.gpa;
239
- return true;
238
}
239
242
- return false;
240
+ return res;
241
}
242
245
-void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes)
243
+static MMUTranslateResult x86_write_mem_ex(CPUState *cpu, void *data, target_ulong gva, int bytes, bool priv_check_exempt)
244
{
245
+ MMUTranslateResult translate_res = MMU_TRANSLATE_SUCCESS;
246
+ MemTxResult mem_tx_res;
247
uint64_t gpa;
248
249
while (bytes > 0) {
250
/* copy page */
251
int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
252
253
- if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
254
- VM_PANIC_EX("%s: mmu_gva_to_gpa " TARGET_FMT_lx " failed\n",
255
- __func__, gva);
256
- } else {
257
- address_space_write(&address_space_memory, gpa,
258
- MEMTXATTRS_UNSPECIFIED, data, copy);
253
+ translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, MMU_TRANSLATE_VALIDATE_WRITE);
254
+ if (translate_res) {
255
+ return translate_res;
256
+ }
257
+
258
+ mem_tx_res = address_space_write(&address_space_memory, gpa,
259
+ MEMTXATTRS_UNSPECIFIED, data, copy);
260
+
261
+ if (mem_tx_res == MEMTX_DECODE_ERROR) {
262
+ warn_report("write to unmapped mmio region gpa=0x%" PRIx64 " size=%i", gpa, bytes);
263
+ return MMU_TRANSLATE_GPA_UNMAPPED;
264
+ } else if (mem_tx_res == MEMTX_ACCESS_ERROR) {
265
+ return MMU_TRANSLATE_GPA_NO_WRITE_ACCESS;
266
}
267
268
bytes -= copy;
269
gva += copy;
270
data += copy;
271
}
272
+ return translate_res;
273
+}
274
+
275
+MMUTranslateResult x86_write_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
276
+{
277
+ return x86_write_mem_ex(cpu, data, gva, bytes, false);
278
+}
279
+
280
+MMUTranslateResult x86_write_mem_priv(CPUState *cpu, void *data, target_ulong gva, int bytes)
281
+{
282
+ return x86_write_mem_ex(cpu, data, gva, bytes, true);
283
}
284
267
-void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
285
+static MMUTranslateResult x86_read_mem_ex(CPUState *cpu, void *data, target_ulong gva, int bytes, bool priv_check_exempt)
286
{
287
+ MMUTranslateResult translate_res = MMU_TRANSLATE_SUCCESS;
288
+ MemTxResult mem_tx_res;
289
uint64_t gpa;
290
291
while (bytes > 0) {
292
/* copy page */
293
int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
294
275
- if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
276
- VM_PANIC_EX("%s: mmu_gva_to_gpa " TARGET_FMT_lx " failed\n",
277
- __func__, gva);
295
+ translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, 0);
296
+ if (translate_res) {
297
+ return translate_res;
298
}
279
- address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
299
+ mem_tx_res = address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
300
data, copy);
301
302
+ if (mem_tx_res == MEMTX_DECODE_ERROR) {
303
+ warn_report("read from unmapped mmio region gpa=0x%" PRIx64 " size=%i", gpa, bytes);
304
+ return MMU_TRANSLATE_GPA_UNMAPPED;
305
+ } else if (mem_tx_res == MEMTX_ACCESS_ERROR) {
306
+ return MMU_TRANSLATE_GPA_NO_READ_ACCESS;
307
+ }
308
+
309
bytes -= copy;
310
gva += copy;
311
data += copy;
312
}
313
+ return translate_res;
314
+}
315
+
316
+MMUTranslateResult x86_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
317
+{
318
+ return x86_read_mem_ex(cpu, data, gva, bytes, false);
319
+}
320
+
321
+MMUTranslateResult x86_read_mem_priv(CPUState *cpu, void *data, target_ulong gva, int bytes)
322
+{
323
+ return x86_read_mem_ex(cpu, data, gva, bytes, true);
324
}