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.


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