[xen-unstable] x86_emulate: Allow emulated injection of exceptions and interrupts.

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[xen-unstable] x86_emulate: Allow emulated injection of exceptions and interrupts.

Xen patchbot-unstable
# HG changeset patch
# User Keir Fraser <[hidden email]>
# Date 1196013910 0
# Node ID f6a587e3d5c981b7cea8c3e97d234e14e3ca81bb
# Parent  d40788f07a4f40ac95c1f297f808c63b85baa3ff
x86_emulate: Allow emulated injection of exceptions and interrupts.
Signed-off-by: Keir Fraser <[hidden email]>
---
 xen/arch/x86/hvm/vmx/realmode.c   |  182 +++++++++++++++++++++++++-------------
 xen/arch/x86/x86_emulate.c        |   46 ++++++++-
 xen/include/asm-x86/x86_emulate.h |   11 ++
 3 files changed, 174 insertions(+), 65 deletions(-)

diff -r d40788f07a4f -r f6a587e3d5c9 xen/arch/x86/hvm/vmx/realmode.c
--- a/xen/arch/x86/hvm/vmx/realmode.c Sun Nov 25 12:43:13 2007 +0000
+++ b/xen/arch/x86/hvm/vmx/realmode.c Sun Nov 25 18:05:10 2007 +0000
@@ -13,6 +13,7 @@
 #include <xen/init.h>
 #include <xen/lib.h>
 #include <xen/sched.h>
+#include <asm/event.h>
 #include <asm/hvm/hvm.h>
 #include <asm/hvm/support.h>
 #include <asm/hvm/vmx/vmx.h>
