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