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.


d60eb9f5a8d59c6fc58d4edbbd541a43886477ae
[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
22
23 static inline int activate_shadow_pt_32(struct guest_info * info) {
24     struct cr3_32 * shadow_cr3 = (struct cr3_32 *)&(info->ctrl_regs.cr3);
25     struct cr3_32 * guest_cr3 = (struct cr3_32 *)&(info->shdw_pg_state.guest_cr3);
26     struct shadow_page_data * shdw_page = create_new_shadow_pt(info);
27
28     shdw_page->cr3 = shdw_page->page_pa;
29     
30     shadow_cr3->pdt_base_addr = PAGE_BASE_ADDR(shdw_page->page_pa);
31     PrintDebug( "Created new shadow page table %p\n", (void *)BASE_TO_PAGE_ADDR(shadow_cr3->pdt_base_addr));
32   
33     shadow_cr3->pwt = guest_cr3->pwt;
34     shadow_cr3->pcd = guest_cr3->pcd;
35   
36     return 0;
37 }
38
39 /* 
40  * *
41  * * 
42  * * 32 bit Page table fault handlers
43  * *
44  * *
45  */
46 static int handle_large_pagefault_32(struct guest_info * info, 
47                                      addr_t fault_addr, pf_error_t error_code, 
48                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde);
49
50 static int handle_shadow_pte32_fault(struct guest_info * info, 
51                                      addr_t fault_addr, 
52                                      pf_error_t error_code,
53                                      pte32_t * shadow_pt, 
54                                      pte32_t * guest_pt);
55
56
57 static inline int handle_shadow_pagefault_32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
58     pde32_t * guest_pd = NULL;
59     pde32_t * shadow_pd = CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
60     addr_t guest_cr3 = CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
61     pt_access_status_t guest_pde_access;
62     pt_access_status_t shadow_pde_access;
63     pde32_t * guest_pde = NULL;
64     pde32_t * shadow_pde = (pde32_t *)&(shadow_pd[PDE32_INDEX(fault_addr)]);
65
66     PrintDebug("Shadow page fault handler: %p\n", (void*) fault_addr );
67
68     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
69         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
70         return -1;
71     } 
72
73     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(fault_addr)]);
74
75
76     // Check the guest page permissions
77     guest_pde_access = v3_can_access_pde32(guest_pd, fault_addr, error_code);
78
79     // Check the shadow page permissions
80     shadow_pde_access = v3_can_access_pde32(shadow_pd, fault_addr, error_code);
81   
82     /* Was the page fault caused by the Guest's page tables? */
83     if (is_guest_pf(guest_pde_access, shadow_pde_access) == 1) {
84         PrintDebug("Injecting PDE pf to guest: (guest access error=%d) (pf error code=%d)\n", 
85                    *(uint_t *)&guest_pde_access, *(uint_t *)&error_code);
86         inject_guest_pf(info, fault_addr, error_code);
87         return 0;
88     }
89
90   
91     if (shadow_pde_access == PT_ACCESS_NOT_PRESENT) 
92         {
93             struct shadow_page_data * shdw_page =  create_new_shadow_pt(info);
94             pte32_t * shadow_pt = (pte32_t *)V3_VAddr((void *)shdw_page->page_pa);
95
96             shadow_pde->present = 1;
97             shadow_pde->user_page = guest_pde->user_page;
98             //    shadow_pde->large_page = guest_pde->large_page;
99             shadow_pde->large_page = 0;
100       
101
102             // VMM Specific options
103             shadow_pde->write_through = guest_pde->write_through;
104             shadow_pde->cache_disable = guest_pde->cache_disable;
105             shadow_pde->global_page = guest_pde->global_page;
106             //
107       
108             guest_pde->accessed = 1;
109       
110             shadow_pde->pt_base_addr = PAGE_BASE_ADDR((addr_t)V3_PAddr(shadow_pt));
111       
112             if (guest_pde->large_page == 0) {
113                 pte32_t * guest_pt = NULL;
114                 shadow_pde->writable = guest_pde->writable;
115
116                 if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
117                     // Machine check the guest
118                     PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
119                     v3_raise_exception(info, MC_EXCEPTION);
120                     return 0;
121                 }
122
123                 if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
124                     PrintError("Error handling Page fault caused by PTE\n");
125                     return -1;
126                 }
127             } else {
128                 // ??  What if guest pde is dirty a this point?
129                 ((pde32_4MB_t *)guest_pde)->dirty = 0;
130                 shadow_pde->writable = 0;
131
132                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
133                     PrintError("Error handling large pagefault\n");
134                     return -1;
135                 }       
136
137             }
138         }
139     else if (shadow_pde_access == PT_ACCESS_OK) 
140         {
141             //
142             // PTE fault
143             //
144             pte32_t * shadow_pt = (pte32_t *)V3_VAddr( (void*)(addr_t) BASE_TO_PAGE_ADDR(shadow_pde->pt_base_addr) );
145
146             if (guest_pde->large_page == 0) {
147                 pte32_t * guest_pt = NULL;
148
149                 if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
150                     // Machine check the guest
151                     PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
152                     v3_raise_exception(info, MC_EXCEPTION);
153                     return 0;
154                 }
155         
156                 if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
157                     PrintError("Error handling Page fault caused by PTE\n");
158                     return -1;
159                 }
160             } else if (guest_pde->large_page == 1) {
161                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
162                     PrintError("Error handling large pagefault\n");
163                     return -1;
164                 }
165             }
166         }
167     else if ((shadow_pde_access == PT_ACCESS_WRITE_ERROR) && 
168              (guest_pde->large_page == 1)) 
169         {
170             //
171             // Page Directory Entry marked read-only
172             // Its a large page and we need to update the dirty bit in the guest
173             //
174
175             PrintDebug("Large page write error... Setting dirty bit and returning\n");
176             ((pde32_4MB_t *)guest_pde)->dirty = 1;
177             shadow_pde->writable = guest_pde->writable;
178             return 0;
179       
180         } 
181     else if (shadow_pde_access == PT_ACCESS_USER_ERROR) 
182         {
183             //
184             // Page Directory Entry marked non-user
185             //      
186             PrintDebug("Shadow Paging User access error (shadow_pde_access=0x%x, guest_pde_access=0x%x)\n", 
187                        shadow_pde_access, guest_pde_access);
188             inject_guest_pf(info, fault_addr, error_code);
189             return 0;
190         }
191     else 
192         {
193             // inject page fault in guest
194             inject_guest_pf(info, fault_addr, error_code);
195             PrintDebug("Unknown Error occurred (shadow_pde_access=%d)\n", shadow_pde_access);
196             PrintDebug("Manual Says to inject page fault into guest\n");
197 #ifdef DEBUG_SHADOW_PAGING
198             PrintDebug("Guest PDE: (access=%d)\n\t", guest_pde_access);
199             PrintPTEntry(info, PAGE_PD32, fault_addr, guest_pde);
200             PrintDebug("Shadow PDE: (access=%d)\n\t", shadow_pde_access);
201             PrintPTEntry(info, PAGE_PD32, fault_addr, shadow_pde);
202 #endif
203
204             return 0; 
205         }
206
207     PrintDebug("Returning end of PDE function (rip=%p)\n", (void *)(addr_t)(info->rip));
208     return 0;
209 }
210
211
212
213 /* The guest status checks have already been done,
214  * only special case shadow checks remain
215  */
216 static int handle_large_pagefault_32(struct guest_info * info, 
217                                      addr_t fault_addr, pf_error_t error_code, 
218                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde) 
219 {
220     pt_access_status_t shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
221     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
222     addr_t guest_fault_pa = BASE_TO_PAGE_ADDR_4MB(large_guest_pde->page_base_addr) + PAGE_OFFSET_4MB(fault_addr);  
223
224     struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info, guest_fault_pa);
225
226  
227     if (shdw_reg == NULL) {
228         // Inject a machine check in the guest
229         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
230         v3_raise_exception(info, MC_EXCEPTION);
231         return -1;
232     }
233
234     if (shadow_pte_access == PT_ACCESS_OK) {
235         // Inconsistent state...
236         // Guest Re-Entry will flush tables and everything should now workd
237         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
238         return 0;
239     }
240
241   
242     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
243         // Get the guest physical address of the fault
244
245         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
246             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
247             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_fault_pa);
248
249             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
250
251             shadow_pte->present = 1;
252
253             /* We are assuming that the PDE entry has precedence
254              * so the Shadow PDE will mirror the guest PDE settings, 
255              * and we don't have to worry about them here
256              * Allow everything
257              */
258             shadow_pte->user_page = 1;
259
260             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
261                 shadow_pte->writable = 0;
262             } else {
263                 shadow_pte->writable = 1;
264             }
265
266             //set according to VMM policy
267             shadow_pte->write_through = large_guest_pde->write_through;
268             shadow_pte->cache_disable = large_guest_pde->cache_disable;
269             shadow_pte->global_page = large_guest_pde->global_page;
270             //
271       
272         } else {
273             // Handle hooked pages as well as other special pages
274             //      if (handle_special_page_fault(info, fault_addr, guest_fault_pa, error_code) == -1) {
275
276             if (v3_handle_mem_full_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
277                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
278                 return -1;
279             }
280         }
281     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
282
283         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
284
285             if (v3_handle_mem_wr_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
286                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
287                 return -1;
288             }
289         }
290
291
292
293
294     } else {
295         PrintError("Error in large page fault handler...\n");
296         PrintError("This case should have been handled at the top level handler\n");
297         return -1;
298     }
299
300     PrintDebug("Returning from large page fault handler\n");
301     return 0;
302 }
303
304
305
306
307 /* 
308  * We assume the the guest pte pointer has already been translated to a host virtual address
309  */
310 static int handle_shadow_pte32_fault(struct guest_info * info, 
311                                      addr_t fault_addr, 
312                                      pf_error_t error_code,
313                                      pte32_t * shadow_pt, 
314                                      pte32_t * guest_pt) {
315
316     pt_access_status_t guest_pte_access;
317     pt_access_status_t shadow_pte_access;
318     pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
319     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
320     addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) +  PAGE_OFFSET(fault_addr);
321
322     struct v3_shadow_region * shdw_reg =  v3_get_shadow_region(info, guest_pa);
323
324     if (shdw_reg == NULL) {
325         // Inject a machine check in the guest
326         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_pa);
327         v3_raise_exception(info, MC_EXCEPTION);
328         return 0;
329     }
330
331     // Check the guest page permissions
332     guest_pte_access = v3_can_access_pte32(guest_pt, fault_addr, error_code);
333
334     // Check the shadow page permissions
335     shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
336   
337 #ifdef DEBUG_SHADOW_PAGING
338     PrintDebug("Guest PTE: (access=%d)\n\t", guest_pte_access);
339     PrintPTEntry(info, PAGE_PT32, fault_addr, guest_pte);
340     PrintDebug("Shadow PTE: (access=%d)\n\t", shadow_pte_access);
341     PrintPTEntry(info, PAGE_PT32, fault_addr, shadow_pte);
342 #endif
343   
344     /* Was the page fault caused by the Guest's page tables? */
345     if (is_guest_pf(guest_pte_access, shadow_pte_access) == 1) {
346         PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", 
347                    guest_pte_access, *(uint_t*)&error_code);    
348         inject_guest_pf(info, fault_addr, error_code);
349         return 0; 
350     }
351
352   
353   
354     if (shadow_pte_access == PT_ACCESS_OK) {
355         // Inconsistent state...
356         // Guest Re-Entry will flush page tables and everything should now work
357         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
358         return 0;
359     }
360
361
362     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
363         // Page Table Entry Not Present
364         PrintDebug("guest_pa =%p\n", (void *)guest_pa);
365
366         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
367             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
368             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
369       
370             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
371       
372             shadow_pte->present = guest_pte->present;
373             shadow_pte->user_page = guest_pte->user_page;
374       
375             //set according to VMM policy
376             shadow_pte->write_through = guest_pte->write_through;
377             shadow_pte->cache_disable = guest_pte->cache_disable;
378             shadow_pte->global_page = guest_pte->global_page;
379             //
380       
381             guest_pte->accessed = 1;
382       
383             if (guest_pte->dirty == 1) {
384                 shadow_pte->writable = guest_pte->writable;
385             } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
386                 shadow_pte->writable = guest_pte->writable;
387                 guest_pte->dirty = 1;
388             } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
389                 shadow_pte->writable = 0;
390             }
391
392
393
394             // Write hooks trump all, and are set Read Only
395             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
396                 shadow_pte->writable = 0;
397             }
398
399         } else {
400             // Page fault handled by hook functions
401
402             if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
403                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
404                 return -1;
405             }
406         }
407     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
408         guest_pte->dirty = 1;
409
410         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
411             if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
412                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
413                 return -1;
414             }
415         } else {
416             PrintDebug("Shadow PTE Write Error\n");
417             shadow_pte->writable = guest_pte->writable;
418         }
419
420
421         return 0;
422
423     } else {
424         // Inject page fault into the guest     
425         inject_guest_pf(info, fault_addr, error_code);
426         PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
427         PrintError("Manual Says to inject page fault into guest\n");
428         return -1;
429     }
430
431     PrintDebug("Returning end of function\n");
432     return 0;
433 }
434
435
436
437 /* If we start to optimize we should look up the guest pages in the cache... */
438 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
439     pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
440     pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
441
442     addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
443     pde32_t * guest_pd = NULL;
444     pde32_t * guest_pde;
445
446     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
447         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
448         return -1;
449     }
450   
451     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
452   
453     if (guest_pde->large_page == 1) {
454         shadow_pde->present = 0;
455         PrintDebug("Invalidating Large Page\n");
456     } else if (shadow_pde->present == 1) {
457         pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
458         pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
459     
460         PrintDebug("Setting not present\n");
461     
462         shadow_pte->present = 0;
463     }
464     return 0;
465 }