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