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