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