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.


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