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.


b205ea116700f0b4dc2d421bd551a66a0ff49fbb
[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                                                           0); // no constraints 
180                                                              
181         if ((void *)region->host_addr == NULL) { 
182             PrintError(vm, VCORE_NONE, "Could not allocate guest memory\n");
183             return -1;
184         }
185
186         // Clear the memory...
187         memset(V3_VAddr((void *)region->host_addr), 0, v3_mem_block_size);
188         
189         // Note assigned numa ID could be different than our request... 
190         region->numa_id = v3_numa_hpa_to_node(region->host_addr);
191
192         region->flags.read = 1;
193         region->flags.write = 1;
194         region->flags.exec = 1;
195         region->flags.base = 1;
196         region->flags.alloced = 1;
197         region->flags.limit32 = will_use_shadow_paging(vm);
198         
199
200         region->unhandled = unhandled_err;
201     }
202
203     v3_register_hypercall(vm, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
204
205     return 0;
206 }
207
208
209 void v3_delete_mem_map(struct v3_vm_info * vm) {
210     struct v3_mem_map * map = &(vm->mem_map);
211     struct rb_node * node = v3_rb_first(&(map->mem_regions));
212     struct v3_mem_region * reg;
213     struct rb_node * tmp_node = NULL;
214     addr_t block_pages = v3_mem_block_size >> 12;
215     int i = 0;
216
217     while (node) {
218         reg = rb_entry(node, struct v3_mem_region, tree_node);
219         tmp_node = node;
220         node = v3_rb_next(node);
221
222         v3_delete_mem_region(vm, reg);
223     }
224
225     for (i = 0; i < map->num_base_regions; i++) {
226         struct v3_mem_region * region = &(map->base_regions[i]);
227         V3_FreePages((void *)(region->host_addr), block_pages);
228     }
229
230     V3_Free(map->base_regions);
231
232 }
233
234
235 struct v3_mem_region * v3_create_mem_region(struct v3_vm_info * vm, uint16_t core_id, 
236                                                addr_t guest_addr_start, addr_t guest_addr_end) {
237     struct v3_mem_region * entry = NULL;
238
239     if (guest_addr_start >= guest_addr_end) {
240         PrintError(vm, VCORE_NONE, "Region start is after region end\n");
241         return NULL;
242     }
243
244     entry = (struct v3_mem_region *)V3_Malloc(sizeof(struct v3_mem_region));
245
246     if (!entry) {
247         PrintError(vm, VCORE_NONE, "Cannot allocate in creating a memory region\n");
248         return NULL;
249     }
250
251     memset(entry, 0, sizeof(struct v3_mem_region));
252
253     entry->guest_start = guest_addr_start;
254     entry->guest_end = guest_addr_end;
255     entry->core_id = core_id;
256     entry->unhandled = unhandled_err;
257
258     return entry;
259 }
260
261
262
263
264 int v3_add_shadow_mem( struct v3_vm_info * vm, uint16_t core_id,
265                        addr_t               guest_addr_start,
266                        addr_t               guest_addr_end,
267                        addr_t               host_addr)
268 {
269     struct v3_mem_region * entry = NULL;
270
271     entry = v3_create_mem_region(vm, core_id, 
272                                  guest_addr_start, 
273                                  guest_addr_end);
274
275     entry->host_addr = host_addr;
276
277     entry->flags.read = 1;
278     entry->flags.write = 1;
279     entry->flags.exec = 1;
280     entry->flags.alloced = 1;
281
282     if (v3_insert_mem_region(vm, entry) == -1) {
283         V3_Free(entry);
284         return -1;
285     }
286
287     return 0;
288 }
289
290
291
292 static inline 
293 struct v3_mem_region * __insert_mem_region(struct v3_vm_info * vm, 
294                                            struct v3_mem_region * region) {
295     struct rb_node ** p = &(vm->mem_map.mem_regions.rb_node);
296     struct rb_node * parent = NULL;
297     struct v3_mem_region * tmp_region;
298
299     while (*p) {
300         parent = *p;
301         tmp_region = rb_entry(parent, struct v3_mem_region, tree_node);
302
303         if (region->guest_end <= tmp_region->guest_start) {
304             p = &(*p)->rb_left;
305         } else if (region->guest_start >= tmp_region->guest_end) {
306             p = &(*p)->rb_right;
307         } else {
308             if ((region->guest_end != tmp_region->guest_end) ||
309                 (region->guest_start != tmp_region->guest_start)) {
310                 PrintError(vm, VCORE_NONE, "Trying to map a partial overlapped core specific page...\n");
311                 return tmp_region; // This is ugly... 
312             } else if (region->core_id == tmp_region->core_id) {
313                 PrintError(vm, VCORE_NONE, "Trying to map a core-overlapping page\n");
314                 return tmp_region;
315             } else if (region->core_id < tmp_region->core_id) {
316                 p = &(*p)->rb_left;
317             } else { 
318                 p = &(*p)->rb_right;
319             }
320         }
321     }
322
323     rb_link_node(&(region->tree_node), parent, p);
324   
325     return NULL;
326 }
327
328
329
330 int v3_insert_mem_region(struct v3_vm_info * vm, struct v3_mem_region * region) {
331     struct v3_mem_region * ret;
332     int i = 0;
333     int rc;
334
335     if ((ret = __insert_mem_region(vm, region))) {
336         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);
337         return -1;
338     }
339
340     v3_rb_insert_color(&(region->tree_node), &(vm->mem_map.mem_regions));
341
342
343     rc = 0;
344
345     for (i = 0; i < vm->num_cores; i++) {
346         struct guest_info * info = &(vm->cores[i]);
347
348         // flush virtual page tables 
349         // 3 cases shadow, shadow passthrough, and nested
350
351         if (info->shdw_pg_mode == SHADOW_PAGING) {
352             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
353             
354             if (mem_mode == PHYSICAL_MEM) {
355                 rc |= v3_invalidate_passthrough_addr_range(info, region->guest_start, region->guest_end-1);
356             } else {
357                 rc |= v3_invalidate_shadow_pts(info);
358             }
359             
360         } else if (info->shdw_pg_mode == NESTED_PAGING) {
361             rc |= v3_invalidate_nested_addr_range(info, region->guest_start, region->guest_end-1);
362         }
363     }
364
365     return rc;
366 }
367                                                  
368
369
370
371 struct v3_mem_region * v3_get_mem_region(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
372     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
373     struct v3_mem_region * reg = NULL;
374
375     while (n) {
376
377         reg = rb_entry(n, struct v3_mem_region, tree_node);
378
379         if (guest_addr < reg->guest_start) {
380             n = n->rb_left;
381         } else if (guest_addr >= reg->guest_end) {
382             n = n->rb_right;
383         } else {
384             if (reg->core_id == V3_MEM_CORE_ANY) {
385                 // found relevant region, it's available on all cores
386                 return reg;
387             } else if (core_id == reg->core_id) { 
388                 // found relevant region, it's available on the indicated core
389                 return reg;
390             } else if (core_id < reg->core_id) { 
391                 // go left, core too big
392                 n = n->rb_left;
393             } else if (core_id > reg->core_id) { 
394                 // go right, core too small
395                 n = n->rb_right;
396             } else {
397                 PrintDebug(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
398                 return NULL;
399             }
400         }
401     }
402
403
404     // There is not registered region, so we check if its a valid address in the base region
405
406     return v3_get_base_region(vm, guest_addr);
407 }
408
409
410
411 /* This returns the next memory region based on a given address. 
412  * If the address falls inside a sub region, that region is returned. 
413  * If the address falls outside a sub region, the next sub region is returned
414  * NOTE that we have to be careful about core_ids here...
415  */
416 static struct v3_mem_region * get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
417     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
418     struct v3_mem_region * reg = NULL;
419     struct v3_mem_region * parent = NULL;
420
421     if (n == NULL) {
422         return NULL;
423     }
424
425     while (n) {
426
427         reg = rb_entry(n, struct v3_mem_region, tree_node);
428
429         if (guest_addr < reg->guest_start) {
430             n = n->rb_left;
431         } else if (guest_addr >= reg->guest_end) {
432             n = n->rb_right;
433         } else {
434             if (reg->core_id == V3_MEM_CORE_ANY) {
435                 // found relevant region, it's available on all cores
436                 return reg;
437             } else if (core_id == reg->core_id) { 
438                 // found relevant region, it's available on the indicated core
439                 return reg;
440             } else if (core_id < reg->core_id) { 
441                 // go left, core too big
442                 n = n->rb_left;
443             } else if (core_id > reg->core_id) { 
444                 // go right, core too small
445                 n = n->rb_right;
446             } else {
447                 PrintError(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
448                 return NULL;
449             }
450         }
451
452         if ((reg->core_id == core_id) || (reg->core_id == V3_MEM_CORE_ANY)) {
453             parent = reg;
454         }
455     }
456
457
458     if (parent->guest_start > guest_addr) {
459         return parent;
460     } else if (parent->guest_end < guest_addr) {
461         struct rb_node * node = &(parent->tree_node);
462
463         while ((node = v3_rb_next(node)) != NULL) {
464             struct v3_mem_region * next_reg = rb_entry(node, struct v3_mem_region, tree_node);
465
466             if ((next_reg->core_id == V3_MEM_CORE_ANY) ||
467                 (next_reg->core_id == core_id)) {
468
469                 // This check is not strictly necessary, but it makes it clearer
470                 if (next_reg->guest_start > guest_addr) {
471                     return next_reg;
472                 }
473             }
474         }
475     }
476
477     return NULL;
478 }
479
480
481
482
483 /* Given an address region of memory, find if there are any regions that overlap with it. 
484  * This checks that the range lies in a single region, and returns that region if it does, 
485  * this can be either the base region or a sub region. 
486  * IF there are multiple regions in the range then it returns NULL
487  */
488 static struct v3_mem_region * get_overlapping_region(struct v3_vm_info * vm, uint16_t core_id, 
489                                                      addr_t start_gpa, addr_t end_gpa) {
490     struct v3_mem_region * start_region = v3_get_mem_region(vm, core_id, start_gpa);
491
492     if (start_region == NULL) {
493         PrintError(vm, VCORE_NONE, "No overlapping region for core=%d, start_gpa=%p\n", core_id, (void*)start_gpa);
494         v3_print_mem_map(vm);
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 }