[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] [XEN] Emulate IN/OUT instructions with full guest GPR context.



# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Node ID ad7b60a1db8c25a592e222263f6251e68f2e67eb
# Parent  9a341c6ef6ae2ce90ccdcf89718d4365426d9d96
[XEN] Emulate IN/OUT instructions with full guest GPR context.
Based on a patch by Jan Beulich <jbeulich@xxxxxxxxxx>
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
 xen/arch/x86/traps.c             |  119 ++++++++++++++++++++++++++-------------
 xen/arch/x86/x86_32/Makefile     |    1 
 xen/arch/x86/x86_32/gpr_switch.S |   43 ++++++++++++++
 xen/arch/x86/x86_64/Makefile     |    1 
 xen/arch/x86/x86_64/gpr_switch.S |   63 ++++++++++++++++++++
 5 files changed, 189 insertions(+), 38 deletions(-)

diff -r 9a341c6ef6ae -r ad7b60a1db8c xen/arch/x86/traps.c
--- a/xen/arch/x86/traps.c      Mon Nov 13 14:25:48 2006 +0000
+++ b/xen/arch/x86/traps.c      Mon Nov 13 16:19:38 2006 +0000
@@ -985,8 +985,7 @@ static inline int admin_io_okay(
     return ioports_access_permitted(v->domain, port, port + bytes - 1);
 }
 
-/* Check admin limits. Silently fail the access if it is disallowed. */
-static inline unsigned char inb_user(
+static inline int guest_inb_okay(
     unsigned int port, struct vcpu *v, struct cpu_user_regs *regs)
 {
     /*
@@ -996,19 +995,21 @@ static inline unsigned char inb_user(
      * Note that we could emulate bit 4 instead of directly reading port 0x61,
      * but there's not really a good reason to do so.
      */
-    if ( admin_io_okay(port, 1, v, regs) || (port == 0x61) )
-        return inb(port);
-    return ~0;
-}
-//#define inb_user(_p, _d, _r) (admin_io_okay(_p, 1, _d, _r) ? inb(_p) : ~0)
-#define inw_user(_p, _d, _r) (admin_io_okay(_p, 2, _d, _r) ? inw(_p) : ~0)
-#define inl_user(_p, _d, _r) (admin_io_okay(_p, 4, _d, _r) ? inl(_p) : ~0)
-#define outb_user(_v, _p, _d, _r) \
-    (admin_io_okay(_p, 1, _d, _r) ? outb(_v, _p) : ((void)0))
-#define outw_user(_v, _p, _d, _r) \
-    (admin_io_okay(_p, 2, _d, _r) ? outw(_v, _p) : ((void)0))
-#define outl_user(_v, _p, _d, _r) \
-    (admin_io_okay(_p, 4, _d, _r) ? outl(_v, _p) : ((void)0))
+    return (admin_io_okay(port, 1, v, regs) || (port == 0x61));
+}
+#define guest_inw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)
+#define guest_inl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)
+#define guest_outb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)
+#define guest_outw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)
+#define guest_outl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)
+
+/* I/O emulation support. Helper routines for, and type of, the stack stub.*/
+void host_to_guest_gpr_switch(struct cpu_user_regs *)
+    __attribute__((__regparm__(1)));
+unsigned long guest_to_host_gpr_switch(unsigned long)
+    __attribute__((__regparm__(1)));
+typedef unsigned long (*io_emul_stub_t)(struct cpu_user_regs *)
+    __attribute__((__regparm__(1)));
 
 /* Instruction fetch with error handling. */
 #define insn_fetch(_type, _size, cs, eip)                                   \
