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.


Context-based output infrastructure (V3_Print, etc) and modifications to use it
[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                 return tmp_region;
196             } else if (region->core_id < tmp_region->core_id) {
197                 p = &(*p)->rb_left;
198             } else { 
199                 p = &(*p)->rb_right;
200             }
201         }
202     }
203
204     rb_link_node(&(region->tree_node), parent, p);
205   
206     return NULL;
207 }
208
209
210
211 int v3_insert_mem_region(struct v3_vm_info * vm, struct v3_mem_region * region) {
212     struct v3_mem_region * ret;
213     int i = 0;
214
215     if ((ret = __insert_mem_region(vm, region))) {
216         return -1;
217     }
218
219     v3_rb_insert_color(&(region->tree_node), &(vm->mem_map.mem_regions));
220
221
222
223     for (i = 0; i < vm->num_cores; i++) {
224         struct guest_info * info = &(vm->cores[i]);
225
226         // flush virtual page tables 
227         // 3 cases shadow, shadow passthrough, and nested
228
229         if (info->shdw_pg_mode == SHADOW_PAGING) {
230             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
231             
232             if (mem_mode == PHYSICAL_MEM) {
233                 addr_t cur_addr;
234                 
235                 for (cur_addr = region->guest_start;
236                      cur_addr < region->guest_end;
237                      cur_addr += PAGE_SIZE_4KB) {
238                     v3_invalidate_passthrough_addr(info, cur_addr);
239                 }
240             } else {
241                 v3_invalidate_shadow_pts(info);
242             }
243             
244         } else if (info->shdw_pg_mode == NESTED_PAGING) {
245             addr_t cur_addr;
246             
247             for (cur_addr = region->guest_start;
248                  cur_addr < region->guest_end;
249                  cur_addr += PAGE_SIZE_4KB) {
250                 
251                 v3_invalidate_nested_addr(info, cur_addr);
252             }
253         }
254     }
255
256     return 0;
257 }
258                                                  
259
260
261
262 struct v3_mem_region * v3_get_mem_region(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
263     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
264     struct v3_mem_region * reg = NULL;
265
266     while (n) {
267
268         reg = rb_entry(n, struct v3_mem_region, tree_node);
269
270         if (guest_addr < reg->guest_start) {
271             n = n->rb_left;
272         } else if (guest_addr >= reg->guest_end) {
273             n = n->rb_right;
274         } else {
275             if (reg->core_id == V3_MEM_CORE_ANY) {
276                 // found relevant region, it's available on all cores
277                 return reg;
278             } else if (core_id == reg->core_id) { 
279                 // found relevant region, it's available on the indicated core
280                 return reg;
281             } else if (core_id < reg->core_id) { 
282                 // go left, core too big
283                 n = n->rb_left;
284             } else if (core_id > reg->core_id) { 
285                 // go right, core too small
286                 n = n->rb_right;
287             } else {
288                 PrintDebug(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
289                 return NULL;
290             }
291         }
292     }
293
294
295     // There is not registered region, so we check if its a valid address in the base region
296
297     if (guest_addr > vm->mem_map.base_region.guest_end) {
298         PrintError(vm, VCORE_NONE, "Guest Address Exceeds Base Memory Size (ga=0x%p), (limit=0x%p) (core=0x%x)\n", 
299                    (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end, core_id);
300         v3_print_mem_map(vm);
301
302         return NULL;
303     }
304
305     return &(vm->mem_map.base_region);
306 }
307
308
309
310 /* This returns the next memory region based on a given address. 
311  * If the address falls inside a sub region, that region is returned. 
312  * If the address falls outside a sub region, the next sub region is returned
313  * NOTE that we have to be careful about core_ids here...
314  */
315 static struct v3_mem_region * get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
316     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
317     struct v3_mem_region * reg = NULL;
318     struct v3_mem_region * parent = NULL;
319
320     if (n == NULL) {
321         return NULL;
322     }
323
324     while (n) {
325
326         reg = rb_entry(n, struct v3_mem_region, tree_node);
327
328         if (guest_addr < reg->guest_start) {
329             n = n->rb_left;
330         } else if (guest_addr >= reg->guest_end) {
331             n = n->rb_right;
332         } else {
333             if (reg->core_id == V3_MEM_CORE_ANY) {
334                 // found relevant region, it's available on all cores
335                 return reg;
336             } else if (core_id == reg->core_id) { 
337                 // found relevant region, it's available on the indicated core
338                 return reg;
339             } else if (core_id < reg->core_id) { 
340                 // go left, core too big
341                 n = n->rb_left;
342             } else if (core_id > reg->core_id) { 
343                 // go right, core too small
344                 n = n->rb_right;
345             } else {
346                 PrintError(vm, VCORE_NONE, "v3_get_mem_region: Impossible!\n");
347                 return NULL;
348             }
349         }
350
351         if ((reg->core_id == core_id) || (reg->core_id == V3_MEM_CORE_ANY)) {
352             parent = reg;
353         }
354     }
355
356
357     if (parent->guest_start > guest_addr) {
358         return parent;
359     } else if (parent->guest_end < guest_addr) {
360         struct rb_node * node = &(parent->tree_node);
361
362         while ((node = v3_rb_next(node)) != NULL) {
363             struct v3_mem_region * next_reg = rb_entry(node, struct v3_mem_region, tree_node);
364
365             if ((next_reg->core_id == V3_MEM_CORE_ANY) ||
366                 (next_reg->core_id == core_id)) {
367
368                 // This check is not strictly necessary, but it makes it clearer
369                 if (next_reg->guest_start > guest_addr) {
370                     return next_reg;
371                 }
372             }
373         }
374     }
375
376     return NULL;
377 }
378
379
380
381
382 /* Given an address region of memory, find if there are any regions that overlap with it. 
383  * This checks that the range lies in a single region, and returns that region if it does, 
384  * this can be either the base region or a sub region. 
385  * IF there are multiple regions in the range then it returns NULL
386  */
387 static struct v3_mem_region * get_overlapping_region(struct v3_vm_info * vm, uint16_t core_id, 
388                                                      addr_t start_gpa, addr_t end_gpa) {
389     struct v3_mem_region * start_region = v3_get_mem_region(vm, core_id, start_gpa);
390
391     if (start_region == NULL) {
392         PrintError(vm, VCORE_NONE, "Invalid memory region\n");
393         return NULL;
394     }
395
396
397     if (start_region->guest_end < end_gpa) {
398         // Region ends before range
399         return NULL;
400     } else if (start_region->flags.base == 0) {
401         // sub region overlaps range
402         return start_region;
403     } else {
404         // Base region, now we have to scan forward for the next sub region
405         struct v3_mem_region * next_reg = get_next_mem_region(vm, core_id, start_gpa);
406         
407         if (next_reg == NULL) {
408             // no sub regions after start_addr, base region is ok
409             return start_region;
410         } else if (next_reg->guest_start >= end_gpa) {
411             // Next sub region begins outside range
412             return start_region;
413         } else {
414             return NULL;
415         }
416     }
417
418
419     // Should never get here
420     return NULL;
421 }
422
423
424
425
426
427 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
428     int i = 0;
429
430     if (reg == NULL) {
431         return;
432     }
433
434
435     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
436
437
438
439     // If the guest isn't running then there shouldn't be anything to invalidate. 
440     // Page tables should __always__ be created on demand during execution
441     // NOTE: This is a sanity check, and can be removed if that assumption changes
442     if (vm->run_state != VM_RUNNING) {
443         V3_Free(reg);
444         return;
445     }
446
447     for (i = 0; i < vm->num_cores; i++) {
448         struct guest_info * info = &(vm->cores[i]);
449
450         // flush virtual page tables 
451         // 3 cases shadow, shadow passthrough, and nested
452
453         if (info->shdw_pg_mode == SHADOW_PAGING) {
454             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
455             
456             if (mem_mode == PHYSICAL_MEM) {
457                 addr_t cur_addr;
458                 
459                 for (cur_addr = reg->guest_start;
460                      cur_addr < reg->guest_end;
461                      cur_addr += PAGE_SIZE_4KB) {
462                     v3_invalidate_passthrough_addr(info, cur_addr);
463                 }
464             } else {
465                 v3_invalidate_shadow_pts(info);
466             }
467             
468         } else if (info->shdw_pg_mode == NESTED_PAGING) {
469             addr_t cur_addr;
470             
471             for (cur_addr = reg->guest_start;
472                  cur_addr < reg->guest_end;
473                  cur_addr += PAGE_SIZE_4KB) {
474                 
475                 v3_invalidate_nested_addr(info, cur_addr);
476             }
477         }
478     }
479
480     V3_Free(reg);
481
482     // flush virtual page tables 
483     // 3 cases shadow, shadow passthrough, and nested
484
485 }
486
487 // Determine if a given address can be handled by a large page of the requested size
488 uint32_t v3_get_max_page_size(struct guest_info * core, addr_t page_addr, v3_cpu_mode_t mode) {
489     addr_t pg_start = 0;
490     addr_t pg_end = 0; 
491     uint32_t page_size = PAGE_SIZE_4KB;
492     struct v3_mem_region * reg = NULL;
493     
494     switch (mode) {
495         case PROTECTED:
496             if (core->use_large_pages == 1) {
497                 pg_start = PAGE_ADDR_4MB(page_addr);
498                 pg_end = (pg_start + PAGE_SIZE_4MB);
499
500                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end); 
501
502                 if ((reg) && ((reg->host_addr % PAGE_SIZE_4MB) == 0)) {
503                     page_size = PAGE_SIZE_4MB;
504                 }
505             }
506             break;
507         case PROTECTED_PAE:
508             if (core->use_large_pages == 1) {
509                 pg_start = PAGE_ADDR_2MB(page_addr);
510                 pg_end = (pg_start + PAGE_SIZE_2MB);
511
512                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
513
514                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
515                     page_size = PAGE_SIZE_2MB;
516                 }
517             }
518             break;
519         case LONG:
520         case LONG_32_COMPAT:
521         case LONG_16_COMPAT:
522             if (core->use_giant_pages == 1) {
523                 pg_start = PAGE_ADDR_1GB(page_addr);
524                 pg_end = (pg_start + PAGE_SIZE_1GB);
525                 
526                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
527                 
528                 if ((reg) && ((reg->host_addr % PAGE_SIZE_1GB) == 0)) {
529                     page_size = PAGE_SIZE_1GB;
530                     break;
531                 }
532             }
533
534             if (core->use_large_pages == 1) {
535                 pg_start = PAGE_ADDR_2MB(page_addr);
536                 pg_end = (pg_start + PAGE_SIZE_2MB);
537
538                 reg = get_overlapping_region(core->vm_info, core->vcpu_id, pg_start, pg_end);
539                 
540                 if ((reg) && ((reg->host_addr % PAGE_SIZE_2MB) == 0)) {
541                     page_size = PAGE_SIZE_2MB;
542                 }
543             }
544             break;
545         default:
546             PrintError(core->vm_info, core, "Invalid CPU mode: %s\n", v3_cpu_mode_to_str(v3_get_vm_cpu_mode(core)));
547             return -1;
548     }
549
550     return page_size;
551 }
552
553
554
555 void v3_print_mem_map(struct v3_vm_info * vm) {
556     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
557     struct v3_mem_region * reg = &(vm->mem_map.base_region);
558     int i = 0;
559
560     V3_Print(vm, VCORE_NONE, "Memory Layout (all cores):\n");
561     
562
563     V3_Print(vm, VCORE_NONE, "Base Region (all cores):  0x%p - 0x%p -> 0x%p\n", 
564                (void *)(reg->guest_start), 
565                (void *)(reg->guest_end - 1), 
566                (void *)(reg->host_addr));
567     
568
569     // If the memory map is empty, don't print it
570     if (node == NULL) {
571         return;
572     }
573
574     do {
575         reg = rb_entry(node, struct v3_mem_region, tree_node);
576
577         V3_Print(vm, VCORE_NONE, "%d:  0x%p - 0x%p -> 0x%p\n", i, 
578                    (void *)(reg->guest_start), 
579                    (void *)(reg->guest_end - 1), 
580                    (void *)(reg->host_addr));
581
582         V3_Print(vm, VCORE_NONE, "\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
583                  reg->flags.value, 
584                  reg->core_id,
585                  reg->unhandled);
586     
587         i++;
588     } while ((node = v3_rb_next(node)));
589 }
590