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.


fixes for single core guest on multicore capable systeM
[palacios.git] / palacios / src / palacios / vmm_mem.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm_mem.h>
21 #include <palacios/vmm.h>
22 #include <palacios/vmm_util.h>
23 #include <palacios/vmm_emulator.h>
24 #include <palacios/vm_guest.h>
25
26 #include <palacios/vmm_shadow_paging.h>
27 #include <palacios/vmm_direct_paging.h>
28
29 #define MEM_OFFSET_HCALL 0x1000
30
31
32 static inline
33 struct v3_shadow_region * insert_shadow_region(struct v3_vm_info * vm, 
34                                                struct v3_shadow_region * region);
35
36
37 static int mem_offset_hypercall(struct guest_info * info, uint_t hcall_id, void * private_data) {
38     PrintDebug("V3Vee: Memory offset hypercall (offset=%p)\n", 
39                (void *)(info->vm_info->mem_map.base_region.host_addr));
40
41     info->vm_regs.rbx = info->vm_info->mem_map.base_region.host_addr;
42
43     return 0;
44 }
45
46
47 int v3_init_mem_map(struct v3_vm_info * vm) {
48     struct v3_mem_map * map = &(vm->mem_map);
49     addr_t mem_pages = vm->mem_size >> 12;
50
51     map->shdw_regions.rb_node = NULL;
52
53
54     map->hook_hvas = V3_VAddr(V3_AllocPages(vm->num_cores));
55
56
57     // There is an underlying region that contains all of the guest memory
58     // PrintDebug("Mapping %d pages of memory (%u bytes)\n", (int)mem_pages, (uint_t)info->mem_size);
59
60     map->base_region.guest_start = 0;
61     map->base_region.guest_end = mem_pages * PAGE_SIZE_4KB;
62     map->base_region.host_type = SHDW_REGION_ALLOCATED;
63     map->base_region.host_addr = (addr_t)V3_AllocPages(mem_pages);
64
65
66     if ((void *)map->base_region.host_addr == NULL) {
67         PrintError("Could not allocate Guest memory\n");
68         return -1;
69     }
70         
71     //memset(V3_VAddr((void *)map->base_region.host_addr), 0xffffffff, map->base_region.guest_end);
72
73     v3_register_hypercall(vm, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
74
75     return 0;
76 }
77
78
79 static inline addr_t get_hook_hva(struct guest_info * info) {
80     return (addr_t)(info->vm_info->mem_map.hook_hvas + (PAGE_SIZE_4KB * info->cpu_id));
81 }
82
83 void v3_delete_shadow_map(struct v3_vm_info * vm) {
84     struct rb_node * node = v3_rb_first(&(vm->mem_map.shdw_regions));
85     struct v3_shadow_region * reg;
86     struct rb_node * tmp_node = NULL;
87   
88     while (node) {
89         reg = rb_entry(node, struct v3_shadow_region, tree_node);
90         tmp_node = node;
91         node = v3_rb_next(node);
92
93         v3_delete_shadow_region(vm, reg);
94     }
95
96     V3_FreePage((void *)(vm->mem_map.base_region.host_addr));
97     V3_FreePage(V3_PAddr((void *)(vm->mem_map.hook_hvas)));
98 }
99
100
101
102
103 int v3_add_shadow_mem( struct v3_vm_info * vm,
104                        addr_t               guest_addr_start,
105                        addr_t               guest_addr_end,
106                        addr_t               host_addr)
107 {
108     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
109
110     entry->guest_start = guest_addr_start;
111     entry->guest_end = guest_addr_end;
112     entry->host_type = SHDW_REGION_ALLOCATED;
113     entry->host_addr = host_addr;
114     entry->write_hook = NULL;
115     entry->read_hook = NULL;
116     entry->priv_data = NULL;
117
118     if (insert_shadow_region(vm, entry)) {
119         V3_Free(entry);
120         return -1;
121     }
122
123     return 0;
124 }
125
126
127
128 int v3_hook_write_mem(struct v3_vm_info * vm, addr_t guest_addr_start, addr_t guest_addr_end, 
129                       addr_t host_addr,
130                       int (*write)(addr_t guest_addr, void * src, uint_t length, void * priv_data),
131                       void * priv_data) {
132
133     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
134
135
136     entry->guest_start = guest_addr_start;
137     entry->guest_end = guest_addr_end;
138     entry->host_type = SHDW_REGION_WRITE_HOOK;
139     entry->host_addr = host_addr;
140     entry->write_hook = write;
141     entry->read_hook = NULL;
142     entry->priv_data = priv_data;
143
144     if (insert_shadow_region(vm, entry)) {
145         V3_Free(entry);
146         return -1;
147     }
148
149     return 0;  
150 }
151
152 int v3_hook_full_mem(struct v3_vm_info * vm, addr_t guest_addr_start, addr_t guest_addr_end,
153                      int (*read)(addr_t guest_addr, void * dst, uint_t length, void * priv_data),
154                      int (*write)(addr_t guest_addr, void * src, uint_t length, void * priv_data),
155                      void * priv_data) {
156   
157     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
158
159     entry->guest_start = guest_addr_start;
160     entry->guest_end = guest_addr_end;
161     entry->host_type = SHDW_REGION_FULL_HOOK;
162     entry->host_addr = (addr_t)NULL;
163     entry->write_hook = write;
164     entry->read_hook = read;
165     entry->priv_data = priv_data;
166   
167     if (insert_shadow_region(vm, entry)) {
168         V3_Free(entry);
169         return -1;
170     }
171
172     return 0;
173 }
174
175
176 // This will unhook the memory hook registered at start address
177 // We do not support unhooking subregions
178 int v3_unhook_mem(struct v3_vm_info * vm, addr_t guest_addr_start) {
179     struct v3_shadow_region * reg = v3_get_shadow_region(vm, guest_addr_start);
180
181     if ((reg->host_type != SHDW_REGION_FULL_HOOK) || 
182         (reg->host_type != SHDW_REGION_WRITE_HOOK)) {
183         PrintError("Trying to unhook a non hooked memory region (addr=%p)\n", (void *)guest_addr_start);
184         return -1;
185     }
186
187     v3_delete_shadow_region(vm, reg);
188
189     return 0;
190 }
191
192
193
194 static inline 
195 struct v3_shadow_region * __insert_shadow_region(struct v3_vm_info * vm, 
196                                                  struct v3_shadow_region * region) {
197     struct rb_node ** p = &(vm->mem_map.shdw_regions.rb_node);
198     struct rb_node * parent = NULL;
199     struct v3_shadow_region * tmp_region;
200
201     while (*p) {
202         parent = *p;
203         tmp_region = rb_entry(parent, struct v3_shadow_region, tree_node);
204
205         if (region->guest_end <= tmp_region->guest_start) {
206             p = &(*p)->rb_left;
207         } else if (region->guest_start >= tmp_region->guest_end) {
208             p = &(*p)->rb_right;
209         } else {
210             return tmp_region;
211         }
212     }
213
214     rb_link_node(&(region->tree_node), parent, p);
215   
216     return NULL;
217 }
218
219
220 static inline
221 struct v3_shadow_region * insert_shadow_region(struct v3_vm_info * vm, 
222                                                struct v3_shadow_region * region) {
223     struct v3_shadow_region * ret;
224     int i = 0;
225
226     if ((ret = __insert_shadow_region(vm, region))) {
227         return ret;
228     }
229
230     v3_rb_insert_color(&(region->tree_node), &(vm->mem_map.shdw_regions));
231
232
233
234     for (i = 0; i < vm->num_cores; i++) {
235         struct guest_info * info = &(vm->cores[i]);
236
237         // flush virtual page tables 
238         // 3 cases shadow, shadow passthrough, and nested
239
240         if (info->shdw_pg_mode == SHADOW_PAGING) {
241             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
242             
243             if (mem_mode == PHYSICAL_MEM) {
244                 addr_t cur_addr;
245                 
246                 for (cur_addr = region->guest_start;
247                      cur_addr < region->guest_end;
248                      cur_addr += PAGE_SIZE_4KB) {
249                     v3_invalidate_passthrough_addr(info, cur_addr);
250                 }
251             } else {
252                 v3_invalidate_shadow_pts(info);
253             }
254             
255         } else if (info->shdw_pg_mode == NESTED_PAGING) {
256             addr_t cur_addr;
257             
258             for (cur_addr = region->guest_start;
259                  cur_addr < region->guest_end;
260                  cur_addr += PAGE_SIZE_4KB) {
261                 
262                 v3_invalidate_nested_addr(info, cur_addr);
263             }
264         }
265     }
266
267     return NULL;
268 }
269                                                  
270
271
272
273 int handle_special_page_fault(struct guest_info * info, 
274                               addr_t fault_gva, addr_t fault_gpa, 
275                               pf_error_t access_info) 
276 {
277     struct v3_shadow_region * reg = v3_get_shadow_region(info->vm_info, fault_gpa);
278
279     PrintDebug("Handling Special Page Fault\n");
280
281     switch (reg->host_type) {
282         case SHDW_REGION_WRITE_HOOK:
283             return v3_handle_mem_wr_hook(info, fault_gva, fault_gpa, reg, access_info);
284         case SHDW_REGION_FULL_HOOK:
285             return v3_handle_mem_full_hook(info, fault_gva, fault_gpa, reg, access_info);
286         default:
287             return -1;
288     }
289
290     return 0;
291
292 }
293
294 int v3_handle_mem_wr_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
295                           struct v3_shadow_region * reg, pf_error_t access_info) {
296
297     addr_t dst_addr = (addr_t)V3_VAddr((void *)v3_get_shadow_addr(reg, guest_pa));
298
299     if (v3_emulate_write_op(info, guest_va, guest_pa, dst_addr, 
300                             reg->write_hook, reg->priv_data) == -1) {
301         PrintError("Write hook emulation failed\n");
302         return -1;
303     }
304
305     return 0;
306 }
307
308 int v3_handle_mem_full_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
309                             struct v3_shadow_region * reg, pf_error_t access_info) {
310   
311     addr_t op_addr = get_hook_hva(info);
312
313     if (access_info.write == 1) {
314         if (v3_emulate_write_op(info, guest_va, guest_pa, op_addr, 
315                                 reg->write_hook, reg->priv_data) == -1) {
316             PrintError("Write Full Hook emulation failed\n");
317             return -1;
318         }
319     } else {
320         if (v3_emulate_read_op(info, guest_va, guest_pa, op_addr, 
321                                reg->read_hook, reg->write_hook, 
322                                reg->priv_data) == -1) {
323             PrintError("Read Full Hook emulation failed\n");
324             return -1;
325         }
326     }
327
328     return 0;
329 }
330
331
332
333 struct v3_shadow_region * v3_get_shadow_region(struct v3_vm_info * vm, addr_t guest_addr) {
334     struct rb_node * n = vm->mem_map.shdw_regions.rb_node;
335     struct v3_shadow_region * reg = NULL;
336
337     while (n) {
338         reg = rb_entry(n, struct v3_shadow_region, tree_node);
339
340         if (guest_addr < reg->guest_start) {
341             n = n->rb_left;
342         } else if (guest_addr >= reg->guest_end) {
343             n = n->rb_right;
344         } else {
345             return reg;
346         }
347     }
348
349
350     // There is not registered region, so we check if its a valid address in the base region
351
352     if (guest_addr > vm->mem_map.base_region.guest_end) {
353         PrintError("Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n", 
354                    (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end);
355         v3_print_mem_map(vm);
356
357         return NULL;
358     }
359     
360     return &(vm->mem_map.base_region);
361 }
362
363
364 void v3_delete_shadow_region(struct v3_vm_info * vm, struct v3_shadow_region * reg) {
365     int i = 0;
366
367     if (reg == NULL) {
368         return;
369     }
370
371     for (i = 0; i < vm->num_cores; i++) {
372         struct guest_info * info = &(vm->cores[i]);
373
374         // flush virtual page tables 
375         // 3 cases shadow, shadow passthrough, and nested
376
377         if (info->shdw_pg_mode == SHADOW_PAGING) {
378             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
379             
380             if (mem_mode == PHYSICAL_MEM) {
381                 addr_t cur_addr;
382                 
383                 for (cur_addr = reg->guest_start;
384                      cur_addr < reg->guest_end;
385                      cur_addr += PAGE_SIZE_4KB) {
386                     v3_invalidate_passthrough_addr(info, cur_addr);
387                 }
388             } else {
389                 v3_invalidate_shadow_pts(info);
390             }
391             
392         } else if (info->shdw_pg_mode == NESTED_PAGING) {
393             addr_t cur_addr;
394             
395             for (cur_addr = reg->guest_start;
396                  cur_addr < reg->guest_end;
397                  cur_addr += PAGE_SIZE_4KB) {
398                 
399                 v3_invalidate_nested_addr(info, cur_addr);
400             }
401         }
402     }
403
404     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.shdw_regions));
405
406     V3_Free(reg);
407
408     // flush virtual page tables 
409     // 3 cases shadow, shadow passthrough, and nested
410
411 }
412
413
414
415
416 addr_t v3_get_shadow_addr(struct v3_shadow_region * reg, addr_t guest_addr) {
417     if ( (reg) && 
418          (reg->host_type != SHDW_REGION_FULL_HOOK)) {
419         return (guest_addr - reg->guest_start) + reg->host_addr;
420     } else {
421         //  PrintError("MEM Region Invalid\n");
422         return 0;
423     }
424
425 }
426
427
428
429 void v3_print_mem_map(struct v3_vm_info * vm) {
430     struct rb_node * node = v3_rb_first(&(vm->mem_map.shdw_regions));
431     struct v3_shadow_region * reg = &(vm->mem_map.base_region);
432     int i = 0;
433
434     V3_Print("Memory Layout:\n");
435     
436
437     V3_Print("Base Region:  0x%p - 0x%p -> 0x%p\n", 
438                (void *)(reg->guest_start), 
439                (void *)(reg->guest_end - 1), 
440                (void *)(reg->host_addr));
441     
442
443     // If the memory map is empty, don't print it
444     if (node == NULL) {
445         return;
446     }
447
448     do {
449         reg = rb_entry(node, struct v3_shadow_region, tree_node);
450
451         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
452                    (void *)(reg->guest_start), 
453                    (void *)(reg->guest_end - 1), 
454                    (void *)(reg->host_addr));
455
456         V3_Print("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n", 
457                    v3_shdw_region_type_to_str(reg->host_type),
458                    (void *)(reg->write_hook), 
459                    (void *)(reg->read_hook));
460     
461         i++;
462     } while ((node = v3_rb_next(node)));
463 }
464
465
466 static const uchar_t  SHDW_REGION_WRITE_HOOK_STR[] = "SHDW_REGION_WRITE_HOOK";
467 static const uchar_t  SHDW_REGION_FULL_HOOK_STR[] = "SHDW_REGION_FULL_HOOK";
468 static const uchar_t  SHDW_REGION_ALLOCATED_STR[] = "SHDW_REGION_ALLOCATED";
469
470 const uchar_t * v3_shdw_region_type_to_str(v3_shdw_region_type_t type) {
471     switch (type) {
472         case SHDW_REGION_WRITE_HOOK:
473             return SHDW_REGION_WRITE_HOOK_STR;
474         case SHDW_REGION_FULL_HOOK:
475             return SHDW_REGION_FULL_HOOK_STR;
476         case SHDW_REGION_ALLOCATED:
477             return SHDW_REGION_ALLOCATED_STR;
478         default:
479             return (uchar_t *)"SHDW_REGION_INVALID";
480     }
481 }
482