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.


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