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.


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