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.


Tweaks to inject page faults on certain conditions (instead of return -1)
[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 addr_t create_new_shadow_pt32(struct guest_info * info) {
46   void * host_pde = 0;
47
48   V3_AllocPages(host_pde, 1);
49   memset(host_pde, 0, PAGE_SIZE);
50
51   return (addr_t)host_pde;
52 }
53
54
55 static int handle_pd32_nonaligned_4MB_page(struct guest_info * info, pte32_t * pt, addr_t guest_addr, pde32_4MB_t * large_shadow_pde) {
56   uint_t i = 0;
57   pte32_t * pte_cursor;
58   addr_t guest_pa = 0;
59
60   for (i = 0; i < 1024; i++) {
61     guest_pa = guest_addr + (PAGE_SIZE * i);
62     host_region_type_t host_page_type = get_shadow_addr_type(info, guest_pa);
63     
64     pte_cursor = &(pt[i]);
65
66     if (host_page_type == HOST_REGION_INVALID) {
67       // Currently we don't support this, but in theory we could
68       PrintDebug("Invalid Host Memory Type\n");
69       return -1;
70     } else if (host_page_type == HOST_REGION_PHYSICAL_MEMORY) {
71       addr_t shadow_pa = get_shadow_addr(info, guest_pa);
72
73
74       pte_cursor->page_base_addr = PT32_BASE_ADDR(shadow_pa);
75       pte_cursor->present = 1;
76       pte_cursor->writable = large_shadow_pde->writable;
77       pte_cursor->user_page = large_shadow_pde->user_page;
78       pte_cursor->write_through = 0;  
79       pte_cursor->cache_disable = 0;
80       pte_cursor->global_page = 0;
81
82     } else {
83       PrintDebug("Unsupported Host Memory Type\n");
84       return -1;
85     }
86   }
87   return 0;
88 }
89
90 int handle_shadow_pagefault32(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
91   pde32_t * guest_pd = NULL;
92   pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32(info->shdw_pg_state.shadow_cr3);
93   addr_t guest_cr3 = CR3_TO_PDE32(info->shdw_pg_state.guest_cr3);
94   pt_access_status_t guest_pde_access;
95   pt_access_status_t shadow_pde_access;
96   pde32_t * guest_pde = NULL;
97   pde32_t * shadow_pde = (pde32_t *)&(shadow_pd[PDE32_INDEX(fault_addr)]);
98
99   if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
100     PrintDebug("Invalid Guest PDE Address: 0x%x\n", guest_cr3);
101     return -1;
102   }
103
104
105   guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(fault_addr)]);
106
107   // Check the guest page permissions
108   guest_pde_access = can_access_pde32(guest_pd, fault_addr, error_code);
109
110   // Check the shadow page permissions
111   shadow_pde_access = can_access_pde32(shadow_pd, fault_addr, error_code);
112   
113   /* This should be redone, 
114      but basically the reasoning is that there can be multiple reasons for a page fault:
115      If there is a permissions failure for a page present in the guest _BUT_ 
116      the reason for the fault was that the page is not present in the shadow, 
117      _THEN_ we have to map the shadow page in and reexecute, this will generate 
118      a permissions fault which is _THEN_ valid to send to the guest
119
120      whew...
121   */
122   if ((guest_pde_access != PT_ACCESS_OK) &&
123       ( (shadow_pde_access != PT_ENTRY_NOT_PRESENT) &&
124         (guest_pde_access != PT_ENTRY_NOT_PRESENT))) { // aka (guest permission error)
125     // inject page fault to the guest (Guest PDE fault)
126
127         info->ctrl_regs.cr2 = fault_addr;
128     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
129
130
131     PrintDebug("Injecting PDE pf to guest: (guest access error=%d) (pf error code=%d)\n", guest_pde_access, error_code);
132     return 0;
133
134     PrintDebug("Guest CR3=%x\n", guest_cr3);
135     PrintDebug("Guest PD\n");
136     PrintPD32(guest_pd);
137     PrintDebug("Shadow PD\n");
138     PrintPD32(shadow_pd);
139
140
141     return -1;
142   }
143
144
145   //shadow_pde_access = can_access_pde32(shadow_pd, fault_addr, error_code);
146
147
148   if (shadow_pde_access == PT_ENTRY_NOT_PRESENT) {
149
150     shadow_pde->present = 1;
151     shadow_pde->user_page = guest_pde->user_page;
152     shadow_pde->large_page = guest_pde->large_page;
153
154
155     // VMM Specific options
156     shadow_pde->write_through = 0;
157     shadow_pde->cache_disable = 0;
158     shadow_pde->global_page = 0;
159     //
160
161     guest_pde->accessed = 1;
162     
163     if (guest_pde->large_page == 0) {
164       pte32_t * shadow_pt = NULL;
165       
166       V3_AllocPages(shadow_pt, 1);
167       memset(shadow_pt, 0, PAGE_SIZE);
168       
169       shadow_pde->pt_base_addr = PD32_BASE_ADDR(shadow_pt);
170
171       shadow_pde->writable = guest_pde->writable;
172     } else {
173       struct shadow_region * mem_reg;
174       pde32_4MB_t * large_guest_pde = (pde32_4MB_t *)guest_pde;
175       pde32_4MB_t * large_shadow_pde = (pde32_4MB_t *)shadow_pde;
176       host_region_type_t host_page_type;
177       addr_t guest_start_addr = PDE32_4MB_T_ADDR(*large_guest_pde);
178       //    addr_t guest_end_addr = guest_start_addr + PAGE_SIZE_4MB; // start address + 4MB
179
180
181       // Check that the Guest PDE entry points to valid memory
182       // else Machine Check the guest
183       PrintDebug("Large Page: Page Base Addr=%x\n", guest_start_addr);
184
185       host_page_type = get_shadow_addr_type(info, guest_start_addr);
186
187       if (host_page_type == HOST_REGION_INVALID) {
188
189         raise_exception(info, MC_EXCEPTION);
190         PrintDebug("Invalid guest address in large page (0x%x)\n", guest_start_addr);
191         return -1;
192       } else if (host_page_type == HOST_REGION_PHYSICAL_MEMORY) {
193         addr_t host_start_addr = 0;
194         addr_t region_end_addr = 0;
195
196         // Check for a large enough region in host memory
197         mem_reg = get_shadow_region_by_addr(&(info->mem_map), guest_start_addr);
198         PrintDebug("Host region: host_addr=%x (guest_start=%x, end=%x)\n", 
199                    mem_reg->host_addr, mem_reg->guest_start, mem_reg->guest_end);
200         host_start_addr = mem_reg->host_addr + (guest_start_addr - mem_reg->guest_start);
201         region_end_addr = mem_reg->host_addr + (mem_reg->guest_end - mem_reg->guest_start);
202
203         PrintDebug("Host Start Addr=%x; Region End Addr=%x\n", host_start_addr, region_end_addr);
204
205
206
207
208         //4f
209         if (large_guest_pde->dirty == 1) { // dirty
210           large_shadow_pde->writable = guest_pde->writable;
211         } else if (error_code.write == 1) { // not dirty, access is write
212           large_shadow_pde->writable = guest_pde->writable;
213           large_guest_pde->dirty = 1;
214         } else { // not dirty, access is read
215           large_shadow_pde->writable = 0;
216         }
217
218
219         // Check if the region is at least an additional 4MB
220
221
222         //4b.
223         if ((PD32_4MB_PAGE_OFFSET(host_start_addr) == 0) && 
224             (region_end_addr >= host_start_addr + PAGE_SIZE_4MB)) {     // if 4MB boundary
225           large_shadow_pde->page_base_addr = PD32_4MB_BASE_ADDR(host_start_addr);
226         } else {          // else generate 4k pages
227           pte32_t * shadow_pt = NULL;
228           PrintDebug("Handling non aligned large page\n");
229
230           shadow_pde->large_page = 0;
231       
232           V3_AllocPages(shadow_pt, 1);
233           memset(shadow_pt, 0, PAGE_SIZE);
234
235           if (handle_pd32_nonaligned_4MB_page(info, shadow_pt, guest_start_addr, large_shadow_pde) == -1) {
236             PrintDebug("Non Aligned Large Page Error\n");
237             V3_Free(shadow_pt);
238             return -1;
239           }
240
241
242           /*
243             PrintDebug("non-aligned Shadow PT\n");
244             PrintPT32(PT32_PAGE_ADDR(fault_addr), shadow_pt);     
245           */
246           shadow_pde->pt_base_addr = PD32_BASE_ADDR(shadow_pt);
247         }
248
249         
250       } else {
251         // Handle hooked pages as well as other special pages
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
259   } else if ((shadow_pde_access == PT_WRITE_ERROR) && 
260              (guest_pde->large_page = 1) && 
261              (((pde32_4MB_t *)guest_pde)->dirty == 0)) {
262
263     //
264     // Page Directory Entry marked read-only
265     //
266
267     ((pde32_4MB_t *)guest_pde)->dirty = 1;
268     shadow_pde->writable = guest_pde->writable;
269     return 0;
270
271   } else if (shadow_pde_access == PT_USER_ERROR) {
272
273     //
274     // Page Directory Entry marked non-user
275     //
276     
277     PrintDebug("Shadow Paging User access error (shadow_pde_access=0x%x, guest_pde_access=0x%x - injecting into guest\n", shadow_pde_access, guest_pde_access);
278     info->ctrl_regs.cr2 = fault_addr;
279     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
280     return 0;
281
282   } else if (shadow_pde_access == PT_ACCESS_OK) {
283     pte32_t * shadow_pt = (pte32_t *)PDE32_T_ADDR((*shadow_pde));
284     pte32_t * guest_pt = NULL;
285
286     // Page Table Entry fault
287     
288     if (guest_pa_to_host_va(info, PDE32_T_ADDR((*guest_pde)), (addr_t*)&guest_pt) == -1) {
289       PrintDebug("Invalid Guest PTE Address: 0x%x\n", PDE32_T_ADDR((*guest_pde)));
290       // Machine check the guest
291
292       raise_exception(info, MC_EXCEPTION);
293       
294       return 0;
295     }
296
297
298     if (handle_shadow_pte32_fault(info, fault_addr, error_code, shadow_pt, guest_pt)  == -1) {
299       PrintDebug("Error handling Page fault caused by PTE\n");
300       return -1;
301     }
302
303  } else {
304     // Unknown error raise page fault in guest
305     info->ctrl_regs.cr2 = fault_addr;
306     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
307
308     // For debugging we will return an error here for the time being, 
309     // this probably shouldn't ever happen
310     PrintDebug("Unknown Error occurred\n");
311     PrintDebug("Manual Says to inject page fault into guest\n");
312     //return -1; Huh?  It's a successful handling of the fault...
313     return 0;
314   }
315
316   //PrintDebugPageTables(shadow_pd);
317   PrintDebug("Returning end of PDE function (rip=%x)\n", info->rip);
318   return 0;
319 }
320
321
322
323 /* 
324  * We assume the the guest pte pointer has already been translated to a host virtual address
325  */
326 int handle_shadow_pte32_fault(struct guest_info * info, 
327                               addr_t fault_addr, 
328                               pf_error_t error_code,
329                               pte32_t * shadow_pt, 
330                               pte32_t * guest_pt) {
331
332   pt_access_status_t guest_pte_access;
333   pt_access_status_t shadow_pte_access;
334   pte32_t * guest_pte = (pte32_t *)&(guest_pt[PTE32_INDEX(fault_addr)]);;
335   pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(fault_addr)]);
336
337
338   // Check the guest page permissions
339   guest_pte_access = can_access_pte32(guest_pt, fault_addr, error_code);
340
341   // Check the shadow page permissions
342   shadow_pte_access = can_access_pte32(shadow_pt, fault_addr, error_code);
343   
344   /* This should be redone, 
345      but basically the reasoning is that there can be multiple reasons for a page fault:
346      If there is a permissions failure for a page present in the guest _BUT_ 
347      the reason for the fault was that the page is not present in the shadow, 
348      _THEN_ we have to map the shadow page in and reexecute, this will generate 
349      a permissions fault which is _THEN_ valid to send to the guest
350
351      whew...
352   */
353   if ((guest_pte_access != PT_ACCESS_OK) && 
354       ((shadow_pte_access != PT_ENTRY_NOT_PRESENT) &&
355        (guest_pte_access != PT_ENTRY_NOT_PRESENT))) { // aka (guest permission error)
356     // Inject page fault into the guest 
357     
358     info->ctrl_regs.cr2 = fault_addr;
359     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
360     
361     PrintDebug("Access error injecting pf to guest (guest access error=%d) (pf error code=%d)\n", guest_pte_access, error_code);
362     return 0; 
363   }
364   
365   
366
367
368   if (shadow_pte_access == PT_ACCESS_OK) {
369     // Inconsistent state...
370     // Guest Re-Entry will flush page tables and everything should now work
371     PrintDebug("Inconsistent state... Guest re-entry should flush tlb\n");
372     return 0;
373   } else if (shadow_pte_access == PT_ENTRY_NOT_PRESENT) {
374     addr_t shadow_pa;
375     addr_t guest_pa = PTE32_T_ADDR((*guest_pte));
376
377     // Page Table Entry Not Present
378
379     host_region_type_t host_page_type = get_shadow_addr_type(info, guest_pa);
380
381     if (host_page_type == HOST_REGION_INVALID) {
382       // Inject a machine check in the guest
383
384       raise_exception(info, MC_EXCEPTION);
385
386       PrintDebug("Invalid Guest Address in page table (0x%x)\n", guest_pa);
387       PrintDebug("fault_addr=0x%x next are guest and shadow ptes \n",fault_addr);
388       PrintPTE32(fault_addr,guest_pte);
389       PrintPTE32(fault_addr,shadow_pte);
390       PrintDebug("Done.\n");
391       return 0;
392
393     } else if (host_page_type == HOST_REGION_PHYSICAL_MEMORY) {
394       
395       shadow_pa = get_shadow_addr(info, guest_pa);
396       
397       shadow_pte->page_base_addr = PT32_BASE_ADDR(shadow_pa);
398       
399       shadow_pte->present = guest_pte->present;
400       shadow_pte->user_page = guest_pte->user_page;
401       
402       //set according to VMM policy
403       shadow_pte->write_through = 0;
404       shadow_pte->cache_disable = 0;
405       shadow_pte->global_page = 0;
406       //
407       
408       guest_pte->accessed = 1;
409       
410       if (guest_pte->dirty == 1) {
411         shadow_pte->writable = guest_pte->writable;
412       } else if ((guest_pte->dirty == 0) && (error_code.write == 1)) {
413         shadow_pte->writable = guest_pte->writable;
414         guest_pte->dirty = 1;
415       } else if ((guest_pte->dirty = 0) && (error_code.write == 0)) {
416         shadow_pte->writable = 0;
417       }
418     } else {
419       // Page fault handled by hook functions
420       if (handle_special_page_fault(info, fault_addr, error_code) == -1) {
421         PrintDebug("Special Page fault handler returned error for address: %x\n", fault_addr);
422         return -1;
423       }
424     }
425
426   } else if ((shadow_pte_access == PT_WRITE_ERROR) &&
427              (guest_pte->dirty == 0)) {
428     guest_pte->dirty = 1;
429     shadow_pte->writable = guest_pte->writable;
430
431     PrintDebug("Shadow PTE Write Error\n");
432
433     return 0;
434   } else {
435     // Inject page fault into the guest 
436         
437     info->ctrl_regs.cr2 = fault_addr;
438     raise_exception_with_error(info, PF_EXCEPTION, *(uint_t *)&error_code);
439
440     PrintDebug("PTE Page fault fell through... Not sure if this should ever happen\n");
441     PrintDebug("Manual Says to inject page fault into guest\n");
442     return -1;
443   }
444
445   PrintDebug("Returning end of function\n");
446   return 0;
447 }
448
449
450
451
452
453
454 /* Currently Does not work with Segmentation!!! */
455 int handle_shadow_invlpg(struct guest_info * info) {
456   if (info->mem_mode != VIRTUAL_MEM) {
457     // Paging must be turned on...
458     // should handle with some sort of fault I think
459     PrintDebug("ERROR: INVLPG called in non paged mode\n");
460     return -1;
461   }
462
463
464   if (info->cpu_mode == PROTECTED) {
465     char instr[15];
466     int ret;
467     int index = 0;
468
469     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
470     if (ret != 15) {
471       PrintDebug("Could not read instruction 0x%x (ret=%d)\n", info->rip, ret);
472       return -1;
473     }
474
475    
476     /* Can INVLPG work with Segments?? */
477     while (is_prefix_byte(instr[index])) {
478       index++;
479     }
480     
481     
482     if ((instr[index] == (uchar_t)0x0f) &&
483         (instr[index + 1] == (uchar_t)0x01)) {
484
485       addr_t first_operand;
486       addr_t second_operand;
487       operand_type_t addr_type;
488       addr_t guest_cr3 = CR3_TO_PDE32(info->shdw_pg_state.guest_cr3);
489
490       pde32_t * guest_pd = NULL;
491
492       if (guest_pa_to_host_va(info, guest_cr3, (addr_t*)&guest_pd) == -1) {
493         PrintDebug("Invalid Guest PDE Address: 0x%x\n", guest_cr3);
494         return -1;
495       }
496
497       
498
499
500       index += 2;
501
502       addr_type = decode_operands32(&(info->vm_regs), instr + index, &index, &first_operand, &second_operand, REG32);
503
504       if (addr_type == MEM_OPERAND) {
505         pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32(info->shdw_pg_state.shadow_cr3);
506         pde32_t * shadow_pde = (pde32_t *)&shadow_pd[PDE32_INDEX(first_operand)];
507         pde32_t * guest_pde;
508
509         //PrintDebug("PDE Index=%d\n", PDE32_INDEX(first_operand));
510         //PrintDebug("FirstOperand = %x\n", first_operand);
511
512         PrintDebug("Invalidating page for %x\n", first_operand);
513
514         guest_pde = (pde32_t *)&(guest_pd[PDE32_INDEX(first_operand)]);
515
516         if (guest_pde->large_page == 1) {
517           shadow_pde->present = 0;
518           PrintDebug("Invalidating Large Page\n");
519         } else {
520          
521           if (shadow_pde->present == 1) {
522             pte32_t * shadow_pt = (pte32_t *)PDE32_T_ADDR((*shadow_pde));
523             pte32_t * shadow_pte = (pte32_t *)&shadow_pt[PTE32_INDEX(first_operand)];
524             PrintDebug("Setting not present\n");
525             PrintPTE32(first_operand, shadow_pte);
526             shadow_pte->present = 0;
527           }
528         }
529
530         info->rip += index;
531
532       } else {
533         PrintDebug("Invalid Operand type\n");
534         return -1;
535       }
536     } else {
537       PrintDebug("invalid Instruction Opcode\n");
538       PrintTraceMemDump(instr, 15);
539       return -1;
540     }
541   }
542
543   return 0;
544 }
545
546
547
548 /* Deprecated */
549 /*
550 addr_t setup_shadow_pt32(struct guest_info * info, addr_t virt_cr3) {
551   addr_t cr3_guest_addr = CR3_TO_PDE32(virt_cr3);
552   pde32_t * guest_pde;
553   pde32_t * host_pde = NULL;
554   int i;
555   
556   // Setup up guest_pde to point to the PageDir in host addr
557   if (guest_pa_to_host_va(info, cr3_guest_addr, (addr_t*)&guest_pde) == -1) {
558     return 0;
559   }
560   
561   V3_AllocPages(host_pde, 1);
562   memset(host_pde, 0, PAGE_SIZE);
563
564   for (i = 0; i < MAX_PDE32_ENTRIES; i++) {
565     if (guest_pde[i].present == 1) {
566       addr_t pt_host_addr;
567       addr_t host_pte;
568
569       if (guest_pa_to_host_va(info, PDE32_T_ADDR(guest_pde[i]), &pt_host_addr) == -1) {
570         return 0;
571       }
572
573       if ((host_pte = setup_shadow_pte32(info, pt_host_addr)) == 0) {
574         return 0;
575       }
576
577       host_pde[i].present = 1;
578       host_pde[i].pt_base_addr = PD32_BASE_ADDR(host_pte);
579
580       //
581       // Set Page DIR flags
582       //
583     }
584   }
585
586   PrintDebugPageTables(host_pde);
587
588   return (addr_t)host_pde;
589 }
590
591
592
593 addr_t setup_shadow_pte32(struct guest_info * info, addr_t pt_host_addr) {
594   pte32_t * guest_pte = (pte32_t *)pt_host_addr;
595   pte32_t * host_pte = NULL;
596   int i;
597
598   V3_AllocPages(host_pte, 1);
599   memset(host_pte, 0, PAGE_SIZE);
600
601   for (i = 0; i < MAX_PTE32_ENTRIES; i++) {
602     if (guest_pte[i].present == 1) {
603       addr_t guest_pa = PTE32_T_ADDR(guest_pte[i]);
604       shadow_mem_type_t page_type;
605       addr_t host_pa = 0;
606
607       page_type = get_shadow_addr_type(info, guest_pa);
608
609       if (page_type == HOST_REGION_PHYSICAL_MEMORY) {
610         host_pa = get_shadow_addr(info, guest_pa);
611       } else {
612         
613         //
614         // Setup various memory types
615         //
616       }
617
618       host_pte[i].page_base_addr = PT32_BASE_ADDR(host_pa);
619       host_pte[i].present = 1;
620     }
621   }
622
623   return (addr_t)host_pte;
624 }
625
626 */