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 "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 #define PAE_PTE_SUPER_PAGE_MASK ((-1llu << (30)) & ((1llu << 52) - 1))
42
43 struct 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
gpt_top_level(CPUState * cpu,bool pae)53 static int gpt_top_level(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
gpt_entry(target_ulong addr,int level,bool pae)65 static 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
pte_size(bool pae)71 static inline int pte_size(bool pae)
72 {
73 return pae ? 8 : 4;
74 }
75
76
get_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,bool pae)77 static bool get_pt_entry(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 /* test page table entry */
test_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,int * largeness,bool pae)99 static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
100 int level, int *largeness, 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 (level && pte_large_page(pte)) {
123 pt->err_code |= MMU_PAGE_PT;
124 *largeness = level;
125 }
126 if (!level) {
127 pt->err_code |= MMU_PAGE_PT;
128 }
129
130 uint32_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0);
131 /* check protection */
132 if (cr0 & CR0_WP_MASK) {
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
146 exit:
147 /* TODO: check reserved bits */
148 return true;
149 }
150
pse_pte_to_page(uint64_t pte)151 static inline uint64_t pse_pte_to_page(uint64_t pte)
152 {
153 return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
154 }
155
large_page_gpa(struct gpt_translation * pt,bool pae,int largeness)156 static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae,
157 int largeness)
158 {
159 VM_PANIC_ON(!pte_large_page(pt->pte[largeness]))
160
161 /* 1Gib large page */
162 if (pae && largeness == 2) {
163 return (pt->pte[2] & PAE_PTE_SUPER_PAGE_MASK) | (pt->gva & 0x3fffffff);
164 }
165
166 VM_PANIC_ON(largeness != 1)
167
168 /* 2Mb large page */
169 if (pae) {
170 return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
171 }
172
173 /* 4Mb large page */
174 return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
175 }
176
177
178
walk_gpt(CPUState * cpu,target_ulong addr,int err_code,struct gpt_translation * pt,bool pae)179 static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code,
180 struct gpt_translation *pt, bool pae)
181 {
182 int top_level, level;
183 int largeness = 0;
184 target_ulong cr3 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR3);
185 uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
186
187 memset(pt, 0, sizeof(*pt));
188 top_level = gpt_top_level(cpu, pae);
189
190 pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
191 pt->gva = addr;
192 pt->user_access = (err_code & MMU_PAGE_US);
193 pt->write_access = (err_code & MMU_PAGE_WT);
194 pt->exec_access = (err_code & MMU_PAGE_NX);
195
196 for (level = top_level; level > 0; level--) {
197 get_pt_entry(cpu, pt, level, pae);
198
199 if (!test_pt_entry(cpu, pt, level - 1, &largeness, pae)) {
200 return false;
201 }
202
203 if (largeness) {
204 break;
205 }
206 }
207
208 if (!largeness) {
209 pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
210 } else {
211 pt->gpa = large_page_gpa(pt, pae, largeness);
212 }
213
214 return true;
215 }
216
217
mmu_gva_to_gpa(CPUState * cpu,target_ulong gva,uint64_t * gpa)218 bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa)
219 {
220 bool res;
221 struct gpt_translation pt;
222 int err_code = 0;
223
224 if (!x86_is_paging_mode(cpu)) {
225 *gpa = gva;
226 return true;
227 }
228
229 res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
230 if (res) {
231 *gpa = pt.gpa;
232 return true;
233 }
234
235 return false;
236 }
237
vmx_write_mem(CPUState * cpu,target_ulong gva,void * data,int bytes)238 void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes)
239 {
240 uint64_t gpa;
241
242 while (bytes > 0) {
243 /* copy page */
244 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
245
246 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
247 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
248 } else {
249 address_space_write(&address_space_memory, gpa,
250 MEMTXATTRS_UNSPECIFIED, data, copy);
251 }
252
253 bytes -= copy;
254 gva += copy;
255 data += copy;
256 }
257 }
258
vmx_read_mem(CPUState * cpu,void * data,target_ulong gva,int bytes)259 void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
260 {
261 uint64_t gpa;
262
263 while (bytes > 0) {
264 /* copy page */
265 int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
266
267 if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
268 VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
269 }
270 address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
271 data, copy);
272
273 bytes -= copy;
274 gva += copy;
275 data += copy;
276 }
277 }
278