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.


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