# 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
|