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.


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