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