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.


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