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.


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