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.


cleaned up 32 bit shadow paging implementation
[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     addr_t shadow_pt;
67         
68     shadow_pt = create_new_shadow_pt(info);
69     
70     shadow_cr3->pdt_base_addr = (addr_t)V3_PAddr((void *)(addr_t)PAGE_BASE_ADDR(shadow_pt));
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       pte32_t * shadow_pt =  (pte32_t *)create_new_shadow_pt(info);
137
138       shadow_pde->present = 1;
139       shadow_pde->user_page = guest_pde->user_page;
140       //    shadow_pde->large_page = guest_pde->large_page;
141       shadow_pde->large_page = 0;
142       
143
144       // VMM Specific options
145       shadow_pde->write_through = 0;
146       shadow_pde->cache_disable = 0;
147       shadow_pde->global_page = 0;
148       //
149       
150       guest_pde->accessed = 1;
151       
152       shadow_pde->pt_base_addr = PAGE_BASE_ADDR((addr_t)V3_PAddr(shadow_pt));
153       
154       if (guest_pde->large_page == 0) {
155         pte32_t * guest_pt = NULL;
156         shadow_pde->writable = guest_pde->writable;
157
158         if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
159           // Machine check the guest
160           PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
161           v3_raise_exception(info, MC_EXCEPTION);
162           return 0;
163         }
164
165         if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
166           PrintError("Error handling Page fault caused by PTE\n");
167           return -1;
168         }
169       } else {
170         // ??  What if guest pde is dirty a this point?
171         ((pde32_4MB_t *)guest_pde)->dirty = 0;
172         shadow_pde->writable = 0;
173
174         if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
175           PrintError("Error handling large pagefault\n");
176           return -1;
177         }       
178
179       }
180     }
181   else if (shadow_pde_access == PT_ACCESS_OK) 
182     {
183       //
184       // PTE fault
185       //
186       pte32_t * shadow_pt = (pte32_t *)V3_VAddr( (void*)(addr_t) BASE_TO_PAGE_ADDR(shadow_pde->pt_base_addr) );
187
188       if (guest_pde->large_page == 0) {
189         pte32_t * guest_pt = NULL;
190
191         if (guest_pa_to_host_va(info, BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr), (addr_t*)&guest_pt) == -1) {
192           // Machine check the guest
193           PrintDebug("Invalid Guest PTE Address: 0x%p\n", (void *)BASE_TO_PAGE_ADDR(guest_pde->pt_base_addr));
194           v3_raise_exception(info, MC_EXCEPTION);
195           return 0;
196         }
197         
198         if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
199           PrintError("Error handling Page fault caused by PTE\n");
200           return -1;
201         }
202       } else if (guest_pde->large_page == 1) {
203         if (handle_large_pagefault_32(info, fault_addr, error_code, shadow_pt, (pde32_4MB_t *)guest_pde) == -1) {
204           PrintError("Error handling large pagefault\n");
205           return -1;
206         }
207       }
208     }
209   else if ((shadow_pde_access == PT_ACCESS_WRITE_ERROR) && 
210            (guest_pde->large_page == 1) && 
211            (((pde32_4MB_t *)guest_pde)->dirty == 0)) 
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   if (shadow_pte_access == PT_ACCESS_OK) {
410     // Inconsistent state...
411     // Guest Re-Entry will flush page tables and everything should now work
412     PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
413     return 0;
414   }
415
416
417   if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
418     // Page Table Entry Not Present
419     PrintDebug("guest_pa =%p\n", (void *)guest_pa);
420
421     if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
422         (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
423       addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
424       
425       shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
426       
427       shadow_pte->present = guest_pte->present;
428       shadow_pte->user_page = guest_pte->user_page;
429       
430       //set according to VMM policy
431       shadow_pte->write_through = 0;
432       shadow_pte->cache_disable = 0;
433       shadow_pte->global_page = 0;
434       //
435       
436       guest_pte->accessed = 1;
437       
438       if (guest_pte->dirty == 1) {
439         shadow_pte->writable = guest_pte->writable;
440       } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
441         shadow_pte->writable = guest_pte->writable;
442         guest_pte->dirty = 1;
443       } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {
444         shadow_pte->writable = 0;
445       }
446
447       // dirty flag has been set, check if its in the cache
448       if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
449         if (error_code.write == 1) {
450           state->cached_cr3 = 0;
451           shadow_pte->writable = guest_pte->writable;
452         } else {
453           shadow_pte->writable = 0;
454         }
455       }
456
457       // Write hooks trump all, and are set Read Only
458       if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
459         shadow_pte->writable = 0;
460       }
461
462     } else {
463       // Page fault handled by hook functions
464
465       if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
466         PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
467         return -1;
468       }
469     }
470   } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
471     guest_pte->dirty = 1;
472
473     if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
474       if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
475         PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
476         return -1;
477       }
478     } else {
479       PrintDebug("Shadow PTE Write Error\n");
480       shadow_pte->writable = guest_pte->writable;
481     }
482
483     if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
484       struct shadow_page_state * state = &(info->shdw_pg_state);
485       PrintDebug("Write operation on Guest PAge Table Page\n");
486       state->cached_cr3 = 0;
487     }
488
489     return 0;
490
491   } else {
492     // Inject page fault into the guest 
493     inject_guest_pf(info, fault_addr, error_code);
494     PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
495     PrintError("Manual Says to inject page fault into guest\n");
496     return -1;
497   }
498
499   PrintDebug("Returning end of function\n");
500   return 0;
501 }
502
503
504
505 /* If we start to optimize we should look up the guest pages in the cache... */
506 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
507   pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
508   pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
509
510   addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
511   pde32_t * guest_pd = NULL;
512   pde32_t * guest_pde;
513
514   if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
515     PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
516     return -1;
517   }
518   
519   guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
520   
521   if (guest_pde->large_page == 1) {
522     shadow_pde->present = 0;
523     PrintDebug("Invalidating Large Page\n");
524   } else if (shadow_pde->present == 1) {
525     pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
526     pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
527     
528     PrintDebug("Setting not present\n");
529     
530     shadow_pte->present = 0;
531   }
532   return 0;
533 }