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