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.


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