ia64/xen-unstable

view xen/arch/x86/hvm/vmx/realmode.c @ 16480:4deb65519d9b

x86 emulate: Emulate atomic read-modify-write instructions as a
straightforward write. Hopefully multiprocessor synchronisation is not
relied upon in real mode!
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Nov 26 16:57:57 2007 +0000 (2007-11-26)
parents 11bfa26dd125
children 4d6f92fa1014
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 unsigned int exn_raised:1;
39 } flags;
40 unsigned int flag_word;
41 };
42 };
44 static void realmode_deliver_exception(
45 unsigned int vector,
46 unsigned int insn_len,
47 struct realmode_emulate_ctxt *rm_ctxt)
48 {
49 struct segment_register *idtr = &rm_ctxt->seg_reg[x86_seg_idtr];
50 struct segment_register *csr = &rm_ctxt->seg_reg[x86_seg_cs];
51 struct cpu_user_regs *regs = rm_ctxt->ctxt.regs;
52 uint32_t cs_eip, pstk;
53 uint16_t frame[3];
54 unsigned int last_byte;
56 again:
57 last_byte = (vector * 4) + 3;
58 if ( idtr->limit < last_byte )
59 {
60 /* Software interrupt? */
61 if ( insn_len != 0 )
62 {
63 insn_len = 0;
64 vector = TRAP_gp_fault;
65 goto again;
66 }
68 /* Exception or hardware interrupt. */
69 switch ( vector )
70 {
71 case TRAP_double_fault:
72 hvm_triple_fault();
73 return;
74 case TRAP_gp_fault:
75 vector = TRAP_double_fault;
76 goto again;
77 default:
78 vector = TRAP_gp_fault;
79 goto again;
80 }
81 }
83 (void)hvm_copy_from_guest_phys(&cs_eip, idtr->base + vector * 4, 4);
85 frame[0] = regs->eip + insn_len;
86 frame[1] = csr->sel;
87 frame[2] = regs->eflags & ~X86_EFLAGS_RF;
89 if ( rm_ctxt->ctxt.addr_size == 32 )
90 {
91 regs->esp -= 6;
92 pstk = regs->esp;
93 }
94 else
95 {
96 pstk = (uint16_t)(regs->esp - 6);
97 regs->esp &= ~0xffff;
98 regs->esp |= pstk;
99 }
101 pstk += rm_ctxt->seg_reg[x86_seg_ss].base;
102 (void)hvm_copy_to_guest_phys(pstk, frame, sizeof(frame));
104 csr->sel = cs_eip >> 16;
105 csr->base = (uint32_t)csr->sel << 4;
106 regs->eip = (uint16_t)cs_eip;
107 regs->eflags &= ~(X86_EFLAGS_AC | X86_EFLAGS_TF |
108 X86_EFLAGS_AC | X86_EFLAGS_RF);
109 }
111 static int
112 realmode_read(
113 enum x86_segment seg,
114 unsigned long offset,
115 unsigned long *val,
116 unsigned int bytes,
117 enum hvm_access_type access_type,
118 struct realmode_emulate_ctxt *rm_ctxt)
119 {
120 uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
121 int todo;
123 *val = 0;
124 todo = hvm_copy_from_guest_phys(val, addr, bytes);
126 if ( todo )
127 {
128 struct vcpu *curr = current;
130 if ( todo != bytes )
131 {
132 gdprintk(XENLOG_WARNING, "RM: Partial read at %08x (%d/%d)\n",
133 addr, todo, bytes);
134 return X86EMUL_UNHANDLEABLE;
135 }
137 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
138 return X86EMUL_UNHANDLEABLE;
140 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
141 {
142 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
143 send_mmio_req(IOREQ_TYPE_COPY, addr, 1, bytes,
144 0, IOREQ_READ, 0, 0);
145 }
147 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
148 return X86EMUL_UNHANDLEABLE;
150 *val = curr->arch.hvm_vmx.real_mode_io_data;
151 curr->arch.hvm_vmx.real_mode_io_completed = 0;
152 }
154 return X86EMUL_OKAY;
155 }
157 static int
158 realmode_emulate_read(
159 enum x86_segment seg,
160 unsigned long offset,
161 unsigned long *val,
162 unsigned int bytes,
163 struct x86_emulate_ctxt *ctxt)
164 {
165 return realmode_read(
166 seg, offset, val, bytes, hvm_access_read,
167 container_of(ctxt, struct realmode_emulate_ctxt, ctxt));
168 }
170 static int
171 realmode_emulate_insn_fetch(
172 enum x86_segment seg,
173 unsigned long offset,
174 unsigned long *val,
175 unsigned int bytes,
176 struct x86_emulate_ctxt *ctxt)
177 {
178 struct realmode_emulate_ctxt *rm_ctxt =
179 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
180 unsigned int insn_off = offset - rm_ctxt->insn_buf_eip;
182 /* Fall back if requested bytes are not in the prefetch cache. */
183 if ( unlikely((insn_off + bytes) > sizeof(rm_ctxt->insn_buf)) )
184 return realmode_read(
185 seg, offset, val, bytes,
186 hvm_access_insn_fetch, rm_ctxt);
188 /* Hit the cache. Simple memcpy. */
189 *val = 0;
190 memcpy(val, &rm_ctxt->insn_buf[insn_off], bytes);
191 return X86EMUL_OKAY;
192 }
194 static int
195 realmode_emulate_write(
196 enum x86_segment seg,
197 unsigned long offset,
198 unsigned long val,
199 unsigned int bytes,
200 struct x86_emulate_ctxt *ctxt)
201 {
202 struct realmode_emulate_ctxt *rm_ctxt =
203 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
204 uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
205 int todo;
207 todo = hvm_copy_to_guest_phys(addr, &val, bytes);
209 if ( todo )
210 {
211 struct vcpu *curr = current;
213 if ( todo != bytes )
214 {
215 gdprintk(XENLOG_WARNING, "RM: Partial write at %08x (%d/%d)\n",
216 addr, todo, bytes);
217 return X86EMUL_UNHANDLEABLE;
218 }
220 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
221 return X86EMUL_UNHANDLEABLE;
223 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
224 send_mmio_req(IOREQ_TYPE_COPY, addr, 1, bytes,
225 val, IOREQ_WRITE, 0, 0);
226 }
228 return X86EMUL_OKAY;
229 }
231 static int
232 realmode_emulate_cmpxchg(
233 enum x86_segment seg,
234 unsigned long offset,
235 unsigned long old,
236 unsigned long new,
237 unsigned int bytes,
238 struct x86_emulate_ctxt *ctxt)
239 {
240 /* Fix this in case the guest is really relying on r-m-w atomicity. */
241 return realmode_emulate_write(seg, offset, new, bytes, ctxt);
242 }
244 static int
245 realmode_read_segment(
246 enum x86_segment seg,
247 struct segment_register *reg,
248 struct x86_emulate_ctxt *ctxt)
249 {
250 struct realmode_emulate_ctxt *rm_ctxt =
251 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
252 memcpy(reg, &rm_ctxt->seg_reg[seg], sizeof(struct segment_register));
253 return X86EMUL_OKAY;
254 }
256 static int
257 realmode_write_segment(
258 enum x86_segment seg,
259 struct segment_register *reg,
260 struct x86_emulate_ctxt *ctxt)
261 {
262 struct realmode_emulate_ctxt *rm_ctxt =
263 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
264 memcpy(&rm_ctxt->seg_reg[seg], reg, sizeof(struct segment_register));
265 if ( seg == x86_seg_ss )
266 rm_ctxt->flags.mov_ss = 1;
267 return X86EMUL_OKAY;
268 }
270 static int
271 realmode_read_io(
272 unsigned int port,
273 unsigned int bytes,
274 unsigned long *val,
275 struct x86_emulate_ctxt *ctxt)
276 {
277 struct vcpu *curr = current;
279 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
280 return X86EMUL_UNHANDLEABLE;
282 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
283 {
284 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
285 send_pio_req(port, 1, bytes, 0, IOREQ_READ, 0, 0);
286 }
288 if ( !curr->arch.hvm_vmx.real_mode_io_completed )
289 return X86EMUL_UNHANDLEABLE;
291 *val = curr->arch.hvm_vmx.real_mode_io_data;
292 curr->arch.hvm_vmx.real_mode_io_completed = 0;
294 return X86EMUL_OKAY;
295 }
297 static int realmode_write_io(
298 unsigned int port,
299 unsigned int bytes,
300 unsigned long val,
301 struct x86_emulate_ctxt *ctxt)
302 {
303 struct vcpu *curr = current;
305 if ( port == 0xe9 )
306 {
307 hvm_print_line(curr, val);
308 return X86EMUL_OKAY;
309 }
311 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
312 return X86EMUL_UNHANDLEABLE;
314 curr->arch.hvm_vmx.real_mode_io_in_progress = 1;
315 send_pio_req(port, 1, bytes, val, IOREQ_WRITE, 0, 0);
317 return X86EMUL_OKAY;
318 }
320 static int
321 realmode_read_cr(
322 unsigned int reg,
323 unsigned long *val,
324 struct x86_emulate_ctxt *ctxt)
325 {
326 switch ( reg )
327 {
328 case 0:
329 case 2:
330 case 3:
331 case 4:
332 *val = current->arch.hvm_vcpu.guest_cr[reg];
333 break;
334 default:
335 return X86EMUL_UNHANDLEABLE;
336 }
338 return X86EMUL_OKAY;
339 }
341 static int realmode_write_rflags(
342 unsigned long val,
343 struct x86_emulate_ctxt *ctxt)
344 {
345 struct realmode_emulate_ctxt *rm_ctxt =
346 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
347 if ( (val & X86_EFLAGS_IF) && !(ctxt->regs->eflags & X86_EFLAGS_IF) )
348 rm_ctxt->flags.sti = 1;
349 return X86EMUL_OKAY;
350 }
352 static int realmode_wbinvd(
353 struct x86_emulate_ctxt *ctxt)
354 {
355 vmx_wbinvd_intercept();
356 return X86EMUL_OKAY;
357 }
359 static int realmode_cpuid(
360 unsigned int *eax,
361 unsigned int *ebx,
362 unsigned int *ecx,
363 unsigned int *edx,
364 struct x86_emulate_ctxt *ctxt)
365 {
366 vmx_cpuid_intercept(eax, ebx, ecx, edx);
367 return X86EMUL_OKAY;
368 }
370 static int realmode_hlt(
371 struct x86_emulate_ctxt *ctxt)
372 {
373 struct realmode_emulate_ctxt *rm_ctxt =
374 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
375 rm_ctxt->flags.hlt = 1;
376 return X86EMUL_OKAY;
377 }
379 static int realmode_inject_hw_exception(
380 uint8_t vector,
381 struct x86_emulate_ctxt *ctxt)
382 {
383 struct realmode_emulate_ctxt *rm_ctxt =
384 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
386 rm_ctxt->flags.exn_raised = 1;
387 realmode_deliver_exception(vector, 0, rm_ctxt);
389 return X86EMUL_OKAY;
390 }
392 static int realmode_inject_sw_interrupt(
393 uint8_t vector,
394 uint8_t insn_len,
395 struct x86_emulate_ctxt *ctxt)
396 {
397 struct realmode_emulate_ctxt *rm_ctxt =
398 container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
400 realmode_deliver_exception(vector, insn_len, rm_ctxt);
402 return X86EMUL_OKAY;
403 }
405 static struct x86_emulate_ops realmode_emulator_ops = {
406 .read = realmode_emulate_read,
407 .insn_fetch = realmode_emulate_insn_fetch,
408 .write = realmode_emulate_write,
409 .cmpxchg = realmode_emulate_cmpxchg,
410 .read_segment = realmode_read_segment,
411 .write_segment = realmode_write_segment,
412 .read_io = realmode_read_io,
413 .write_io = realmode_write_io,
414 .read_cr = realmode_read_cr,
415 .write_rflags = realmode_write_rflags,
416 .wbinvd = realmode_wbinvd,
417 .cpuid = realmode_cpuid,
418 .hlt = realmode_hlt,
419 .inject_hw_exception = realmode_inject_hw_exception,
420 .inject_sw_interrupt = realmode_inject_sw_interrupt
421 };
423 void vmx_realmode(struct cpu_user_regs *regs)
424 {
425 struct vcpu *curr = current;
426 struct realmode_emulate_ctxt rm_ctxt;
427 unsigned long intr_info;
428 int i, rc;
429 u32 intr_shadow, new_intr_shadow;
431 rm_ctxt.ctxt.regs = regs;
433 for ( i = 0; i < 10; i++ )
434 hvm_get_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
436 rm_ctxt.ctxt.addr_size =
437 rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
438 rm_ctxt.ctxt.sp_size =
439 rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
441 intr_info = __vmread(VM_ENTRY_INTR_INFO);
442 if ( intr_info & INTR_INFO_VALID_MASK )
443 {
444 __vmwrite(VM_ENTRY_INTR_INFO, 0);
445 realmode_deliver_exception((uint8_t)intr_info, 0, &rm_ctxt);
446 }
448 intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
449 new_intr_shadow = intr_shadow;
451 while ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) &&
452 !softirq_pending(smp_processor_id()) &&
453 !hvm_local_events_need_delivery(curr) )
454 {
455 rm_ctxt.insn_buf_eip = regs->eip;
456 (void)hvm_copy_from_guest_phys(
457 rm_ctxt.insn_buf,
458 (uint32_t)(rm_ctxt.seg_reg[x86_seg_cs].base + regs->eip),
459 sizeof(rm_ctxt.insn_buf));
461 rm_ctxt.flag_word = 0;
463 rc = x86_emulate(&rm_ctxt.ctxt, &realmode_emulator_ops);
465 /* MOV-SS instruction toggles MOV-SS shadow, else we just clear it. */
466 if ( rm_ctxt.flags.mov_ss )
467 new_intr_shadow ^= VMX_INTR_SHADOW_MOV_SS;
468 else
469 new_intr_shadow &= ~VMX_INTR_SHADOW_MOV_SS;
471 /* STI instruction toggles STI shadow, else we just clear it. */
472 if ( rm_ctxt.flags.sti )
473 new_intr_shadow ^= VMX_INTR_SHADOW_STI;
474 else
475 new_intr_shadow &= ~VMX_INTR_SHADOW_STI;
477 /* Update interrupt shadow information in VMCS only if it changes. */
478 if ( intr_shadow != new_intr_shadow )
479 {
480 intr_shadow = new_intr_shadow;
481 __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow);
482 }
484 /* HLT happens after instruction retire, if no interrupt/exception. */
485 if ( unlikely(rm_ctxt.flags.hlt) &&
486 !rm_ctxt.flags.exn_raised &&
487 !hvm_local_events_need_delivery(curr) )
488 hvm_hlt(regs->eflags);
490 if ( curr->arch.hvm_vmx.real_mode_io_in_progress )
491 break;
493 if ( rc == X86EMUL_UNHANDLEABLE )
494 {
495 gdprintk(XENLOG_DEBUG,
496 "RM %04x:%08lx: %02x %02x %02x %02x %02x %02x\n",
497 rm_ctxt.seg_reg[x86_seg_cs].sel, rm_ctxt.insn_buf_eip,
498 rm_ctxt.insn_buf[0], rm_ctxt.insn_buf[1],
499 rm_ctxt.insn_buf[2], rm_ctxt.insn_buf[3],
500 rm_ctxt.insn_buf[4], rm_ctxt.insn_buf[5]);
501 gdprintk(XENLOG_ERR, "Emulation failed\n");
502 domain_crash_synchronous();
503 }
504 }
506 for ( i = 0; i < 10; i++ )
507 hvm_set_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
508 }
510 int vmx_realmode_io_complete(void)
511 {
512 struct vcpu *curr = current;
513 ioreq_t *p = &get_ioreq(curr)->vp_ioreq;
515 if ( !curr->arch.hvm_vmx.real_mode_io_in_progress )
516 return 0;
518 #if 0
519 gdprintk(XENLOG_DEBUG, "RM I/O %d %c bytes=%d addr=%lx data=%lx\n",
520 p->type, p->dir ? 'R' : 'W',
521 (int)p->size, (long)p->addr, (long)p->data);
522 #endif
524 curr->arch.hvm_vmx.real_mode_io_in_progress = 0;
525 if ( p->dir == IOREQ_READ )
526 {
527 curr->arch.hvm_vmx.real_mode_io_completed = 1;
528 curr->arch.hvm_vmx.real_mode_io_data = p->data;
529 }
531 return 1;
532 }