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.


Context-based output infrastructure (V3_Print, etc) and modifications to use it
[palacios.git] / palacios / src / palacios / vmm_mem_hook.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.h>
21 #include <palacios/vm_guest.h>
22 #include <palacios/vmm_mem_hook.h>
23 #include <palacios/vmm_emulator.h>
24 #include <palacios/vm_guest_mem.h>
25 #include <palacios/vmm_hashtable.h>
26 #include <palacios/vmm_decoder.h>
27
28 struct mem_hook {
29   
30     // Called when data is read from a memory page
31     int (*read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data);
32     // Called when data is written to a memory page
33     int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data);
34     // Called when memory page is accessed
35     int (*access)(struct guest_info *core, addr_t guest_va, addr_t guest_pa, struct v3_mem_region *region, pf_error_t access_info, void *priv_data);
36
37     void * priv_data;
38     struct v3_mem_region * region;
39
40
41     struct list_head hook_node;
42 };
43
44
45
46 static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook);
47
48 static uint_t mem_hash_fn(addr_t key) {
49     return v3_hash_long(key, sizeof(void *) * 8);
50 }
51
52 static int mem_eq_fn(addr_t key1, addr_t key2) {
53     return (key1 == key2);
54 }
55
56 int v3_init_mem_hooks(struct v3_vm_info * vm) {
57     void *temp;
58
59     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
60
61     temp = V3_AllocPages(vm->num_cores);
62
63     if (!temp) {
64         PrintError(vm, VCORE_NONE, "Cannot allocate space for mem hooks\n");
65         return -1;
66     }
67
68     hooks->hook_hvas_1 = V3_VAddr(temp);
69
70     temp = V3_AllocPages(vm->num_cores);
71
72     if (!temp) {
73         PrintError(vm, VCORE_NONE,"Cannot allocate space for mem hooks\n");
74         V3_FreePages(hooks->hook_hvas_1,vm->num_cores);
75         return -1;
76     }
77
78     hooks->hook_hvas_2 = V3_VAddr(temp);
79
80     INIT_LIST_HEAD(&(hooks->hook_list));
81
82     hooks->reg_table = v3_create_htable(0, mem_hash_fn, mem_eq_fn);
83
84     return 0;
85 }
86
87
88 // We'll assume the actual hooks were either already cleared,
89 // or will be cleared by the memory map
90 int v3_deinit_mem_hooks(struct v3_vm_info * vm) {
91     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
92     struct mem_hook * hook = NULL;
93     struct mem_hook * tmp = NULL;
94
95
96     // This is nasty...
97     // We delete the hook info but leave its memory region intact
98     // We rely on the memory map to clean up any orphaned regions as a result of this
99     // This needs to be fixed at some point...
100     list_for_each_entry_safe(hook, tmp, &(hooks->hook_list), hook_node) {
101         free_hook(vm, hook);
102     }
103
104
105     v3_free_htable(hooks->reg_table, 0, 0);
106
107     V3_FreePages(V3_PAddr(hooks->hook_hvas_1), vm->num_cores);
108     V3_FreePages(V3_PAddr(hooks->hook_hvas_2), vm->num_cores);
109
110     return 0;
111 }
112
113
114
115 static inline int get_op_length(struct x86_instr * instr, struct x86_operand * operand, addr_t tgt_addr) {
116
117     if (instr->is_str_op) {
118         if ((instr->str_op_length * operand->size)  < (0x1000 - PAGE_OFFSET_4KB(tgt_addr))) {
119             return (instr->str_op_length * operand->size);
120         } else {
121             return (0x1000 - PAGE_OFFSET_4KB(tgt_addr));
122         }
123     } else {
124         return instr->src_operand.size;
125     }
126
127 }
128
129
130
131 static int handle_mem_hook(struct guest_info * core, addr_t guest_va, addr_t guest_pa, 
132                            struct v3_mem_region * reg, pf_error_t access_info) {
133     struct v3_mem_hooks * hooks = &(core->vm_info->mem_hooks);
134     struct x86_instr instr;
135     void * instr_ptr = NULL;
136     int bytes_emulated = 0;
137     int mem_op_size = 0;
138     int ret = 0;
139
140     struct mem_hook * src_hook = NULL;
141     addr_t src_mem_op_hva = 0;
142     addr_t src_mem_op_gpa = 0;
143     int src_req_size = -1;
144
145     struct mem_hook * dst_hook = NULL;
146     addr_t dst_mem_op_hva = 0;
147     addr_t dst_mem_op_gpa = 0;
148     int dst_req_size = -1;
149
150
151     struct mem_hook *access_hook = NULL;
152
153     // Let access hooks take priority
154     access_hook = (struct mem_hook *) reg->priv_data; 
155     if (access_hook && access_hook->access) { 
156         return access_hook->access(core, guest_va, guest_pa, reg, access_info, access_hook->priv_data);
157     } 
158
159
160     /* Find and decode hooked instruction */
161     if (core->mem_mode == PHYSICAL_MEM) { 
162         ret = v3_gpa_to_hva(core, get_addr_linear(core, core->rip, &(core->segments.cs)), (addr_t *)&instr_ptr);
163     } else { 
164         ret = v3_gva_to_hva(core, get_addr_linear(core, core->rip, &(core->segments.cs)), (addr_t *)&instr_ptr);
165     }
166
167     if (ret == -1) {
168       PrintError(core->vm_info, core, "Could not translate Instruction Address (%p)\n", (void *)(addr_t)core->rip);
169         return -1;
170     }
171
172     if (v3_decode(core, (addr_t)instr_ptr, &instr) == -1) {
173         PrintError(core->vm_info, core, "Decoding Error\n");
174         return -1;
175     }
176
177
178
179     // Test source operand, if it's memory we need to do some translations, and handle a possible hook
180     if (instr.src_operand.type == MEM_OPERAND) {
181         struct v3_mem_region * src_reg = NULL;
182
183         if (core->mem_mode == PHYSICAL_MEM) { 
184             src_mem_op_gpa = instr.src_operand.operand;
185         } else { 
186             if (v3_gva_to_gpa(core, instr.src_operand.operand, &src_mem_op_gpa) == -1) {
187                 pf_error_t error = access_info;
188
189                 error.present = 0;
190                 v3_inject_guest_pf(core, instr.src_operand.operand, error);
191
192                 return 0;
193             }
194         }
195
196         if ((src_mem_op_gpa >= reg->guest_start) && 
197             (src_mem_op_gpa < reg->guest_end)) {   
198             // Src address corresponds to faulted region
199             src_reg = reg;
200         } else {
201             // Note that this should only trigger for string operations
202             src_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, src_mem_op_gpa);
203         }
204
205         if (src_reg == NULL) {
206             PrintError(core->vm_info, core, "Error finding Source region (addr=%p)\n", (void *)src_mem_op_gpa);
207             return -1;
208         }
209
210         src_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)src_reg);
211
212         // We don't check whether the region is a hook here because it doesn't yet matter.
213         // These hva calculations will be true regardless
214         if (src_reg->flags.alloced == 0) {
215             src_mem_op_hva = (addr_t)(hooks->hook_hvas_1 + (PAGE_SIZE * core->vcpu_id));
216         } else {
217             // We already have the region so we can do the conversion ourselves
218             src_mem_op_hva = (addr_t)V3_VAddr((void *)((src_mem_op_gpa - src_reg->guest_start) + src_reg->host_addr));
219         }
220
221         src_req_size = get_op_length(&instr, &(instr.src_operand), src_mem_op_hva);
222     }
223
224     // Now do the same translation / hook handling for the second operand
225     if (instr.dst_operand.type == MEM_OPERAND) {
226         struct v3_mem_region * dst_reg = NULL;
227
228
229         if (core->mem_mode == PHYSICAL_MEM) { 
230             dst_mem_op_gpa = instr.dst_operand.operand;
231         } else { 
232             if (v3_gva_to_gpa(core, instr.dst_operand.operand, &dst_mem_op_gpa) == -1) {
233                 pf_error_t error = access_info;
234
235                 error.present = 0;
236                 v3_inject_guest_pf(core, instr.dst_operand.operand, error);
237
238                 return 0;
239             }
240         }
241
242         if ((dst_mem_op_gpa >= reg->guest_start) && 
243             (dst_mem_op_gpa < reg->guest_end)) {
244             // Dst address corresponds to faulted region
245             dst_reg = reg;
246         } else {
247             // Note that this should only trigger for string operations
248             dst_reg = v3_get_mem_region(core->vm_info, core->vcpu_id, dst_mem_op_gpa);
249         }
250
251         if (dst_reg == NULL) {
252             PrintError(core->vm_info, core, "Error finding Source region (addr=%p)\n", (void *)dst_mem_op_gpa);
253             return -1;
254         }
255         
256         dst_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)dst_reg);
257
258         // We don't check whether the region is a hook here because it doesn't yet matter.
259         // These hva calculations will be true regardless
260         if (dst_reg->flags.alloced == 0) {
261             dst_mem_op_hva = (addr_t)(hooks->hook_hvas_2 + (PAGE_SIZE * core->vcpu_id));
262         } else {
263             // We already have the region so we can do the conversion ourselves
264             dst_mem_op_hva = (addr_t)V3_VAddr((void *)((dst_mem_op_gpa - dst_reg->guest_start) + dst_reg->host_addr));
265         }
266
267         dst_req_size = get_op_length(&instr, &(instr.dst_operand), dst_mem_op_hva);
268     }
269
270
271     mem_op_size = ((uint_t)src_req_size < (uint_t)dst_req_size) ? src_req_size : dst_req_size;
272
273     if (mem_op_size == -1) {
274         PrintError(core->vm_info, core, "Error: Did not detect any memory operands...\n");
275         return -1;
276     }
277
278
279     /* Now handle the hooks if necessary */    
280     if ( (src_hook != NULL)  && (src_hook->read != NULL) &&
281          (instr.src_operand.read == 1) ) {
282         
283         // Read in data from hook
284         
285         if (src_hook->read(core, src_mem_op_gpa, (void *)src_mem_op_hva, mem_op_size, src_hook->priv_data) == -1) {
286             PrintError(core->vm_info, core, "Read hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
287             return -1;
288         }
289     }
290
291     if ( (dst_hook != NULL)  && (dst_hook->read != NULL) &&
292          (instr.dst_operand.read == 1) ) {
293         
294         // Read in data from hook
295         
296         if (dst_hook->read(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, mem_op_size, dst_hook->priv_data) == -1) {
297             PrintError(core->vm_info, core, "Read hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
298             return -1;
299         }
300     }
301     
302     bytes_emulated = v3_emulate(core, &instr, mem_op_size, src_mem_op_hva, dst_mem_op_hva);
303
304     if (bytes_emulated == -1) {
305       PrintError(core->vm_info, core, "Error emulating instruction\n");
306         return -1;
307     }
308
309
310     if ( (src_hook != NULL) && (src_hook->write != NULL) &&
311          (instr.src_operand.write == 1) ) {
312
313         if (src_hook->write(core, src_mem_op_gpa, (void *)src_mem_op_hva, bytes_emulated, src_hook->priv_data) == -1) {
314             PrintError(core->vm_info, core, "Write hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
315             return -1;
316         }
317
318     }
319
320
321     if ( (dst_hook != NULL) && (dst_hook->write != NULL) &&
322          (instr.dst_operand.write == 1) ) {
323
324         if (dst_hook->write(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, bytes_emulated, dst_hook->priv_data) == -1) {
325             PrintError(core->vm_info, core, "Write hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
326             return -1;
327         }
328     }
329
330
331     if (instr.is_str_op == 0) {
332         core->rip += instr.instr_length;
333     }
334
335
336     return 0;
337 }
338
339
340
341 int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id,
342                       addr_t guest_addr_start, addr_t guest_addr_end, addr_t host_addr,
343                       int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
344                       void * priv_data) {
345     struct v3_mem_region * entry = NULL;
346     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
347     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
348
349     if (!hook) {
350         PrintError(vm, VCORE_NONE, "Cannot allocate in hooking memory for full access\n");
351         return -1;
352     }
353
354     memset(hook, 0, sizeof(struct mem_hook));
355
356     hook->write = write;
357     hook->read = NULL;
358     hook->priv_data = priv_data;
359
360     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
361
362     hook->region = entry;
363
364     entry->host_addr = host_addr;
365     entry->unhandled = handle_mem_hook;
366     entry->priv_data = hook;
367
368     entry->flags.read = 1;
369     entry->flags.exec = 1;
370     entry->flags.alloced = 1;
371
372     if (v3_insert_mem_region(vm, entry) == -1) {
373         V3_Free(entry);
374         V3_Free(hook);
375         return -1;
376     }
377
378     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
379     list_add(&(hook->hook_node), &(hooks->hook_list));
380
381     return 0;  
382 }
383
384
385
386 int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, 
387                      addr_t guest_addr_start, addr_t guest_addr_end,
388                      int (*read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data),
389                      int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
390                      void * priv_data) {
391   
392     struct v3_mem_region * entry = NULL;
393     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
394     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
395
396     if (!hook) {
397         PrintError(vm, VCORE_NONE, "Cannot allocate in hooking memory for writing\n");
398         return -1;
399     }
400
401     memset(hook, 0, sizeof(struct mem_hook));
402
403     hook->write = write;
404     hook->read = read;
405     hook->priv_data = priv_data;
406
407     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
408
409     if (!entry) {
410         PrintError(vm, VCORE_NONE, "Cannot create memory region\n");
411         V3_Free(hook);
412         return -1;
413     }
414
415     hook->region = entry;
416
417     entry->unhandled = handle_mem_hook;
418     entry->priv_data = hook;
419
420     if (v3_insert_mem_region(vm, entry)) {
421         PrintError(vm, VCORE_NONE, "Cannot insert memory region\n");
422         V3_Free(entry);
423         V3_Free(hook);
424         return -1;
425     }
426
427     list_add(&(hook->hook_node), &(hooks->hook_list));
428     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
429
430
431     return 0;
432 }
433
434
435 int v3_hook_access_mem(struct v3_vm_info * vm, uint16_t core_id, 
436                        addr_t guest_addr_start, addr_t guest_addr_end,
437                        int (*access)(struct guest_info * core, 
438                                      addr_t guest_va, 
439                                      addr_t guest_pa, 
440                                      struct v3_mem_region *reg, 
441                                      pf_error_t access_info, 
442                                      void *priv_data),
443                        void * priv_data) 
444 {
445   
446     struct v3_mem_region * entry = NULL;
447     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
448     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
449
450     if (!hook) {
451         PrintError(vm, VCORE_NONE,"Cannot allocate in hooking memory for access\n");
452         return -1;
453     }
454
455     memset(hook, 0, sizeof(struct mem_hook));
456
457     hook->access = access;
458     hook->priv_data = priv_data;
459
460     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
461
462     if (!entry) {
463         PrintError(vm, VCORE_NONE, "Cannot create memory region\n");
464         V3_Free(hook);
465         return -1;
466     }
467
468     hook->region = entry;
469
470     entry->unhandled = handle_mem_hook;
471     entry->priv_data = hook;
472
473     if (v3_insert_mem_region(vm, entry)) {
474         PrintError(vm, VCORE_NONE, "Cannot insert memory region\n");
475         V3_Free(entry);
476         V3_Free(hook);
477         return -1;
478     }
479
480     list_add(&(hook->hook_node), &(hooks->hook_list));
481     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
482
483     return 0;
484 }
485
486
487 static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook) {  
488     v3_delete_mem_region(vm, hook->region);
489     list_del(&(hook->hook_node));
490
491     V3_Free(hook);
492
493     return 0;
494 }
495
496
497 // This will unhook the memory hook registered at start address
498 // We do not support unhooking subregions
499 int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_start) {
500     struct v3_mem_region * reg = v3_get_mem_region(vm, core_id, guest_addr_start);
501     struct mem_hook * hook = NULL;
502
503     if (reg == NULL) {
504         PrintError(vm, VCORE_NONE, "Could not find region at %p\n", (void *)guest_addr_start);
505         return -1;
506     }
507
508     hook = reg->priv_data;
509
510     if (hook == NULL) {
511         PrintError(vm, VCORE_NONE, "Trying to unhook region that is not a hook at %p\n", (void *)guest_addr_start);
512         return -1;
513     }
514     
515     
516     free_hook(vm, hook);
517
518     return 0;
519 }
520
521