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.


256f0de0f2e9923d0814688b6a3a266601ca7de3
[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) (shdw access error=%d)  (pf error code=%d)\n", 
89                    *(uint_t *)&guest_pde_access, *(uint_t *)&shadow_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 for vaddr %p\n", (void *)fault_addr);
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 for vaddr %p\n", (void *)fault_addr);
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 for vaddr %p\n", (void *)fault_addr);
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 static int handle_pte_shadow_pagefault_32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code,
201                                           pte32_t * shadow_pt, pte32_t * guest_pt) {
202
203     pt_access_status_t guest_pte_access;
204     pt_access_status_t shadow_pte_access;
205     pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
206     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
207     addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) +  PAGE_OFFSET(fault_addr);
208
209     struct v3_shadow_region * shdw_reg =  v3_get_shadow_region(info, guest_pa);
210
211     if (shdw_reg == NULL) {
212         // Inject a machine check in the guest
213         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_pa);
214         v3_raise_exception(info, MC_EXCEPTION);
215         return 0;
216     }
217
218     // Check the guest page permissions
219     guest_pte_access = v3_can_access_pte32(guest_pt, fault_addr, error_code);
220
221     // Check the shadow page permissions
222     shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
223   
224   
225     /* Was the page fault caused by the Guest's page tables? */
226     if (is_guest_pf(guest_pte_access, shadow_pte_access) == 1) {
227
228         PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", 
229                    guest_pte_access, *(uint_t*)&error_code);
230         
231 #ifdef CONFIG_SYMBIOTIC_SWAP
232         if (is_swapped_pte32(guest_pte)) {
233             pf_error_t swap_perms;
234
235 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
236             if (error_code.write == 0) {
237                 info->swap_state.read_faults++;
238             } else {
239                 info->swap_state.write_faults++;
240             }
241 #endif
242
243             if (v3_get_vaddr_perms(info, fault_addr, guest_pte, &swap_perms) == -1) {
244                 PrintError("Error getting Swapped page permissions\n");
245                 return -1;
246             }
247
248
249             // swap_perms.write == 1 || error_code.write == 0
250             // swap_perms.user == 0 || error_code.user == 1
251
252             // This checks for permissions violations that require a guest PF injection
253             if ( (swap_perms.present == 1) && 
254                  ( (swap_perms.write == 1) || 
255                    (error_code.write == 0) ) &&
256                  ( (swap_perms.user == 1) || 
257                    (error_code.user == 0) ) ) {
258                 addr_t swp_pg_addr = 0;
259
260                 V3_Print("Page fault on swapped out page (vaddr=%p) (pte=%x) (error_code=%x)\n", 
261                          (void *)fault_addr, *(uint32_t *)guest_pte, *(uint32_t *)&error_code);
262
263                 swp_pg_addr = v3_get_swapped_pg_addr(info, shadow_pte, guest_pte);
264
265                 V3_Print("Swapped page address=%p\n", (void *)swp_pg_addr);
266
267                 if (swp_pg_addr != 0) {
268                     shadow_pte->writable = swap_perms.write;
269                     shadow_pte->user_page = swap_perms.user;
270                     
271                     shadow_pte->write_through = 0;
272                     shadow_pte->cache_disable = 0;
273                     shadow_pte->global_page = 0;
274                     
275                     shadow_pte->present = 1;
276                     
277                     shadow_pte->page_base_addr = swp_pg_addr;
278                     
279 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
280                     info->swap_state.mapped_pages++;
281 #endif
282
283                     return 0;
284                 }
285             }
286         }
287 #endif
288         
289
290         if (inject_guest_pf(info, fault_addr, error_code) == -1) {
291             PrintError("Could not inject guest page fault for vaddr %p\n", (void *)fault_addr);
292             return -1;
293         }       
294
295         return 0; 
296     }
297
298   
299   
300     if (shadow_pte_access == PT_ACCESS_OK) {
301         // Inconsistent state...
302         // Guest Re-Entry will flush page tables and everything should now work
303         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
304         return 0;
305     }
306
307
308     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
309         // Page Table Entry Not Present
310         PrintDebug("guest_pa =%p\n", (void *)guest_pa);
311
312         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
313             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
314             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
315       
316             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
317
318             PrintDebug("\tMapping shadow page (%p)\n", (void *)BASE_TO_PAGE_ADDR(shadow_pte->page_base_addr));
319       
320             shadow_pte->present = guest_pte->present;
321             shadow_pte->user_page = guest_pte->user_page;
322       
323             //set according to VMM policy
324             shadow_pte->write_through = guest_pte->write_through;
325             shadow_pte->cache_disable = guest_pte->cache_disable;
326             shadow_pte->global_page = guest_pte->global_page;
327             //
328       
329             guest_pte->accessed = 1;
330       
331             if (guest_pte->dirty == 1) {
332                 shadow_pte->writable = guest_pte->writable;
333             } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
334                 shadow_pte->writable = guest_pte->writable;
335                 guest_pte->dirty = 1;
336             } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
337                 shadow_pte->writable = 0;
338             }
339
340
341
342             // Write hooks trump all, and are set Read Only
343             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
344                 shadow_pte->writable = 0;
345             }
346
347         } else {
348             // Page fault handled by hook functions
349
350             if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
351                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
352                 return -1;
353             }
354         }
355     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
356         guest_pte->dirty = 1;
357
358         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
359             if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
360                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
361                 return -1;
362             }
363         } else {
364             PrintDebug("Shadow PTE Write Error\n");
365             shadow_pte->writable = guest_pte->writable;
366         }
367
368
369         return 0;
370
371     } else {
372         // Inject page fault into the guest     
373         if (inject_guest_pf(info, fault_addr, error_code) == -1) {
374             PrintError("Could not inject guest page fault for vaddr %p\n", (void *)fault_addr);
375             return -1;
376         }
377
378         PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
379         PrintError("Manual Says to inject page fault into guest\n");
380         return -1;
381     }
382
383     return 0;
384 }
385
386
387
388 static int handle_4MB_shadow_pagefault_32(struct guest_info * info, 
389                                      addr_t fault_addr, pf_error_t error_code, 
390                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde) 
391 {
392     pt_access_status_t shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
393     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
394     addr_t guest_fault_pa = BASE_TO_PAGE_ADDR_4MB(large_guest_pde->page_base_addr) + PAGE_OFFSET_4MB(fault_addr);  
395
396
397     PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
398     PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
399
400     struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info, guest_fault_pa);
401
402  
403     if (shdw_reg == NULL) {
404         // Inject a machine check in the guest
405         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
406         v3_raise_exception(info, MC_EXCEPTION);
407         return -1;
408     }
409
410     if (shadow_pte_access == PT_ACCESS_OK) {
411         // Inconsistent state...
412         // Guest Re-Entry will flush tables and everything should now workd
413         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
414         return 0;
415     }
416
417   
418     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
419         // Get the guest physical address of the fault
420
421         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
422             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
423             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_fault_pa);
424
425             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
426
427             PrintDebug("\tMapping shadow page (%p)\n", (void *)BASE_TO_PAGE_ADDR(shadow_pte->page_base_addr));
428
429             shadow_pte->present = 1;
430
431             /* We are assuming that the PDE entry has precedence
432              * so the Shadow PDE will mirror the guest PDE settings, 
433              * and we don't have to worry about them here
434              * Allow everything
435              */
436             shadow_pte->user_page = 1;
437
438             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
439                 shadow_pte->writable = 0;
440             } else {
441                 shadow_pte->writable = 1;
442             }
443
444             //set according to VMM policy
445             shadow_pte->write_through = large_guest_pde->write_through;
446             shadow_pte->cache_disable = large_guest_pde->cache_disable;
447             shadow_pte->global_page = large_guest_pde->global_page;
448             //
449       
450         } else {
451             if (v3_handle_mem_full_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
452                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
453                 return -1;
454             }
455         }
456     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
457
458         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
459
460             if (v3_handle_mem_wr_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
461                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
462                 return -1;
463             }
464         }
465
466     } else {
467         PrintError("Error in large page fault handler...\n");
468         PrintError("This case should have been handled at the top level handler\n");
469         return -1;
470     }
471
472     PrintDebug("Returning from large page fault handler\n");
473     return 0;
474 }
475
476
477
478
479
480
481
482
483
484
485
486
487 /* If we start to optimize we should look up the guest pages in the cache... */
488 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
489     pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
490     pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
491
492     addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
493     pde32_t * guest_pd = NULL;
494     pde32_t * guest_pde;
495
496     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
497         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
498         return -1;
499     }
500   
501     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
502   
503     if (guest_pde->large_page == 1) {
504         shadow_pde->present = 0;
505         PrintDebug("Invalidating Large Page\n");
506     } else if (shadow_pde->present == 1) {
507         pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
508         pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
509     
510         PrintDebug("Setting not present\n");
511     
512         shadow_pte->present = 0;
513     }
514     return 0;
515 }