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.


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