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.


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