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.


*** empty log message ***
[palacios.git] / palacios / src / palacios / vmm_shadow_paging.c
1 #include <palacios/vmm_shadow_paging.h>
2
3
4 #include <palacios/vmm.h>
5 #include <palacios/vm_guest_mem.h>
6 #include <palacios/vmm_decoder.h>
7
8
9
10 int init_shadow_page_state(struct shadow_page_state * state) {
11   state->guest_mode = PDE32;
12   state->shadow_mode = PDE32;
13   
14   state->guest_cr3 = 0;
15   state->shadow_cr3 = 0;
16
17   return 0;
18 }
19
20 int handle_shadow_pagefault(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
21   
22   if (info->mem_mode == PHYSICAL_MEM) {
23     // If paging is not turned on we need to handle the special cases
24     return handle_special_page_fault(info, fault_addr, error_code);
25   } else if (info->mem_mode == VIRTUAL_MEM) {
26
27     switch (info->cpu_mode) {
28     case PROTECTED:
29       return handle_shadow_pagefault32(info, fault_addr, error_code);
30       break;
31     case PROTECTED_PAE:
32     case LONG:
33       // currently not handled
34       return -1;
35       break;
36     default:
37       return -1;
38     }
39   } else {
40     PrintDebug("Invalid Memory mode\n");
41     return -1;
42   }
43 }
44
45
46 int handle_shadow_pagefault32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
47   pde32_t * guest_pde = NULL;
48   pde32_t * shadow_pde = (pde32_t *)CR3_TO_PDE32(info->shdw_pg_state.shadow_cr3);
49   addr_t guest_cr3 = CR3_TO_PDE32(info->shdw_pg_state.guest_cr3);
50   pt_access_status_t guest_pde_access;
51   pt_access_status_t shadow_pde_access;
52   pde32_t * guest_pde_entry = NULL;
53   pde32_t * shadow_pde_entry = (pde32_t *)&(shadow_pde[PDE32_INDEX(fault_addr)]);
54
55   if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pde) == -1) {
56     PrintDebug("Invalid Guest PDE Address: 0x%x\n", guest_cr3);
57     return -1;
58   }
59
60
61   guest_pde_entry = (pde32_t *)&(guest_pde[PDE32_INDEX(fault_addr)]);
62
63   // Check the guest page permissions
64   guest_pde_access = can_access_pde32(guest_pde, fault_addr, error_code);
65
66   if (guest_pde_access != PT_ACCESS_OK) {
67     // inject page fault to the guest (Guest PDE fault)
68
69     info->ctrl_regs.cr2 = fault_addr;
70     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
71
72     PrintDebug("Injecting PDE pf to guest\n");
73     return 0;
74   }
75
76
77   // Check that the Guest PDE entry points to valid memory
78   // else Machine Check the guest
79
80   shadow_pde_access = can_access_pde32(shadow_pde, fault_addr, error_code);
81
82
83   if (shadow_pde_access == PT_ENTRY_NOT_PRESENT) {
84     pte32_t * shadow_pte = NULL;
85
86     V3_AllocPages(shadow_pte, 1);
87     memset(shadow_pte, 0, PAGE_SIZE);
88
89     shadow_pde_entry->pt_base_addr = PD32_BASE_ADDR(shadow_pte);
90     
91
92     shadow_pde_entry->present = 1;
93     shadow_pde_entry->user_page = guest_pde_entry->user_page;
94     
95     // VMM Specific options
96     shadow_pde_entry->write_through = 0;
97     shadow_pde_entry->cache_disable = 0;
98     shadow_pde_entry->global_page = 0;
99     //
100
101     guest_pde_entry->accessed = 1;
102
103     if (guest_pde_entry->large_page == 0) {
104       shadow_pde_entry->writable = guest_pde_entry->writable;
105     } else {
106       /*
107        * Check the Intel manual because we are ignoring Large Page issues here
108        * Also be wary of hooked pages
109        */
110
111       PrintDebug("Large PAge!!!\n");
112       return -1;
113
114     }
115
116   } else if (shadow_pde_access == PT_WRITE_ERROR) {
117
118     //
119     // Page Directory Entry marked read-only
120     //
121
122     PrintDebug("Shadow Paging Write Error\n");
123     return -1;
124   } else if (shadow_pde_access == PT_USER_ERROR) {
125
126     //
127     // Page Directory Entry marked non-user
128     //
129     
130     PrintDebug("Shadow Paging User access error\n");
131     return -1;
132   } else if (shadow_pde_access == PT_ACCESS_OK) {
133     pte32_t * shadow_pte = (pte32_t *)PDE32_T_ADDR((*shadow_pde_entry));
134     pte32_t * guest_pte = NULL;
135
136     // Page Table Entry fault
137     
138     if (guest_pa_to_host_va(info, PDE32_T_ADDR((*guest_pde_entry)), (addr_t*)&guest_pte) == -1) {
139       PrintDebug("Invalid Guest PTE Address: 0x%x\n", PDE32_T_ADDR((*guest_pde_entry)));
140       // Machine check the guest
141
142       raise_exception(info, MC_EXCEPTION);
143       
144       return 0;
145     }
146
147
148     if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pte, guest_pte)  == -1) {
149       PrintDebug("Error handling Page fault caused by PTE\n");
150       return -1;
151     }
152
153  } else {
154     // Unknown error raise page fault in guest
155     info->ctrl_regs.cr2 = fault_addr;
156     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
157
158     // For debugging we will return an error here for the time being, 
159     // this probably shouldn't ever happen
160     PrintDebug("Unknown Error occurred\n");
161     PrintDebug("Manual Says to inject page fault into guest\n");
162     return -1;
163   }
164
165   //PrintDebugPageTables(shadow_pde);
166   PrintDebug("Returning end of PDE function\n");
167   return 0;
168 }
169
170
171
172 /* 
173  * We assume the the guest pte pointer has already been translated to a host virtual address
174  */
175 int handle_shadow_pte32_fault(struct guest_info * info, 
176                               addr_t fault_addr, 
177                               pf_error_t error_code,
178                               pte32_t * shadow_pte, 
179                               pte32_t * guest_pte) {
180
181   pt_access_status_t guest_pte_access;
182   pt_access_status_t shadow_pte_access;
183   pte32_t * guest_pte_entry = (pte32_t *)&(guest_pte[PTE32_INDEX(fault_addr)]);;
184   pte32_t * shadow_pte_entry = (pte32_t *)&(shadow_pte[PTE32_INDEX(fault_addr)]);
185
186
187   // Check the guest page permissions
188   guest_pte_access = can_access_pte32(guest_pte, fault_addr, error_code);
189
190   
191   if (guest_pte_access != PT_ACCESS_OK) {
192     // Inject page fault into the guest 
193     
194     info->ctrl_regs.cr2 = fault_addr;
195     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
196     
197     PrintDebug("Access error injecting pf to guest\n");
198     return 0;
199   }
200   
201   
202   shadow_pte_access = can_access_pte32(shadow_pte, fault_addr, error_code);
203
204   if (shadow_pte_access == PT_ACCESS_OK) {
205     // Inconsistent state...
206     // Guest Re-Entry will flush page tables and everything should now work
207     PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
208     return 0;
209   } else if (shadow_pte_access == PT_ENTRY_NOT_PRESENT) {
210     addr_t shadow_pa;
211     addr_t guest_pa = PTE32_T_ADDR((*guest_pte_entry));
212
213     // Page Table Entry Not Present
214
215     host_region_type_t host_page_type = get_shadow_addr_type(info, guest_pa);
216
217     if (host_page_type == HOST_REGION_INVALID) {
218       // Inject a machine check in the guest
219
220       raise_exception(info, MC_EXCEPTION);
221
222       PrintDebug("Invalid Guest Address in page table (0x%x)\n", guest_pa);
223       return 0;
224
225     } else if (host_page_type == HOST_REGION_PHYSICAL_MEMORY) {
226       
227       shadow_pa = get_shadow_addr(info, guest_pa);
228       
229       shadow_pte_entry->page_base_addr = PT32_BASE_ADDR(shadow_pa);
230       
231       shadow_pte_entry->present = guest_pte_entry->present;
232       shadow_pte_entry->user_page = guest_pte_entry->user_page;
233       
234       //set according to VMM policy
235       shadow_pte_entry->write_through = 0;
236       shadow_pte_entry->cache_disable = 0;
237       shadow_pte_entry->global_page = 0;
238       //
239       
240       guest_pte_entry->accessed = 1;
241       
242       if (guest_pte_entry->dirty == 1) {
243         shadow_pte_entry->writable = guest_pte_entry->writable;
244       } else if ((guest_pte_entry->dirty == 0) && (error_code.write == 1)) {
245         shadow_pte_entry->writable = guest_pte_entry->writable;
246         guest_pte_entry->dirty = 1;
247       } else if ((guest_pte_entry->dirty = 0) && (error_code.write == 0)) {
248         shadow_pte_entry->writable = 0;
249       }
250     } else {
251       // Page fault handled by hook functions
252       if (handle_special_page_fault(info, fault_addr, error_code) == -1) {
253         PrintDebug("Special Page fault handler returned error for address: %x\n", fault_addr);
254         return -1;
255       }
256     }
257
258   } else if ((shadow_pte_access == PT_WRITE_ERROR) &&
259              (guest_pte_entry->dirty == 0)) {
260     guest_pte_entry->dirty = 1;
261     shadow_pte_entry->writable = guest_pte_entry->writable;
262
263     PrintDebug("Shadow PTE Write Error\n");
264
265     return 0;
266   } else {
267     // Inject page fault into the guest 
268         
269     info->ctrl_regs.cr2 = fault_addr;
270     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
271
272     PrintDebug("PTE Page fault fell through... Not sure if this should ever happen\n");
273     PrintDebug("Manual Says to inject page fault into guest\n");
274     return -1;
275   }
276
277   PrintDebug("Returning end of function\n");
278   return 0;
279 }
280
281
282
283 addr_t create_new_shadow_pt32(struct guest_info * info) {
284   void * host_pde = 0;
285
286   V3_AllocPages(host_pde, 1);
287   memset(host_pde, 0, PAGE_SIZE);
288
289   return (addr_t)host_pde;
290 }
291
292
293
294 /* Currently Does not work with Segmentation!!! */
295 int handle_shadow_invlpg(struct guest_info * info) {
296   if (info->mem_mode != VIRTUAL_MEM) {
297     // Paging must be turned on...
298     // should handle with some sort of fault I think
299     PrintDebug("ERROR: INVLPG called in non paged mode\n");
300     return -1;
301   }
302
303
304   if (info->cpu_mode == PROTECTED) {
305     char instr[15];
306     int ret;
307     int index = 0;
308
309     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
310     if (ret != 15) {
311       PrintDebug("Could not read instruction 0x%x (ret=%d)\n", info->rip, ret);
312       return -1;
313     }
314
315    
316     /* Can INVLPG work with Segments?? */
317     while (is_prefix_byte(instr[index])) {
318       index++;
319     }
320     
321     
322     if ((instr[index] == (uchar_t)0x0f) &&
323         (instr[index + 1] == (uchar_t)0x01)) {
324
325       addr_t first_operand;
326       addr_t second_operand;
327       operand_type_t addr_type;
328
329       index += 2;
330
331       addr_type = decode_operands32(&(info->vm_regs), instr + index, &index, &first_operand, &second_operand, REG32);
332
333       if (addr_type == MEM_OPERAND) {
334         pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32(info->shdw_pg_state.shadow_cr3);
335         pde32_t * shadow_pde_entry = (pde32_t *)&shadow_pd[PDE32_INDEX(first_operand)];
336
337         //PrintDebug("PDE Index=%d\n", PDE32_INDEX(first_operand));
338         //PrintDebug("FirstOperand = %x\n", first_operand);
339
340         if (shadow_pde_entry->large_page == 1) {
341           shadow_pde_entry->present = 0;
342         } else {
343           if (shadow_pde_entry->present == 1) {
344             pte32_t * shadow_pt = (pte32_t *)PDE32_T_ADDR((*shadow_pde_entry));
345             pte32_t * shadow_pte_entry = (pte32_t *)&shadow_pt[PTE32_INDEX(first_operand)];
346
347             shadow_pte_entry->present = 0;
348           }
349         }
350
351         info->rip += index;
352
353       } else {
354         PrintDebug("Invalid Operand type\n");
355         return -1;
356       }
357     } else {
358       PrintDebug("invalid Instruction Opcode\n");
359       PrintTraceMemDump(instr, 15);
360       return -1;
361     }
362   }
363
364   return 0;
365 }
366
367
368
369 /* Deprecated */
370 /*
371 addr_t setup_shadow_pt32(struct guest_info * info, addr_t virt_cr3) {
372   addr_t cr3_guest_addr = CR3_TO_PDE32(virt_cr3);
373   pde32_t * guest_pde;
374   pde32_t * host_pde = NULL;
375   int i;
376   
377   // Setup up guest_pde to point to the PageDir in host addr
378   if (guest_pa_to_host_va(info, cr3_guest_addr, (addr_t*)&guest_pde) == -1) {
379     return 0;
380   }
381   
382   V3_AllocPages(host_pde, 1);
383   memset(host_pde, 0, PAGE_SIZE);
384
385   for (i = 0; i < MAX_PDE32_ENTRIES; i++) {
386     if (guest_pde[i].present == 1) {
387       addr_t pt_host_addr;
388       addr_t host_pte;
389
390       if (guest_pa_to_host_va(info, PDE32_T_ADDR(guest_pde[i]), &pt_host_addr) == -1) {
391         return 0;
392       }
393
394       if ((host_pte = setup_shadow_pte32(info, pt_host_addr)) == 0) {
395         return 0;
396       }
397
398       host_pde[i].present = 1;
399       host_pde[i].pt_base_addr = PD32_BASE_ADDR(host_pte);
400
401       //
402       // Set Page DIR flags
403       //
404     }
405   }
406
407   PrintDebugPageTables(host_pde);
408
409   return (addr_t)host_pde;
410 }
411
412
413
414 addr_t setup_shadow_pte32(struct guest_info * info, addr_t pt_host_addr) {
415   pte32_t * guest_pte = (pte32_t *)pt_host_addr;
416   pte32_t * host_pte = NULL;
417   int i;
418
419   V3_AllocPages(host_pte, 1);
420   memset(host_pte, 0, PAGE_SIZE);
421
422   for (i = 0; i < MAX_PTE32_ENTRIES; i++) {
423     if (guest_pte[i].present == 1) {
424       addr_t guest_pa = PTE32_T_ADDR(guest_pte[i]);
425       shadow_mem_type_t page_type;
426       addr_t host_pa = 0;
427
428       page_type = get_shadow_addr_type(info, guest_pa);
429
430       if (page_type == HOST_REGION_PHYSICAL_MEMORY) {
431         host_pa = get_shadow_addr(info, guest_pa);
432       } else {
433         
434         //
435         // Setup various memory types
436         //
437       }
438
439       host_pte[i].page_base_addr = PT32_BASE_ADDR(host_pa);
440       host_pte[i].present = 1;
441     }
442   }
443
444   return (addr_t)host_pte;
445 }
446
447 */