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.


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