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.


1d49294ba4f188bbb09ecf2a6a5ccd0a52341df3
[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     if (!entry) { 
363        PrintError(vm, VCORE_NONE, "Cannot allocate a memory region\n");
364        V3_Free(hook);
365        return -1;
366     }
367
368     hook->region = entry;
369
370     entry->host_addr = host_addr;
371     entry->unhandled = handle_mem_hook;
372     entry->priv_data = hook;
373
374     entry->flags.read = 1;
375     entry->flags.exec = 1;
376     entry->flags.alloced = 1;
377
378     if (v3_insert_mem_region(vm, entry) == -1) {
379         PrintError(vm, VCORE_NONE, "Cannot insert memory region\n");
380         V3_Free(entry);
381         V3_Free(hook);
382         return -1;
383     }
384
385     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
386     list_add(&(hook->hook_node), &(hooks->hook_list));
387
388     return 0;  
389 }
390
391
392
393 int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, 
394                      addr_t guest_addr_start, addr_t guest_addr_end,
395                      int (*read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data),
396                      int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
397                      void * priv_data) {
398   
399     struct v3_mem_region * entry = NULL;
400     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
401     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
402
403     if (!hook) {
404         PrintError(vm, VCORE_NONE, "Cannot allocate in hooking memory for writing\n");
405         return -1;
406     }
407
408     memset(hook, 0, sizeof(struct mem_hook));
409
410     hook->write = write;
411     hook->read = read;
412     hook->priv_data = priv_data;
413
414     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
415
416     if (!entry) {
417         PrintError(vm, VCORE_NONE, "Cannot create memory region\n");
418         V3_Free(hook);
419         return -1;
420     }
421
422     hook->region = entry;
423
424     entry->unhandled = handle_mem_hook;
425     entry->priv_data = hook;
426
427     if (v3_insert_mem_region(vm, entry)) {
428         PrintError(vm, VCORE_NONE, "Cannot insert memory region\n");
429         V3_Free(entry);
430         V3_Free(hook);
431         return -1;
432     }
433
434     list_add(&(hook->hook_node), &(hooks->hook_list));
435     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
436
437
438     return 0;
439 }
440
441
442 int v3_hook_access_mem(struct v3_vm_info * vm, uint16_t core_id, 
443                        addr_t guest_addr_start, addr_t guest_addr_end,
444                        int (*access)(struct guest_info * core, 
445                                      addr_t guest_va, 
446                                      addr_t guest_pa, 
447                                      struct v3_mem_region *reg, 
448                                      pf_error_t access_info, 
449                                      void *priv_data),
450                        void * priv_data) 
451 {
452   
453     struct v3_mem_region * entry = NULL;
454     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
455     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
456
457     if (!hook) {
458         PrintError(vm, VCORE_NONE,"Cannot allocate in hooking memory for access\n");
459         return -1;
460     }
461
462     memset(hook, 0, sizeof(struct mem_hook));
463
464     hook->access = access;
465     hook->priv_data = priv_data;
466
467     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
468
469     if (!entry) {
470         PrintError(vm, VCORE_NONE, "Cannot create memory region\n");
471         V3_Free(hook);
472         return -1;
473     }
474
475     hook->region = entry;
476
477     entry->unhandled = handle_mem_hook;
478     entry->priv_data = hook;
479
480     if (v3_insert_mem_region(vm, entry)) {
481         PrintError(vm, VCORE_NONE, "Cannot insert memory region\n");
482         V3_Free(entry);
483         V3_Free(hook);
484         return -1;
485     }
486
487     list_add(&(hook->hook_node), &(hooks->hook_list));
488     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
489
490     return 0;
491 }
492
493
494 static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook) {  
495     v3_delete_mem_region(vm, hook->region);
496     list_del(&(hook->hook_node));
497
498     V3_Free(hook);
499
500     return 0;
501 }
502
503
504 // This will unhook the memory hook registered at start address
505 // We do not support unhooking subregions
506 int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_start) {
507     struct v3_mem_region * reg = v3_get_mem_region(vm, core_id, guest_addr_start);
508     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
509     struct mem_hook * hook = NULL;
510
511     if (reg == NULL) {
512         PrintError(vm, VCORE_NONE, "Could not find region at %p\n", (void *)guest_addr_start);
513         return -1;
514     }
515
516     hook = reg->priv_data;
517
518     if (hook == NULL) {
519         PrintError(vm, VCORE_NONE, "Trying to unhook region that is not a hook at %p\n", (void *)guest_addr_start);
520         return -1;
521     }
522     
523     
524     free_hook(vm, hook);
525
526     v3_htable_remove(hooks->reg_table, (addr_t)reg, 0);
527
528     return 0;
529 }
530
531