@@ -1028,6 +1029,7 @@ static int emulate_privileged_op(struct 
     unsigned long *reg, eip = regs->eip, cs = regs->cs, res;
     u8 opcode, modrm_reg = 0, modrm_rm = 0, rep_prefix = 0;
     unsigned int port, i, op_bytes = 4, data, rc;
+    char io_emul_stub[16];
     u32 l, h;
 
     /* Legacy prefixes. */
@@ -1068,6 +1070,9 @@ static int emulate_privileged_op(struct 
         opcode = insn_fetch(u8, 1, cs, eip);
     }
 #endif
+
+    if ( opcode == 0x0f )
+        goto twobyte_opcode;
     
     /* Input/Output String instructions. */
     if ( (opcode >= 0x6c) && (opcode <= 0x6f) )
@@ -1083,16 +1088,17 @@ static int emulate_privileged_op(struct 
         case 0x6d: /* INSW/INSL */
             if ( !guest_io_okay((u16)regs->edx, op_bytes, v, regs) )
                 goto fail;
+            port = (u16)regs->edx;
             switch ( op_bytes )
             {
             case 1:
-                data = (u8)inb_user((u16)regs->edx, v, regs);
+                data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) : ~0);
                 break;
             case 2:
-                data = (u16)inw_user((u16)regs->edx, v, regs);
+                data = (u16)(guest_inw_okay(port, v, regs) ? inw(port) : ~0);
                 break;
             case 4:
-                data = (u32)inl_user((u16)regs->edx, v, regs);
+                data = (u32)(guest_inl_okay(port, v, regs) ? inl(port) : ~0);
                 break;
             }
             if ( (rc = copy_to_user((void *)regs->edi, &data, op_bytes)) != 0 )
@@ -1115,16 +1121,20 @@ static int emulate_privileged_op(struct 
                 propagate_page_fault(regs->esi + op_bytes - rc, 0);
                 return EXCRET_fault_fixed;
             }
+            port = (u16)regs->edx;
             switch ( op_bytes )
             {
             case 1:
-                outb_user((u8)data, (u16)regs->edx, v, regs);
+                if ( guest_outb_okay(port, v, regs) )
+                    outb((u8)data, port);
                 break;
             case 2:
-                outw_user((u16)data, (u16)regs->edx, v, regs);
+                if ( guest_outw_okay(port, v, regs) )
+                    outw((u16)data, port);
                 break;
             case 4:
-                outl_user((u32)data, (u16)regs->edx, v, regs);
+                if ( guest_outl_okay(port, v, regs) )
+                    outl((u32)data, port);
                 break;
             }
             regs->esi += (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes);
@@ -1140,6 +1150,27 @@ static int emulate_privileged_op(struct 
 
         goto done;
     }
+
+    /*
+     * Very likely to be an I/O instruction (IN/OUT).
+     * Build an on-stack stub to execute the instruction with full guest
+     * GPR context. This is needed for some systems which (ab)use IN/OUT
+     * to communicate with BIOS code in system-management mode.
+     */
+    /* call host_to_guest_gpr_switch */
+    io_emul_stub[0] = 0xe8;
+    *(s32 *)&io_emul_stub[1] =
+        (char *)host_to_guest_gpr_switch - &io_emul_stub[5];
+    /* data16 or nop */
+    io_emul_stub[5] = (op_bytes != 2) ? 0x90 : 0x66;
+    /* <io-access opcode> */
+    io_emul_stub[6] = opcode;
+    /* imm8 or nop */
+    io_emul_stub[7] = 0x90;
+    /* jmp guest_to_host_gpr_switch */
+    io_emul_stub[8] = 0xe9;
+    *(s32 *)&io_emul_stub[9] =
+        (char *)guest_to_host_gpr_switch - &io_emul_stub[13];
 
     /* I/O Port and Interrupt Flag instructions. */
     switch ( opcode )
