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