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.


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