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 to memory hooks
[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
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->cpu_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->cpu_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->cpu_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->cpu_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
244     /* Now handle the hooks if necessary */    
245     if ( (src_hook != NULL)  && (src_hook->read != NULL) &&
246          (instr.src_operand.read == 1) ) {
247         
248         // Read in data from hook
249         
250         if (src_hook->read(core, src_mem_op_gpa, (void *)src_mem_op_hva, mem_op_size, src_hook->priv_data) == -1) {
251             PrintError("Read hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
252             return -1;
253         }
254     }
255
256     if ( (dst_hook != NULL)  && (dst_hook->read != NULL) &&
257          (instr.dst_operand.read == 1) ) {
258         
259         // Read in data from hook
260         
261         if (dst_hook->read(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, mem_op_size, dst_hook->priv_data) == -1) {
262             PrintError("Read hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
263             return -1;
264         }
265     }
266     
267     bytes_emulated = v3_emulate(core, &instr, mem_op_size, src_mem_op_hva, dst_mem_op_hva);
268
269     if (bytes_emulated == -1) {
270         PrintError("Error emulating instruction\n");
271         return -1;
272     }
273
274
275     if ( (src_hook != NULL) && (src_hook->write != NULL) &&
276          (instr.src_operand.write == 1) ) {
277
278         if (src_hook->write(core, src_mem_op_gpa, (void *)src_mem_op_hva, bytes_emulated, src_hook->priv_data) == -1) {
279             PrintError("Write hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa);
280             return -1;
281         }
282
283     }
284
285
286     if ( (dst_hook != NULL) && (dst_hook->write != NULL) &&
287          (instr.dst_operand.write == 1) ) {
288
289         if (dst_hook->write(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, bytes_emulated, dst_hook->priv_data) == -1) {
290             PrintError("Write hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa);
291             return -1;
292         }
293     }
294
295
296     if (instr.is_str_op == 0) {
297         core->rip += instr.instr_length;
298     }
299
300
301     return 0;
302 }
303
304
305
306
307 int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id,
308                       addr_t guest_addr_start, addr_t guest_addr_end, addr_t host_addr,
309                       int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
310                       void * priv_data) {
311     struct v3_mem_region * entry = NULL;
312     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
313     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
314
315     memset(hook, 0, sizeof(struct mem_hook));
316
317     hook->write = write;
318     hook->read = NULL;
319     hook->priv_data = priv_data;
320
321     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
322
323     hook->region = entry;
324
325     entry->host_addr = host_addr;
326     entry->unhandled = handle_mem_hook;
327     entry->priv_data = hook;
328
329     entry->flags.read = 1;
330     entry->flags.exec = 1;
331     entry->flags.alloced = 1;
332
333     if (v3_insert_mem_region(vm, entry) == -1) {
334         V3_Free(entry);
335         V3_Free(hook);
336         return -1;
337     }
338
339     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
340     list_add(&(hook->hook_node), &(hooks->hook_list));
341
342     return 0;  
343 }
344
345
346
347 int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, 
348                      addr_t guest_addr_start, addr_t guest_addr_end,
349                      int (*read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data),
350                      int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data),
351                      void * priv_data) {
352   
353     struct v3_mem_region * entry = NULL;
354     struct mem_hook * hook = V3_Malloc(sizeof(struct mem_hook));
355     struct v3_mem_hooks * hooks = &(vm->mem_hooks);
356
357     memset(hook, 0, sizeof(struct mem_hook));
358
359     hook->write = write;
360     hook->read = read;
361     hook->priv_data = priv_data;
362
363     entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end);
364     hook->region = entry;
365
366     entry->unhandled = handle_mem_hook;
367     entry->priv_data = hook;
368
369     if (v3_insert_mem_region(vm, entry)) {
370         V3_Free(entry);
371         V3_Free(hook);
372         return -1;
373     }
374
375     list_add(&(hook->hook_node), &(hooks->hook_list));
376     v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook);
377
378
379     return 0;
380 }
381
382
383 static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook) {  
384     v3_delete_mem_region(vm, hook->region);
385     list_del(&(hook->hook_node));
386
387     V3_Free(hook);
388
389     return 0;
390 }
391
392
393 // This will unhook the memory hook registered at start address
394 // We do not support unhooking subregions
395 int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_start) {
396     struct v3_mem_region * reg = v3_get_mem_region(vm, core_id, guest_addr_start);
397     struct mem_hook * hook = reg->priv_data;
398
399     free_hook(vm, hook);
400
401     return 0;
402 }
403
404