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.


changed memory map to be layered on top of a single contiguous allocation
[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 static int cache_page_tables_32(struct guest_info * info, addr_t pde) {
22     struct shadow_page_state * state = &(info->shdw_pg_state);
23     addr_t pde_host_addr;
24     pde32_t * tmp_pde;
25     struct hashtable * pte_cache = NULL;
26     int i = 0;
27
28     if (pde == state->cached_cr3) {
29         return 1;
30     }
31
32     if (state->cached_ptes != NULL) {
33         hashtable_destroy(state->cached_ptes, 0, 0);
34         state->cached_ptes = NULL;
35     }
36
37     state->cached_cr3 = pde;
38
39     pte_cache = create_hashtable(0, &pte_hash_fn, &pte_equals);
40     state->cached_ptes = pte_cache;
41
42     if (guest_pa_to_host_va(info, pde, &pde_host_addr) == -1) {
43         PrintError("Could not lookup host address of guest PDE\n");
44         return -1;
45     }
46
47     tmp_pde = (pde32_t *)pde_host_addr;
48
49     add_pte_map(pte_cache, pde, pde_host_addr);
50
51
52     for (i = 0; i < MAX_PDE32_ENTRIES; i++) {
53         if ((tmp_pde[i].present) && (tmp_pde[i].large_page == 0)) {
54             addr_t pte_host_addr;
55
56             if (guest_pa_to_host_va(info, (addr_t)(BASE_TO_PAGE_ADDR(tmp_pde[i].pt_base_addr)), &pte_host_addr) == -1) {
57                 PrintError("Could not lookup host address of guest PDE\n");
58                 return -1;
59             }
60
61             add_pte_map(pte_cache, (addr_t)(BASE_TO_PAGE_ADDR(tmp_pde[i].pt_base_addr)), pte_host_addr); 
62         }
63     }
64
65     return 0;
66
67 }
68
69
70
71 // We assume that shdw_pg_state.guest_cr3 is pointing to the page tables we want to activate
72 // We also assume that the CPU mode has not changed during this page table transition
73 static inline int activate_shadow_pt_32(struct guest_info * info) {
74     struct cr3_32 * shadow_cr3 = (struct cr3_32 *)&(info->ctrl_regs.cr3);
75     struct cr3_32 * guest_cr3 = (struct cr3_32 *)&(info->shdw_pg_state.guest_cr3);
76     int cached = 0;
77   
78     // Check if shadow page tables are in the cache
79     cached = cache_page_tables_32(info, CR3_TO_PDE32_PA(*(addr_t *)guest_cr3));
80   
81     if (cached == -1) {
82         PrintError("CR3 Cache failed\n");
83         return -1;
84     } else if (cached == 0) {
85         struct shadow_page_data * shdw_page = create_new_shadow_pt(info);
86
87         shdw_page->cr3 = shdw_page->page_pa;
88     
89         shadow_cr3->pdt_base_addr = PAGE_BASE_ADDR(shdw_page->page_pa);
90         PrintDebug( "Created new shadow page table %p\n", (void *)BASE_TO_PAGE_ADDR(shadow_cr3->pdt_base_addr));
91     } else {
92         PrintDebug("Reusing cached shadow Page table\n");
93     }
94   
95     shadow_cr3->pwt = guest_cr3->pwt;
96     shadow_cr3->pcd = guest_cr3->pcd;
97   
98     return 0;
99 }
100
101 /* 
102  * *
103  * * 
104  * * 32 bit Page table fault handlers
105  * *
106  * *
107  */
108 static int handle_large_pagefault_32(struct guest_info * info, 
109                                      addr_t fault_addr, pf_error_t error_code, 
110                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde);
111
112 static int handle_shadow_pte32_fault(struct guest_info * info, 
113                                      addr_t fault_addr, 
114                                      pf_error_t error_code,
115                                      pte32_t * shadow_pt, 
116                                      pte32_t * guest_pt);
117
118
119 static inline int handle_shadow_pagefault_32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
120     pde32_t * guest_pd = NULL;
121     pde32_t * shadow_pd = CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
122     addr_t guest_cr3 = CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
123     pt_access_status_t guest_pde_access;
124     pt_access_status_t shadow_pde_access;
125     pde32_t * guest_pde = NULL;
126     pde32_t * shadow_pde = (pde32_t *)&(shadow_pd[PDE32_INDEX(fault_addr)]);
127
128     PrintDebug("Shadow page fault handler: %p\n", (void*) fault_addr );
129
130     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
131         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
132         return -1;
133     } 
134
135     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(fault_addr)]);
136
137
138     // Check the guest page permissions
139     guest_pde_access = v3_can_access_pde32(guest_pd, fault_addr, error_code);
140
141     // Check the shadow page permissions
142     shadow_pde_access = v3_can_access_pde32(shadow_pd, fault_addr, error_code);
143   
144     /* Was the page fault caused by the Guest's page tables? */
145     if (is_guest_pf(guest_pde_access, shadow_pde_access) == 1) {
146         PrintDebug("Injecting PDE pf to guest: (guest access error=%d) (pf error code=%d)\n", 
147                    *(uint_t *)&guest_pde_access, *(uint_t *)&error_code);
148         inject_guest_pf(info, fault_addr, error_code);
149         return 0;
150     }
151
152   
153     if (shadow_pde_access == PT_ACCESS_NOT_PRESENT) 
154         {
155             struct shadow_page_data * shdw_page =  create_new_shadow_pt(info);
156             pte32_t * shadow_pt = (pte32_t *)V3_VAddr((void *)shdw_page->page_pa);
157
158             shadow_pde->present = 1;
159             shadow_pde->user_page = guest_pde->user_page;
160             //    shadow_pde->large_page = guest_pde->large_page;
161             shadow_pde->large_page = 0;
162       
163
164             // VMM Specific options
165             shadow_pde->write_through = guest_pde->write_through;
166             shadow_pde->cache_disable = guest_pde->cache_disable;
167             shadow_pde->global_page = guest_pde->global_page;
168             //
169       
170             guest_pde->accessed = 1;
171       
172             shadow_pde->pt_base_addr = PAGE_BASE_ADDR((addr_t)V3_PAddr(shadow_pt));
173       
174             if (guest_pde->large_page == 0) {
175                 pte32_t * guest_pt = NULL;
176                 shadow_pde->writable = guest_pde->writable;
177
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_shadow_pte32_fault(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                 // ??  What if guest pde is dirty a this point?
191                 ((pde32_4MB_t *)guest_pde)->dirty = 0;
192                 shadow_pde->writable = 0;
193
194                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
195                     PrintError("Error handling large pagefault\n");
196                     return -1;
197                 }       
198
199             }
200         }
201     else if (shadow_pde_access == PT_ACCESS_OK) 
202         {
203             //
204             // PTE fault
205             //
206             pte32_t * shadow_pt = (pte32_t *)V3_VAddr( (void*)(addr_t) BASE_TO_PAGE_ADDR(shadow_pde->pt_base_addr) );
207
208             if (guest_pde->large_page == 0) {
209                 pte32_t * guest_pt = NULL;
210
211                 if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
212                     // Machine check the guest
213                     PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
214                     v3_raise_exception(info, MC_EXCEPTION);
215                     return 0;
216                 }
217         
218                 if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
219                     PrintError("Error handling Page fault caused by PTE\n");
220                     return -1;
221                 }
222             } else if (guest_pde->large_page == 1) {
223                 if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
224                     PrintError("Error handling large pagefault\n");
225                     return -1;
226                 }
227             }
228         }
229     else if ((shadow_pde_access == PT_ACCESS_WRITE_ERROR) && 
230              (guest_pde->large_page == 1)) 
231         {
232             //
233             // Page Directory Entry marked read-only
234             // Its a large page and we need to update the dirty bit in the guest
235             //
236
237             PrintDebug("Large page write error... Setting dirty bit and returning\n");
238             ((pde32_4MB_t *)guest_pde)->dirty = 1;
239             shadow_pde->writable = guest_pde->writable;
240             return 0;
241       
242         } 
243     else if (shadow_pde_access == PT_ACCESS_USER_ERROR) 
244         {
245             //
246             // Page Directory Entry marked non-user
247             //      
248             PrintDebug("Shadow Paging User access error (shadow_pde_access=0x%x, guest_pde_access=0x%x)\n", 
249                        shadow_pde_access, guest_pde_access);
250             inject_guest_pf(info, fault_addr, error_code);
251             return 0;
252         }
253     else 
254         {
255             // inject page fault in guest
256             inject_guest_pf(info, fault_addr, error_code);
257             PrintDebug("Unknown Error occurred (shadow_pde_access=%d)\n", shadow_pde_access);
258             PrintDebug("Manual Says to inject page fault into guest\n");
259 #ifdef DEBUG_SHADOW_PAGING
260             PrintDebug("Guest PDE: (access=%d)\n\t", guest_pde_access);
261             PrintPTEntry(info, PAGE_PD32, fault_addr, guest_pde);
262             PrintDebug("Shadow PDE: (access=%d)\n\t", shadow_pde_access);
263             PrintPTEntry(info, PAGE_PD32, fault_addr, shadow_pde);
264 #endif
265
266             return 0; 
267         }
268
269     PrintDebug("Returning end of PDE function (rip=%p)\n", (void *)(addr_t)(info->rip));
270     return 0;
271 }
272
273
274
275 /* The guest status checks have already been done,
276  * only special case shadow checks remain
277  */
278 static int handle_large_pagefault_32(struct guest_info * info, 
279                                      addr_t fault_addr, pf_error_t error_code, 
280                                      pte32_t * shadow_pt, pde32_4MB_t * large_guest_pde) 
281 {
282     pt_access_status_t shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
283     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
284     addr_t guest_fault_pa = BASE_TO_PAGE_ADDR_4MB(large_guest_pde->page_base_addr) + PAGE_OFFSET_4MB(fault_addr);  
285     struct shadow_page_state * state = &(info->shdw_pg_state);
286
287     struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info, guest_fault_pa);
288
289  
290     if (shdw_reg == NULL) {
291         // Inject a machine check in the guest
292         PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
293         v3_raise_exception(info, MC_EXCEPTION);
294         return -1;
295     }
296
297     if (shadow_pte_access == PT_ACCESS_OK) {
298         // Inconsistent state...
299         // Guest Re-Entry will flush tables and everything should now workd
300         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
301         return 0;
302     }
303
304   
305     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
306         // Get the guest physical address of the fault
307
308         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
309             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
310             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_fault_pa);
311
312             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
313
314             shadow_pte->present = 1;
315
316             /* We are assuming that the PDE entry has precedence
317              * so the Shadow PDE will mirror the guest PDE settings, 
318              * and we don't have to worry about them here
319              * Allow everything
320              */
321             shadow_pte->user_page = 1;
322
323             if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_fault_pa)) != NULL) {
324                 // Check if the entry is a page table...
325                 PrintDebug("Marking page as Guest Page Table (large page)\n");
326                 shadow_pte->writable = 0;
327             } else if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
328                 shadow_pte->writable = 0;
329             } else {
330                 shadow_pte->writable = 1;
331             }
332
333             //set according to VMM policy
334             shadow_pte->write_through = large_guest_pde->write_through;
335             shadow_pte->cache_disable = large_guest_pde->cache_disable;
336             shadow_pte->global_page = large_guest_pde->global_page;
337             //
338       
339         } else {
340             // Handle hooked pages as well as other special pages
341             //      if (handle_special_page_fault(info, fault_addr, guest_fault_pa, error_code) == -1) {
342
343             if (v3_handle_mem_full_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
344                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
345                 return -1;
346             }
347         }
348     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
349
350         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
351
352             if (v3_handle_mem_wr_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
353                 PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
354                 return -1;
355             }
356         }
357
358
359         if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_fault_pa)) != NULL) {
360             struct shadow_page_state * state = &(info->shdw_pg_state);
361             PrintDebug("Write operation on Guest PAge Table Page (large page)\n");
362             state->cached_cr3 = 0;
363             shadow_pte->writable = 1;
364         }
365
366     } else {
367         PrintError("Error in large page fault handler...\n");
368         PrintError("This case should have been handled at the top level handler\n");
369         return -1;
370     }
371
372     PrintDebug("Returning from large page fault handler\n");
373     return 0;
374 }
375
376
377
378
379 /* 
380  * We assume the the guest pte pointer has already been translated to a host virtual address
381  */
382 static int handle_shadow_pte32_fault(struct guest_info * info, 
383                                      addr_t fault_addr, 
384                                      pf_error_t error_code,
385                                      pte32_t * shadow_pt, 
386                                      pte32_t * guest_pt) {
387
388     pt_access_status_t guest_pte_access;
389     pt_access_status_t shadow_pte_access;
390     pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
391     pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
392     addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) +  PAGE_OFFSET(fault_addr);
393     struct shadow_page_state * state = &(info->shdw_pg_state);
394
395     struct v3_shadow_region * shdw_reg =  v3_get_shadow_region(info, guest_pa);
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_pa);
400         v3_raise_exception(info, MC_EXCEPTION);
401         return 0;
402     }
403
404     // Check the guest page permissions
405     guest_pte_access = v3_can_access_pte32(guest_pt, fault_addr, error_code);
406
407     // Check the shadow page permissions
408     shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
409   
410 #ifdef DEBUG_SHADOW_PAGING
411     PrintDebug("Guest PTE: (access=%d)\n\t", guest_pte_access);
412     PrintPTEntry(info, PAGE_PT32, fault_addr, guest_pte);
413     PrintDebug("Shadow PTE: (access=%d)\n\t", shadow_pte_access);
414     PrintPTEntry(info, PAGE_PT32, fault_addr, shadow_pte);
415 #endif
416   
417     /* Was the page fault caused by the Guest's page tables? */
418     if (is_guest_pf(guest_pte_access, shadow_pte_access) == 1) {
419         PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", 
420                    guest_pte_access, *(uint_t*)&error_code);    
421         inject_guest_pf(info, fault_addr, error_code);
422         return 0; 
423     }
424
425   
426   
427     if (shadow_pte_access == PT_ACCESS_OK) {
428         // Inconsistent state...
429         // Guest Re-Entry will flush page tables and everything should now work
430         PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
431         return 0;
432     }
433
434
435     if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
436         // Page Table Entry Not Present
437         PrintDebug("guest_pa =%p\n", (void *)guest_pa);
438
439         if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
440             (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
441             addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
442       
443             shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
444       
445             shadow_pte->present = guest_pte->present;
446             shadow_pte->user_page = guest_pte->user_page;
447       
448             //set according to VMM policy
449             shadow_pte->write_through = guest_pte->write_through;
450             shadow_pte->cache_disable = guest_pte->cache_disable;
451             shadow_pte->global_page = guest_pte->global_page;
452             //
453       
454             guest_pte->accessed = 1;
455       
456             if (guest_pte->dirty == 1) {
457                 shadow_pte->writable = guest_pte->writable;
458             } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
459                 shadow_pte->writable = guest_pte->writable;
460                 guest_pte->dirty = 1;
461             } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
462                 shadow_pte->writable = 0;
463             }
464
465             // dirty flag has been set, check if its in the cache
466             if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
467                 if (error_code.write == 1) {
468                     state->cached_cr3 = 0;
469                     shadow_pte->writable = guest_pte->writable;
470                 } else {
471                     shadow_pte->writable = 0;
472                 }
473             }
474
475             // Write hooks trump all, and are set Read Only
476             if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
477                 shadow_pte->writable = 0;
478             }
479
480         } else {
481             // Page fault handled by hook functions
482
483             if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
484                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
485                 return -1;
486             }
487         }
488     } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
489         guest_pte->dirty = 1;
490
491         if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
492             if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
493                 PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
494                 return -1;
495             }
496         } else {
497             PrintDebug("Shadow PTE Write Error\n");
498             shadow_pte->writable = guest_pte->writable;
499         }
500
501         if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
502             struct shadow_page_state * state = &(info->shdw_pg_state);
503             PrintDebug("Write operation on Guest PAge Table Page\n");
504             state->cached_cr3 = 0;
505         }
506
507         return 0;
508
509     } else {
510         // Inject page fault into the guest     
511         inject_guest_pf(info, fault_addr, error_code);
512         PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
513         PrintError("Manual Says to inject page fault into guest\n");
514         return -1;
515     }
516
517     PrintDebug("Returning end of function\n");
518     return 0;
519 }
520
521
522
523 /* If we start to optimize we should look up the guest pages in the cache... */
524 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
525     pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
526     pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
527
528     addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
529     pde32_t * guest_pd = NULL;
530     pde32_t * guest_pde;
531
532     if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
533         PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
534         return -1;
535     }
536   
537     guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
538   
539     if (guest_pde->large_page == 1) {
540         shadow_pde->present = 0;
541         PrintDebug("Invalidating Large Page\n");
542     } else if (shadow_pde->present == 1) {
543         pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
544         pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
545     
546         PrintDebug("Setting not present\n");
547     
548         shadow_pte->present = 0;
549     }
550     return 0;
551 }