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.


updated io map to allow early hooking
[palacios.git] / palacios / src / palacios / svm_io.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/svm_io.h>
21 #include <palacios/vmm_io.h>
22 #include <palacios/vmm_ctrl_regs.h>
23 #include <palacios/vmm_decoder.h>
24 #include <palacios/vm_guest_mem.h>
25
26 #ifndef CONFIG_DEBUG_IO
27 #undef PrintDebug
28 #define PrintDebug(fmt, args...)
29 #endif
30
31
32 static int update_map(struct guest_info * info, uint16_t port, int hook_read, int hook_write) {
33     uchar_t * bitmap = (uint8_t *)(info->io_map.arch_data);;
34     int major = port / 8;
35     int minor = port % 8;
36
37     if ((hook_read == 0) && (hook_write == 0)) {
38         *(bitmap + major) &= ~(0x1 << minor);
39     } else {
40         *(bitmap + major) |= (0x1 << minor);
41     }
42
43     return 0;
44 }
45
46
47 int v3_init_svm_io_map(struct guest_info * info) {
48     info->io_map.update_map = update_map;
49
50     info->io_map.arch_data = V3_VAddr(V3_AllocPages(3));
51     memset(info->io_map.arch_data, 0, PAGE_SIZE_4KB * 3);
52
53
54     v3_refresh_io_map(info);
55
56     return 0;
57 }
58
59
60
61 // This should package up an IO request and call vmm_handle_io
62 int v3_handle_svm_io_in(struct guest_info * info) {
63     vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data));
64     //  vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
65     struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1);
66
67     struct v3_io_hook * hook = v3_get_io_hook(info, io_info->port);
68     int read_size = 0;
69
70     if (hook == NULL) {
71         PrintError("Hook Not present for in on port 0x%x\n", io_info->port);
72         // error, we should not have exited on this port
73         return -1;
74     }
75
76
77     if (io_info->sz8) { 
78         read_size = 1;
79     } else if (io_info->sz16) {
80         read_size = 2;
81     } else if (io_info->sz32) {
82         read_size = 4;
83     }
84
85     PrintDebug("IN of %d bytes on port %d (0x%x)\n", read_size, io_info->port, io_info->port);
86
87     if (hook->read(io_info->port, &(info->vm_regs.rax), read_size, hook->priv_data) != read_size) {
88         // not sure how we handle errors.....
89         PrintError("Read Failure for in on port 0x%x\n", io_info->port);
90         return -1;
91     }
92
93     info->rip = ctrl_area->exit_info2;
94
95     return 0;
96 }
97
98
99
100
101
102 /* We might not handle wrap around of the RDI register correctly...
103  * In that if we do wrap around the effect will manifest in the higher bits of the register
104  */
105 int v3_handle_svm_io_ins(struct guest_info * info) {
106     vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data));
107     vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
108   
109     struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1);
110   
111     struct v3_io_hook * hook = v3_get_io_hook(info, io_info->port);
112     int read_size = 0;
113     addr_t dst_addr = 0;
114     uint_t rep_num = 1;
115     ullong_t mask = 0;
116     struct v3_segment * theseg = &(info->segments.es); // default is ES
117     addr_t inst_ptr;
118
119
120     // This is kind of hacky...
121     // direction can equal either 1 or -1
122     // We will multiply the final added offset by this value to go the correct direction
123     int direction = 1;
124     struct rflags * flags = (struct rflags *)&(guest_state->rflags);  
125
126     if (flags->df) {
127         direction = -1;
128     }
129
130
131     if (hook == NULL) {
132         PrintError("Hook Not present for ins on port 0x%x\n", io_info->port);
133         // error, we should not have exited on this port
134         return -1;
135     }
136
137
138
139     if (guest_va_to_host_va(info, get_addr_linear(info, info->rip, &(info->segments.cs)), &inst_ptr) == -1) {
140         PrintError("Can't access instruction\n");
141         return -1;
142     }
143
144     while (is_prefix_byte(*((char *)inst_ptr))) {
145         switch (*((char *)inst_ptr)) {
146             case PREFIX_CS_OVERRIDE:
147                 theseg = &(info->segments.cs);
148                 break;
149             case PREFIX_SS_OVERRIDE:
150                 theseg = &(info->segments.ss);
151                 break;
152             case PREFIX_DS_OVERRIDE:
153                 theseg = &(info->segments.ds);
154                 break;
155             case PREFIX_ES_OVERRIDE:
156                 theseg = &(info->segments.es);
157                 break;
158             case PREFIX_FS_OVERRIDE:
159                 theseg = &(info->segments.fs);
160                 break;
161             case PREFIX_GS_OVERRIDE:
162                 theseg = &(info->segments.gs);
163                 break;
164             default:
165                 break;
166         }
167         inst_ptr++;
168     }
169
170
171     PrintDebug("INS on  port %d (0x%x)\n", io_info->port, io_info->port);
172
173     if (io_info->sz8) {
174         read_size = 1;
175     } else if (io_info->sz16) {
176         read_size = 2;
177     } else if (io_info->sz32) {
178         read_size = 4;
179     } else {
180         PrintError("io_info Invalid Size\n");
181         return -1;
182     }
183
184   
185     if (io_info->addr16) {
186         mask = 0xffff;
187     } else if (io_info->addr32) {
188         mask = 0xffffffff;
189     } else if (io_info->addr64) {
190         mask = 0xffffffffffffffffLL;
191     } else {
192         // This value should be set depending on the host register size...
193         mask = get_gpr_mask(info);
194
195         PrintDebug("INS io_info invalid address size, mask=0x%p, io_info=0x%p\n",
196                    (void *)(addr_t)mask, (void *)(addr_t)(io_info));
197         // PrintDebug("INS Aborted... Check implementation\n");
198         //return -1;
199     }
200
201     if (io_info->rep) {
202         rep_num = info->vm_regs.rcx & mask;
203         //rep_num = info->vm_regs.rcx;
204     }
205
206
207     PrintDebug("INS size=%d for %d steps\n", read_size, rep_num);
208
209     while (rep_num > 0) {
210         addr_t host_addr;
211         dst_addr = get_addr_linear(info, (info->vm_regs.rdi & mask), theseg);
212     
213         //      PrintDebug("Writing 0x%p\n", (void *)dst_addr);
214
215         if (guest_va_to_host_va(info, dst_addr, &host_addr) == -1) {
216             // either page fault or gpf...
217             PrintError("Could not convert Guest VA to host VA\n");
218             return -1;
219         }
220
221         if (hook->read(io_info->port, (char *)host_addr, read_size, hook->priv_data) != read_size) {
222             // not sure how we handle errors.....
223             PrintError("Read Failure for ins on port 0x%x\n", io_info->port);
224             return -1;
225         }
226
227         info->vm_regs.rdi += (read_size * direction);
228
229         if (io_info->rep) {
230             info->vm_regs.rcx--;
231         }
232
233         rep_num--;
234     }
235
236
237     info->rip = ctrl_area->exit_info2;
238
239     return 0;
240 }
241
242 int v3_handle_svm_io_out(struct guest_info * info) {
243     vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data));
244     //  vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
245     struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1);
246
247     struct v3_io_hook * hook = v3_get_io_hook(info, io_info->port);
248     int write_size = 0;
249
250     if (hook == NULL) {
251         PrintError("Hook Not present for out on port 0x%x\n", io_info->port);
252         // error, we should not have exited on this port
253         return -1;
254     }
255
256
257     if (io_info->sz8) { 
258         write_size = 1;
259     } else if (io_info->sz16) {
260         write_size = 2;
261     } else if (io_info->sz32) {
262         write_size = 4;
263     }
264
265     PrintDebug("OUT of %d bytes on  port %d (0x%x)\n", write_size, io_info->port, io_info->port);
266
267     if (hook->write(io_info->port, &(info->vm_regs.rax), write_size, hook->priv_data) != write_size) {
268         // not sure how we handle errors.....
269         PrintError("Write Failure for out on port 0x%x\n", io_info->port);
270         return -1;
271     }
272
273     info->rip = ctrl_area->exit_info2;
274
275     return 0;
276 }
277
278
279 /* We might not handle wrap around of the RSI register correctly...
280  * In that if we do wrap around the effect will manifest in the higher bits of the register
281  */
282
283 int v3_handle_svm_io_outs(struct guest_info * info) {
284     vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data));
285     vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data));
286
287   
288     struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1);
289   
290     struct v3_io_hook * hook = v3_get_io_hook(info, io_info->port);
291     int write_size = 0;
292     addr_t dst_addr = 0;
293     uint_t rep_num = 1;
294     ullong_t mask = 0;
295     addr_t inst_ptr;
296     struct v3_segment * theseg = &(info->segments.es); // default is ES
297
298     // This is kind of hacky...
299     // direction can equal either 1 or -1
300     // We will multiply the final added offset by this value to go the correct direction
301     int direction = 1;
302     struct rflags * flags = (struct rflags *)&(guest_state->rflags);  
303
304     if (flags->df) {
305         direction = -1;
306     }
307
308
309     if (hook == NULL) {
310         PrintError("Hook Not present for outs on port 0x%x\n", io_info->port);
311         // error, we should not have exited on this port
312         return -1;
313     }
314
315     PrintDebug("OUTS on  port %d (0x%x)\n", io_info->port, io_info->port);
316
317     if (io_info->sz8) { 
318         write_size = 1;
319     } else if (io_info->sz16) {
320         write_size = 2;
321     } else if (io_info->sz32) {
322         write_size = 4;
323     }
324
325
326     if (io_info->addr16) {
327         mask = 0xffff;
328     } else if (io_info->addr32) {
329         mask = 0xffffffff;
330     } else if (io_info->addr64) {
331         mask = 0xffffffffffffffffLL;
332     } else {
333         // This value should be set depending on the host register size...
334         mask = get_gpr_mask(info);
335
336         PrintDebug("OUTS io_info invalid address size, mask=0%p, io_info=0x%p\n",
337                    (void *)(addr_t)mask, (void *)(addr_t)io_info);
338         // PrintDebug("INS Aborted... Check implementation\n");
339         //return -1;
340         // should never happen
341         //PrintDebug("Invalid Address length\n");
342         //return -1;
343     }
344
345     if (io_info->rep) {
346         rep_num = info->vm_regs.rcx & mask;
347     }
348
349   
350
351
352     if (guest_va_to_host_va(info, get_addr_linear(info, info->rip, &(info->segments.cs)), &inst_ptr) == -1) {
353         PrintError("Can't access instruction\n");
354         return -1;
355     }
356
357     while (is_prefix_byte(*((char *)inst_ptr))) {
358         switch (*((char *)inst_ptr)) {
359             case PREFIX_CS_OVERRIDE:
360                 theseg = &(info->segments.cs);
361                 break;
362             case PREFIX_SS_OVERRIDE:
363                 theseg = &(info->segments.ss);
364                 break;
365             case PREFIX_DS_OVERRIDE:
366                 theseg = &(info->segments.ds);
367                 break;
368             case PREFIX_ES_OVERRIDE:
369                 theseg = &(info->segments.es);
370                 break;
371             case PREFIX_FS_OVERRIDE:
372                 theseg = &(info->segments.fs);
373                 break;
374             case PREFIX_GS_OVERRIDE:
375                 theseg = &(info->segments.gs);
376                 break;
377             default:
378                 break;
379         }
380         inst_ptr++;
381     }
382
383     PrintDebug("OUTS size=%d for %d steps\n", write_size, rep_num);
384
385     while (rep_num > 0) {
386         addr_t host_addr;
387
388         dst_addr = get_addr_linear(info, (info->vm_regs.rsi & mask), theseg);
389     
390         if (guest_va_to_host_va(info, dst_addr, &host_addr) == -1) {
391             // either page fault or gpf...
392         }
393
394         if (hook->write(io_info->port, (char*)host_addr, write_size, hook->priv_data) != write_size) {
395             // not sure how we handle errors.....
396             PrintError("Write Failure for outs on port 0x%x\n", io_info->port);
397             return -1;
398         }
399
400         info->vm_regs.rsi += write_size * direction;
401
402         if (io_info->rep) {
403             info->vm_regs.rcx--;
404         }
405
406         rep_num--;
407     }
408
409
410     info->rip = ctrl_area->exit_info2;
411
412
413     return 0;
414 }