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.


1feb2e5bf80f9188b975f850382270b52062d92b
[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 (guest_pa_to_host_va(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 (guest_pa_to_host_va(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_shadow_region * shdw_reg =  v3_get_shadow_region(info->vm_info, info->cpu_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 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 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->host_type == SHDW_REGION_ALLOCATED) ||
342             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
343             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, info->cpu_id, guest_pa);
344       
345             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
346
347             PrintDebug("\tMapping shadow page (%p)\n", (void *)BASE_TO_PAGE_ADDR(shadow_pte->page_base_addr));
348       
349             shadow_pte->present = guest_pte->present;
350             shadow_pte->user_page = guest_pte->user_page;
351       
352             //set according to VMM policy
353             shadow_pte->write_through = guest_pte->write_through;
354             shadow_pte->cache_disable = guest_pte->cache_disable;
355             shadow_pte->global_page = guest_pte->global_page;
356             //
357       
358             guest_pte->accessed = 1;
359       
360             if (guest_pte->dirty == 1) {
361                 shadow_pte->writable = guest_pte->writable;
362             } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
363                 shadow_pte->writable = guest_pte->writable;
364                 guest_pte->dirty = 1;
365             } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
366                 shadow_pte->writable = 0;
367             }
368
369
370
371             // Write hooks trump all, and are set Read Only
372             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
373                 shadow_pte->writable = 0;
374             }
375
376         } else {
377             // Page fault handled by hook functions
378
379             if (v3_handle_mem_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
380                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
381                 return -1;
382             }
383         }
384     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
385         guest_pte->dirty = 1;
386
387         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
388             if (v3_handle_mem_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
389                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
390                 return -1;
391             }
392         } else {
393             PrintDebug("Shadow PTE Write Error\n");
394             shadow_pte->writable = guest_pte->writable;
395         }
396
397
398         return 0;
399
400     } else {
401         // Inject page fault into the guest     
402         if (v3_inject_guest_pf(info, fault_addr, error_code) == -1) {
403             PrintError("Could not inject guest page fault for vaddr %p\n", (void *)fault_addr);
404             return -1;
405         }
406
407         PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
408         PrintError("Manual Says to inject page fault into guest\n");
409         return -1;
410     }
411
412     return 0;
413 }
414
415
416
417 static int handle_4MB_shadow_pagefault_32(struct guest_info * info, 
418                                      addr_t fault_addr, pf_error_t error_code, 
419                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde) 
420 {
421     pt_access_status_t shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
422     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
423     addr_t guest_fault_pa = BASE_TO_PAGE_ADDR_4MB(large_guest_pde->page_base_addr) + PAGE_OFFSET_4MB(fault_addr);  
424
425
426     PrintDebug("Handling 4MB fault (guest_fault_pa=%p) (error_code=%x)\n", (void *)guest_fault_pa, *(uint_t*)&error_code);
427     PrintDebug("ShadowPT=%p, LargeGuestPDE=%p\n", shadow_pt, large_guest_pde);
428
429     struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info->vm_info, info->cpu_id, guest_fault_pa);
430
431  
432     if (shdw_reg == NULL) {
433         // Inject a machine check in the guest
434         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
435         v3_raise_exception(info, MC_EXCEPTION);
436         return -1;
437     }
438
439     if (shadow_pte_access == PT_ACCESS_OK) {
440         // Inconsistent state...
441         // Guest Re-Entry will flush tables and everything should now workd
442         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
443         return 0;
444     }
445
446   
447     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
448         // Get the guest physical address of the fault
449
450         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
451             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
452             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, info->cpu_id, guest_fault_pa);
453
454             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
455
456             PrintDebug("\tMapping shadow page (%p)\n", (void *)BASE_TO_PAGE_ADDR(shadow_pte->page_base_addr));
457
458             shadow_pte->present = 1;
459
460             /* We are assuming that the PDE entry has precedence
461              * so the Shadow PDE will mirror the guest PDE settings, 
462              * and we don't have to worry about them here
463              * Allow everything
464              */
465             shadow_pte->user_page = 1;
466
467             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
468                 shadow_pte->writable = 0;
469             } else {
470                 shadow_pte->writable = 1;
471             }
472
473             //set according to VMM policy
474             shadow_pte->write_through = large_guest_pde->write_through;
475             shadow_pte->cache_disable = large_guest_pde->cache_disable;
476             shadow_pte->global_page = large_guest_pde->global_page;
477             //
478       
479         } else {
480             if (v3_handle_mem_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
481                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
482                 return -1;
483             }
484         }
485     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
486
487         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
488
489             if (v3_handle_mem_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
490                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
491                 return -1;
492             }
493         }
494
495     } else {
496         PrintError("Error in large page fault handler...\n");
497         PrintError("This case should have been handled at the top level handler\n");
498         return -1;
499     }
500
501     PrintDebug("Returning from large page fault handler\n");
502     return 0;
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516 /* If we start to optimize we should look up the guest pages in the cache... */
517 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
518     pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
519     pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
520
521     addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
522     pde32_t * guest_pd = NULL;
523     pde32_t * guest_pde;
524
525     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
526         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
527         return -1;
528     }
529   
530     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
531   
532     if (guest_pde->large_page == 1) {
533         shadow_pde->present = 0;
534         PrintDebug("Invalidating Large Page\n");
535     } else if (shadow_pde->present == 1) {
536         pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
537         pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
538     
539         PrintDebug("Setting not present\n");
540     
541         shadow_pte->present = 0;
542     }
543     return 0;
544 }