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.


b012bc3d244c81f79859cbb068308a0fb94e64ce
[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         v3_print_mem_map(info);
331
332         return NULL;
333     }
334     
335     return &(info->mem_map.base_region);
336 }
337
338
339 void v3_delete_shadow_region(struct guest_info * info, struct v3_shadow_region * reg) {
340     if (reg == NULL) {
341         return;
342     }
343
344     // flush virtual page tables 
345     // 3 cases shadow, shadow passthrough, and nested
346     if (info->shdw_pg_mode == SHADOW_PAGING) {
347         v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(info);
348             
349         if (mem_mode == PHYSICAL_MEM) {
350             addr_t cur_addr;
351                 
352             for (cur_addr = reg->guest_start;
353                  cur_addr < reg->guest_end;
354                  cur_addr += PAGE_SIZE_4KB) {
355                 v3_invalidate_passthrough_addr(info, cur_addr);
356             }
357         } else {
358             v3_invalidate_shadow_pts(info);
359         }
360
361     } else if (info->shdw_pg_mode == NESTED_PAGING) {
362         addr_t cur_addr;
363
364         for (cur_addr = reg->guest_start;
365              cur_addr < reg->guest_end;
366              cur_addr += PAGE_SIZE_4KB) {
367             
368             v3_invalidate_nested_addr(info, cur_addr);
369         }
370     }
371
372
373     v3_rb_erase(&(reg->tree_node), &(info->mem_map.shdw_regions));
374
375     V3_Free(reg);
376
377     // flush virtual page tables 
378     // 3 cases shadow, shadow passthrough, and nested
379
380 }
381
382
383
384
385 addr_t v3_get_shadow_addr(struct v3_shadow_region * reg, addr_t guest_addr) {
386     if ( (reg) && 
387          (reg->host_type != SHDW_REGION_FULL_HOOK)) {
388         return (guest_addr - reg->guest_start) + reg->host_addr;
389     } else {
390         //  PrintError("MEM Region Invalid\n");
391         return 0;
392     }
393
394 }
395
396
397
398 void v3_print_mem_map(struct guest_info * info) {
399     struct rb_node * node = v3_rb_first(&(info->mem_map.shdw_regions));
400     struct v3_shadow_region * reg = &(info->mem_map.base_region);
401     int i = 0;
402
403     V3_Print("Memory Layout:\n");
404     
405
406     V3_Print("Base Region:  0x%p - 0x%p -> 0x%p\n", 
407                (void *)(reg->guest_start), 
408                (void *)(reg->guest_end - 1), 
409                (void *)(reg->host_addr));
410     
411
412     // If the memory map is empty, don't print it
413     if (node == NULL) {
414         return;
415     }
416
417     do {
418         reg = rb_entry(node, struct v3_shadow_region, tree_node);
419
420         V3_Print("%d:  0x%p - 0x%p -> 0x%p\n", i, 
421                    (void *)(reg->guest_start), 
422                    (void *)(reg->guest_end - 1), 
423                    (void *)(reg->host_addr));
424
425         V3_Print("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n", 
426                    v3_shdw_region_type_to_str(reg->host_type),
427                    (void *)(reg->write_hook), 
428                    (void *)(reg->read_hook));
429     
430         i++;
431     } while ((node = v3_rb_next(node)));
432 }
433
434
435 static const uchar_t  SHDW_REGION_WRITE_HOOK_STR[] = "SHDW_REGION_WRITE_HOOK";
436 static const uchar_t  SHDW_REGION_FULL_HOOK_STR[] = "SHDW_REGION_FULL_HOOK";
437 static const uchar_t  SHDW_REGION_ALLOCATED_STR[] = "SHDW_REGION_ALLOCATED";
438
439 const uchar_t * v3_shdw_region_type_to_str(v3_shdw_region_type_t type) {
440     switch (type) {
441         case SHDW_REGION_WRITE_HOOK:
442             return SHDW_REGION_WRITE_HOOK_STR;
443         case SHDW_REGION_FULL_HOOK:
444             return SHDW_REGION_FULL_HOOK_STR;
445         case SHDW_REGION_ALLOCATED:
446             return SHDW_REGION_ALLOCATED_STR;
447         default:
448             return (uchar_t *)"SHDW_REGION_INVALID";
449     }
450 }
451