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.


added license
[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 = 0;
166       shadow_pde->cache_disable = 0;
167       shadow_pde->global_page = 0;
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 = 0;
336       shadow_pte->cache_disable = 0;
337       shadow_pte->global_page = 0;
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 = 0;
452       shadow_pte->cache_disable = 0;
453       shadow_pte->global_page = 0;
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 }