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.


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