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.


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