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.


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