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.


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