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.


Changes to support large shadow pages *correctly*.
[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     map->base_region.guest_start = 0;
65     map->base_region.guest_end = mem_pages * PAGE_SIZE_4KB;
66
67 #ifdef CONFIG_ALIGNED_PG_ALLOC
68     map->base_region.host_addr = (addr_t)V3_AllocAlignedPages(mem_pages, vm->mem_align);
69 #else
70     map->base_region.host_addr = (addr_t)V3_AllocPages(mem_pages);
71 #endif
72
73     map->base_region.flags.read = 1;
74     map->base_region.flags.write = 1;
75     map->base_region.flags.exec = 1;
76     map->base_region.flags.base = 1;
77     map->base_region.flags.alloced = 1;
78     
79     map->base_region.unhandled = unhandled_err;
80
81     if ((void *)map->base_region.host_addr == NULL) {
82         PrintError("Could not allocate Guest memory\n");
83         return -1;
84     }
85         
86     //memset(V3_VAddr((void *)map->base_region.host_addr), 0xffffffff, map->base_region.guest_end);
87
88     v3_register_hypercall(vm, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
89
90     return 0;
91 }
92
93
94 void v3_delete_mem_map(struct v3_vm_info * vm) {
95     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
96     struct v3_mem_region * reg;
97     struct rb_node * tmp_node = NULL;
98   
99     while (node) {
100         reg = rb_entry(node, struct v3_mem_region, tree_node);
101         tmp_node = node;
102         node = v3_rb_next(node);
103
104         v3_delete_mem_region(vm, reg);
105     }
106
107     V3_FreePage((void *)(vm->mem_map.base_region.host_addr));
108 }
109
110
111 struct v3_mem_region * v3_create_mem_region(struct v3_vm_info * vm, uint16_t core_id, 
112                                                addr_t guest_addr_start, addr_t guest_addr_end) {
113     
114     struct v3_mem_region * entry = (struct v3_mem_region *)V3_Malloc(sizeof(struct v3_mem_region));
115     memset(entry, 0, sizeof(struct v3_mem_region));
116
117     entry->guest_start = guest_addr_start;
118     entry->guest_end = guest_addr_end;
119     entry->core_id = core_id;
120     entry->unhandled = unhandled_err;
121
122     return entry;
123 }
124
125
126
127
128 int v3_add_shadow_mem( struct v3_vm_info * vm, uint16_t core_id,
129                        addr_t               guest_addr_start,
130                        addr_t               guest_addr_end,
131                        addr_t               host_addr)
132 {
133     struct v3_mem_region * entry = NULL;
134
135     entry = v3_create_mem_region(vm, core_id, 
136                                  guest_addr_start, 
137                                  guest_addr_end);
138
139     entry->host_addr = host_addr;
140
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 /* Given an address, find the successor region. If the address is within a region, return that
294  * region. Input is an address, because the address may not have a region associated with it.
295  *
296  * Returns a region following or touching the given address. If address is invalid, NULL is
297  * returned, else the base region is returned if no region exists at or after the given address.
298  */
299 struct v3_mem_region * v3_get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
300     struct rb_node * current_n          = vm->mem_map.mem_regions.rb_node;
301     struct rb_node * successor_n        = NULL; /* left-most node greater than guest_addr */
302     struct v3_mem_region * current_r    = NULL;
303
304     /* current_n tries to find the region containing guest_addr, going right when smaller and left when
305      * greater. Each time current_n becomes greater than guest_addr, update successor <- current_n.
306      * current_n becomes successively closer to guest_addr than the previous time it was greater
307      * than guest_addr.
308      */
309
310     /* | is address, ---- is region, + is intersection */
311     while (current_n) {
312         current_r = rb_entry(current_n, struct v3_mem_region, tree_node);
313         if (current_r->guest_start > guest_addr) { /* | ---- */
314             successor_n = current_n;
315             current_n = current_n->rb_left;
316         } else {
317             if (current_r->guest_end > guest_addr) {
318                 return current_r; /* +--- or --+- */
319             }
320             current_n = current_n->rb_right; /* ---- | */
321         }
322     }
323
324     /* Address does not have its own region. Check if it's a valid address in the base region */
325
326     if (guest_addr >= vm->mem_map.base_region.guest_end) {
327         PrintError("%s: Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n",
328                 __FUNCTION__, (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end);
329         v3_print_mem_map(vm);
330         return NULL;
331     }
332
333     return &(vm->mem_map.base_region);
334 }
335
336
337
338
339 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
340     int i = 0;
341
342     if (reg == NULL) {
343         return;
344     }
345
346     for (i = 0; i < vm->num_cores; i++) {
347         struct guest_info * info = &(vm->cores[i]);
348
349         // flush virtual page tables 
350         // 3 cases shadow, shadow passthrough, and nested
351
352         if (info->shdw_pg_mode == SHADOW_PAGING) {
353             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
354             
355             if (mem_mode == PHYSICAL_MEM) {
356                 addr_t cur_addr;
357                 
358                 for (cur_addr = reg->guest_start;
359                      cur_addr < reg->guest_end;
360                      cur_addr += PAGE_SIZE_4KB) {
361                     v3_invalidate_passthrough_addr(info, cur_addr);
362                 }
363             } else {
364                 v3_invalidate_shadow_pts(info);
365             }
366             
367         } else if (info->shdw_pg_mode == NESTED_PAGING) {
368             addr_t cur_addr;
369             
370             for (cur_addr = reg->guest_start;
371                  cur_addr < reg->guest_end;
372                  cur_addr += PAGE_SIZE_4KB) {
373                 
374                 v3_invalidate_nested_addr(info, cur_addr);
375             }
376         }
377     }
378
379     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
380
381     V3_Free(reg);
382
383     // flush virtual page tables 
384     // 3 cases shadow, shadow passthrough, and nested
385
386 }
387
388 // Determine if a given address can be handled by a large page of the requested size
389 uint32_t v3_get_max_page_size(struct guest_info * core, addr_t fault_addr, uint32_t req_size) {
390     addr_t pg_start = 0UL, pg_end = 0UL; // large page containing the faulting addres
391     struct v3_mem_region * pg_next_reg = NULL; // next immediate mem reg after page start addr
392     uint32_t page_size = PAGE_SIZE_4KB;
393
394    /* If the guest has been configured for large pages, then we must check for hooked regions of
395      * memory which may overlap with the large page containing the faulting address (due to
396      * potentially differing access policies in place for e.g. i/o devices and APIC). A large page
397      * can be used if a) no region overlaps the page [or b) a region does overlap but fully contains
398      * the page]. The [bracketed] text pertains to the #if 0'd code below, state D. TODO modify this
399      * note if someone decides to enable this optimization. It can be tested with the SeaStar
400      * mapping.
401      *
402      * Examples: (CAPS regions are returned by v3_get_next_mem_region; state A returns the base reg)
403      *
404      *    |region| |region|                               2MiB mapped (state A)
405      *                   |reg|          |REG|             2MiB mapped (state B)
406      *   |region|     |reg|   |REG| |region|   |reg|      4KiB mapped (state C)
407      *        |reg|  |reg|   |--REGION---|                [2MiB mapped (state D)]
408      * |--------------------------------------------|     RAM
409      *                             ^                      fault addr
410      * |----|----|----|----|----|page|----|----|----|     2MB pages
411      *                           >>>>>>>>>>>>>>>>>>>>     search space
412      */
413
414
415     // guest page maps to a host page + offset (so when we shift, it aligns with a host page)
416     switch (req_size) {
417         case PAGE_SIZE_4KB:
418                 return PAGE_SIZE_4KB;
419         case PAGE_SIZE_2MB:
420                 pg_start = PAGE_ADDR_2MB(fault_addr);
421                 pg_end = (pg_start + PAGE_SIZE_2MB);
422                 break;
423         case PAGE_SIZE_4MB:
424                 pg_start = PAGE_ADDR_4MB(fault_addr);
425                 pg_end = (pg_start + PAGE_SIZE_4MB);
426                 break;
427         case PAGE_SIZE_1GB:
428                 pg_start = PAGE_ADDR_1GB(fault_addr);
429                 pg_end = (pg_start + PAGE_SIZE_1GB);
430                 break;
431         default:
432                 PrintError("Invalid large page size requested.\n");
433                 return -1;
434     }
435
436     PrintDebug("%s: page   [%p,%p) contains address\n", __FUNCTION__, (void *)pg_start, (void *)pg_end);
437
438     pg_next_reg = v3_get_next_mem_region(core->vm_info, core->cpu_id, pg_start);
439
440     if (pg_next_reg == NULL) {
441         PrintError("%s: Error: address not in base region, %p\n", __FUNCTION__, (void *)fault_addr);
442         return PAGE_SIZE_4KB;
443     }
444
445     if (pg_next_reg->flags.base == 1) {
446         page_size = req_size; // State A
447         PrintDebug("%s: base region [%p,%p) contains page.\n", __FUNCTION__,
448                    (void *)pg_next_reg->guest_start, (void *)pg_next_reg->guest_end);
449     } else {
450 #if 0       // State B/C and D optimization
451         if ((pg_next_reg->guest_end >= pg_end) &&
452             ((pg_next_reg->guest_start >= pg_end) || (pg_next_reg->guest_start <= pg_start))) {     
453             page_size = req_size;
454         }
455
456         PrintDebug("%s: region [%p,%p) %s partially overlap with page\n", __FUNCTION__,
457                    (void *)pg_next_reg->guest_start, (void *)pg_next_reg->guest_end, 
458                    (page_size == req_size) ? "does not" : "does");
459
460 #else       // State B/C
461         if (pg_next_reg->guest_start >= pg_end) {
462             
463             page_size = req_size;
464         }
465
466         PrintDebug("%s: region [%p,%p) %s overlap with page\n", __FUNCTION__,
467                    (void *)pg_next_reg->guest_start, (void *)pg_next_reg->guest_end,
468                    (page_size == req_size) ? "does not" : "does");
469
470 #endif
471     }
472
473     return page_size;
474 }
475
476
477 void v3_print_mem_map(struct v3_vm_info * vm) {
478     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
479     struct v3_mem_region * reg = &(vm->mem_map.base_region);
480     int i = 0;
481
482     V3_Print("Memory Layout (all cores):\n");
483     
484
485     V3_Print("Base Region (all cores):  0x%p - 0x%p -> 0x%p\n", 
486                (void *)(reg->guest_start), 
487                (void *)(reg->guest_end - 1), 
488                (void *)(reg->host_addr));
489     
490
491     // If the memory map is empty, don't print it
492     if (node == NULL) {
493         return;
494     }
495
496     do {
497         reg = rb_entry(node, struct v3_mem_region, tree_node);
498
499         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
500                    (void *)(reg->guest_start), 
501                    (void *)(reg->guest_end - 1), 
502                    (void *)(reg->host_addr));
503
504         V3_Print("\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
505                  reg->flags.value, 
506                  reg->core_id,
507                  reg->unhandled);
508     
509         i++;
510     } while ((node = v3_rb_next(node)));
511 }
512