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.


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