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 memory alignment option to vmm memory mangement initialization and allocation
[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
54 static inline uint32_t get_alignment(char * align_str) {
55     if (align_str != NULL) {
56         if (strncasecmp(align_str, "2MB", strlen("2MB")) == 0) {
57             return PAGE_SIZE_2MB;
58         } else if (strncasecmp(align_str, "4MB", strlen("4MB")) == 0) {
59             return PAGE_SIZE_4MB;
60         }
61     }
62     
63     // default is 4KB alignment
64     return PAGE_SIZE_4KB;
65 }
66
67 int v3_init_mem_map(struct v3_vm_info * vm) {
68     struct v3_mem_map * map = &(vm->mem_map);
69     v3_cfg_tree_t * pg_cfg = v3_cfg_subtree(vm->cfg_data->cfg, "memory");
70     uint32_t alignment = get_alignment(v3_cfg_val(pg_cfg, "alignment"));
71     addr_t mem_pages = vm->mem_size >> 12;
72
73     memset(&(map->base_region), 0, sizeof(struct v3_mem_region));
74
75     map->mem_regions.rb_node = NULL;
76
77
78     // There is an underlying region that contains all of the guest memory
79     // PrintDebug("Mapping %d pages of memory (%u bytes)\n", (int)mem_pages, (uint_t)info->mem_size);
80
81     map->base_region.guest_start = 0;
82     map->base_region.guest_end = mem_pages * PAGE_SIZE_4KB;
83
84 #ifdef ALIGNED_PG_ALLOC
85     map->base_region.host_addr = (addr_t)V3_AllocAlignedPages(mem_pages, alignment);
86 #else
87     if (alignment != PAGE_SIZE_4KB) {
88         PrintError("Aligned page allocations are not supported in this host (requested alignment=%d)\n", alignment);
89         PrintError("Ignoring alignment request\n");
90     }
91     map->base_region.host_addr = (addr_t)V3_AllocPages(mem_pages);
92 #endif
93
94     map->base_region.flags.read = 1;
95     map->base_region.flags.write = 1;
96     map->base_region.flags.exec = 1;
97     map->base_region.flags.base = 1;
98     map->base_region.flags.alloced = 1;
99     
100     map->base_region.unhandled = unhandled_err;
101
102     if ((void *)map->base_region.host_addr == NULL) {
103         PrintError("Could not allocate Guest memory\n");
104         return -1;
105     }
106         
107     //memset(V3_VAddr((void *)map->base_region.host_addr), 0xffffffff, map->base_region.guest_end);
108
109     v3_register_hypercall(vm, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
110
111     return 0;
112 }
113
114
115 void v3_delete_mem_map(struct v3_vm_info * vm) {
116     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
117     struct v3_mem_region * reg;
118     struct rb_node * tmp_node = NULL;
119   
120     while (node) {
121         reg = rb_entry(node, struct v3_mem_region, tree_node);
122         tmp_node = node;
123         node = v3_rb_next(node);
124
125         v3_delete_mem_region(vm, reg);
126     }
127
128     V3_FreePage((void *)(vm->mem_map.base_region.host_addr));
129 }
130
131
132 struct v3_mem_region * v3_create_mem_region(struct v3_vm_info * vm, uint16_t core_id, 
133                                                addr_t guest_addr_start, addr_t guest_addr_end) {
134     
135     struct v3_mem_region * entry = (struct v3_mem_region *)V3_Malloc(sizeof(struct v3_mem_region));
136     memset(entry, 0, sizeof(struct v3_mem_region));
137
138     entry->guest_start = guest_addr_start;
139     entry->guest_end = guest_addr_end;
140     entry->core_id = core_id;
141     entry->unhandled = unhandled_err;
142
143     return entry;
144 }
145
146
147
148
149 int v3_add_shadow_mem( struct v3_vm_info * vm, uint16_t core_id,
150                        addr_t               guest_addr_start,
151                        addr_t               guest_addr_end,
152                        addr_t               host_addr)
153 {
154     struct v3_mem_region * entry = NULL;
155
156     entry = v3_create_mem_region(vm, core_id, 
157                                  guest_addr_start, 
158                                  guest_addr_end);
159
160     entry->host_addr = host_addr;
161
162
163     entry->flags.read = 1;
164     entry->flags.write = 1;
165     entry->flags.exec = 1;
166     entry->flags.alloced = 1;
167
168     if (v3_insert_mem_region(vm, entry) == -1) {
169         V3_Free(entry);
170         return -1;
171     }
172
173     return 0;
174 }
175
176
177
178 static inline 
179 struct v3_mem_region * __insert_mem_region(struct v3_vm_info * vm, 
180                                                  struct v3_mem_region * region) {
181     struct rb_node ** p = &(vm->mem_map.mem_regions.rb_node);
182     struct rb_node * parent = NULL;
183     struct v3_mem_region * tmp_region;
184
185     while (*p) {
186         parent = *p;
187         tmp_region = rb_entry(parent, struct v3_mem_region, tree_node);
188
189         if (region->guest_end <= tmp_region->guest_start) {
190             p = &(*p)->rb_left;
191         } else if (region->guest_start >= tmp_region->guest_end) {
192             p = &(*p)->rb_right;
193         } else {
194             if ((region->guest_end != tmp_region->guest_end) ||
195                 (region->guest_start != tmp_region->guest_start)) {
196                 PrintError("Trying to map a partial overlapped core specific page...\n");
197                 return tmp_region; // This is ugly... 
198             } else if (region->core_id == tmp_region->core_id) {
199                 return tmp_region;
200             } else if (region->core_id < tmp_region->core_id) {
201                 p = &(*p)->rb_left;
202             } else { 
203                 p = &(*p)->rb_right;
204             }
205         }
206     }
207
208     rb_link_node(&(region->tree_node), parent, p);
209   
210     return NULL;
211 }
212
213
214
215 int v3_insert_mem_region(struct v3_vm_info * vm, struct v3_mem_region * region) {
216     struct v3_mem_region * ret;
217     int i = 0;
218
219     if ((ret = __insert_mem_region(vm, region))) {
220         return -1;
221     }
222
223     v3_rb_insert_color(&(region->tree_node), &(vm->mem_map.mem_regions));
224
225
226
227     for (i = 0; i < vm->num_cores; i++) {
228         struct guest_info * info = &(vm->cores[i]);
229
230         // flush virtual page tables 
231         // 3 cases shadow, shadow passthrough, and nested
232
233         if (info->shdw_pg_mode == SHADOW_PAGING) {
234             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
235             
236             if (mem_mode == PHYSICAL_MEM) {
237                 addr_t cur_addr;
238                 
239                 for (cur_addr = region->guest_start;
240                      cur_addr < region->guest_end;
241                      cur_addr += PAGE_SIZE_4KB) {
242                     v3_invalidate_passthrough_addr(info, cur_addr);
243                 }
244             } else {
245                 v3_invalidate_shadow_pts(info);
246             }
247             
248         } else if (info->shdw_pg_mode == NESTED_PAGING) {
249             addr_t cur_addr;
250             
251             for (cur_addr = region->guest_start;
252                  cur_addr < region->guest_end;
253                  cur_addr += PAGE_SIZE_4KB) {
254                 
255                 v3_invalidate_nested_addr(info, cur_addr);
256             }
257         }
258     }
259
260     return 0;
261 }
262                                                  
263
264
265
266 struct v3_mem_region * v3_get_mem_region(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
267     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
268     struct v3_mem_region * reg = NULL;
269
270     while (n) {
271
272         reg = rb_entry(n, struct v3_mem_region, tree_node);
273
274         if (guest_addr < reg->guest_start) {
275             n = n->rb_left;
276         } else if (guest_addr >= reg->guest_end) {
277             n = n->rb_right;
278         } else {
279             if (reg->core_id == V3_MEM_CORE_ANY) {
280                 // found relevant region, it's available on all cores
281                 return reg;
282             } else if (core_id == reg->core_id) { 
283                 // found relevant region, it's available on the indicated core
284                 return reg;
285             } else if (core_id < reg->core_id) { 
286                 // go left, core too big
287                 n = n->rb_left;
288             } else if (core_id > reg->core_id) { 
289                 // go right, core too small
290                 n = n->rb_right;
291             } else {
292                 PrintDebug("v3_get_mem_region: Impossible!\n");
293                 return NULL;
294             }
295         }
296     }
297
298
299     // There is not registered region, so we check if its a valid address in the base region
300
301     if (guest_addr > vm->mem_map.base_region.guest_end) {
302         PrintError("Guest Address Exceeds Base Memory Size (ga=0x%p), (limit=0x%p) (core=0x%x)\n", 
303                    (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end, core_id);
304         v3_print_mem_map(vm);
305
306         return NULL;
307     }
308
309     return &(vm->mem_map.base_region);
310 }
311
312
313
314 /* Search the "hooked" memory regions for a region that ends after the given address.  If the
315  * address is invalid, return NULL. Else, return the first region found or the base region if no
316  * region ends after the given address.
317  */
318 struct v3_mem_region * v3_get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
319     struct rb_node * n = vm->mem_map.mem_regions.rb_node;
320     struct v3_mem_region * reg = NULL;
321
322     // Keep going to the right in the tree while the address is greater than the current region's
323     // end address.
324     while (n) {
325         reg = rb_entry(n, struct v3_mem_region, tree_node);
326         if (guest_addr >= reg->guest_end) { // reg is [start,end)
327             n = n->rb_right;
328         } else {
329             if ((core_id == reg->core_id) || (reg->core_id == V3_MEM_CORE_ANY)) {
330                 return reg;
331             } else {
332                 n = n->rb_right;
333             }
334         }
335     }
336
337     // There is no registered region, so we check if it's a valid address in the base region
338
339     if (guest_addr >= vm->mem_map.base_region.guest_end) {
340         PrintError("%s: Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n",
341                 __FUNCTION__, (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end);
342         v3_print_mem_map(vm);
343         return NULL;
344     }
345
346     return &(vm->mem_map.base_region);
347 }
348
349
350
351
352 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
353     int i = 0;
354
355     if (reg == NULL) {
356         return;
357     }
358
359     for (i = 0; i < vm->num_cores; i++) {
360         struct guest_info * info = &(vm->cores[i]);
361
362         // flush virtual page tables 
363         // 3 cases shadow, shadow passthrough, and nested
364
365         if (info->shdw_pg_mode == SHADOW_PAGING) {
366             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
367             
368             if (mem_mode == PHYSICAL_MEM) {
369                 addr_t cur_addr;
370                 
371                 for (cur_addr = reg->guest_start;
372                      cur_addr < reg->guest_end;
373                      cur_addr += PAGE_SIZE_4KB) {
374                     v3_invalidate_passthrough_addr(info, cur_addr);
375                 }
376             } else {
377                 v3_invalidate_shadow_pts(info);
378             }
379             
380         } else if (info->shdw_pg_mode == NESTED_PAGING) {
381             addr_t cur_addr;
382             
383             for (cur_addr = reg->guest_start;
384                  cur_addr < reg->guest_end;
385                  cur_addr += PAGE_SIZE_4KB) {
386                 
387                 v3_invalidate_nested_addr(info, cur_addr);
388             }
389         }
390     }
391
392     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
393
394     V3_Free(reg);
395
396     // flush virtual page tables 
397     // 3 cases shadow, shadow passthrough, and nested
398
399 }
400
401
402
403 void v3_print_mem_map(struct v3_vm_info * vm) {
404     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
405     struct v3_mem_region * reg = &(vm->mem_map.base_region);
406     int i = 0;
407
408     V3_Print("Memory Layout (all cores):\n");
409     
410
411     V3_Print("Base Region (all cores):  0x%p - 0x%p -> 0x%p\n", 
412                (void *)(reg->guest_start), 
413                (void *)(reg->guest_end - 1), 
414                (void *)(reg->host_addr));
415     
416
417     // If the memory map is empty, don't print it
418     if (node == NULL) {
419         return;
420     }
421
422     do {
423         reg = rb_entry(node, struct v3_mem_region, tree_node);
424
425         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
426                    (void *)(reg->guest_start), 
427                    (void *)(reg->guest_end - 1), 
428                    (void *)(reg->host_addr));
429
430         V3_Print("\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
431                  reg->flags.value, 
432                  reg->core_id,
433                  reg->unhandled);
434     
435         i++;
436     } while ((node = v3_rb_next(node)));
437 }
438