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.


cleaned up the emulation code to be more general
[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 *)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     // Test source operand, if it's memory we need to do some translations, and handle a possible hook
149     if (instr.src_operand.type == MEM_OPERAND) {
150         struct v3_mem_region * src_reg = NULL;
151
152         if (core->mem_mode == PHYSICAL_MEM) { 
153             src_mem_op_gpa = instr.src_operand.operand;
154         } else { 
155             if (v3_gva_to_gpa(core, instr.src_operand.operand, &src_mem_op_gpa) == -1) {
156                 pf_error_t error = access_info;
157
158                 error.present = 0;
159                 v3_inject_guest_pf(core, instr.src_operand.operand, error);
160
161                 return 0;
162             }
163         }
164
165         if ((guest_pa >= reg->guest_start) && 
166             (guest_pa <= reg->guest_end)) {
167             // Src address corresponds to faulted region
168             src_reg = reg;
169         } else {
170             // Note that this should only trigger for string operations
171             src_reg = v3_get_mem_region(core->vm_info, core->cpu_id, src_mem_op_gpa);
172         }
173
174         if (src_reg == NULL) {
175             PrintError("Error finding Source region (addr=%p)\n", (void *)src_mem_op_gpa);
176             return -1;
177         }
178
179         src_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)src_reg);
180
181         // We don't check whether the region is a hook here because it doesn't yet matter.
182         // These hva calculations will be true regardless
183         if (src_reg->flags.alloced == 0) {
184             src_mem_op_hva = (addr_t)(hooks->hook_hvas_1 + (PAGE_SIZE * core->cpu_id));
185         } else {
186             // We already have the region so we can do the conversion ourselves
187             src_mem_op_hva = (addr_t)V3_VAddr((void *)((src_mem_op_gpa - src_reg->guest_start) + src_reg->host_addr));
188         }
189
190         src_req_size = get_op_length(&instr, &(instr.src_operand), src_mem_op_hva);
191     }
192
193     // Now do the same translation / hook handling for the second operand
194     if (instr.dst_operand.type == MEM_OPERAND) {
195         struct v3_mem_region * dst_reg = NULL;
196
197
198         if (core->mem_mode == PHYSICAL_MEM) { 
199             dst_mem_op_gpa = instr.dst_operand.operand;
200         } else { 
201             if (v3_gva_to_gpa(core, instr.dst_operand.operand, &dst_mem_op_gpa) == -1) {
202                 pf_error_t error = access_info;
203
204                 error.present = 0;
205                 v3_inject_guest_pf(core, instr.dst_operand.operand, error);
206
207                 return 0;
208             }
209         }
210
211         if ((guest_pa >= reg->guest_start) && 
212             (guest_pa <= reg->guest_end)) {
213             // Dst address corresponds to faulted region
214             dst_reg = reg;
215         } else {
216             // Note that this should only trigger for string operations
217             dst_reg = v3_get_mem_region(core->vm_info, core->cpu_id, dst_mem_op_gpa);
218         }
219
220         if (dst_reg == NULL) {
221             PrintError("Error finding Source region (addr=%p)\n", (void *)dst_mem_op_gpa);
222             return -1;
223         }
224         
225         dst_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)dst_reg);
226
227         // We don't check whether the region is a hook here because it doesn't yet matter.
228         // These hva calculations will be true regardless
229         if (dst_reg->flags.alloced == 0) {
230             dst_mem_op_hva = (addr_t)(hooks->hook_hvas_2 + (PAGE_SIZE * core->cpu_id));
231         } else {
232             // We already have the region so we can do the conversion ourselves
233             dst_mem_op_hva = (addr_t)V3_VAddr((void *)((dst_mem_op_gpa - dst_reg->guest_start) + dst_reg->host_addr));
234         }
235
236         dst_req_size = get_op_length(&instr, &(instr.dst_operand), dst_mem_op_hva);
237     }
238
239
240     mem_op_size = ((uint_t)src_req_size < (uint_t)dst_req_size) ? src_req_size : dst_req_size;
241
242
243     /* Now handle the hooks if necessary */    
244     if ( (src_hook != NULL)  && (src_hook->read != NULL) &&
245          (instr.src_operand.read == 1) ) {
246         
247         // Read in data from hook
248         
249         if (src_hook->read(core, src_mem_op_gpa, (void *)src_mem_op_hva, mem_op_size, src_hook->priv_data) == -1) {
250             PrintError("Read hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
251             return -1;
252         }
253     }
254
255     if ( (dst_hook != NULL)  && (dst_hook->read != NULL) &&
256          (instr.dst_operand.read == 1) ) {
257         
258         // Read in data from hook
259         
260         if (dst_hook->read(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, mem_op_size, dst_hook->priv_data) == -1) {
261             PrintError("Read hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
262             return -1;
263         }
264     }
265     
266     bytes_emulated = v3_emulate(core, &instr, mem_op_size, src_mem_op_hva, dst_mem_op_hva);
267
268     if (bytes_emulated == -1) {
269         PrintError("Error emulating instruction\n");
270         return -1;
271     }
272
273
274     if ( (src_hook != NULL) && (src_hook->write != NULL) &&
275          (instr.src_operand.write == 1) ) {
276
277         if (src_hook->write(core, src_mem_op_gpa, (void *)src_mem_op_hva, bytes_emulated, src_hook->priv_data) == -1) {
278             PrintError("Write hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
279             return -1;
280         }
281
282     }
283
284
285     if ( (dst_hook != NULL) && (dst_hook->write != NULL) &&
286          (instr.dst_operand.write == 1) ) {
287
288         if (dst_hook->write(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, bytes_emulated, dst_hook->priv_data) == -1) {
289             PrintError("Write hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
290             return -1;
291         }
292     }
293
294
295     if (instr.is_str_op == 0) {
296         core->rip += instr.instr_length;
297     }
298
299
300     return 0;
301 }
302
303
304
305
306 int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id,
307                       addr_t guest_addr_start, addr_t guest_addr_end, addr_t host_addr,
308                       int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
309                       void * priv_data) {
310     struct v3_mem_region * entry = NULL;
311     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
312     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
313
314     memset(hook, 0, sizeof(struct mem_hook));
315
316     hook->write = write;
317     hook->read = NULL;
318     hook->priv_data = priv_data;
319
320     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
321
322     hook->region = entry;
323
324     entry->host_addr = host_addr;
325     entry->unhandled = handle_mem_hook;
326     entry->priv_data = hook;
327
328     entry->flags.read = 1;
329     entry->flags.exec = 1;
330     entry->flags.alloced = 1;
331
332     if (v3_insert_mem_region(vm, entry) == -1) {
333         V3_Free(entry);
334         V3_Free(hook);
335         return -1;
336     }
337
338     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
339     list_add(&(hook->hook_node), &(hooks->hook_list));
340
341     return 0;  
342 }
343
344
345
346 int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, 
347                      addr_t guest_addr_start, addr_t guest_addr_end,
348                      int (*read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data),
349                      int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
350                      void * priv_data) {
351   
352     struct v3_mem_region * entry = NULL;
353     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
354     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
355
356     memset(hook, 0, sizeof(struct mem_hook));
357
358     hook->write = write;
359     hook->read = read;
360     hook->priv_data = priv_data;
361
362     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
363     hook->region = entry;
364
365     entry->unhandled = handle_mem_hook;
366     entry->priv_data = hook;
367
368     if (v3_insert_mem_region(vm, entry)) {
369         V3_Free(entry);
370         V3_Free(hook);
371         return -1;
372     }
373
374     list_add(&(hook->hook_node), &(hooks->hook_list));
375     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
376
377
378     return 0;
379 }
380
381
382 static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook) {  
383     v3_delete_mem_region(vm, hook->region);
384     list_del(&(hook->hook_node));
385
386     V3_Free(hook);
387
388     return 0;
389 }
390
391
392 // This will unhook the memory hook registered at start address
393 // We do not support unhooking subregions
394 int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_start) {
395     struct v3_mem_region * reg = v3_get_mem_region(vm, core_id, guest_addr_start);
396     struct mem_hook * hook = reg->priv_data;
397
398     free_hook(vm, hook);
399
400     return 0;
401 }
402
403