1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include "qemu/osdep.h"
20#include "panic.h"
21#include "cpu.h"
22#include "x86.h"
23#include "x86_mmu.h"
24#include "vmcs.h"
25#include "vmx.h"
26
27#define pte_present(pte) (pte & PT_PRESENT)
28#define pte_write_access(pte) (pte & PT_WRITE)
29#define pte_user_access(pte) (pte & PT_USER)
30#define pte_exec_access(pte) (!(pte & PT_NX))
31
32#define pte_large_page(pte) (pte & PT_PS)
33#define pte_global_access(pte) (pte & PT_GLOBAL)
34
35#define PAE_CR3_MASK (~0x1fllu)
36#define LEGACY_CR3_MASK (0xffffffff)
37
38#define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12)
39#define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1))
40#define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1))
41
42struct gpt_translation {
43 target_ulong gva;
44 uint64_t gpa;
45 int err_code;
46 uint64_t pte[5];
47 bool write_access;
48 bool user_access;
49 bool exec_access;
50};
51
52static int gpt_top_level(struct CPUState *cpu, bool pae)
53{
54 if (!pae) {
55 return 2;
56 }
57 if (x86_is_long_mode(cpu)) {
58 return 4;
59 }
60
61 return 3;
62}
63
64static inline int gpt_entry(target_ulong addr, int level, bool pae)
65{
66 int level_shift = pae ? 9 : 10;
67 return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
68}
69
70static inline int pte_size(bool pae)
71{
72 return pae ? 8 : 4;
73}
74
75
76static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
77 int level, bool pae)
78{
79 int index;
80 uint64_t pte = 0;
81 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
82 uint64_t gpa = pt->pte[level] & page_mask;
83
84 if (level == 3 && !x86_is_long_mode(cpu)) {
85 gpa = pt->pte[level];
86 }
87
88 index = gpt_entry(pt->gva, level, pae);
89 address_space_read(&address_space_memory, gpa + index * pte_size(pae),
90 MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
91
92 pt->pte[level - 1] = pte;
93
94 return true;
95}
96
97
98static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
99 int level, bool *is_large, bool pae)
100{
101 uint64_t pte = pt->pte[level];
102
103 if (pt->write_access) {
104 pt->err_code |= MMU_PAGE_WT;
105 }
106 if (pt->user_access) {
107 pt->err_code |= MMU_PAGE_US;
108 }
109 if (pt->exec_access) {
110 pt->err_code |= MMU_PAGE_NX;
111 }
112
113 if (!pte_present(pte)) {
114 return false;
115 }
116
117 if (pae && !x86_is_long_mode(cpu) && 2 == level) {
118 goto exit;
119 }
120
121 if (1 == level && pte_large_page(pte)) {
122 pt->err_code |= MMU_PAGE_PT;
123 *is_large = true;
124 }
125 if (!level) {
126 pt->err_code |= MMU_PAGE_PT;
127 }
128
129 uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0);
130
131 if (cr0 & CR0_WP_MASK) {
132 if (pt->write_access && !pte_write_access(pte)) {
133 return false;
134 }
135 }
136
137 if (pt->user_access && !pte_user_access(pte)) {
138 return false;
139 }
140
141 if (pae && pt->exec_access && !pte_exec_access(pte)) {
142 return false;
143 }
144
145exit:
146
147 return true;
148}
149
150static inline uint64_t pse_pte_to_page(uint64_t pte)
151{
152 return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
153}
154
155static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae)
156{
157 VM_PANIC_ON(!pte_large_page(pt->pte[1]))
158
159 if (pae) {
160 return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
161 }
162
163
164 return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
165}
166
167
168
169static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code,
170 struct gpt_translation *pt, bool pae)
171{
172 int top_level, level;
173 bool is_large = false;
174 target_ulong cr3 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR3);
175 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
176
177 memset(pt, 0, sizeof(*pt));
178 top_level = gpt_top_level(cpu, pae);
179
180 pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
181 pt->gva = addr;
182 pt->user_access = (err_code & MMU_PAGE_US);
183 pt->write_access = (err_code & MMU_PAGE_WT);
184 pt->exec_access = (err_code & MMU_PAGE_NX);
185
186 for (level = top_level; level > 0; level--) {
187 get_pt_entry(cpu, pt, level, pae);
188
189 if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) {
190 return false;
191 }
192
193 if (is_large) {
194 break;
195 }
196 }
197
198 if (!is_large) {
199 pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
200 } else {
201 pt->gpa = large_page_gpa(pt, pae);
202 }
203
204 return true;
205}
206
207
208bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa)
209{
210 bool res;
211 struct gpt_translation pt;
212 int err_code = 0;
213
214 if (!x86_is_paging_mode(cpu)) {
215 *gpa = gva;
216 return true;
217 }
218
219 res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
220 if (res) {
221 *gpa = pt.gpa;
222 return true;
223 }
224
225 return false;
226}
227
228void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes)
229{
230 uint64_t gpa;
231
232 while (bytes > 0) {
233
234 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
235
236 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
237 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
238 } else {
239 address_space_write(&address_space_memory, gpa,
240 MEMTXATTRS_UNSPECIFIED, data, copy);
241 }
242
243 bytes -= copy;
244 gva += copy;
245 data += copy;
246 }
247}
248
249void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes)
250{
251 uint64_t gpa;
252
253 while (bytes > 0) {
254
255 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
256
257 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
258 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
259 }
260 address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
261 data, copy);
262
263 bytes -= copy;
264 gva += copy;
265 data += copy;
266 }
267}
268