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.


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