[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |