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.


this is a fix for the get_next_region function.
[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 /* Given an address, find the successor region. If the address is within a region, return that
315  * region. Input is an address, because the address may not have a region associated with it.
316  *
317  * Returns a region following or touching the given address. If address is invalid, NULL is
318  * returned, else the base region is returned if no region exists at or after the given address.
319  */
320 struct v3_mem_region * v3_get_next_mem_region( struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr) {
321     struct rb_node * current_n          = vm->mem_map.mem_regions.rb_node;
322     struct rb_node * successor_n        = NULL; /* left-most node greater than guest_addr */
323     struct v3_mem_region * current_r    = NULL;
324
325     /* current_n tries to find the region containing guest_addr, going right when smaller and left when
326      * greater. Each time current_n becomes greater than guest_addr, update successor <- current_n.
327      * current_n becomes successively closer to guest_addr than the previous time it was greater
328      * than guest_addr.
329      */
330
331     /* | is address, ---- is region, + is intersection */
332     while (current_n) {
333         current_r = rb_entry(current_n, struct v3_mem_region, tree_node);
334         if (current_r->guest_start > guest_addr) { /* | ---- */
335             successor_n = current_n;
336             current_n = current_n->rb_left;
337         } else {
338             if (current_r->guest_end > guest_addr) {
339                 return current_r; /* +--- or --+- */
340             }
341             current_n = current_n->rb_right; /* ---- | */
342         }
343     }
344
345     /* Address does not have its own region. Check if it's a valid address in the base region */
346
347     if (guest_addr >= vm->mem_map.base_region.guest_end) {
348         PrintError("%s: Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n",
349                 __FUNCTION__, (void *)guest_addr, (void *)vm->mem_map.base_region.guest_end);
350         v3_print_mem_map(vm);
351         return NULL;
352     }
353
354     return &(vm->mem_map.base_region);
355 }
356
357
358
359
360 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
361     int i = 0;
362
363     if (reg == NULL) {
364         return;
365     }
366
367     for (i = 0; i < vm->num_cores; i++) {
368         struct guest_info * info = &(vm->cores[i]);
369
370         // flush virtual page tables 
371         // 3 cases shadow, shadow passthrough, and nested
372
373         if (info->shdw_pg_mode == SHADOW_PAGING) {
374             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
375             
376             if (mem_mode == PHYSICAL_MEM) {
377                 addr_t cur_addr;
378                 
379                 for (cur_addr = reg->guest_start;
380                      cur_addr < reg->guest_end;
381                      cur_addr += PAGE_SIZE_4KB) {
382                     v3_invalidate_passthrough_addr(info, cur_addr);
383                 }
384             } else {
385                 v3_invalidate_shadow_pts(info);
386             }
387             
388         } else if (info->shdw_pg_mode == NESTED_PAGING) {
389             addr_t cur_addr;
390             
391             for (cur_addr = reg->guest_start;
392                  cur_addr < reg->guest_end;
393                  cur_addr += PAGE_SIZE_4KB) {
394                 
395                 v3_invalidate_nested_addr(info, cur_addr);
396             }
397         }
398     }
399
400     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
401
402     V3_Free(reg);
403
404     // flush virtual page tables 
405     // 3 cases shadow, shadow passthrough, and nested
406
407 }
408
409
410
411 void v3_print_mem_map(struct v3_vm_info * vm) {
412     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
413     struct v3_mem_region * reg = &(vm->mem_map.base_region);
414     int i = 0;
415
416     V3_Print("Memory Layout (all cores):\n");
417     
418
419     V3_Print("Base Region (all cores):  0x%p - 0x%p -> 0x%p\n", 
420                (void *)(reg->guest_start), 
421                (void *)(reg->guest_end - 1), 
422                (void *)(reg->host_addr));
423     
424
425     // If the memory map is empty, don't print it
426     if (node == NULL) {
427         return;
428     }
429
430     do {
431         reg = rb_entry(node, struct v3_mem_region, tree_node);
432
433         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
434                    (void *)(reg->guest_start), 
435                    (void *)(reg->guest_end - 1), 
436                    (void *)(reg->host_addr));
437
438         V3_Print("\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
439                  reg->flags.value, 
440                  reg->core_id,
441                  reg->unhandled);
442     
443         i++;
444     } while ((node = v3_rb_next(node)));
445 }
446