Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


updated shadow paging to handle cache-disabled and write-through pages
[palacios.git] / palacios / src / palacios / vmm_shadow_paging_32.h
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20
21 static int cache_page_tables_32(struct guest_info * info, addr_t pde) {
22     struct shadow_page_state * state = &(info->shdw_pg_state);
23     addr_t pde_host_addr;
24     pde32_t * tmp_pde;
25     struct hashtable * pte_cache = NULL;
26     int i = 0;
27
28     if (pde == state->cached_cr3) {
29         return 1;
30     }
31
32     if (state->cached_ptes != NULL) {
33         hashtable_destroy(state->cached_ptes, 0, 0);
34         state->cached_ptes = NULL;
35     }
36
37     state->cached_cr3 = pde;
38
39     pte_cache = create_hashtable(0, &pte_hash_fn, &pte_equals);
40     state->cached_ptes = pte_cache;
41
42     if (guest_pa_to_host_va(info, pde, &pde_host_addr) == -1) {
43         PrintError("Could not lookup host address of guest PDE\n");
44         return -1;
45     }
46
47     tmp_pde = (pde32_t *)pde_host_addr;
48
49     add_pte_map(pte_cache, pde, pde_host_addr);
50
51
52     for (i = 0; i < MAX_PDE32_ENTRIES; i++) {
53         if ((tmp_pde[i].present) && (tmp_pde[i].large_page == 0)) {
54             addr_t pte_host_addr;
55
56             if (guest_pa_to_host_va(info, (addr_t)(BASE_TO_PAGE_ADDR(tmp_pde[i].pt_base_addr)), &pte_host_addr) == -1) {
57                 PrintError("Could not lookup host address of guest PDE\n");
58                 return -1;
59             }
60
61             add_pte_map(pte_cache, (addr_t)(BASE_TO_PAGE_ADDR(tmp_pde[i].pt_base_addr)), pte_host_addr); 
62         }
63     }
64
65     return 0;
66
67 }
68
69
70
71 // We assume that shdw_pg_state.guest_cr3 is pointing to the page tables we want to activate
72 // We also assume that the CPU mode has not changed during this page table transition
73 static inline int activate_shadow_pt_32(struct guest_info * info) {
74     struct cr3_32 * shadow_cr3 = (struct cr3_32 *)&(info->ctrl_regs.cr3);
75     struct cr3_32 * guest_cr3 = (struct cr3_32 *)&(info->shdw_pg_state.guest_cr3);
76     int cached = 0;
77   
78     // Check if shadow page tables are in the cache
79     cached = cache_page_tables_32(info, CR3_TO_PDE32_PA(*(addr_t *)guest_cr3));
80   
81     if (cached == -1) {
82         PrintError("CR3 Cache failed\n");
83         return -1;
84     } else if (cached == 0) {
85         struct shadow_page_data * shdw_page = create_new_shadow_pt(info);
86
87         shdw_page->cr3 = shdw_page->page_pa;
88     
89         shadow_cr3->pdt_base_addr = PAGE_BASE_ADDR(shdw_page->page_pa);
90         PrintDebug( "Created new shadow page table %p\n", (void *)BASE_TO_PAGE_ADDR(shadow_cr3->pdt_base_addr));
91     } else {
92         PrintDebug("Reusing cached shadow Page table\n");
93     }
94   
95     shadow_cr3->pwt = guest_cr3->pwt;
96     shadow_cr3->pcd = guest_cr3->pcd;
97   
98     return 0;
99 }
100
101 /* 
102  * *
103  * * 
104  * * 32 bit Page table fault handlers
105  * *
106  * *
107  */
108 static int handle_large_pagefault_32(struct guest_info * info, 
109                                      addr_t fault_addr, pf_error_t error_code, 
110                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde);
111
112 static int handle_shadow_pte32_fault(struct guest_info * info, 
113                                      addr_t fault_addr, 
114                                      pf_error_t error_code,
115                                      pte32_t * shadow_pt, 
116                                      pte32_t * guest_pt);
117
118
119 static inline int handle_shadow_pagefault_32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
120     pde32_t * guest_pd = NULL;
121     pde32_t * shadow_pd = CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
122     addr_t guest_cr3 = CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
123     pt_access_status_t guest_pde_access;
124     pt_access_status_t shadow_pde_access;
125     pde32_t * guest_pde = NULL;
126     pde32_t * shadow_pde = (pde32_t *)&(shadow_pd[PDE32_INDEX(fault_addr)]);
127
128     PrintDebug("Shadow page fault handler: %p\n", (void*) fault_addr );
129
130     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
131         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
132         return -1;
133     } 
134
135     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(fault_addr)]);
136
137
138     // Check the guest page permissions
139     guest_pde_access = v3_can_access_pde32(guest_pd, fault_addr, error_code);
140
141     // Check the shadow page permissions
142     shadow_pde_access = v3_can_access_pde32(shadow_pd, fault_addr, error_code);
143   
144     /* Was the page fault caused by the Guest's page tables? */
145     if (is_guest_pf(guest_pde_access, shadow_pde_access) == 1) {
146         PrintDebug("Injecting PDE pf to guest: (guest access error=%d) (pf error code=%d)\n", 
147                    *(uint_t *)&guest_pde_access, *(uint_t *)&error_code);
148         inject_guest_pf(info, fault_addr, error_code);
149         return 0;
150     }
151
152   
153     if (shadow_pde_access == PT_ACCESS_NOT_PRESENT) 
154         {
155             struct shadow_page_data * shdw_page =  create_new_shadow_pt(info);
156             pte32_t * shadow_pt = (pte32_t *)V3_VAddr((void *)shdw_page->page_pa);
157
158             shadow_pde->present = 1;
159             shadow_pde->user_page = guest_pde->user_page;
160             //    shadow_pde->large_page = guest_pde->large_page;
161             shadow_pde->large_page = 0;
162       
163
164             // VMM Specific options
165             shadow_pde->write_through = guest_pde->write_through;
166             shadow_pde->cache_disable = guest_pde->cache_disable;
167             shadow_pde->global_page = guest_pde->global_page;
168             //
169       
170             guest_pde->accessed = 1;
171       
172             shadow_pde->pt_base_addr = PAGE_BASE_ADDR((addr_t)V3_PAddr(shadow_pt));
173       
174             if (guest_pde->large_page == 0) {
175                 pte32_t * guest_pt = NULL;
176                 shadow_pde->writable = guest_pde->writable;
177
178                 if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
179                     // Machine check the guest
180                     PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
181                     v3_raise_exception(info, MC_EXCEPTION);
182                     return 0;
183                 }
184
185                 if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
186                     PrintError("Error handling Page fault caused by PTE\n");
187                     return -1;
188                 }
189             } else {
190                 // ??  What if guest pde is dirty a this point?
191                 ((pde32_4MB_t *)guest_pde)->dirty = 0;
192                 shadow_pde->writable = 0;
193
194                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
195                     PrintError("Error handling large pagefault\n");
196                     return -1;
197                 }       
198
199             }
200         }
201     else if (shadow_pde_access == PT_ACCESS_OK) 
202         {
203             //
204             // PTE fault
205             //
206             pte32_t * shadow_pt = (pte32_t *)V3_VAddr( (void*)(addr_t) BASE_TO_PAGE_ADDR(shadow_pde->pt_base_addr) );
207
208             if (guest_pde->large_page == 0) {
209                 pte32_t * guest_pt = NULL;
210
211                 if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
212                     // Machine check the guest
213                     PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
214                     v3_raise_exception(info, MC_EXCEPTION);
215                     return 0;
216                 }
217         
218                 if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
219                     PrintError("Error handling Page fault caused by PTE\n");
220                     return -1;
221                 }
222             } else if (guest_pde->large_page == 1) {
223                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
224                     PrintError("Error handling large pagefault\n");
225                     return -1;
226                 }
227             }
228         }
229     else if ((shadow_pde_access == PT_ACCESS_WRITE_ERROR) && 
230              (guest_pde->large_page == 1)) 
231         {
232             //
233             // Page Directory Entry marked read-only
234             // Its a large page and we need to update the dirty bit in the guest
235             //
236
237             PrintDebug("Large page write error... Setting dirty bit and returning\n");
238             ((pde32_4MB_t *)guest_pde)->dirty = 1;
239             shadow_pde->writable = guest_pde->writable;
240             return 0;
241       
242         } 
243     else if (shadow_pde_access == PT_ACCESS_USER_ERROR) 
244         {
245             //
246             // Page Directory Entry marked non-user
247             //      
248             PrintDebug("Shadow Paging User access error (shadow_pde_access=0x%x, guest_pde_access=0x%x)\n", 
249                        shadow_pde_access, guest_pde_access);
250             inject_guest_pf(info, fault_addr, error_code);
251             return 0;
252         }
253     else 
254         {
255             // inject page fault in guest
256             inject_guest_pf(info, fault_addr, error_code);
257             PrintDebug("Unknown Error occurred (shadow_pde_access=%d)\n", shadow_pde_access);
258             PrintDebug("Manual Says to inject page fault into guest\n");
259 #ifdef DEBUG_SHADOW_PAGING
260             PrintDebug("Guest PDE: (access=%d)\n\t", guest_pde_access);
261             PrintPTEntry(info, PAGE_PD32, fault_addr, guest_pde);
262             PrintDebug("Shadow PDE: (access=%d)\n\t", shadow_pde_access);
263             PrintPTEntry(info, PAGE_PD32, fault_addr, shadow_pde);
264 #endif
265
266             return 0; 
267         }
268
269     PrintDebug("Returning end of PDE function (rip=%p)\n", (void *)(addr_t)(info->rip));
270     return 0;
271 }
272
273
274
275 /* The guest status checks have already been done,
276  * only special case shadow checks remain
277  */
278 static int handle_large_pagefault_32(struct guest_info * info, 
279                                      addr_t fault_addr, pf_error_t error_code, 
280                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde) 
281 {
282     pt_access_status_t shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
283     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
284     addr_t guest_fault_pa = BASE_TO_PAGE_ADDR_4MB(large_guest_pde->page_base_addr) + PAGE_OFFSET_4MB(fault_addr);  
285     struct shadow_page_state * state = &(info->shdw_pg_state);
286
287     struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info, guest_fault_pa);
288
289  
290     if ((shdw_reg == NULL) || 
291         (shdw_reg->host_type == SHDW_REGION_INVALID)) {
292         // Inject a machine check in the guest
293         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
294         v3_raise_exception(info, MC_EXCEPTION);
295         return -1;
296     }
297
298     if (shadow_pte_access == PT_ACCESS_OK) {
299         // Inconsistent state...
300         // Guest Re-Entry will flush tables and everything should now workd
301         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
302         return 0;
303     }
304
305   
306     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
307         // Get the guest physical address of the fault
308
309         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
310             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
311             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_fault_pa);
312
313             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
314
315             shadow_pte->present = 1;
316
317             /* We are assuming that the PDE entry has precedence
318              * so the Shadow PDE will mirror the guest PDE settings, 
319              * and we don't have to worry about them here
320              * Allow everything
321              */
322             shadow_pte->user_page = 1;
323
324             if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_fault_pa)) != NULL) {
325                 // Check if the entry is a page table...
326                 PrintDebug("Marking page as Guest Page Table (large page)\n");
327                 shadow_pte->writable = 0;
328             } else if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
329                 shadow_pte->writable = 0;
330             } else {
331                 shadow_pte->writable = 1;
332             }
333
334             //set according to VMM policy
335             shadow_pte->write_through = large_guest_pde->write_through;
336             shadow_pte->cache_disable = large_guest_pde->cache_disable;
337             shadow_pte->global_page = large_guest_pde->global_page;
338             //
339       
340         } else {
341             // Handle hooked pages as well as other special pages
342             //      if (handle_special_page_fault(info, fault_addr, guest_fault_pa, error_code) == -1) {
343
344             if (v3_handle_mem_full_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
345                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
346                 return -1;
347             }
348         }
349     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
350
351         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
352
353             if (v3_handle_mem_wr_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
354                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
355                 return -1;
356             }
357         }
358
359
360         if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_fault_pa)) != NULL) {
361             struct shadow_page_state * state = &(info->shdw_pg_state);
362             PrintDebug("Write operation on Guest PAge Table Page (large page)\n");
363             state->cached_cr3 = 0;
364             shadow_pte->writable = 1;
365         }
366
367     } else {
368         PrintError("Error in large page fault handler...\n");
369         PrintError("This case should have been handled at the top level handler\n");
370         return -1;
371     }
372
373     PrintDebug("Returning from large page fault handler\n");
374     return 0;
375 }
376
377
378
379
380 /* 
381  * We assume the the guest pte pointer has already been translated to a host virtual address
382  */
383 static int handle_shadow_pte32_fault(struct guest_info * info, 
384                                      addr_t fault_addr, 
385                                      pf_error_t error_code,
386                                      pte32_t * shadow_pt, 
387                                      pte32_t * guest_pt) {
388
389     pt_access_status_t guest_pte_access;
390     pt_access_status_t shadow_pte_access;
391     pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
392     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
393     addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) +  PAGE_OFFSET(fault_addr);
394     struct shadow_page_state * state = &(info->shdw_pg_state);
395
396     struct v3_shadow_region * shdw_reg =  v3_get_shadow_region(info, guest_pa);
397
398     if ((shdw_reg == NULL) || 
399         (shdw_reg->host_type == SHDW_REGION_INVALID)) {
400         // Inject a machine check in the guest
401         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_pa);
402         v3_raise_exception(info, MC_EXCEPTION);
403         return 0;
404     }
405
406     // Check the guest page permissions
407     guest_pte_access = v3_can_access_pte32(guest_pt, fault_addr, error_code);
408
409     // Check the shadow page permissions
410     shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
411   
412 #ifdef DEBUG_SHADOW_PAGING
413     PrintDebug("Guest PTE: (access=%d)\n\t", guest_pte_access);
414     PrintPTEntry(info, PAGE_PT32, fault_addr, guest_pte);
415     PrintDebug("Shadow PTE: (access=%d)\n\t", shadow_pte_access);
416     PrintPTEntry(info, PAGE_PT32, fault_addr, shadow_pte);
417 #endif
418   
419     /* Was the page fault caused by the Guest's page tables? */
420     if (is_guest_pf(guest_pte_access, shadow_pte_access) == 1) {
421         PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", 
422                    guest_pte_access, *(uint_t*)&error_code);    
423         inject_guest_pf(info, fault_addr, error_code);
424         return 0; 
425     }
426
427   
428   
429     if (shadow_pte_access == PT_ACCESS_OK) {
430         // Inconsistent state...
431         // Guest Re-Entry will flush page tables and everything should now work
432         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
433         return 0;
434     }
435
436
437     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
438         // Page Table Entry Not Present
439         PrintDebug("guest_pa =%p\n", (void *)guest_pa);
440
441         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
442             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
443             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
444       
445             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
446       
447             shadow_pte->present = guest_pte->present;
448             shadow_pte->user_page = guest_pte->user_page;
449       
450             //set according to VMM policy
451             shadow_pte->write_through = guest_pte->write_through;
452             shadow_pte->cache_disable = guest_pte->cache_disable;
453             shadow_pte->global_page = guest_pte->global_page;
454             //
455       
456             guest_pte->accessed = 1;
457       
458             if (guest_pte->dirty == 1) {
459                 shadow_pte->writable = guest_pte->writable;
460             } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
461                 shadow_pte->writable = guest_pte->writable;
462                 guest_pte->dirty = 1;
463             } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
464                 shadow_pte->writable = 0;
465             }
466
467             // dirty flag has been set, check if its in the cache
468             if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
469                 if (error_code.write == 1) {
470                     state->cached_cr3 = 0;
471                     shadow_pte->writable = guest_pte->writable;
472                 } else {
473                     shadow_pte->writable = 0;
474                 }
475             }
476
477             // Write hooks trump all, and are set Read Only
478             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
479                 shadow_pte->writable = 0;
480             }
481
482         } else {
483             // Page fault handled by hook functions
484
485             if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
486                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
487                 return -1;
488             }
489         }
490     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
491         guest_pte->dirty = 1;
492
493         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
494             if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
495                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
496                 return -1;
497             }
498         } else {
499             PrintDebug("Shadow PTE Write Error\n");
500             shadow_pte->writable = guest_pte->writable;
501         }
502
503         if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
504             struct shadow_page_state * state = &(info->shdw_pg_state);
505             PrintDebug("Write operation on Guest PAge Table Page\n");
506             state->cached_cr3 = 0;
507         }
508
509         return 0;
510
511     } else {
512         // Inject page fault into the guest     
513         inject_guest_pf(info, fault_addr, error_code);
514         PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
515         PrintError("Manual Says to inject page fault into guest\n");
516         return -1;
517     }
518
519     PrintDebug("Returning end of function\n");
520     return 0;
521 }
522
523
524
525 /* If we start to optimize we should look up the guest pages in the cache... */
526 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
527     pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
528     pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
529
530     addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
531     pde32_t * guest_pd = NULL;
532     pde32_t * guest_pde;
533
534     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
535         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
536         return -1;
537     }
538   
539     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
540   
541     if (guest_pde->large_page == 1) {
542         shadow_pde->present = 0;
543         PrintDebug("Invalidating Large Page\n");
544     } else if (shadow_pde->present == 1) {
545         pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
546         pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
547     
548         PrintDebug("Setting not present\n");
549     
550         shadow_pte->present = 0;
551     }
552     return 0;
553 }