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.


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