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.


3a9b16d4a182c9f60778ab1308e7d372472a4ab3
[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     v3_delete_shadow_region(info, reg);
165
166     return 0;
167 }
168
169
170
171 static inline 
172 struct v3_shadow_region * __insert_shadow_region(struct guest_info * info, 
173                                                  struct v3_shadow_region * region) {
174     struct rb_node ** p = &(info->mem_map.shdw_regions.rb_node);
175     struct rb_node * parent = NULL;
176     struct v3_shadow_region * tmp_region;
177
178     while (*p) {
179         parent = *p;
180         tmp_region = rb_entry(parent, struct v3_shadow_region, tree_node);
181
182         if (region->guest_end <= tmp_region->guest_start) {
183             p = &(*p)->rb_left;
184         } else if (region->guest_start >= tmp_region->guest_end) {
185             p = &(*p)->rb_right;
186         } else {
187             return tmp_region;
188         }
189     }
190
191     rb_link_node(&(region->tree_node), parent, p);
192   
193     return NULL;
194 }
195
196
197 static inline
198 struct v3_shadow_region * insert_shadow_region(struct guest_info * info, 
199                                                struct v3_shadow_region * region) {
200     struct v3_shadow_region * ret;
201
202     if ((ret = __insert_shadow_region(info, region))) {
203         return ret;
204     }
205   
206     v3_rb_insert_color(&(region->tree_node), &(info->mem_map.shdw_regions));
207
208
209
210     // flush virtual page tables 
211     // 3 cases shadow, shadow passthrough, and nested
212     if (info->shdw_pg_mode == SHADOW_PAGING) {
213         v3_vm_mem_mode_t mem_mode = v3_get_mem_mode(info);
214
215         if (mem_mode == PHYSICAL_MEM) {
216             addr_t cur_addr;
217
218             for (cur_addr = region->guest_start;
219                  cur_addr < region->guest_end;
220                  cur_addr += PAGE_SIZE_4KB) {
221                 v3_invalidate_passthrough_addr(info, cur_addr);
222             }
223         } else {
224             v3_invalidate_shadow_pts(info);
225         }
226
227     } else if (info->shdw_pg_mode == NESTED_PAGING) {
228         addr_t cur_addr;
229
230         for (cur_addr = region->guest_start;
231              cur_addr < region->guest_end;
232              cur_addr += PAGE_SIZE_4KB) {
233             
234             v3_invalidate_nested_addr(info, cur_addr);
235         }
236     }
237
238     return NULL;
239 }
240                                                  
241
242
243
244 int handle_special_page_fault(struct guest_info * info, 
245                               addr_t fault_gva, addr_t fault_gpa, 
246                               pf_error_t access_info) 
247 {
248     struct v3_shadow_region * reg = v3_get_shadow_region(info, fault_gpa);
249
250     PrintDebug("Handling Special Page Fault\n");
251
252     switch (reg->host_type) {
253         case SHDW_REGION_WRITE_HOOK:
254             return v3_handle_mem_wr_hook(info, fault_gva, fault_gpa, reg, access_info);
255         case SHDW_REGION_FULL_HOOK:
256             return v3_handle_mem_full_hook(info, fault_gva, fault_gpa, reg, access_info);
257         default:
258             return -1;
259     }
260
261     return 0;
262
263 }
264
265 int v3_handle_mem_wr_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
266                           struct v3_shadow_region * reg, pf_error_t access_info) {
267
268     addr_t dst_addr = (addr_t)V3_VAddr((void *)v3_get_shadow_addr(reg, guest_pa));
269
270     if (v3_emulate_write_op(info, guest_va, guest_pa, dst_addr, 
271                             reg->write_hook, reg->priv_data) == -1) {
272         PrintError("Write hook emulation failed\n");
273         return -1;
274     }
275
276     return 0;
277 }
278
279 int v3_handle_mem_full_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, 
280                             struct v3_shadow_region * reg, pf_error_t access_info) {
281   
282     addr_t op_addr = info->mem_map.hook_hva;
283
284     if (access_info.write == 1) {
285         if (v3_emulate_write_op(info, guest_va, guest_pa, op_addr, 
286                                 reg->write_hook, reg->priv_data) == -1) {
287             PrintError("Write Full Hook emulation failed\n");
288             return -1;
289         }
290     } else {
291         if (v3_emulate_read_op(info, guest_va, guest_pa, op_addr, 
292                                reg->read_hook, reg->write_hook, 
293                                reg->priv_data) == -1) {
294             PrintError("Read Full Hook emulation failed\n");
295             return -1;
296         }
297     }
298
299     return 0;
300 }
301
302
303
304 struct v3_shadow_region * v3_get_shadow_region(struct guest_info * info, addr_t guest_addr) {
305     struct rb_node * n = info->mem_map.shdw_regions.rb_node;
306     struct v3_shadow_region * reg = NULL;
307
308     while (n) {
309         reg = rb_entry(n, struct v3_shadow_region, tree_node);
310
311         if (guest_addr < reg->guest_start) {
312             n = n->rb_left;
313         } else if (guest_addr >= reg->guest_end) {
314             n = n->rb_right;
315         } else {
316             return reg;
317         }
318     }
319
320
321     // There is not registered region, so we check if its a valid address in the base region
322
323     if (guest_addr > info->mem_map.base_region.guest_end) {
324         PrintError("Guest Address Exceeds Base Memory Size (ga=%p), (limit=%p)\n", 
325                    (void *)guest_addr, (void *)info->mem_map.base_region.guest_end);
326         return NULL;
327     }
328     
329     return &(info->mem_map.base_region);
330 }
331
332
333 void v3_delete_shadow_region(struct guest_info * info, struct v3_shadow_region * reg) {
334     if (reg == NULL) {
335         return;
336     }
337
338     // flush virtual page tables 
339     // 3 cases shadow, shadow passthrough, and nested
340     if (info->shdw_pg_mode == SHADOW_PAGING) {
341         v3_vm_mem_mode_t mem_mode = v3_get_mem_mode(info);
342             
343         if (mem_mode == PHYSICAL_MEM) {
344             addr_t cur_addr;
345                 
346             for (cur_addr = reg->guest_start;
347                  cur_addr < reg->guest_end;
348                  cur_addr += PAGE_SIZE_4KB) {
349                 v3_invalidate_passthrough_addr(info, cur_addr);
350             }
351         } else {
352             v3_invalidate_shadow_pts(info);
353         }
354
355     } else if (info->shdw_pg_mode == NESTED_PAGING) {
356         addr_t cur_addr;
357
358         for (cur_addr = reg->guest_start;
359              cur_addr < reg->guest_end;
360              cur_addr += PAGE_SIZE_4KB) {
361             
362             v3_invalidate_nested_addr(info, cur_addr);
363         }
364     }
365
366
367     v3_rb_erase(&(reg->tree_node), &(info->mem_map.shdw_regions));
368
369     V3_Free(reg);
370
371     // flush virtual page tables 
372     // 3 cases shadow, shadow passthrough, and nested
373
374 }
375
376
377
378
379 addr_t v3_get_shadow_addr(struct v3_shadow_region * reg, addr_t guest_addr) {
380     if ( (reg) && 
381          (reg->host_type != SHDW_REGION_FULL_HOOK)) {
382         return (guest_addr - reg->guest_start) + reg->host_addr;
383     } else {
384         PrintDebug("MEM Region Invalid\n");
385         return 0;
386     }
387
388 }
389
390
391
392 void print_shadow_map(struct guest_info * info) {
393     struct rb_node * node = v3_rb_first(&(info->mem_map.shdw_regions));
394     struct v3_shadow_region * reg = &(info->mem_map.base_region);
395     int i = 0;
396
397     PrintDebug("Memory Layout:\n");
398     
399
400     PrintDebug("Base Region:  0x%p - 0x%p -> 0x%p\n", 
401                (void *)(reg->guest_start), 
402                (void *)(reg->guest_end - 1), 
403                (void *)(reg->host_addr));
404     
405     do {
406         reg = rb_entry(node, struct v3_shadow_region, tree_node);
407
408         PrintDebug("%d:  0x%p - 0x%p -> 0x%p\n", i, 
409                    (void *)(reg->guest_start), 
410                    (void *)(reg->guest_end - 1), 
411                    (void *)(reg->host_addr));
412
413         PrintDebug("\t(%s) (WriteHook = 0x%p) (ReadHook = 0x%p)\n", 
414                    v3_shdw_region_type_to_str(reg->host_type),
415                    (void *)(reg->write_hook), 
416                    (void *)(reg->read_hook));
417     
418         i++;
419     } while ((node = v3_rb_next(node)));
420 }
421
422
423 static const uchar_t  SHDW_REGION_WRITE_HOOK_STR[] = "SHDW_REGION_WRITE_HOOK";
424 static const uchar_t  SHDW_REGION_FULL_HOOK_STR[] = "SHDW_REGION_FULL_HOOK";
425 static const uchar_t  SHDW_REGION_ALLOCATED_STR[] = "SHDW_REGION_ALLOCATED";
426
427 const uchar_t * v3_shdw_region_type_to_str(v3_shdw_region_type_t type) {
428     switch (type) {
429         case SHDW_REGION_WRITE_HOOK:
430             return SHDW_REGION_WRITE_HOOK_STR;
431         case SHDW_REGION_FULL_HOOK:
432             return SHDW_REGION_FULL_HOOK_STR;
433         case SHDW_REGION_ALLOCATED:
434             return SHDW_REGION_ALLOCATED_STR;
435         default:
436             return (uchar_t *)"SHDW_REGION_INVALID";
437     }
438 }
439