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