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.


Use page allocation for base memory region array
[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 #include <palacios/vmm_debug.h>
26
27 #include <palacios/vmm_shadow_paging.h>
28 #include <palacios/vmm_direct_paging.h>
29
30 #include <interfaces/vmm_numa.h>
31
32 #ifdef V3_CONFIG_SWAPPING
33 #include <palacios/vmm_swapping.h>
34 #endif
35
36 uint64_t v3_mem_block_size = V3_CONFIG_MEM_BLOCK_SIZE;
37
38
39
40
41 struct v3_mem_region * v3_get_base_region(struct v3_vm_info * vm, addr_t gpa) {
42    
43     //PrintDebug(VM_NONE, VCORE_NONE, "get_base_region called"); 
44     struct v3_mem_map * map = &(vm->mem_map);
45     uint32_t block_index = gpa / v3_mem_block_size;
46     struct v3_mem_region *reg;
47     if ((gpa >= (map->num_base_regions * v3_mem_block_size)) ||
48         (block_index >= map->num_base_regions)) {
49         PrintError(vm, VCORE_NONE, "Guest Address Exceeds Base Memory Size (ga=0x%p), (limit=0x%p)\n", 
50                    (void *)gpa, (void *)vm->mem_size);
51         v3_print_mem_map(vm);
52
53         return NULL;
54     }
55
56     reg = &(map->base_regions[block_index]);
57
58 #ifdef V3_CONFIG_SWAPPING
59     if(vm->swap_state.enable_swapping) {
60         if (reg->flags.swapped) {
61             if (v3_swap_in_region(vm,reg)) { 
62                 PrintError(vm, VCORE_NONE, "Unable to swap in region GPA=%p..%p!!!\n",(void*)reg->guest_start,(void*)reg->guest_end);
63                 v3_print_mem_map(vm);
64                 return NULL;
65             }
66         }
67     }
68     v3_touch_region(vm,reg);
69 #endif
70
71     return reg;
72 }
73
74
75
76 static int mem_offset_hypercall(struct guest_info * info, uint_t hcall_id, void * private_data) {
77     /*
78     PrintDebug(info->vm_info, info,"V3Vee: Memory offset hypercall (offset=%p)\n", 
79                (void *)(info->vm_info->mem_map.base_region.host_addr));
80
81     info->vm_regs.rbx = info->vm_info->mem_map.base_region.host_addr;
82     */
83     return -1;
84 }
85
86 static int unhandled_err(struct guest_info * core, addr_t guest_va, addr_t guest_pa, 
87                          struct v3_mem_region * reg, pf_error_t access_info) {
88
89     PrintError(core->vm_info, core, "Unhandled memory access error (gpa=%p, gva=%p, error_code=%d)\n",
90                (void *)guest_pa, (void *)guest_va, *(uint32_t *)&access_info);
91
92     v3_print_mem_map(core->vm_info);
93
94     v3_print_guest_state(core);
95
96     return -1;
97 }
98
99 static int gpa_to_node_from_cfg(struct v3_vm_info * vm, addr_t gpa) {
100     v3_cfg_tree_t * layout_cfg = v3_cfg_subtree(vm->cfg_data->cfg, "mem_layout");
101     v3_cfg_tree_t * region_desc = v3_cfg_subtree(layout_cfg, "region");
102
103     while (region_desc) {
104         char * start_addr_str = v3_cfg_val(region_desc, "start_addr");
105         char * end_addr_str = v3_cfg_val(region_desc, "end_addr");
106         char * node_id_str = v3_cfg_val(region_desc, "node");
107
108         addr_t start_addr = 0;
109         addr_t end_addr = 0;
110         int node_id = 0;
111         
112         if ((!start_addr_str) || (!end_addr_str) || (!node_id_str)) {
113             PrintError(vm, VCORE_NONE, "Invalid memory layout in configuration\n");
114             return -1;
115         }
116         
117         start_addr = atox(start_addr_str);
118         end_addr = atox(end_addr_str);
119         node_id = atoi(node_id_str);
120
121         if ((gpa >= start_addr) && (gpa < end_addr)) {
122             return node_id;
123         }
124
125         region_desc = v3_cfg_next_branch(region_desc);
126     }
127
128     return -1;
129 }
130
131 //
132 // This code parallels that in vmm_shadow_paging.c:v3_init_shdw_impl() 
133 // and vmm_config.c:determine_paging_mode.   The determination of which
134 // paging mode will be used is determined much later than the allocation of
135 // the guest memory regions, so we need to do this here to decide if they
136 // need to be below 4 GB or not.
137 static int will_use_shadow_paging(struct v3_vm_info *vm)
138 {
139     v3_cfg_tree_t * pg_cfg = v3_cfg_subtree(vm->cfg_data->cfg, "paging");
140     char * pg_mode = v3_cfg_val(pg_cfg, "mode");
141    
142     if (pg_mode == NULL) { 
143         return 1; // did not ask, get shadow
144     } else {
145         if (strcasecmp(pg_mode, "nested") == 0) {
146             extern v3_cpu_arch_t v3_mach_type;
147             if ((v3_mach_type == V3_SVM_REV3_CPU) || 
148                 (v3_mach_type == V3_VMX_EPT_CPU) ||
149                 (v3_mach_type == V3_VMX_EPT_UG_CPU)) {
150                 return 0; // ask for nested, get nested
151             } else { 
152                 return 1; // ask for nested, get shadow
153             }
154         } else if (strcasecmp(pg_mode, "shadow") != 0) { 
155             return 1;     // ask for shadow, get shadow
156         } else {
157             return 1;     // ask for something else, get shadow
158         }
159     }
160 }
161
162 #define CEIL_DIV(x,y) (((x)/(y)) + !!((x)%(y)))
163
164
165 int v3_init_mem_map(struct v3_vm_info * vm) {
166     struct v3_mem_map * map = &(vm->mem_map);
167     addr_t block_pages = v3_mem_block_size >> 12;
168     int i = 0;
169     uint64_t num_base_regions_host_mem;
170
171     map->num_base_regions = CEIL_DIV(vm->mem_size, v3_mem_block_size); 
172
173     num_base_regions_host_mem=map->num_base_regions;  // without swapping
174
175     PrintDebug(VM_NONE, VCORE_NONE, "v3_init_mem_map: num_base_regions:%d",map->num_base_regions);
176
177     map->mem_regions.rb_node = NULL;
178
179 #ifdef V3_CONFIG_SWAPPING
180     if (vm->swap_state.enable_swapping) {
181         num_base_regions_host_mem = CEIL_DIV(vm->swap_state.host_mem_size, v3_mem_block_size);
182     } 
183 #endif
184
185     PrintDebug(VM_NONE, VCORE_NONE, "v3_init_mem_map: %llu base regions will be allocated of %llu base regions in guest\n",
186                (uint64_t)num_base_regions_host_mem, (uint64_t)map->num_base_regions);
187     
188     map->base_regions = V3_AllocPages(CEIL_DIV(sizeof(struct v3_mem_region) * map->num_base_regions, PAGE_SIZE_4KB));
189     if (map->base_regions == NULL) {
190         PrintError(vm, VCORE_NONE, "Could not allocate base region array\n");
191         return -1;
192     }
193     map->base_regions = V3_VAddr(map->base_regions);
194
195     memset(map->base_regions, 0, sizeof(struct v3_mem_region) * map->num_base_regions);
196
197     for (i = 0; i < map->num_base_regions; i++) {
198   
199
200         struct v3_mem_region * region = &(map->base_regions[i]);
201         int node_id = -1;
202
203         // 2MB page alignment needed for 2MB hardware nested paging
204         // If swapping is enabled, the host memory will be allocated to low address regions at initialization
205         region->guest_start = v3_mem_block_size * i;
206         region->guest_end = region->guest_start + v3_mem_block_size;
207
208         // We assume that the xml config was smart enough to align the layout to the block size
209         // If they didn't we're going to ignore their settings 
210         //     and use whatever node the first byte of the block is assigned to
211         node_id = gpa_to_node_from_cfg(vm, region->guest_start);
212         
213
214         if (i < num_base_regions_host_mem) {
215             //The regions within num_base_regions_in_mem are allocated in host memory
216             V3_Print(vm, VCORE_NONE, "Allocating block %d on node %d\n", i, node_id);
217
218 #ifdef V3_CONFIG_SWAPPING
219             // nothing to do - memset will have done it.
220 #endif
221     
222             region->host_addr = (addr_t)V3_AllocPagesExtended(block_pages,
223                                                               PAGE_SIZE_4KB,
224                                                               node_id,
225                                                               0); // no constraints 
226             
227             if ((void *)region->host_addr == NULL) { 
228                 PrintError(vm, VCORE_NONE, "Could not allocate guest memory\n");
229                 return -1;
230             }
231             
232             // Clear the memory...
233             memset(V3_VAddr((void *)region->host_addr), 0, v3_mem_block_size);
234
235         } else {
236
237 #ifdef V3_CONFIG_SWAPPING
238             if(vm->swap_state.enable_swapping) {        
239                 // The regions beyond num_base_regions_in_mem are allocated on disk to start
240                 region->flags.swapped = 1;
241                 region->host_addr=(addr_t) 0;
242                 // other flags / state correctly set up by zeroing the region earlier
243             }
244 #endif
245
246         }
247
248         
249         // Note assigned numa ID could be different than our request... 
250         // Also note that when swapping is used, the numa info will
251         // reflect the numa id of address 0x0 for unallocated regions
252         //
253         region->numa_id = v3_numa_hpa_to_node(region->host_addr);
254
255         region->flags.read = 1;
256         region->flags.write = 1;
257         region->flags.exec = 1;
258         region->flags.base = 1;
259         region->flags.alloced = 1;
260         region->flags.limit32 = will_use_shadow_paging(vm);
261         
262         region->unhandled = unhandled_err;
263     }
264
265     v3_register_hypercall(vm, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
266
267     return 0;
268 }
269
270
271 void v3_delete_mem_map(struct v3_vm_info * vm) {
272     struct v3_mem_map * map = &(vm->mem_map);
273     struct rb_node * node = v3_rb_first(&(map->mem_regions));
274     struct v3_mem_region * reg;
275     struct rb_node * tmp_node = NULL;
276     addr_t block_pages = v3_mem_block_size >> 12;
277     int i = 0;
278
279     while (node) {
280         reg = rb_entry(node, struct v3_mem_region, tree_node);
281         tmp_node = node;
282         node = v3_rb_next(node);
283
284         v3_delete_mem_region(vm, reg);
285     }
286
287     for (i = 0; i < map->num_base_regions; i++) {
288         struct v3_mem_region * region = &(map->base_regions[i]);
289 #ifdef V3_CONFIG_SWAPPING
290         if (vm->swap_state.enable_swapping) { 
291             if (!region->flags.swapped) { 
292                 V3_FreePages((void *)(region->host_addr), block_pages);
293             } // otherwise this is not allocated space
294         }
295 #else
296         V3_FreePages((void *)(region->host_addr), block_pages);
297 #endif
298     }
299
300     V3_FreePages(V3_PAddr(map->base_regions),
301                  CEIL_DIV(sizeof(struct v3_mem_region) * map->num_base_regions, PAGE_SIZE_4KB));
302 }
303
304
305 struct v3_mem_region * v3_create_mem_region(struct v3_vm_info * vm, uint16_t core_id, 
306                                                addr_t guest_addr_start, addr_t guest_addr_end) {
307     struct v3_mem_region * entry = NULL;
308
309     if (guest_addr_start >= guest_addr_end) {
310         PrintError(vm, VCORE_NONE, "Region start is after region end\n");
311         return NULL;
312     }
313
314     entry = (struct v3_mem_region *)V3_Malloc(sizeof(struct v3_mem_region));
315
316     if (!entry) {
317         PrintError(vm, VCORE_NONE, "Cannot allocate in creating a memory region\n");
318         return NULL;
319     }
320
321     memset(entry, 0, sizeof(struct v3_mem_region));
322
323     entry->guest_start = guest_addr_start;
324     entry->guest_end = guest_addr_end;
325     entry->core_id = core_id;
326     entry->unhandled = unhandled_err;
327
328     return entry;
329 }
330
331
332
333
334 int v3_add_shadow_mem( struct v3_vm_info * vm, uint16_t core_id,
335                        addr_t               guest_addr_start,
336                        addr_t               guest_addr_end,
337                        addr_t               host_addr)
338 {
339     struct v3_mem_region * entry = NULL;
340
341     entry = v3_create_mem_region(vm, core_id, 
342                                  guest_addr_start, 
343                                  guest_addr_end);
344
345     entry->host_addr = host_addr;
346
347     entry->flags.read = 1;
348     entry->flags.write = 1;
349     entry->flags.exec = 1;
350     entry->flags.alloced = 1;
351
352     if (v3_insert_mem_region(vm, entry) == -1) {
353         V3_Free(entry);
354         return -1;
355     }
356
357     return 0;
358 }
359
360
361
362 static inline 
363 struct v3_mem_region * __insert_mem_region(struct v3_vm_info * vm, 
364                                            struct v3_mem_region * region) {
365     struct rb_node ** p = &(vm->mem_map.mem_regions.rb_node);
366     struct rb_node * parent = NULL;
367     struct v3_mem_region * tmp_region;
368
369     while (*p) {
370         parent = *p;
371         tmp_region = rb_entry(parent, struct v3_mem_region, tree_node);
372
373         if (region->guest_end <= tmp_region->guest_start) {
374             p = &(*p)->rb_left;
375         } else if (region->guest_start >= tmp_region->guest_end) {
376             p = &(*p)->rb_right;
377         } else {
378             if ((region->guest_end != tmp_region->guest_end) ||
379                 (region->guest_start != tmp_region->guest_start)) {
380                 PrintError(vm, VCORE_NONE, "Trying to map a partial overlapped core specific page...\n");
381                 return tmp_region; // This is ugly... 
382             } else if (region->core_id == tmp_region->core_id) {
383                 PrintError(vm, VCORE_NONE, "Trying to map a core-overlapping page\n");
384                 return tmp_region;
385             } else if (region->core_id < tmp_region->core_id) {
386                 p = &(*p)->rb_left;
387             } else { 
388                 p = &(*p)->rb_right;
389             }
390         }
391     }
392
393     rb_link_node(&(region->tree_node), parent, p);
394   
395     return NULL;
396 }
397
398
399
400 int v3_insert_mem_region(struct v3_vm_info * vm, struct v3_mem_region * region) {
401     struct v3_mem_region * ret;
402     int i = 0;
403     int rc;
404
405     if ((ret = __insert_mem_region(vm, region))) {
406         PrintError(vm, VCORE_NONE, "Internal insert failed returned region is from 0x%p to 0x%p on vcore %d\n", (void*)(ret->guest_start), (void*)(ret->guest_end), ret->core_id);
407         return -1;
408     }
409
410     v3_rb_insert_color(&(region->tree_node), &(vm->mem_map.mem_regions));
411
412
413     rc = 0;
414
415     for (i = 0; i < vm->num_cores; i++) {
416         struct guest_info * info = &(vm->cores[i]);
417
418         // flush virtual page tables 
419         // 3 cases shadow, shadow passthrough, and nested
420
421         if (info->shdw_pg_mode == SHADOW_PAGING) {
422             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
423             
424             if (mem_mode == PHYSICAL_MEM) {
425               rc |= v3_invalidate_passthrough_addr_range(info, region->guest_start, region->guest_end-1,NULL,NULL);
426             } else {
427                 rc |= v3_invalidate_shadow_pts(info);
428             }
429             
430         } else if (info->shdw_pg_mode == NESTED_PAGING) {
431           rc |= v3_invalidate_nested_addr_range(info, region->guest_start, region->guest_end-1,NULL,NULL);
432         }
433     }
434
435     return rc;
436 }
437                                                  
438
439
440
441 struct v3_mem_region * v3_get_mem_region(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
442     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
443     struct v3_mem_region * reg = NULL;
444
445     while (n) {
446
447         reg = rb_entry(n, struct v3_mem_region, tree_node);
448
449         if (guest_addr < reg->guest_start) {
450             n = n->rb_left;
451         } else if (guest_addr >= reg->guest_end) {
452             n = n->rb_right;
453         } else {
454             if (reg->core_id == V3_MEM_CORE_ANY) {
455                 // found relevant region, it's available on all cores
456                 return reg;
457             } else if (core_id == reg->core_id) { 
458                 // found relevant region, it's available on the indicated core
459                 return reg;
460             } else if (core_id < reg->core_id) { 
461                 // go left, core too big
462                 n = n->rb_left;
463             } else if (core_id > reg->core_id) { 
464                 // go right, core too small
465                 n = n->rb_right;
466             } else {
467                 PrintDebug(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
468                 return NULL;
469             }
470         }
471     }
472
473
474     // There is not registered region, so we check if its a valid address in the base region
475
476     return v3_get_base_region(vm, guest_addr);
477 }
478
479
480
481 /* This returns the next memory region based on a given address. 
482  * If the address falls inside a sub region, that region is returned. 
483  * If the address falls outside a sub region, the next sub region is returned
484  * NOTE that we have to be careful about core_ids here...
485  */
486 static struct v3_mem_region * get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
487     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
488     struct v3_mem_region * reg = NULL;
489     struct v3_mem_region * parent = NULL;
490
491     if (n == NULL) {
492         return NULL;
493     }
494
495     while (n) {
496
497         reg = rb_entry(n, struct v3_mem_region, tree_node);
498
499         if (guest_addr < reg->guest_start) {
500             n = n->rb_left;
501         } else if (guest_addr >= reg->guest_end) {
502             n = n->rb_right;
503         } else {
504             if (reg->core_id == V3_MEM_CORE_ANY) {
505                 // found relevant region, it's available on all cores
506                 return reg;
507             } else if (core_id == reg->core_id) { 
508                 // found relevant region, it's available on the indicated core
509                 return reg;
510             } else if (core_id < reg->core_id) { 
511                 // go left, core too big
512                 n = n->rb_left;
513             } else if (core_id > reg->core_id) { 
514                 // go right, core too small
515                 n = n->rb_right;
516             } else {
517                 PrintError(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
518                 return NULL;
519             }
520         }
521
522         if ((reg->core_id == core_id) || (reg->core_id == V3_MEM_CORE_ANY)) {
523             parent = reg;
524         }
525     }
526
527
528     if (parent->guest_start > guest_addr) {
529         return parent;
530     } else if (parent->guest_end < guest_addr) {
531         struct rb_node * node = &(parent->tree_node);
532
533         while ((node = v3_rb_next(node)) != NULL) {
534             struct v3_mem_region * next_reg = rb_entry(node, struct v3_mem_region, tree_node);
535
536             if ((next_reg->core_id == V3_MEM_CORE_ANY) ||
537                 (next_reg->core_id == core_id)) {
538
539                 // This check is not strictly necessary, but it makes it clearer
540                 if (next_reg->guest_start > guest_addr) {
541                     return next_reg;
542                 }
543             }
544         }
545     }
546
547     return NULL;
548 }
549
550
551
552
553 /* Given an address region of memory, find if there are any regions that overlap with it. 
554  * This checks that the range lies in a single region, and returns that region if it does, 
555  * this can be either the base region or a sub region. 
556  * IF there are multiple regions in the range then it returns NULL
557  */
558 static struct v3_mem_region * get_overlapping_region(struct v3_vm_info * vm, uint16_t core_id, 
559                                                      addr_t start_gpa, addr_t end_gpa) {
560     struct v3_mem_region * start_region = v3_get_mem_region(vm, core_id, start_gpa);
561
562     if (start_region == NULL) {
563         PrintError(vm, VCORE_NONE, "No overlapping region for core=%d, start_gpa=%p\n", core_id, (void*)start_gpa);
564         v3_print_mem_map(vm);
565         return NULL;
566     }
567
568
569     if (start_region->guest_end < end_gpa) {
570         // Region ends before range
571         return NULL;
572     } else if (start_region->flags.base == 0) {
573         // sub region overlaps range
574         return start_region;
575     } else {
576         // Base region, now we have to scan forward for the next sub region
577         struct v3_mem_region * next_reg = get_next_mem_region(vm, core_id, start_gpa);
578         
579         if (next_reg == NULL) {
580             // no sub regions after start_addr, base region is ok
581             return start_region;
582         } else if (next_reg->guest_start >= end_gpa) {
583             // Next sub region begins outside range
584             return start_region;
585         } else {
586             return NULL;
587         }
588     }
589
590
591     // Should never get here
592     return NULL;
593 }
594
595
596
597
598
599 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
600     int i = 0;
601     int rc;
602
603     if (reg == NULL) {
604         return;
605     }
606
607
608     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
609
610
611
612     // If the guest isn't running then there shouldn't be anything to invalidate. 
613     // Page tables should __always__ be created on demand during execution
614     // NOTE: This is a sanity check, and can be removed if that assumption changes
615     if (vm->run_state != VM_RUNNING) {
616         V3_Free(reg);
617         return;
618     }
619
620     rc = 0;
621
622     for (i = 0; i < vm->num_cores; i++) {
623         struct guest_info * info = &(vm->cores[i]);
624
625         // flush virtual page tables 
626         // 3 cases shadow, shadow passthrough, and nested
627
628         if (info->shdw_pg_mode == SHADOW_PAGING) {
629             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
630             
631             if (mem_mode == PHYSICAL_MEM) {
632               rc |= v3_invalidate_passthrough_addr_range(info,reg->guest_start, reg->guest_end-1,NULL,NULL);
633             } else {
634               rc |= v3_invalidate_shadow_pts(info);
635             }
636             
637         } else if (info->shdw_pg_mode == NESTED_PAGING) {
638           rc |= v3_invalidate_nested_addr_range(info,reg->guest_start, reg->guest_end-1,NULL,NULL);
639         }
640     }
641
642     V3_Free(reg);
643
644     // flush virtual page tables 
645     // 3 cases shadow, shadow passthrough, and nested
646
647     if (rc) { PrintError(vm, VCORE_NONE, "Error in deleting memory region\n"); }
648 }
649
650 // Determine if a given address can be handled by a large page of the requested size
651 uint32_t v3_get_max_page_size(struct guest_info * core, addr_t page_addr, v3_cpu_mode_t mode) {
652     addr_t pg_start = 0;
653     addr_t pg_end = 0; 
654     uint32_t page_size = PAGE_SIZE_4KB;
655     struct v3_mem_region * reg = NULL;
656     
657     switch (mode) {
658         case PROTECTED:
659             if (core->use_large_pages == 1) {
660                 pg_start = PAGE_ADDR_4MB(page_addr);
661                 pg_end = (pg_start + PAGE_SIZE_4MB);
662
663                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end); 
664
665                 if ((reg) && ((reg->host_addr % PAGE_SIZE_4MB) == 0)) {
666                     page_size = PAGE_SIZE_4MB;
667                 }
668             }
669             break;
670         case PROTECTED_PAE:
671             if (core->use_large_pages == 1) {
672                 pg_start = PAGE_ADDR_2MB(page_addr);
673                 pg_end = (pg_start + PAGE_SIZE_2MB);
674
675                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
676
677                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
678                     page_size = PAGE_SIZE_2MB;
679                 }
680             }
681             break;
682         case LONG:
683         case LONG_32_COMPAT:
684         case LONG_16_COMPAT:
685             if (core->use_giant_pages == 1) {
686                 pg_start = PAGE_ADDR_1GB(page_addr);
687                 pg_end = (pg_start + PAGE_SIZE_1GB);
688                 
689                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
690                 
691                 if ((reg) && ((reg->host_addr % PAGE_SIZE_1GB) == 0)) {
692                     page_size = PAGE_SIZE_1GB;
693                     break;
694                 }
695             }
696
697             if (core->use_large_pages == 1) {
698                 pg_start = PAGE_ADDR_2MB(page_addr);
699                 pg_end = (pg_start + PAGE_SIZE_2MB);
700
701                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
702                 
703                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
704                     page_size = PAGE_SIZE_2MB;
705                 }
706             }
707             break;
708         default:
709             PrintError(core->vm_info, core, "Invalid CPU mode: %s\n", v3_cpu_mode_to_str(v3_get_vm_cpu_mode(core)));
710             return -1;
711     }
712
713     return page_size;
714 }
715
716
717
718 void v3_print_mem_map(struct v3_vm_info * vm) {
719     struct v3_mem_map * map = &(vm->mem_map);
720     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
721     struct v3_mem_region * reg = NULL;
722     int i = 0;
723
724     V3_Print(vm, VCORE_NONE, "Memory Layout (all cores):\n");
725     
726     V3_Print(vm, VCORE_NONE, "Base Memory: (%d regions)\n", map->num_base_regions);
727
728     for (i = 0; i < map->num_base_regions; i++) {
729         reg = &(map->base_regions[i]);
730
731         V3_Print(vm, VCORE_NONE, "Base Region[%d] (all cores):  0x%p - 0x%p -> 0x%p\n", 
732                  i, 
733                  (void *)(reg->guest_start), 
734                  (void *)(reg->guest_end - 1), 
735                  (void *)(reg->host_addr));
736     
737     }
738
739     // If the memory map is empty, don't print it
740     if (node == NULL) {
741         return;
742     }
743
744     do {
745         reg = rb_entry(node, struct v3_mem_region, tree_node);
746
747         V3_Print(vm, VCORE_NONE, "%d:  0x%p - 0x%p -> 0x%p\n", i, 
748                    (void *)(reg->guest_start), 
749                    (void *)(reg->guest_end - 1), 
750                    (void *)(reg->host_addr));
751
752         V3_Print(vm, VCORE_NONE, "\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
753                  reg->flags.value, 
754                  reg->core_id,
755                  reg->unhandled);
756     
757         i++;
758     } while ((node = v3_rb_next(node)));
759 }
760
761
762 void v3_init_mem()
763 {
764     char *arg = v3_lookup_option("mem_block_size");
765
766     if (arg) { 
767         v3_mem_block_size = atoi(arg);
768         V3_Print(VM_NONE,VCORE_NONE,"memory block size set to %llu bytes\n",v3_mem_block_size);
769     } else {
770         V3_Print(VM_NONE,VCORE_NONE,"default memory block size of %llu bytes is in use\n",v3_mem_block_size);
771     }
772 }
773
774 void v3_deinit_mem()
775 {
776     // currently nothing
777 }