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.


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