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.


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