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.


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