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.


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