@@ -15,6 +15,19 @@ uint32_t e820_entries;
15
15
struct e820_entry e820 [E820_MAX_ENTRIES ];
16
16
struct e820_mem_params e820_mem ;
17
17
18
+ struct page_walk_info {
19
+ uint64_t top_entry ; /* Top level paging structure entry */
20
+ int level ;
21
+ int width ;
22
+ bool is_user_mode ;
23
+ bool is_write_access ;
24
+ bool is_inst_fetch ;
25
+ bool pse ; /* CR4.PSE for 32bit paing,
26
+ * true for PAE/4-level paing */
27
+ bool wp ; /* CR0.WP */
28
+ bool nxe ; /* MSR_IA32_EFER_NXE_BIT */
29
+ };
30
+
18
31
inline bool
19
32
is_vm0 (struct vm * vm )
20
33
{
@@ -173,34 +186,179 @@ enum vm_paging_mode get_vcpu_paging_mode(struct vcpu *vcpu)
173
186
return PAGING_MODE_4_LEVEL ;
174
187
}
175
188
176
- uint64_t gva2gpa (struct vm * vm , uint64_t cr3 , uint64_t gva )
189
+ /* TODO: Add code to check for Revserved bits, SMAP and PKE when do translation
190
+ * during page walk */
191
+ static int _gva2gpa_common (struct vcpu * vcpu , struct page_walk_info * pw_info ,
192
+ uint64_t gva , uint64_t * gpa , uint32_t * err_code )
177
193
{
178
- int level , index , shift ;
179
- uint64_t * base , addr , entry , page_size ;
180
- uint64_t gpa = 0 ;
181
-
182
- addr = cr3 ;
183
-
184
- for (level = 3 ; level >= 0 ; level -- ) {
194
+ int i , index , shift ;
195
+ uint8_t * base ;
196
+ uint64_t entry ;
197
+ uint64_t addr , page_size ;
198
+ int ret = 0 ;
199
+ int fault = 0 ;
200
+
201
+ if (pw_info -> level < 1 )
202
+ return - EINVAL ;
203
+
204
+ addr = pw_info -> top_entry ;
205
+ for (i = pw_info -> level - 1 ; i >= 0 ; i -- ) {
185
206
addr = addr & IA32E_REF_MASK ;
186
- base = GPA2HVA (vm , addr );
187
- ASSERT (base != NULL , "invalid ptp base." );
188
- shift = level * 9 + 12 ;
189
- index = (gva >> shift ) & 0x1FF ;
207
+ base = GPA2HVA (vcpu -> vm , addr );
208
+ if (base == NULL ) {
209
+ ret = - EFAULT ;
210
+ goto out ;
211
+ }
212
+
213
+ shift = i * pw_info -> width + 12 ;
214
+ index = (gva >> shift ) & ((1UL << pw_info -> width ) - 1 );
190
215
page_size = 1UL << shift ;
191
216
192
- entry = base [index ];
193
- if (level > 0 && (entry & MMU_32BIT_PDE_PS ) != 0 )
217
+ if (pw_info -> width == 10 )
218
+ /* 32bit entry */
219
+ entry = * ((uint32_t * )(base + 4 * index ));
220
+ else
221
+ entry = * ((uint64_t * )(base + 8 * index ));
222
+
223
+ /* check if the entry present */
224
+ if (!(entry & MMU_32BIT_PDE_P )) {
225
+ ret = - EFAULT ;
226
+ goto out ;
227
+ }
228
+ /* check for R/W */
229
+ if (pw_info -> is_write_access && !(entry & MMU_32BIT_PDE_RW )) {
230
+ /* Case1: Supermode and wp is 1
231
+ * Case2: Usermode */
232
+ if (!(!pw_info -> is_user_mode && !pw_info -> wp ))
233
+ fault = 1 ;
234
+ }
235
+ /* check for nx, since for 32-bit paing, the XD bit is
236
+ * reserved(0), use the same logic as PAE/4-level paging */
237
+ if (pw_info -> is_inst_fetch && pw_info -> nxe &&
238
+ (entry & MMU_MEM_ATTR_BIT_EXECUTE_DISABLE ))
239
+ fault = 1 ;
240
+
241
+ /* check for U/S */
242
+ if (!(entry & MMU_32BIT_PDE_US ) && pw_info -> is_user_mode )
243
+ fault = 1 ;
244
+
245
+ if (pw_info -> pse && (i > 0 && (entry & MMU_32BIT_PDE_PS )))
194
246
break ;
195
247
addr = entry ;
196
248
}
197
249
198
- entry >>= shift ; entry <<= (shift + 12 ); entry >>= 12 ;
199
- gpa = entry | (gva & (page_size - 1 ));
250
+ entry >>= shift ;
251
+ /* shift left 12bit more and back to clear XD/Prot Key/Ignored bits */
252
+ entry <<= (shift + 12 );
253
+ entry >>= 12 ;
254
+ * gpa = entry | (gva & (page_size - 1 ));
255
+ out :
256
+
257
+ if (fault ) {
258
+ ret = - EFAULT ;
259
+ * err_code |= PAGE_FAULT_P_FLAG ;
260
+ }
261
+ return ret ;
262
+ }
263
+
264
+ static int _gva2gpa_pae (struct vcpu * vcpu , struct page_walk_info * pw_info ,
265
+ uint64_t gva , uint64_t * gpa , uint32_t * err_code )
266
+ {
267
+ int index ;
268
+ uint64_t * base ;
269
+ uint64_t entry ;
270
+ uint64_t addr ;
271
+ int ret ;
272
+
273
+ addr = pw_info -> top_entry & 0xFFFFFFF0UL ;
274
+ base = GPA2HVA (vcpu -> vm , addr );
275
+ if (base == NULL ) {
276
+ ret = - EFAULT ;
277
+ goto out ;
278
+ }
279
+
280
+ index = (gva >> 30 ) & 0x3 ;
281
+ entry = base [index ];
282
+
283
+ if (!(entry & MMU_32BIT_PDE_P )) {
284
+ ret = - EFAULT ;
285
+ goto out ;
286
+ }
287
+
288
+ pw_info -> level = 2 ;
289
+ pw_info -> top_entry = entry ;
290
+ ret = _gva2gpa_common (vcpu , pw_info , gva , gpa , err_code );
291
+
292
+ out :
293
+ return ret ;
200
294
201
- return gpa ;
202
295
}
203
296
297
+ /* Refer to SDM Vol.3A 6-39 section 6.15 for the format of paging fault error
298
+ * code.
299
+ *
300
+ * Caller should set the contect of err_code properly according to the address
301
+ * usage when calling this function:
302
+ * - If it is an address for write, set PAGE_FAULT_WR_FLAG in err_code.
303
+ * - If it is an address for instruction featch, set PAGE_FAULT_ID_FLAG in
304
+ * err_code.
305
+ * Caller should check the return value to confirm if the function success or
306
+ * not.
307
+ * If a protection volation detected during page walk, this function still will
308
+ * give the gpa translated, it is up to caller to decide if it need to inject a
309
+ * #PF or not.
310
+ * - Return 0 for success.
311
+ * - Return -EINVAL for invalid parameter.
312
+ * - Return -EFAULT for paging fault, and refer to err_code for paging fault
313
+ * error code.
314
+ */
315
+ int gva2gpa (struct vcpu * vcpu , uint64_t gva , uint64_t * gpa ,
316
+ uint32_t * err_code )
317
+ {
318
+ struct run_context * cur_context =
319
+ & vcpu -> arch_vcpu .contexts [vcpu -> arch_vcpu .cur_context ];
320
+ enum vm_paging_mode pm = get_vcpu_paging_mode (vcpu );
321
+ struct page_walk_info pw_info ;
322
+ int ret = 0 ;
323
+
324
+ if (!gpa || !err_code )
325
+ return - EINVAL ;
326
+ * gpa = 0 ;
327
+
328
+ pw_info .top_entry = cur_context -> cr3 ;
329
+ pw_info .level = pm ;
330
+ pw_info .is_write_access = !!(* err_code & PAGE_FAULT_WR_FLAG );
331
+ pw_info .is_inst_fetch = !!(* err_code & PAGE_FAULT_ID_FLAG );
332
+ pw_info .is_user_mode = ((exec_vmread (VMX_GUEST_CS_SEL ) & 0x3 ) == 3 );
333
+ pw_info .pse = true;
334
+ pw_info .nxe = cur_context -> ia32_efer & MSR_IA32_EFER_NXE_BIT ;
335
+ pw_info .wp = !!(cur_context -> cr0 & CR0_WP );
336
+
337
+ * err_code &= ~PAGE_FAULT_P_FLAG ;
338
+
339
+ if (pm == PAGING_MODE_4_LEVEL ) {
340
+ pw_info .width = 9 ;
341
+ ret = _gva2gpa_common (vcpu , & pw_info , gva , gpa , err_code );
342
+ } else if (pm == PAGING_MODE_3_LEVEL ) {
343
+ pw_info .width = 9 ;
344
+ ret = _gva2gpa_pae (vcpu , & pw_info , gva , gpa , err_code );
345
+ } else if (pm == PAGING_MODE_2_LEVEL ) {
346
+ pw_info .width = 10 ;
347
+ pw_info .pse = !!(cur_context -> cr4 & CR4_PSE );
348
+ pw_info .nxe = false;
349
+ ret = _gva2gpa_common (vcpu , & pw_info , gva , gpa , err_code );
350
+ } else
351
+ * gpa = gva ;
352
+
353
+ if (ret == - EFAULT ) {
354
+ if (pw_info .is_user_mode )
355
+ * err_code |= PAGE_FAULT_US_FLAG ;
356
+ }
357
+
358
+ return ret ;
359
+ }
360
+
361
+
204
362
void init_e820 (void )
205
363
{
206
364
unsigned int i ;
0 commit comments