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.


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