@@ -1148,21 +1179,31 @@ static int emulate_privileged_op(struct 
         op_bytes = 1;
     case 0xe5: /* IN imm8,%eax */
         port = insn_fetch(u8, 1, cs, eip);
+        io_emul_stub[7] = port; /* imm8 */
     exec_in:
         if ( !guest_io_okay(port, op_bytes, v, regs) )
             goto fail;
         switch ( op_bytes )
         {
         case 1:
-            regs->eax &= ~0xffUL;
-            regs->eax |= (u8)inb_user(port, v, regs);
+            res = regs->eax & ~0xffUL;
+            if ( guest_inb_okay(port, v, regs) )
+                regs->eax = res | (u8)((io_emul_stub_t)io_emul_stub)(regs);
+            else
+                regs->eax = res | (u8)~0;
             break;
         case 2:
-            regs->eax &= ~0xffffUL;
-            regs->eax |= (u16)inw_user(port, v, regs);
+            res = regs->eax & ~0xffffUL;
+            if ( guest_inw_okay(port, v, regs) )
+                regs->eax = res | (u16)((io_emul_stub_t)io_emul_stub)(regs);
+            else
+                regs->eax = res | (u16)~0;
             break;
         case 4:
-            regs->eax = (u32)inl_user(port, v, regs);
+            if ( guest_inl_okay(port, v, regs) )
+                regs->eax = (u32)((io_emul_stub_t)io_emul_stub)(regs);
+            else
+                regs->eax = (u32)~0;
             break;
         }
         goto done;
@@ -1177,19 +1218,23 @@ static int emulate_privileged_op(struct 
         op_bytes = 1;
     case 0xe7: /* OUT %eax,imm8 */
         port = insn_fetch(u8, 1, cs, eip);
+        io_emul_stub[7] = port; /* imm8 */
     exec_out:
         if ( !guest_io_okay(port, op_bytes, v, regs) )
             goto fail;
         switch ( op_bytes )
         {
         case 1:
-            outb_user((u8)regs->eax, port, v, regs);
+            if ( guest_outb_okay(port, v, regs) )
+                ((io_emul_stub_t)io_emul_stub)(regs);
             break;
         case 2:
-            outw_user((u16)regs->eax, port, v, regs);
+            if ( guest_outw_okay(port, v, regs) )
+                ((io_emul_stub_t)io_emul_stub)(regs);
             break;
         case 4:
-            outl_user((u32)regs->eax, port, v, regs);
+            if ( guest_outl_okay(port, v, regs) )
+                ((io_emul_stub_t)io_emul_stub)(regs);
             break;
         }
         goto done;
@@ -1212,15 +1257,13 @@ static int emulate_privileged_op(struct 
          */
         /*v->vcpu_info->evtchn_upcall_mask = (opcode == 0xfa);*/
         goto done;
-
-    case 0x0f: /* Two-byte opcode */
-        break;
-
-    default:
-        goto fail;
-    }
-
-    /* Remaining instructions only emulated from guest kernel. */
+    }
+
+    /* No decode of this single-byte opcode. */
+    goto fail;
+
+ twobyte_opcode:
+    /* Two-byte opcodes only emulated from guest kernel. */
     if ( !guest_kernel_mode(v, regs) )
         goto fail;
 
diff -r 9a341c6ef6ae -r ad7b60a1db8c xen/arch/x86/x86_32/Makefile
--- a/xen/arch/x86/x86_32/Makefile      Mon Nov 13 14:25:48 2006 +0000
+++ b/xen/arch/x86/x86_32/Makefile      Mon Nov 13 16:19:38 2006 +0000
@@ -1,5 +1,6 @@ obj-y += domain_page.o
 obj-y += domain_page.o
 obj-y += entry.o
+obj-y += gpr_switch.o
 obj-y += mm.o
 obj-y += seg_fixup.o
 obj-y += traps.o
diff -r 9a341c6ef6ae -r ad7b60a1db8c xen/arch/x86/x86_64/Makefile
--- a/xen/arch/x86/x86_64/Makefile      Mon Nov 13 14:25:48 2006 +0000
+++ b/xen/arch/x86/x86_64/Makefile      Mon Nov 13 16:19:38 2006 +0000
@@ -1,3 +1,4 @@ obj-y += entry.o
 obj-y += entry.o
+obj-y += gpr_switch.o
 obj-y += mm.o
 obj-y += traps.o
diff -r 9a341c6ef6ae -r ad7b60a1db8c xen/arch/x86/x86_32/gpr_switch.S
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/x86_32/gpr_switch.S  Mon Nov 13 16:19:38 2006 +0000
@@ -0,0 +1,43 @@
+/*
+ * GPR context switch between host and guest.
+ * Used by IO-port-access emulation stub.
+ *
+ * Copyright (c) 2006, Novell, Inc.
+ */
+
+#include <xen/config.h>
+#include <asm/asm_defns.h>
+
+ENTRY(host_to_guest_gpr_switch)
+        movl  (%esp), %ecx
+        movl  %eax, (%esp)
+        movl  UREGS_edx(%eax), %edx
+        pushl %ebx
+        movl  UREGS_ebx(%eax), %ebx
+        pushl %ebp
+        movl  UREGS_ebp(%eax), %ebp
+        pushl %esi
+        movl  UREGS_esi(%eax), %esi
+        pushl %edi
+        movl  UREGS_edi(%eax), %edi
+        pushl %ecx
+        movl  UREGS_ecx(%eax), %ecx
+        movl  UREGS_eax(%eax), %eax
+        ret
+
+ENTRY(guest_to_host_gpr_switch)
+        pushl %edx
+        movl  5*4(%esp), %edx
+        movl  %eax, UREGS_eax(%edx)
+        popl  UREGS_edx(%edx)
+        movl  %edi, UREGS_edi(%edx)
+        popl  %edi
+        movl  %esi, UREGS_esi(%edx)
+        popl  %esi
+        movl  %ebp, UREGS_ebp(%edx)
+        popl  %ebp
+        movl  %ebx, UREGS_ebx(%edx)
+        popl  %ebx
+        movl  %ecx, UREGS_ecx(%edx)
+        popl  %ecx
+        ret
diff -r 9a341c6ef6ae -r ad7b60a1db8c xen/arch/x86/x86_64/gpr_switch.S
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/x86_64/gpr_switch.S  Mon Nov 13 16:19:38 2006 +0000
@@ -0,0 +1,63 @@
+/*
+ * GPR context switch between host and guest.
+ * Used by IO-port-access emulation stub.
+ *
+ * Copyright (c) 2006, Novell, Inc.
+ */
+
+#include <xen/config.h>
+#include <asm/asm_defns.h>
+
+ENTRY(host_to_guest_gpr_switch)
+        movq  (%rsp), %rcx
+        movq  %rdi, (%rsp)
+        movq  UREGS_rdx(%rdi), %rdx
+        pushq %rbx
+        movq  UREGS_rax(%rdi), %rax
+        movq  UREGS_rbx(%rdi), %rbx
+        pushq %rbp
+        movq  UREGS_rsi(%rdi), %rsi
+        movq  UREGS_rbp(%rdi), %rbp
+        pushq %r12
+        movq  UREGS_r8(%rdi), %r8
+        movq  UREGS_r12(%rdi), %r12
+        pushq %r13
+        movq  UREGS_r9(%rdi), %r9
+        movq  UREGS_r13(%rdi), %r13
+        pushq %r14
+        movq  UREGS_r10(%rdi), %r10
+        movq  UREGS_r14(%rdi), %r14
+        pushq %r15
+        movq  UREGS_r11(%rdi), %r11
+        movq  UREGS_r15(%rdi), %r15
+        pushq %rcx
+        movq  UREGS_rcx(%rdi), %rcx
+        movq  UREGS_rdi(%rdi), %rdi
+        ret
+
+ENTRY(guest_to_host_gpr_switch)
+        pushq %rdi
+        movq  7*8(%rsp), %rdi
+        movq  %rax, UREGS_rax(%rdi)
+        popq  UREGS_rdi(%rdi)
+        movq  %r15, UREGS_r15(%rdi)
+        movq  %r11, UREGS_r11(%rdi)
+        popq  %r15
+        movq  %r14, UREGS_r14(%rdi)
+        movq  %r10, UREGS_r10(%rdi)
+        popq  %r14
+        movq  %r13, UREGS_r13(%rdi)
+        movq  %r9, UREGS_r9(%rdi)
+        popq  %r13
+        movq  %r12, UREGS_r12(%rdi)
+        movq  %r8, UREGS_r8(%rdi)
+        popq  %r12
+        movq  %rbp, UREGS_rbp(%rdi)
+        movq  %rsi, UREGS_rsi(%rdi)
+        popq  %rbp
+        movq  %rbx, UREGS_rbx(%rdi)
+        movq  %rdx, UREGS_rdx(%rdi)
+        popq  %rbx
+        movq  %rcx, UREGS_rcx(%rdi)
+        popq  %rcx
+        ret

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.