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.


Dynamic migration of memory from node to node
[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, "Invalid memory region\n");
495         return NULL;
496     }
497
498
499     if (start_region->guest_end < end_gpa) {
500         // Region ends before range
501         return NULL;
502     } else if (start_region->flags.base == 0) {
503         // sub region overlaps range
504         return start_region;
505     } else {
506         // Base region, now we have to scan forward for the next sub region
507         struct v3_mem_region * next_reg = get_next_mem_region(vm, core_id, start_gpa);
508         
509         if (next_reg == NULL) {
510             // no sub regions after start_addr, base region is ok
511             return start_region;
512         } else if (next_reg->guest_start >= end_gpa) {
513             // Next sub region begins outside range
514             return start_region;
515         } else {
516             return NULL;
517         }
518     }
519
520
521     // Should never get here
522     return NULL;
523 }
524
525
526
527
528
529 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
530     int i = 0;
531     int rc;
532
533     if (reg == NULL) {
534         return;
535     }
536
537
538     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
539
540
541
542     // If the guest isn't running then there shouldn't be anything to invalidate. 
543     // Page tables should __always__ be created on demand during execution
544     // NOTE: This is a sanity check, and can be removed if that assumption changes
545     if (vm->run_state != VM_RUNNING) {
546         V3_Free(reg);
547         return;
548     }
549
550     rc = 0;
551
552     for (i = 0; i < vm->num_cores; i++) {
553         struct guest_info * info = &(vm->cores[i]);
554
555         // flush virtual page tables 
556         // 3 cases shadow, shadow passthrough, and nested
557
558         if (info->shdw_pg_mode == SHADOW_PAGING) {
559             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
560             
561             if (mem_mode == PHYSICAL_MEM) {
562               rc |= v3_invalidate_passthrough_addr_range(info,reg->guest_start, reg->guest_end-1);
563             } else {
564               rc |= v3_invalidate_shadow_pts(info);
565             }
566             
567         } else if (info->shdw_pg_mode == NESTED_PAGING) {
568           rc |= v3_invalidate_nested_addr_range(info,reg->guest_start, reg->guest_end-1);
569         }
570     }
571
572     V3_Free(reg);
573
574     // flush virtual page tables 
575     // 3 cases shadow, shadow passthrough, and nested
576
577     if (rc) { PrintError(vm, VCORE_NONE, "Error in deleting memory region\n"); }
578 }
579
580 // Determine if a given address can be handled by a large page of the requested size
581 uint32_t v3_get_max_page_size(struct guest_info * core, addr_t page_addr, v3_cpu_mode_t mode) {
582     addr_t pg_start = 0;
583     addr_t pg_end = 0; 
584     uint32_t page_size = PAGE_SIZE_4KB;
585     struct v3_mem_region * reg = NULL;
586     
587     switch (mode) {
588         case PROTECTED:
589             if (core->use_large_pages == 1) {
590                 pg_start = PAGE_ADDR_4MB(page_addr);
591                 pg_end = (pg_start + PAGE_SIZE_4MB);
592
593                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end); 
594
595                 if ((reg) && ((reg->host_addr % PAGE_SIZE_4MB) == 0)) {
596                     page_size = PAGE_SIZE_4MB;
597                 }
598             }
599             break;
600         case PROTECTED_PAE:
601             if (core->use_large_pages == 1) {
602                 pg_start = PAGE_ADDR_2MB(page_addr);
603                 pg_end = (pg_start + PAGE_SIZE_2MB);
604
605                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
606
607                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
608                     page_size = PAGE_SIZE_2MB;
609                 }
610             }
611             break;
612         case LONG:
613         case LONG_32_COMPAT:
614         case LONG_16_COMPAT:
615             if (core->use_giant_pages == 1) {
616                 pg_start = PAGE_ADDR_1GB(page_addr);
617                 pg_end = (pg_start + PAGE_SIZE_1GB);
618                 
619                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
620                 
621                 if ((reg) && ((reg->host_addr % PAGE_SIZE_1GB) == 0)) {
622                     page_size = PAGE_SIZE_1GB;
623                     break;
624                 }
625             }
626
627             if (core->use_large_pages == 1) {
628                 pg_start = PAGE_ADDR_2MB(page_addr);
629                 pg_end = (pg_start + PAGE_SIZE_2MB);
630
631                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
632                 
633                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
634                     page_size = PAGE_SIZE_2MB;
635                 }
636             }
637             break;
638         default:
639             PrintError(core->vm_info, core, "Invalid CPU mode: %s\n", v3_cpu_mode_to_str(v3_get_vm_cpu_mode(core)));
640             return -1;
641     }
642
643     return page_size;
644 }
645
646
647
648 void v3_print_mem_map(struct v3_vm_info * vm) {
649     struct v3_mem_map * map = &(vm->mem_map);
650     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
651     struct v3_mem_region * reg = NULL;
652     int i = 0;
653
654     V3_Print(vm, VCORE_NONE, "Memory Layout (all cores):\n");
655     
656     V3_Print(vm, VCORE_NONE, "Base Memory: (%d regions)\n", map->num_base_regions);
657
658     for (i = 0; i < map->num_base_regions; i++) {
659         reg = &(map->base_regions[i]);
660
661         V3_Print(vm, VCORE_NONE, "Base Region[%d] (all cores):  0x%p - 0x%p -> 0x%p\n", 
662                  i, 
663                  (void *)(reg->guest_start), 
664                  (void *)(reg->guest_end - 1), 
665                  (void *)(reg->host_addr));
666     
667     }
668
669     // If the memory map is empty, don't print it
670     if (node == NULL) {
671         return;
672     }
673
674     do {
675         reg = rb_entry(node, struct v3_mem_region, tree_node);
676
677         V3_Print(vm, VCORE_NONE, "%d:  0x%p - 0x%p -> 0x%p\n", i, 
678                    (void *)(reg->guest_start), 
679                    (void *)(reg->guest_end - 1), 
680                    (void *)(reg->host_addr));
681
682         V3_Print(vm, VCORE_NONE, "\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
683                  reg->flags.value, 
684                  reg->core_id,
685                  reg->unhandled);
686     
687         i++;
688     } while ((node = v3_rb_next(node)));
689 }
690
691
692 void v3_init_mem()
693 {
694     char *arg = v3_lookup_option("mem_block_size");
695
696     if (arg) { 
697         v3_mem_block_size = atoi(arg);
698         V3_Print(VM_NONE,VCORE_NONE,"memory block size set to %llu bytes\n",v3_mem_block_size);
699     } else {
700         V3_Print(VM_NONE,VCORE_NONE,"default memory block size of %llu bytes is in use\n",v3_mem_block_size);
701     }
702 }
703
704 void v3_deinit_mem()
705 {
706     // currently nothing
707 }