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.


Partially functional support for APIC/ICC clustered delivery,
[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
329
330 void v3_delete_mem_region(struct v3_vm_info * vm, struct v3_mem_region * reg) {
331     int i = 0;
332
333     if (reg == NULL) {
334         return;
335     }
336
337     for (i = 0; i < vm->num_cores; i++) {
338         struct guest_info * info = &(vm->cores[i]);
339
340         // flush virtual page tables 
341         // 3 cases shadow, shadow passthrough, and nested
342
343         if (info->shdw_pg_mode == SHADOW_PAGING) {
344             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
345             
346             if (mem_mode == PHYSICAL_MEM) {
347                 addr_t cur_addr;
348                 
349                 for (cur_addr = reg->guest_start;
350                      cur_addr < reg->guest_end;
351                      cur_addr += PAGE_SIZE_4KB) {
352                     v3_invalidate_passthrough_addr(info, cur_addr);
353                 }
354             } else {
355                 v3_invalidate_shadow_pts(info);
356             }
357             
358         } else if (info->shdw_pg_mode == NESTED_PAGING) {
359             addr_t cur_addr;
360             
361             for (cur_addr = reg->guest_start;
362                  cur_addr < reg->guest_end;
363                  cur_addr += PAGE_SIZE_4KB) {
364                 
365                 v3_invalidate_nested_addr(info, cur_addr);
366             }
367         }
368     }
369
370     v3_rb_erase(&(reg->tree_node), &(vm->mem_map.mem_regions));
371
372     V3_Free(reg);
373
374     // flush virtual page tables 
375     // 3 cases shadow, shadow passthrough, and nested
376
377 }
378
379
380
381 void v3_print_mem_map(struct v3_vm_info * vm) {
382     struct rb_node * node = v3_rb_first(&(vm->mem_map.mem_regions));
383     struct v3_mem_region * reg = &(vm->mem_map.base_region);
384     int i = 0;
385
386     V3_Print("Memory Layout (all cores):\n");
387     
388
389     V3_Print("Base Region (all cores):  0x%p - 0x%p -> 0x%p\n", 
390                (void *)(reg->guest_start), 
391                (void *)(reg->guest_end - 1), 
392                (void *)(reg->host_addr));
393     
394
395     // If the memory map is empty, don't print it
396     if (node == NULL) {
397         return;
398     }
399
400     do {
401         reg = rb_entry(node, struct v3_mem_region, tree_node);
402
403         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
404                    (void *)(reg->guest_start), 
405                    (void *)(reg->guest_end - 1), 
406                    (void *)(reg->host_addr));
407
408         V3_Print("\t(flags=0x%x) (core=0x%x) (unhandled = 0x%p)\n", 
409                  reg->flags.value, 
410                  reg->core_id,
411                  reg->unhandled);
412     
413         i++;
414     } while ((node = v3_rb_next(node)));
415 }
416