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.


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