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.


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