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.


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