@@ -23,35 +24,78 @@ struct realmode_emulate_ctxt {
 struct realmode_emulate_ctxt {
     struct x86_emulate_ctxt ctxt;
 
-    /* Cache of up to 31 bytes of instruction. */
-    uint8_t insn_buf[31];
-    uint8_t insn_buf_bytes;
+    /* Cache of 16 bytes of instruction. */
+    uint8_t insn_buf[16];
     unsigned long insn_buf_eip;
 
     struct segment_register seg_reg[10];
 };
 
-static int realmode_translate_linear_addr(
-    enum x86_segment seg,
-    unsigned long offset,
-    unsigned int bytes,
-    enum hvm_access_type access_type,
-    struct realmode_emulate_ctxt *rm_ctxt,
-    unsigned long *paddr)
-{
-    struct segment_register *reg = &rm_ctxt->seg_reg[seg];
-    int okay;
-
-    okay = hvm_virtual_to_linear_addr(
-        seg, reg, offset, bytes, access_type, rm_ctxt->ctxt.addr_size, paddr);
-
-    if ( !okay )
-    {
-        hvm_inject_exception(TRAP_gp_fault, 0, 0);
-        return X86EMUL_EXCEPTION;
-    }
-
-    return 0;
+static void realmode_deliver_exception(
+    unsigned int vector,
+    unsigned int insn_len,
+    struct realmode_emulate_ctxt *rm_ctxt)
+{
+    struct segment_register *idtr = &rm_ctxt->seg_reg[x86_seg_idtr];
+    struct segment_register *csr = &rm_ctxt->seg_reg[x86_seg_cs];
+    struct cpu_user_regs *regs = rm_ctxt->ctxt.regs;
+    uint32_t cs_eip, pstk;
+    uint16_t frame[3];
+    unsigned int last_byte;
+
+ again:
+    last_byte = (vector * 4) + 3;
+    if ( idtr->limit < last_byte )
+    {
+        /* Software interrupt? */
+        if ( insn_len != 0 )
+        {
+            insn_len = 0;
+            vector = TRAP_gp_fault;
+            goto again;
+        }
+
+        /* Exception or hardware interrupt. */
+        switch ( vector )
+        {
+        case TRAP_double_fault:
+            hvm_triple_fault();
+            return;
+        case TRAP_gp_fault:
+            vector = TRAP_double_fault;
+            goto again;
+        default:
+            vector = TRAP_gp_fault;
+            goto again;
+        }
+    }
+
+    (void)hvm_copy_from_guest_phys(&cs_eip, idtr->base + vector * 4, 4);
+
+    frame[0] = regs->eip + insn_len;
+    frame[1] = csr->sel;
+    frame[2] = regs->eflags & ~X86_EFLAGS_RF;
+
+    if ( rm_ctxt->ctxt.addr_size == 32 )
+    {
+        regs->esp -= 4;
+        pstk = regs->esp;
+    }
+    else
+    {
+        pstk = (uint16_t)(regs->esp - 4);
+        regs->esp &= ~0xffff;
+        regs->esp |= pstk;
+    }
+
+    pstk += rm_ctxt->seg_reg[x86_seg_ss].base;
+    (void)hvm_copy_to_guest_phys(pstk, frame, sizeof(frame));
+
+    csr->sel  = cs_eip >> 16;
+    csr->base = (uint32_t)csr->sel << 4;
+    regs->eip = (uint16_t)cs_eip;
+    regs->eflags &= ~(X86_EFLAGS_AC | X86_EFLAGS_TF |
+                      X86_EFLAGS_AC | X86_EFLAGS_RF);
 }
 
 static int
@@ -63,14 +107,7 @@ realmode_read(
     enum hvm_access_type access_type,
     struct realmode_emulate_ctxt *rm_ctxt)
 {
-    unsigned long addr;
-    int rc;
-
-    rc = realmode_translate_linear_addr(
-        seg, offset, bytes, access_type, rm_ctxt, &addr);
-    if ( rc )
-        return rc;
-
+    uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
     *val = 0;
     (void)hvm_copy_from_guest_phys(val, addr, bytes);
     return X86EMUL_OKAY;
@@ -102,7 +139,7 @@ realmode_emulate_insn_fetch(
     unsigned int insn_off = offset - rm_ctxt->insn_buf_eip;
 
     /* Fall back if requested bytes are not in the prefetch cache. */
-    if ( unlikely((insn_off + bytes) > rm_ctxt->insn_buf_bytes) )
+    if ( unlikely((insn_off + bytes) > sizeof(rm_ctxt->insn_buf)) )
         return realmode_read(
             seg, offset, val, bytes,
             hvm_access_insn_fetch, rm_ctxt);
@@ -123,14 +160,7 @@ realmode_emulate_write(
 {
     struct realmode_emulate_ctxt *rm_ctxt =
         container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
-    unsigned long addr;
-    int rc;
-
-    rc = realmode_translate_linear_addr(
-        seg, offset, bytes, hvm_access_write, rm_ctxt, &addr);
-    if ( rc )
-        return rc;
-
+    uint32_t addr = rm_ctxt->seg_reg[seg].base + offset;
     (void)hvm_copy_to_guest_phys(addr, &val, bytes);
     return X86EMUL_OKAY;
 }
@@ -254,6 +284,31 @@ static int realmode_write_rflags(
         intr_shadow ^= VMX_INTR_SHADOW_STI;
         __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow);
     }
+
+    return X86EMUL_OKAY;
+}
+
+static int realmode_inject_hw_exception(
+    uint8_t vector,
+    struct x86_emulate_ctxt *ctxt)
+{
+    struct realmode_emulate_ctxt *rm_ctxt =
+        container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
+
+    realmode_deliver_exception(vector, 0, rm_ctxt);
+
+    return X86EMUL_OKAY;
+}
+
+static int realmode_inject_sw_interrupt(
+    uint8_t vector,
+    uint8_t insn_len,
+    struct x86_emulate_ctxt *ctxt)
+{
+    struct realmode_emulate_ctxt *rm_ctxt =
+        container_of(ctxt, struct realmode_emulate_ctxt, ctxt);
+
+    realmode_deliver_exception(vector, insn_len, rm_ctxt);
 
     return X86EMUL_OKAY;
 }
@@ -268,37 +323,44 @@ static struct x86_emulate_ops realmode_e
     .read_io       = realmode_read_io,
     .write_io      = realmode_write_io,
     .read_cr       = realmode_read_cr,
-    .write_rflags  = realmode_write_rflags
+    .write_rflags  = realmode_write_rflags,
+    .inject_hw_exception = realmode_inject_hw_exception,
+    .inject_sw_interrupt = realmode_inject_sw_interrupt
 };
 
 int vmx_realmode(struct cpu_user_regs *regs)
 {
     struct vcpu *curr = current;
     struct realmode_emulate_ctxt rm_ctxt;
-    unsigned long addr;
+    unsigned long intr_info;
     int i, rc = 0;
+
+    rm_ctxt.ctxt.regs = regs;
 
     for ( i = 0; i < 10; i++ )
         hvm_get_segment_register(curr, i, &rm_ctxt.seg_reg[i]);
 
+    rm_ctxt.ctxt.addr_size =
+        rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
+    rm_ctxt.ctxt.sp_size =
+        rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
+
+    intr_info = __vmread(VM_ENTRY_INTR_INFO);
+    if ( intr_info & INTR_INFO_VALID_MASK )
+    {
+        __vmwrite(VM_ENTRY_INTR_INFO, 0);
+        realmode_deliver_exception((uint8_t)intr_info, 0, &rm_ctxt);
+    }
+
     while ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) &&
-            !softirq_pending(smp_processor_id()) )
-    {
-        rm_ctxt.ctxt.regs = regs;
-        rm_ctxt.ctxt.addr_size =
-            rm_ctxt.seg_reg[x86_seg_cs].attr.fields.db ? 32 : 16;
-        rm_ctxt.ctxt.sp_size =
-            rm_ctxt.seg_reg[x86_seg_ss].attr.fields.db ? 32 : 16;
-
+            !softirq_pending(smp_processor_id()) &&
+            !hvm_local_events_need_delivery(curr) )
+    {
         rm_ctxt.insn_buf_eip = regs->eip;
-        rm_ctxt.insn_buf_bytes =
-            (hvm_virtual_to_linear_addr(
-                x86_seg_cs, &rm_ctxt.seg_reg[x86_seg_cs],
-                regs->eip, sizeof(rm_ctxt.insn_buf),
-                hvm_access_insn_fetch, rm_ctxt.ctxt.addr_size, &addr) &&
-             !hvm_copy_from_guest_virt(
-                 rm_ctxt.insn_buf, addr, sizeof(rm_ctxt.insn_buf)))
-            ? sizeof(rm_ctxt.insn_buf) : 0;
+        (void)hvm_copy_from_guest_phys(
+            rm_ctxt.insn_buf,
+            (uint32_t)(rm_ctxt.seg_reg[x86_seg_cs].base + regs->eip),
+            sizeof(rm_ctxt.insn_buf));
 
         rc = x86_emulate(&rm_ctxt.ctxt, &realmode_emulator_ops);
 
@@ -308,7 +370,7 @@ int vmx_realmode(struct cpu_user_regs *r
             break;
         }
 
-        if ( rc )
+        if ( rc == X86EMUL_UNHANDLEABLE )
         {
             gdprintk(XENLOG_DEBUG,
                      "RM %04x:%08lx: %02x %02x %02x %02x %02x %02x\n",
diff -r d40788f07a4f -r f6a587e3d5c9 xen/arch/x86/x86_emulate.c
--- a/xen/arch/x86/x86_emulate.c Sun Nov 25 12:43:13 2007 +0000
+++ b/xen/arch/x86/x86_emulate.c Sun Nov 25 18:05:10 2007 +0000
@@ -149,7 +149,7 @@ static uint8_t opcode_table[256] = {
     ImplicitOps, ImplicitOps,
     0, 0, ByteOp|DstMem|SrcImm|ModRM|Mov, DstMem|SrcImm|ModRM|Mov,
     /* 0xC8 - 0xCF */
-    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, ImplicitOps, ImplicitOps, ImplicitOps, 0,
     /* 0xD0 - 0xD7 */
     ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
     ByteOp|DstMem|SrcImplicit|ModRM, DstMem|SrcImplicit|ModRM,
@@ -163,7 +163,7 @@ static uint8_t opcode_table[256] = {
     ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
     ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
     /* 0xF0 - 0xF7 */
-    0, 0, 0, 0,
+    0, ImplicitOps, 0, 0,
     0, ImplicitOps, ByteOp|DstMem|SrcNone|ModRM, DstMem|SrcNone|ModRM,
     /* 0xF8 - 0xFF */
     ImplicitOps, ImplicitOps, ImplicitOps, ImplicitOps,
@@ -270,6 +270,7 @@ struct operand {
 #define EFLG_OF (1<<11)
 #define EFLG_DF (1<<10)
 #define EFLG_IF (1<<9)
+#define EFLG_TF (1<<8)
 #define EFLG_SF (1<<7)
 #define EFLG_ZF (1<<6)
 #define EFLG_AF (1<<4)
@@ -278,6 +279,9 @@ struct operand {
 
 /* Exception definitions. */
 #define EXC_DE  0
+#define EXC_DB  1
+#define EXC_BP  3
+#define EXC_OF  4
 #define EXC_BR  5
 #define EXC_UD  6
 #define EXC_GP 13
@@ -477,8 +481,13 @@ do {                                    
     if ( rc ) goto done;                                \
 } while (0)
 
-/* In future we will be able to generate arbitrary exceptions. */
-#define generate_exception_if(p, e) fail_if(p)
+#define generate_exception_if(p, e)                                     \
+({  if ( (p) ) {                                                        \
+        fail_if(ops->inject_hw_exception == NULL);                      \
+        rc = ops->inject_hw_exception(e, ctxt) ? : X86EMUL_EXCEPTION;   \
+        goto done;                                                      \
+    }                                                                   \
+})
 
 /* Given byte has even parity (even number of 1s)? */
 static int even_parity(uint8_t v)
@@ -1771,7 +1780,11 @@ x86_emulate(
     /* Commit shadow register state. */
     _regs.eflags &= ~EFLG_RF;
     *ctxt->regs = _regs;
-    /* FIXME generate_exception_if(_regs.eflags & EFLG_TF, EXC_DB); */
+
+    if ( (_regs.eflags & EFLG_TF) &&
+         (rc == X86EMUL_OKAY) &&
+         (ops->inject_hw_exception != NULL) )
+        rc = ops->inject_hw_exception(EXC_DB, ctxt) ? : X86EMUL_EXCEPTION;
 
  done:
     return rc;
@@ -2152,6 +2165,25 @@ x86_emulate(
         break;
     }
 
+    case 0xcc: /* int3 */
+        src.val = EXC_BP;
+        goto swint;
+
+    case 0xcd: /* int imm8 */
+        src.val = insn_fetch_type(uint8_t);
+    swint:
+        fail_if(ops->inject_sw_interrupt == NULL);
+        rc = ops->inject_sw_interrupt(src.val, _regs.eip - ctxt->regs->eip,
+                                      ctxt) ? : X86EMUL_EXCEPTION;
+        goto done;
+
+    case 0xce: /* into */
+        generate_exception_if(mode_64bit(), EXC_UD);
+        if ( !(_regs.eflags & EFLG_OF) )
+            break;
+        src.val = EXC_OF;
+        goto swint;
+
     case 0xd4: /* aam */ {
         unsigned int base = insn_fetch_type(uint8_t);
         uint8_t al = _regs.eax;
@@ -2291,6 +2323,10 @@ x86_emulate(
     case 0xeb: /* jmp (short) */
         jmp_rel(insn_fetch_type(int8_t));
         break;
+
+    case 0xf1: /* int1 (icebp) */
+        src.val = EXC_DB;
+        goto swint;
 
     case 0xf5: /* cmc */
         _regs.eflags ^= EFLG_CF;
diff -r d40788f07a4f -r f6a587e3d5c9 xen/include/asm-x86/x86_emulate.h
--- a/xen/include/asm-x86/x86_emulate.h Sun Nov 25 12:43:13 2007 +0000
+++ b/xen/include/asm-x86/x86_emulate.h Sun Nov 25 18:05:10 2007 +0000
@@ -274,6 +274,17 @@ struct x86_emulate_ops
     /* wbinvd: Write-back and invalidate cache contents. */
     int (*wbinvd)(
         struct x86_emulate_ctxt *ctxt);
+
+    /* inject_hw_exception */
+    int (*inject_hw_exception)(
+        uint8_t vector,
+        struct x86_emulate_ctxt *ctxt);
+
+    /* inject_sw_interrupt */
+    int (*inject_sw_interrupt)(
+        uint8_t vector,
+        uint8_t insn_len,
+        struct x86_emulate_ctxt *ctxt);
 };
 
 struct cpu_user_regs;

_______________________________________________
Xen-changelog mailing list
[hidden email]
http://lists.xensource.com/xen-changelog