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.


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