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.


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