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.


fixed null memory list bug in print_shadow_map
[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
25 #include <palacios/vmm_shadow_paging.h>
26 #include <palacios/vmm_direct_paging.h>
27
28
29 #define MEM_OFFSET_HCALL 0x1000
30
31
32 static inline
33 struct v3_shadow_region * insert_shadow_region(struct guest_info * info, 
34                                                struct v3_shadow_region * region);
35
36
37 static int mem_offset_hypercall(struct guest_info * info, uint_t hcall_id, void * private_data) {
38     info->vm_regs.rbx = info->mem_map.base_region.host_addr;
39
40     return 0;
41 }
42
43
44 void v3_init_shadow_map(struct guest_info * info) {
45     v3_shdw_map_t * map = &(info->mem_map);
46     addr_t mem_pages = info->mem_size >> 12;
47
48     map->shdw_regions.rb_node = NULL;
49     map->hook_hva = (addr_t)V3_VAddr(V3_AllocPages(1));
50
51     // There is an underlying region that contains all of the guest memory
52     // PrintDebug("Mapping %d pages of memory (%u bytes)\n", (int)mem_pages, (uint_t)info->mem_size);
53
54     map->base_region.guest_start = 0;
55     map->base_region.guest_end = mem_pages * PAGE_SIZE_4KB;
56     map->base_region.host_type = SHDW_REGION_ALLOCATED;
57     map->base_region.host_addr = (addr_t)V3_AllocPages(mem_pages);
58
59     //memset(V3_VAddr((void *)map->base_region.host_addr), 0xffffffff, map->base_region.guest_end);
60
61     v3_register_hypercall(info, MEM_OFFSET_HCALL, mem_offset_hypercall, NULL);
62 }
63
64 void v3_delete_shadow_map(struct guest_info * info) {
65     struct rb_node * node = v3_rb_first(&(info->mem_map.shdw_regions));
66     struct v3_shadow_region * reg;
67     struct rb_node * tmp_node = NULL;
68   
69     while (node) {
70         reg = rb_entry(node, struct v3_shadow_region, tree_node);
71         tmp_node = node;
72         node = v3_rb_next(node);
73
74         v3_delete_shadow_region(info, reg);
75     }
76
77     V3_FreePage((void *)(info->mem_map.base_region.host_addr));
78     V3_FreePage(V3_PAddr((void *)(info->mem_map.hook_hva)));
79 }
80
81
82
83
84 int v3_add_shadow_mem( struct guest_info *  info,
85                        addr_t               guest_addr_start,
86                        addr_t               guest_addr_end,
87                        addr_t               host_addr)
88 {
89     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
90
91     entry->guest_start = guest_addr_start;
92     entry->guest_end = guest_addr_end;
93     entry->host_type = SHDW_REGION_ALLOCATED;
94     entry->host_addr = host_addr;
95     entry->write_hook = NULL;
96     entry->read_hook = NULL;
97     entry->priv_data = NULL;
98
99     if (insert_shadow_region(info, entry)) {
100         V3_Free(entry);
101         return -1;
102     }
103
104     return 0;
105 }
106
107
108
109 int v3_hook_write_mem(struct guest_info * info, addr_t guest_addr_start, addr_t guest_addr_end, 
110                       addr_t host_addr,
111                       int (*write)(addr_t guest_addr, void * src, uint_t length, void * priv_data),
112                       void * priv_data) {
113
114     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
115
116
117     entry->guest_start = guest_addr_start;
118     entry->guest_end = guest_addr_end;
119     entry->host_type = SHDW_REGION_WRITE_HOOK;
120     entry->host_addr = host_addr;
121     entry->write_hook = write;
122     entry->read_hook = NULL;
123     entry->priv_data = priv_data;
124
125     if (insert_shadow_region(info, entry)) {
126         V3_Free(entry);
127         return -1;
128     }
129
130     return 0;  
131 }
132
133 int v3_hook_full_mem(struct guest_info * info, addr_t guest_addr_start, addr_t guest_addr_end,
134                      int (*read)(addr_t guest_addr, void * dst, uint_t length, void * priv_data),
135                      int (*write)(addr_t guest_addr, void * src, uint_t length, void * priv_data),
136                      void * priv_data) {
137   
138     struct v3_shadow_region * entry = (struct v3_shadow_region *)V3_Malloc(sizeof(struct v3_shadow_region));
139
140     entry->guest_start = guest_addr_start;
141     entry->guest_end = guest_addr_end;
142     entry->host_type = SHDW_REGION_FULL_HOOK;
143     entry->host_addr = (addr_t)NULL;
144     entry->write_hook = write;
145     entry->read_hook = read;
146     entry->priv_data = priv_data;
147   
148     if (insert_shadow_region(info, entry)) {
149         V3_Free(entry);
150         return -1;
151     }
152
153     return 0;
154 }
155
156
157 // This will unhook the memory hook registered at start address
158 // We do not support unhooking subregions
159 int v3_unhook_mem(struct guest_info * info, addr_t guest_addr_start) {
160     struct v3_shadow_region * reg = v3_get_shadow_region(info, guest_addr_start);
161
162     if ((reg->host_type != SHDW_REGION_FULL_HOOK) || 
163         (reg->host_type != SHDW_REGION_WRITE_HOOK)) {
164         PrintError("Trying to unhook a non hooked memory region (addr=%p)\n", (void *)guest_addr_start);
165         return -1;
166     }
167
168     v3_delete_shadow_region(info, reg);
169
170     return 0;
171 }
172
173
174
175 static inline 
176 struct v3_shadow_region * __insert_shadow_region(struct guest_info * info, 
177                                                  struct v3_shadow_region * region) {
178     struct rb_node ** p = &(info->mem_map.shdw_regions.rb_node);
179     struct rb_node * parent = NULL;
180     struct v3_shadow_region * tmp_region;
181
182     while (*p) {
183         parent = *p;
184         tmp_region = rb_entry(parent, struct v3_shadow_region, tree_node);
185
186         if (region->guest_end <= tmp_region->guest_start) {
187             p = &(*p)->rb_left;
188         } else if (region->guest_start >= tmp_region->guest_end) {
189             p = &(*p)->rb_right;
190         } else {
191             return tmp_region;
192         }
193     }
194
195     rb_link_node(&(region->tree_node), parent, p);
196   
197     return NULL;
198 }
199
200
201 static inline
202 struct v3_shadow_region * insert_shadow_region(struct guest_info * info, 
203                                                struct v3_shadow_region * region) {
204     struct v3_shadow_region * ret;
205
206     if ((ret = __insert_shadow_region(info, region))) {
207         return ret;
208     }
209
210     v3_rb_insert_color(&(region->tree_node), &(info->mem_map.shdw_regions));
211
212
213
214     // flush virtual page tables 
215     // 3 cases shadow, shadow passthrough, and nested
216     if (info->shdw_pg_mode == SHADOW_PAGING) {
217         v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
218
219         if (mem_mode == PHYSICAL_MEM) {
220             addr_t cur_addr;
221
222             for (cur_addr = region->guest_start;
223                  cur_addr < region->guest_end;
224                  cur_addr += PAGE_SIZE_4KB) {
225                 v3_invalidate_passthrough_addr(info, cur_addr);
226             }
227         } else {
228             v3_invalidate_shadow_pts(info);
229         }
230
231     } else if (info->shdw_pg_mode == NESTED_PAGING) {
232         addr_t cur_addr;
233
234         for (cur_addr = region->guest_start;
235              cur_addr < region->guest_end;
236              cur_addr += PAGE_SIZE_4KB) {
237             
238             v3_invalidate_nested_addr(info, cur_addr);
239         }
240     }
241
242     return NULL;
243 }
244                                                  
245
246
247
248 int handle_special_page_fault(struct guest_info * info, 
249                               addr_t fault_gva, addr_t fault_gpa, 
250                               pf_error_t access_info) 
251 {
252     struct v3_shadow_region * reg = v3_get_shadow_region(info, fault_gpa);
253
254     PrintDebug("Handling Special Page Fault\n");
255
256     switch (reg->host_type) {
257         case SHDW_REGION_WRITE_HOOK:
258             return v3_handle_mem_wr_hook(info, fault_gva, fault_gpa, reg, access_info);
259         case SHDW_REGION_FULL_HOOK:
260             return v3_handle_mem_full_hook(info, fault_gva, fault_gpa, reg, access_info);
261         default:
262             return -1;
263     }
264
265     return 0;
266
267 }
268
269 int v3_handle_mem_wr_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
270                           struct v3_shadow_region * reg, pf_error_t access_info) {
271
272     addr_t dst_addr = (addr_t)V3_VAddr((void *)v3_get_shadow_addr(reg, guest_pa));
273
274     if (v3_emulate_write_op(info, guest_va, guest_pa, dst_addr, 
275                             reg->write_hook, reg->priv_data) == -1) {
276         PrintError("Write hook emulation failed\n");
277         return -1;
278     }
279
280     return 0;
281 }
282
283 int v3_handle_mem_full_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
284                             struct v3_shadow_region * reg, pf_error_t access_info) {
285   
286     addr_t op_addr = info->mem_map.hook_hva;
287
288     if (access_info.write == 1) {
289         if (v3_emulate_write_op(info, guest_va, guest_pa, op_addr, 
290                                 reg->write_hook, reg->priv_data) == -1) {
291             PrintError("Write Full Hook emulation failed\n");
292             return -1;
293         }
294     } else {
295         if (v3_emulate_read_op(info, guest_va, guest_pa, op_addr, 
296                                reg->read_hook, reg->write_hook, 
297                                reg->priv_data) == -1) {
298             PrintError("Read Full Hook emulation failed\n");
299             return -1;
300         }
301     }
302
303     return 0;
304 }
305
306
307
308 struct v3_shadow_region * v3_get_shadow_region(struct guest_info * info, addr_t guest_addr) {
309     struct rb_node * n = info->mem_map.shdw_regions.rb_node;
310     struct v3_shadow_region * reg = NULL;
311
312     while (n) {
313         reg = rb_entry(n, struct v3_shadow_region, tree_node);
314
315         if (guest_addr < reg->guest_start) {
316             n = n->rb_left;
317         } else if (guest_addr >= reg->guest_end) {
318             n = n->rb_right;
319         } else {
320             return reg;
321         }
322     }
323
324
325     // There is not registered region, so we check if its a valid address in the base region
326
327     if (guest_addr > info->mem_map.base_region.guest_end) {
328         PrintError("Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n", 
329                    (void *)guest_addr, (void *)info->mem_map.base_region.guest_end);
330         return NULL;
331     }
332     
333     return &(info->mem_map.base_region);
334 }
335
336
337 void v3_delete_shadow_region(struct guest_info * info, struct v3_shadow_region * reg) {
338     if (reg == NULL) {
339         return;
340     }
341
342     // flush virtual page tables 
343     // 3 cases shadow, shadow passthrough, and nested
344     if (info->shdw_pg_mode == SHADOW_PAGING) {
345         v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
346             
347         if (mem_mode == PHYSICAL_MEM) {
348             addr_t cur_addr;
349                 
350             for (cur_addr = reg->guest_start;
351                  cur_addr < reg->guest_end;
352                  cur_addr += PAGE_SIZE_4KB) {
353                 v3_invalidate_passthrough_addr(info, cur_addr);
354             }
355         } else {
356             v3_invalidate_shadow_pts(info);
357         }
358
359     } else if (info->shdw_pg_mode == NESTED_PAGING) {
360         addr_t cur_addr;
361
362         for (cur_addr = reg->guest_start;
363              cur_addr < reg->guest_end;
364              cur_addr += PAGE_SIZE_4KB) {
365             
366             v3_invalidate_nested_addr(info, cur_addr);
367         }
368     }
369
370
371     v3_rb_erase(&(reg->tree_node), &(info->mem_map.shdw_regions));
372
373     V3_Free(reg);
374
375     // flush virtual page tables 
376     // 3 cases shadow, shadow passthrough, and nested
377
378 }
379
380
381
382
383 addr_t v3_get_shadow_addr(struct v3_shadow_region * reg, addr_t guest_addr) {
384     if ( (reg) && 
385          (reg->host_type != SHDW_REGION_FULL_HOOK)) {
386         return (guest_addr - reg->guest_start) + reg->host_addr;
387     } else {
388         PrintError("MEM Region Invalid\n");
389         return 0;
390     }
391
392 }
393
394
395
396 void print_shadow_map(struct guest_info * info) {
397     struct rb_node * node = v3_rb_first(&(info->mem_map.shdw_regions));
398     struct v3_shadow_region * reg = &(info->mem_map.base_region);
399     int i = 0;
400
401     PrintDebug("Memory Layout:\n");
402     
403
404     PrintDebug("Base Region:  0x%p - 0x%p -> 0x%p\n", 
405                (void *)(reg->guest_start), 
406                (void *)(reg->guest_end - 1), 
407                (void *)(reg->host_addr));
408     
409
410     // If the memory map is empty, don't print it
411     if (node == NULL) {
412         return;
413     }
414
415     do {
416         reg = rb_entry(node, struct v3_shadow_region, tree_node);
417
418         PrintDebug("%d:  0x%p - 0x%p -> 0x%p\n", i, 
419                    (void *)(reg->guest_start), 
420                    (void *)(reg->guest_end - 1), 
421                    (void *)(reg->host_addr));
422
423         PrintDebug("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n", 
424                    v3_shdw_region_type_to_str(reg->host_type),
425                    (void *)(reg->write_hook), 
426                    (void *)(reg->read_hook));
427     
428         i++;
429     } while ((node = v3_rb_next(node)));
430 }
431
432
433 static const uchar_t  SHDW_REGION_WRITE_HOOK_STR[] = "SHDW_REGION_WRITE_HOOK";
434 static const uchar_t  SHDW_REGION_FULL_HOOK_STR[] = "SHDW_REGION_FULL_HOOK";
435 static const uchar_t  SHDW_REGION_ALLOCATED_STR[] = "SHDW_REGION_ALLOCATED";
436
437 const uchar_t * v3_shdw_region_type_to_str(v3_shdw_region_type_t type) {
438     switch (type) {
439         case SHDW_REGION_WRITE_HOOK:
440             return SHDW_REGION_WRITE_HOOK_STR;
441         case SHDW_REGION_FULL_HOOK:
442             return SHDW_REGION_FULL_HOOK_STR;
443         case SHDW_REGION_ALLOCATED:
444             return SHDW_REGION_ALLOCATED_STR;
445         default:
446             return (uchar_t *)"SHDW_REGION_INVALID";
447     }
448 }
449