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.


starting work on 64 bit shadow 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     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
267   struct v3_shadow_region * shdw_reg = v3_get_shadow_region(info, guest_fault_pa);
268
269  
270   if ((shdw_reg == NULL) || 
271       (shdw_reg->host_type == SHDW_REGION_INVALID)) {
272     // Inject a machine check in the guest
273     PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_fault_pa);
274     v3_raise_exception(info, MC_EXCEPTION);
275     return -1;
276   }
277
278   if (shadow_pte_access == PT_ACCESS_OK) {
279     // Inconsistent state...
280     // Guest Re-Entry will flush tables and everything should now workd
281     PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
282     return 0;
283   }
284
285   
286   if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
287     // Get the guest physical address of the fault
288
289     if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) || 
290         (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
291       struct shadow_page_state * state = &(info->shdw_pg_state);
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->vmm_info = PT32_GUEST_PT;
309         shadow_pte->writable = 0;
310       } else if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
311         shadow_pte->writable = 0;
312       } else {
313         shadow_pte->writable = 1;
314       }
315
316       //set according to VMM policy
317       shadow_pte->write_through = 0;
318       shadow_pte->cache_disable = 0;
319       shadow_pte->global_page = 0;
320       //
321       
322     } else {
323       // Handle hooked pages as well as other special pages
324       //      if (handle_special_page_fault(info, fault_addr, guest_fault_pa, error_code) == -1) {
325
326       if (v3_handle_mem_full_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
327         PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
328         return -1;
329       }
330     }
331   } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
332
333     if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
334
335       if (v3_handle_mem_wr_hook(info, fault_addr, guest_fault_pa, shdw_reg, error_code) == -1) {
336         PrintError("Special Page Fault handler returned error for address: %p\n", (void *)fault_addr);
337         return -1;
338       }
339     } else if (shadow_pte->vmm_info == PT32_GUEST_PT) {
340       struct shadow_page_state * state = &(info->shdw_pg_state);
341       PrintDebug("Write operation on Guest PAge Table Page (large page)\n");
342       state->cached_cr3 = 0;
343       shadow_pte->writable = 1;
344     }
345
346   } else {
347     PrintError("Error in large page fault handler...\n");
348     PrintError("This case should have been handled at the top level handler\n");
349     return -1;
350   }
351
352   PrintDebug("Returning from large page fault handler\n");
353   return 0;
354 }
355
356
357
358
359 /* 
360  * We assume the the guest pte pointer has already been translated to a host virtual address
361  */
362 static int handle_shadow_pte32_fault(struct guest_info * info, 
363                                      addr_t fault_addr, 
364                                      pf_error_t error_code,
365                                      pte32_t * shadow_pt, 
366                                      pte32_t * guest_pt) {
367
368   pt_access_status_t guest_pte_access;
369   pt_access_status_t shadow_pte_access;
370   pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
371   pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
372   addr_t guest_pa = BASE_TO_PAGE_ADDR((addr_t)(guest_pte->page_base_addr)) +  PAGE_OFFSET(fault_addr);
373
374   struct v3_shadow_region * shdw_reg =  v3_get_shadow_region(info, guest_pa);
375
376   if ((shdw_reg == NULL) || 
377       (shdw_reg->host_type == SHDW_REGION_INVALID)) {
378     // Inject a machine check in the guest
379     PrintDebug("Invalid Guest Address in page table (0x%p)\n", (void *)guest_pa);
380     v3_raise_exception(info, MC_EXCEPTION);
381     return 0;
382   }
383
384   // Check the guest page permissions
385   guest_pte_access = v3_can_access_pte32(guest_pt, fault_addr, error_code);
386
387   // Check the shadow page permissions
388   shadow_pte_access = v3_can_access_pte32(shadow_pt, fault_addr, error_code);
389   
390 #ifdef DEBUG_SHADOW_PAGING
391   PrintDebug("Guest PTE: (access=%d)\n\t", guest_pte_access);
392   PrintPTEntry(PAGE_PT32, fault_addr, guest_pte);
393   PrintDebug("Shadow PTE: (access=%d)\n\t", shadow_pte_access);
394   PrintPTEntry(PAGE_PT32, fault_addr, shadow_pte);
395 #endif
396   
397   /* Was the page fault caused by the Guest's page tables? */
398   if (is_guest_pf(guest_pte_access, shadow_pte_access) == 1) {
399     PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", 
400                guest_pte_access, *(uint_t*)&error_code);    
401     inject_guest_pf(info, fault_addr, error_code);
402     return 0; 
403   }
404   
405   
406   if (shadow_pte_access == PT_ACCESS_OK) {
407     // Inconsistent state...
408     // Guest Re-Entry will flush page tables and everything should now work
409     PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
410     return 0;
411   }
412
413
414   if (shadow_pte_access == PT_ACCESS_NOT_PRESENT) {
415     // Page Table Entry Not Present
416     PrintDebug("guest_pa =%p\n", (void *)guest_pa);
417
418     if ((shdw_reg->host_type == SHDW_REGION_ALLOCATED) ||
419         (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK)) {
420       struct shadow_page_state * state = &(info->shdw_pg_state);
421       addr_t shadow_pa = v3_get_shadow_addr(shdw_reg, guest_pa);
422       
423       shadow_pte->page_base_addr = PAGE_BASE_ADDR(shadow_pa);
424       
425       shadow_pte->present = guest_pte->present;
426       shadow_pte->user_page = guest_pte->user_page;
427       
428       //set according to VMM policy
429       shadow_pte->write_through = 0;
430       shadow_pte->cache_disable = 0;
431       shadow_pte->global_page = 0;
432       //
433       
434       guest_pte->accessed = 1;
435       
436       if (find_pte_map(state->cached_ptes, PAGE_ADDR(guest_pa)) != NULL) {
437         // Check if the entry is a page table...
438         PrintDebug("Marking page as Guest Page Table %d\n", shadow_pte->writable);
439         shadow_pte->vmm_info = PT32_GUEST_PT;
440       }
441
442       if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
443         shadow_pte->writable = 0;
444       } else if (guest_pte->dirty == 1) {
445         shadow_pte->writable = guest_pte->writable;
446       } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
447         shadow_pte->writable = guest_pte->writable;
448         guest_pte->dirty = 1;
449         
450         if (shadow_pte->vmm_info == PT32_GUEST_PT) {
451           // Well that was quick...
452           struct shadow_page_state * state = &(info->shdw_pg_state);
453           PrintDebug("Immediate Write operation on Guest PAge Table Page\n");
454           state->cached_cr3 = 0;
455         }
456
457       } else if ((guest_pte->dirty == 0) && (error_code.write == 0)) {  // was =
458         shadow_pte->writable = 0;
459       }
460
461     } else {
462       // Page fault handled by hook functions
463
464       if (v3_handle_mem_full_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
465         PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
466         return -1;
467       }
468     }
469   } else if (shadow_pte_access == PT_ACCESS_WRITE_ERROR) {
470     guest_pte->dirty = 1;
471
472     if (shdw_reg->host_type == SHDW_REGION_WRITE_HOOK) {
473       if (v3_handle_mem_wr_hook(info, fault_addr, guest_pa, shdw_reg, error_code) == -1) {
474         PrintError("Special Page fault handler returned error for address: %p\n",  (void *)fault_addr);
475         return -1;
476       }
477     } else {
478       PrintDebug("Shadow PTE Write Error\n");
479       shadow_pte->writable = guest_pte->writable;
480     }
481
482     if (shadow_pte->vmm_info == PT32_GUEST_PT) {
483       struct shadow_page_state * state = &(info->shdw_pg_state);
484       PrintDebug("Write operation on Guest PAge Table Page\n");
485       state->cached_cr3 = 0;
486     }
487
488     return 0;
489
490   } else {
491     // Inject page fault into the guest 
492     inject_guest_pf(info, fault_addr, error_code);
493     PrintError("PTE Page fault fell through... Not sure if this should ever happen\n");
494     PrintError("Manual Says to inject page fault into guest\n");
495     return -1;
496   }
497
498   PrintDebug("Returning end of function\n");
499   return 0;
500 }
501
502
503
504 /* If we start to optimize we should look up the guest pages in the cache... */
505 static inline int handle_shadow_invlpg_32(struct guest_info * info, addr_t vaddr) {
506   pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32_VA(info->ctrl_regs.cr3);
507   pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(vaddr)];
508
509   addr_t guest_cr3 =  CR3_TO_PDE32_PA(info->shdw_pg_state.guest_cr3);
510   pde32_t * guest_pd = NULL;
511   pde32_t * guest_pde;
512
513   if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
514     PrintError("Invalid Guest PDE Address: 0x%p\n",  (void *)guest_cr3);
515     return -1;
516   }
517   
518   guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(vaddr)]);
519   
520   if (guest_pde->large_page == 1) {
521     shadow_pde->present = 0;
522     PrintDebug("Invalidating Large Page\n");
523   } else if (shadow_pde->present == 1) {
524     pte32_t * shadow_pt = (pte32_t *)(addr_t)BASE_TO_PAGE_ADDR_4KB(shadow_pde->pt_base_addr);
525     pte32_t * shadow_pte = (pte32_t *) V3_VAddr( (void*) &shadow_pt[PTE32_INDEX(vaddr)] );
526     
527     PrintDebug("Setting not present\n");
528     
529     shadow_pte->present = 0;
530   }
531   return 0;
532 }