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.


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