debuggers.hg

view xen/arch/x86/hvm/vmx/realmode.c @ 16599:a74efcdc87b3

vmx realmode: Fix exception delivery w.r.t. in-flight I/O.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Dec 07 16:39:15 2007 +0000 (2007-12-07)
parents 26e766e0c628
children e818c24cec03
line source
1 /******************************************************************************
2 * arch/x86/hvm/vmx/realmode.c
3 *
4 * Real-mode emulation for VMX.
5 *
6 * Copyright (c) 2007 Citrix Systems, Inc.
7 *
8 * Authors:
9 * Keir Fraser <keir.fraser@citrix.com>
10 */
12 #include <xen/config.h>
13 #include <xen/init.h>
14 #include <xen/lib.h>
15 #include <xen/sched.h>
16 #include <asm/event.h>
17 #include <asm/hvm/hvm.h>
18 #include <asm/hvm/support.h>
19 #include <asm/hvm/vmx/vmx.h>
20 #include <asm/hvm/vmx/vmcs.h>
21 #include <asm/hvm/vmx/cpu.h>
22 #include <asm/x86_emulate.h>
24 struct realmode_emulate_ctxt {
25 struct x86_emulate_ctxt ctxt;
27 /* Cache of 16 bytes of instruction. */
28 uint8_t insn_buf[16];
29 unsigned long insn_buf_eip;
31 struct segment_register seg_reg[10];
33 union {
34 struct {
35 unsigned int hlt:1;
36 unsigned int mov_ss:1;
37 unsigned int sti:1;
38 } flags;
39 unsigned int flag_word;
40 };
42 uint8_t exn_vector;
43 uint8_t exn_insn_len;
44 };
46 static void realmode_deliver_exception(
47 unsigned int vector,
48 unsigned int insn_len,
49 struct realmode_emulate_ctxt *rm_ctxt)
50 {
51 struct segment_register *idtr = &rm_ctxt->seg_reg[x86_seg_idtr];
52 struct segment_register *csr = &rm_ctxt->seg_reg[x86_seg_cs];
53 struct cpu_user_regs *regs = rm_ctxt->ctxt.regs;
54 uint32_t cs_eip, pstk;
55 uint16_t frame[3];
56 unsigned int last_byte;
58 again:
59 last_byte = (vector * 4) + 3;
60 if ( idtr->limit < last_byte )
61 {
62 /* Software interrupt? */
63 if ( insn_len != 0 )
64 {
65 insn_len = 0;
66 vector = TRAP_gp_fault;
67 goto again;
68 }
70 /* Exception or hardware interrupt. */
71 switch ( vector )
72 {
73 case TRAP_double_fault:
74 hvm_triple_fault();
75 return;
76 case TRAP_gp_fault:
77 vector = TRAP_double_fault;
78 goto again;
79 default:
80 vector = TRAP_gp_fault;
81 goto again;
82 }
83 }
85 (void)hvm_copy_from_guest_phys(&cs_eip, idtr->base + vector * 4, 4);
87 frame[0] = regs->eip + insn_len;
88 frame[1] = csr->sel;
89 frame[2] = regs->eflags & ~X86_EFLAGS_RF;
91 if ( rm_ctxt->ctxt.addr_size == 32 )
92 {
93 regs->esp -= 6;
94 pstk = regs->esp;
95 }
96 else
97 {
98 pstk = (uint16_t)(regs->esp - 6);
99 regs->esp &= ~0xffff;
100 regs->esp |= pstk;
101 }
103 pstk += rm_ctxt->seg_reg[x86_seg_ss].base;
104 (void)hvm_copy_to_guest_phys(pstk, frame, sizeof(frame));
106 csr->sel = cs_eip >> 16;
107 csr->base = (uint32_t)csr->sel << 4;
108 regs->eip = (uint16_t)cs_eip;
109 regs->eflags &= ~(X86_EFLAGS_TF | X86_EFLAGS_IF | X86_EFLAGS_RF);
110 }
112 static int
113 realmode_read(
114 enum x86_segment seg,
115 unsigned long offset,
116 unsigned long *val,
117 unsigned int bytes,
118 enum hvm_access_type access_type,
119 struct realmode_emulate_ctxt *rm_ctxt)
120 {
121 uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
122 int todo;
124 *val = 0;
125 todo = hvm_copy_from_guest_phys(val, addr, bytes);
127 if ( todo )
128 {
129 struct vcpu *curr = current;
131 if ( todo != bytes )
132 {
133 gdprintk(XENLOG_WARNING, "RM: Partial read at %08x (%d/%d)\n",
134 addr, todo, bytes);
135 return X86EMUL_UNHANDLEABLE;
136 }
138 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
139 return X86EMUL_UNHANDLEABLE;
141 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
142 {
143 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
144 send_mmio_req(IOREQ_TYPE_COPY, addr, 1, bytes,
145 0, IOREQ_READ, 0, 0);
146 }
148 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
149 return X86EMUL_RETRY;
151 *val = curr->arch.hvm_vmx.real_mode_io_data;
152 curr->arch.hvm_vmx.real_mode_io_completed = 0;
153 }
155 return X86EMUL_OKAY;
156 }
158 static int
159 realmode_emulate_read(
160 enum x86_segment seg,
161 unsigned long offset,
162 unsigned long *val,
163 unsigned int bytes,
164 struct x86_emulate_ctxt *ctxt)
165 {
166 return realmode_read(
167 seg, offset, val, bytes, hvm_access_read,
168 container_of(ctxt, struct realmode_emulate_ctxt, ctxt));
169 }
171 static int
172 realmode_emulate_insn_fetch(
173 enum x86_segment seg,
174 unsigned long offset,
175 unsigned long *val,
176 unsigned int bytes,
177 struct x86_emulate_ctxt *ctxt)
178 {
179 struct realmode_emulate_ctxt *rm_ctxt =
180 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
181 unsigned int insn_off = offset - rm_ctxt->insn_buf_eip;
183 /* Fall back if requested bytes are not in the prefetch cache. */
184 if ( unlikely((insn_off + bytes) > sizeof(rm_ctxt->insn_buf)) )
185 return realmode_read(
186 seg, offset, val, bytes,
187 hvm_access_insn_fetch, rm_ctxt);
189 /* Hit the cache. Simple memcpy. */
190 *val = 0;
191 memcpy(val, &rm_ctxt->insn_buf[insn_off], bytes);
192 return X86EMUL_OKAY;
193 }
195 static int
196 realmode_emulate_write(
197 enum x86_segment seg,
198 unsigned long offset,
199 unsigned long val,
200 unsigned int bytes,
201 struct x86_emulate_ctxt *ctxt)
202 {
203 struct realmode_emulate_ctxt *rm_ctxt =
204 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
205 uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
206 int todo;
208 todo = hvm_copy_to_guest_phys(addr, &val, bytes);
210 if ( todo )
211 {
212 struct vcpu *curr = current;
214 if ( todo != bytes )
215 {
216 gdprintk(XENLOG_WARNING, "RM: Partial write at %08x (%d/%d)\n",
217 addr, todo, bytes);
218 return X86EMUL_UNHANDLEABLE;
219 }
221 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
222 return X86EMUL_UNHANDLEABLE;
224 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
225 send_mmio_req(IOREQ_TYPE_COPY, addr, 1, bytes,
226 val, IOREQ_WRITE, 0, 0);
227 }
229 return X86EMUL_OKAY;
230 }
232 static int
233 realmode_emulate_cmpxchg(
234 enum x86_segment seg,
235 unsigned long offset,
236 unsigned long old,
237 unsigned long new,
238 unsigned int bytes,
239 struct x86_emulate_ctxt *ctxt)
240 {
241 /* Fix this in case the guest is really relying on r-m-w atomicity. */
242 return realmode_emulate_write(seg, offset, new, bytes, ctxt);
243 }
245 static int
246 realmode_read_segment(
247 enum x86_segment seg,
248 struct segment_register *reg,
249 struct x86_emulate_ctxt *ctxt)
250 {
251 struct realmode_emulate_ctxt *rm_ctxt =
252 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
253 memcpy(reg, &rm_ctxt->seg_reg[seg], sizeof(struct segment_register));
254 return X86EMUL_OKAY;
255 }
257 static int
258 realmode_write_segment(
259 enum x86_segment seg,
260 struct segment_register *reg,
261 struct x86_emulate_ctxt *ctxt)
262 {
263 struct realmode_emulate_ctxt *rm_ctxt =
264 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
265 memcpy(&rm_ctxt->seg_reg[seg], reg, sizeof(struct segment_register));
266 if ( seg == x86_seg_ss )
267 rm_ctxt->flags.mov_ss = 1;
268 return X86EMUL_OKAY;
269 }
271 static int
272 realmode_read_io(
273 unsigned int port,
274 unsigned int bytes,
275 unsigned long *val,
276 struct x86_emulate_ctxt *ctxt)
277 {
278 struct vcpu *curr = current;
280 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
281 return X86EMUL_UNHANDLEABLE;
283 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
284 {
285 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
286 send_pio_req(port, 1, bytes, 0, IOREQ_READ, 0, 0);
287 }
289 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
290 return X86EMUL_RETRY;
292 *val = curr->arch.hvm_vmx.real_mode_io_data;
293 curr->arch.hvm_vmx.real_mode_io_completed = 0;
295 return X86EMUL_OKAY;
296 }
298 static int realmode_write_io(
299 unsigned int port,
300 unsigned int bytes,
301 unsigned long val,
302 struct x86_emulate_ctxt *ctxt)
303 {
304 struct vcpu *curr = current;
306 if ( port == 0xe9 )
307 {
308 hvm_print_line(curr, val);
309 return X86EMUL_OKAY;
310 }
312 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
313 return X86EMUL_UNHANDLEABLE;
315 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
316 send_pio_req(port, 1, bytes, val, IOREQ_WRITE, 0, 0);
318 return X86EMUL_OKAY;
319 }
321 static int
322 realmode_read_cr(
323 unsigned int reg,
324 unsigned long *val,
325 struct x86_emulate_ctxt *ctxt)
326 {
327 switch ( reg )
328 {
329 case 0:
330 case 2:
331 case 3:
332 case 4:
333 *val = current->arch.hvm_vcpu.guest_cr[reg];
334 break;
335 default:
336 return X86EMUL_UNHANDLEABLE;
337 }
339 return X86EMUL_OKAY;
340 }
342 static int
343 realmode_write_cr(
344 unsigned int reg,
345 unsigned long val,
346 struct x86_emulate_ctxt *ctxt)
347 {
348 switch ( reg )
349 {
350 case 0:
351 if ( !hvm_set_cr0(val) )
352 return X86EMUL_UNHANDLEABLE;
353 break;
354 case 2:
355 current->arch.hvm_vcpu.guest_cr[2] = val;
356 break;
357 case 3:
358 if ( !hvm_set_cr3(val) )
359 return X86EMUL_UNHANDLEABLE;
360 break;
361 case 4:
362 if ( !hvm_set_cr4(val) )
363 return X86EMUL_UNHANDLEABLE;
364 break;
365 default:
366 return X86EMUL_UNHANDLEABLE;
367 }
369 return X86EMUL_OKAY;
370 }
372 static int realmode_write_rflags(
373 unsigned long val,
374 struct x86_emulate_ctxt *ctxt)
375 {
376 struct realmode_emulate_ctxt *rm_ctxt =
377 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
378 if ( (val & X86_EFLAGS_IF) && !(ctxt->regs->eflags & X86_EFLAGS_IF) )
379 rm_ctxt->flags.sti = 1;
380 return X86EMUL_OKAY;
381 }
383 static int realmode_wbinvd(
384 struct x86_emulate_ctxt *ctxt)
385 {
386 vmx_wbinvd_intercept();
387 return X86EMUL_OKAY;
388 }
390 static int realmode_cpuid(
391 unsigned int *eax,
392 unsigned int *ebx,
393 unsigned int *ecx,
394 unsigned int *edx,
395 struct x86_emulate_ctxt *ctxt)
396 {
397 vmx_cpuid_intercept(eax, ebx, ecx, edx);
398 return X86EMUL_OKAY;
399 }
401 static int realmode_hlt(
402 struct x86_emulate_ctxt *ctxt)
403 {
404 struct realmode_emulate_ctxt *rm_ctxt =
405 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
406 rm_ctxt->flags.hlt = 1;
407 return X86EMUL_OKAY;
408 }
410 static int realmode_inject_hw_exception(
411 uint8_t vector,
412 struct x86_emulate_ctxt *ctxt)
413 {
414 struct realmode_emulate_ctxt *rm_ctxt =
415 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
417 rm_ctxt->exn_vector = vector;
418 rm_ctxt->exn_insn_len = 0;
420 return X86EMUL_OKAY;
421 }
423 static int realmode_inject_sw_interrupt(
424 uint8_t vector,
425 uint8_t insn_len,
426 struct x86_emulate_ctxt *ctxt)
427 {
428 struct realmode_emulate_ctxt *rm_ctxt =
429 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
431 rm_ctxt->exn_vector = vector;
432 rm_ctxt->exn_insn_len = insn_len;
434 return X86EMUL_OKAY;
435 }
437 static struct x86_emulate_ops realmode_emulator_ops = {
438 .read = realmode_emulate_read,
439 .insn_fetch = realmode_emulate_insn_fetch,
440 .write = realmode_emulate_write,
441 .cmpxchg = realmode_emulate_cmpxchg,
442 .read_segment = realmode_read_segment,
443 .write_segment = realmode_write_segment,
444 .read_io = realmode_read_io,
445 .write_io = realmode_write_io,
446 .read_cr = realmode_read_cr,
447 .write_cr = realmode_write_cr,
448 .write_rflags = realmode_write_rflags,
449 .wbinvd = realmode_wbinvd,
450 .cpuid = realmode_cpuid,
451 .hlt = realmode_hlt,
452 .inject_hw_exception = realmode_inject_hw_exception,
453 .inject_sw_interrupt = realmode_inject_sw_interrupt
454 };
456 void vmx_realmode(struct cpu_user_regs *regs)
457 {
458 struct vcpu *curr = current;
459 struct realmode_emulate_ctxt rm_ctxt;
460 unsigned long intr_info;
461 int i, rc;
462 u32 intr_shadow, new_intr_shadow;
464 rm_ctxt.ctxt.regs = regs;
466 for ( i = 0; i < 10; i++ )
467 hvm_get_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
469 rm_ctxt.ctxt.addr_size =
470 rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
471 rm_ctxt.ctxt.sp_size =
472 rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
474 intr_info = __vmread(VM_ENTRY_INTR_INFO);
475 intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
476 new_intr_shadow = intr_shadow;
478 while ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) &&
479 !softirq_pending(smp_processor_id()) &&
480 !hvm_local_events_need_delivery(curr) &&
481 !curr->arch.hvm_vmx.real_mode_io_in_progress )
482 {
483 if ( (intr_info & INTR_INFO_VALID_MASK) &&
484 !curr->arch.hvm_vmx.real_mode_io_completed )
485 {
486 realmode_deliver_exception((uint8_t)intr_info, 0, &rm_ctxt);
487 __vmwrite(VM_ENTRY_INTR_INFO, 0);
488 intr_info = 0;
489 }
491 rm_ctxt.insn_buf_eip = regs->eip;
492 (void)hvm_copy_from_guest_phys(
493 rm_ctxt.insn_buf,
494 (uint32_t)(rm_ctxt.seg_reg[x86_seg_cs].base + regs->eip),
495 sizeof(rm_ctxt.insn_buf));
497 rm_ctxt.flag_word = 0;
499 rc = x86_emulate(&rm_ctxt.ctxt, &realmode_emulator_ops);
501 if ( rc == X86EMUL_RETRY )
502 continue;
504 if ( rc == X86EMUL_UNHANDLEABLE )
505 {
506 gdprintk(XENLOG_ERR,
507 "Real-mode emulation failed @ %04x:%08lx: "
508 "%02x %02x %02x %02x %02x %02x\n",
509 rm_ctxt.seg_reg[x86_seg_cs].sel, rm_ctxt.insn_buf_eip,
510 rm_ctxt.insn_buf[0], rm_ctxt.insn_buf[1],
511 rm_ctxt.insn_buf[2], rm_ctxt.insn_buf[3],
512 rm_ctxt.insn_buf[4], rm_ctxt.insn_buf[5]);
513 domain_crash_synchronous();
514 }
516 /* MOV-SS instruction toggles MOV-SS shadow, else we just clear it. */
517 if ( rm_ctxt.flags.mov_ss )
518 new_intr_shadow ^= VMX_INTR_SHADOW_MOV_SS;
519 else
520 new_intr_shadow &= ~VMX_INTR_SHADOW_MOV_SS;
522 /* STI instruction toggles STI shadow, else we just clear it. */
523 if ( rm_ctxt.flags.sti )
524 new_intr_shadow ^= VMX_INTR_SHADOW_STI;
525 else
526 new_intr_shadow &= ~VMX_INTR_SHADOW_STI;
528 /* Update interrupt shadow information in VMCS only if it changes. */
529 if ( intr_shadow != new_intr_shadow )
530 {
531 intr_shadow = new_intr_shadow;
532 __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow);
533 }
535 if ( rc == X86EMUL_EXCEPTION )
536 {
537 realmode_deliver_exception(
538 rm_ctxt.exn_vector, rm_ctxt.exn_insn_len, &rm_ctxt);
539 }
540 else if ( rm_ctxt.flags.hlt && !hvm_local_events_need_delivery(curr) )
541 {
542 hvm_hlt(regs->eflags);
543 }
544 }
546 /*
547 * Cannot enter protected mode with bogus selector RPLs and DPLs. Hence we
548 * fix up as best we can, even though this deviates from native execution
549 */
550 if ( curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE )
551 {
552 /* CS.RPL == SS.RPL == SS.DPL == 0. */
553 rm_ctxt.seg_reg[x86_seg_cs].sel &= ~3;
554 rm_ctxt.seg_reg[x86_seg_ss].sel &= ~3;
555 /* DS,ES,FS,GS: The most uninvasive trick is to set DPL == RPL. */
556 rm_ctxt.seg_reg[x86_seg_ds].attr.fields.dpl =
557 rm_ctxt.seg_reg[x86_seg_ds].sel & 3;
558 rm_ctxt.seg_reg[x86_seg_es].attr.fields.dpl =
559 rm_ctxt.seg_reg[x86_seg_es].sel & 3;
560 rm_ctxt.seg_reg[x86_seg_fs].attr.fields.dpl =
561 rm_ctxt.seg_reg[x86_seg_fs].sel & 3;
562 rm_ctxt.seg_reg[x86_seg_gs].attr.fields.dpl =
563 rm_ctxt.seg_reg[x86_seg_gs].sel & 3;
564 }
566 for ( i = 0; i < 10; i++ )
567 hvm_set_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
568 }
570 int vmx_realmode_io_complete(void)
571 {
572 struct vcpu *curr = current;
573 ioreq_t *p = &get_ioreq(curr)->vp_ioreq;
575 if ( !curr->arch.hvm_vmx.real_mode_io_in_progress )
576 return 0;
578 curr->arch.hvm_vmx.real_mode_io_in_progress = 0;
579 if ( p->dir == IOREQ_READ )
580 {
581 curr->arch.hvm_vmx.real_mode_io_completed = 1;
582 curr->arch.hvm_vmx.real_mode_io_data = p->data;
583 }
585 return 1;
586 }