ia64/xen-unstable
changeset 12428:ad7b60a1db8c
[XEN] Emulate IN/OUT instructions with full guest GPR context.
Based on a patch by Jan Beulich <jbeulich@novell.com>
Signed-off-by: Keir Fraser <keir@xensource.com>
Based on a patch by Jan Beulich <jbeulich@novell.com>
Signed-off-by: Keir Fraser <keir@xensource.com>
author | kfraser@localhost.localdomain |
---|---|
date | Mon Nov 13 16:19:38 2006 +0000 (2006-11-13) |
parents | 9a341c6ef6ae |
children | 2511ac1f1e21 |
files | xen/arch/x86/traps.c xen/arch/x86/x86_32/Makefile xen/arch/x86/x86_32/gpr_switch.S xen/arch/x86/x86_64/Makefile xen/arch/x86/x86_64/gpr_switch.S |
line diff
1.1 --- a/xen/arch/x86/traps.c Mon Nov 13 14:25:48 2006 +0000 1.2 +++ b/xen/arch/x86/traps.c Mon Nov 13 16:19:38 2006 +0000 1.3 @@ -985,8 +985,7 @@ static inline int admin_io_okay( 1.4 return ioports_access_permitted(v->domain, port, port + bytes - 1); 1.5 } 1.6 1.7 -/* Check admin limits. Silently fail the access if it is disallowed. */ 1.8 -static inline unsigned char inb_user( 1.9 +static inline int guest_inb_okay( 1.10 unsigned int port, struct vcpu *v, struct cpu_user_regs *regs) 1.11 { 1.12 /* 1.13 @@ -996,19 +995,21 @@ static inline unsigned char inb_user( 1.14 * Note that we could emulate bit 4 instead of directly reading port 0x61, 1.15 * but there's not really a good reason to do so. 1.16 */ 1.17 - if ( admin_io_okay(port, 1, v, regs) || (port == 0x61) ) 1.18 - return inb(port); 1.19 - return ~0; 1.20 + return (admin_io_okay(port, 1, v, regs) || (port == 0x61)); 1.21 } 1.22 -//#define inb_user(_p, _d, _r) (admin_io_okay(_p, 1, _d, _r) ? inb(_p) : ~0) 1.23 -#define inw_user(_p, _d, _r) (admin_io_okay(_p, 2, _d, _r) ? inw(_p) : ~0) 1.24 -#define inl_user(_p, _d, _r) (admin_io_okay(_p, 4, _d, _r) ? inl(_p) : ~0) 1.25 -#define outb_user(_v, _p, _d, _r) \ 1.26 - (admin_io_okay(_p, 1, _d, _r) ? outb(_v, _p) : ((void)0)) 1.27 -#define outw_user(_v, _p, _d, _r) \ 1.28 - (admin_io_okay(_p, 2, _d, _r) ? outw(_v, _p) : ((void)0)) 1.29 -#define outl_user(_v, _p, _d, _r) \ 1.30 - (admin_io_okay(_p, 4, _d, _r) ? outl(_v, _p) : ((void)0)) 1.31 +#define guest_inw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r) 1.32 +#define guest_inl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r) 1.33 +#define guest_outb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r) 1.34 +#define guest_outw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r) 1.35 +#define guest_outl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r) 1.36 + 1.37 +/* I/O emulation support. Helper routines for, and type of, the stack stub.*/ 1.38 +void host_to_guest_gpr_switch(struct cpu_user_regs *) 1.39 + __attribute__((__regparm__(1))); 1.40 +unsigned long guest_to_host_gpr_switch(unsigned long) 1.41 + __attribute__((__regparm__(1))); 1.42 +typedef unsigned long (*io_emul_stub_t)(struct cpu_user_regs *) 1.43 + __attribute__((__regparm__(1))); 1.44 1.45 /* Instruction fetch with error handling. */ 1.46 #define insn_fetch(_type, _size, cs, eip) \ 1.47 @@ -1028,6 +1029,7 @@ static int emulate_privileged_op(struct 1.48 unsigned long *reg, eip = regs->eip, cs = regs->cs, res; 1.49 u8 opcode, modrm_reg = 0, modrm_rm = 0, rep_prefix = 0; 1.50 unsigned int port, i, op_bytes = 4, data, rc; 1.51 + char io_emul_stub[16]; 1.52 u32 l, h; 1.53 1.54 /* Legacy prefixes. */ 1.55 @@ -1068,6 +1070,9 @@ static int emulate_privileged_op(struct 1.56 opcode = insn_fetch(u8, 1, cs, eip); 1.57 } 1.58 #endif 1.59 + 1.60 + if ( opcode == 0x0f ) 1.61 + goto twobyte_opcode; 1.62 1.63 /* Input/Output String instructions. */ 1.64 if ( (opcode >= 0x6c) && (opcode <= 0x6f) ) 1.65 @@ -1083,16 +1088,17 @@ static int emulate_privileged_op(struct 1.66 case 0x6d: /* INSW/INSL */ 1.67 if ( !guest_io_okay((u16)regs->edx, op_bytes, v, regs) ) 1.68 goto fail; 1.69 + port = (u16)regs->edx; 1.70 switch ( op_bytes ) 1.71 { 1.72 case 1: 1.73 - data = (u8)inb_user((u16)regs->edx, v, regs); 1.74 + data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) : ~0); 1.75 break; 1.76 case 2: 1.77 - data = (u16)inw_user((u16)regs->edx, v, regs); 1.78 + data = (u16)(guest_inw_okay(port, v, regs) ? inw(port) : ~0); 1.79 break; 1.80 case 4: 1.81 - data = (u32)inl_user((u16)regs->edx, v, regs); 1.82 + data = (u32)(guest_inl_okay(port, v, regs) ? inl(port) : ~0); 1.83 break; 1.84 } 1.85 if ( (rc = copy_to_user((void *)regs->edi, &data, op_bytes)) != 0 ) 1.86 @@ -1115,16 +1121,20 @@ static int emulate_privileged_op(struct 1.87 propagate_page_fault(regs->esi + op_bytes - rc, 0); 1.88 return EXCRET_fault_fixed; 1.89 } 1.90 + port = (u16)regs->edx; 1.91 switch ( op_bytes ) 1.92 { 1.93 case 1: 1.94 - outb_user((u8)data, (u16)regs->edx, v, regs); 1.95 + if ( guest_outb_okay(port, v, regs) ) 1.96 + outb((u8)data, port); 1.97 break; 1.98 case 2: 1.99 - outw_user((u16)data, (u16)regs->edx, v, regs); 1.100 + if ( guest_outw_okay(port, v, regs) ) 1.101 + outw((u16)data, port); 1.102 break; 1.103 case 4: 1.104 - outl_user((u32)data, (u16)regs->edx, v, regs); 1.105 + if ( guest_outl_okay(port, v, regs) ) 1.106 + outl((u32)data, port); 1.107 break; 1.108 } 1.109 regs->esi += (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes); 1.110 @@ -1141,6 +1151,27 @@ static int emulate_privileged_op(struct 1.111 goto done; 1.112 } 1.113 1.114 + /* 1.115 + * Very likely to be an I/O instruction (IN/OUT). 1.116 + * Build an on-stack stub to execute the instruction with full guest 1.117 + * GPR context. This is needed for some systems which (ab)use IN/OUT 1.118 + * to communicate with BIOS code in system-management mode. 1.119 + */ 1.120 + /* call host_to_guest_gpr_switch */ 1.121 + io_emul_stub[0] = 0xe8; 1.122 + *(s32 *)&io_emul_stub[1] = 1.123 + (char *)host_to_guest_gpr_switch - &io_emul_stub[5]; 1.124 + /* data16 or nop */ 1.125 + io_emul_stub[5] = (op_bytes != 2) ? 0x90 : 0x66; 1.126 + /* <io-access opcode> */ 1.127 + io_emul_stub[6] = opcode; 1.128 + /* imm8 or nop */ 1.129 + io_emul_stub[7] = 0x90; 1.130 + /* jmp guest_to_host_gpr_switch */ 1.131 + io_emul_stub[8] = 0xe9; 1.132 + *(s32 *)&io_emul_stub[9] = 1.133 + (char *)guest_to_host_gpr_switch - &io_emul_stub[13]; 1.134 + 1.135 /* I/O Port and Interrupt Flag instructions. */ 1.136 switch ( opcode ) 1.137 { 1.138 @@ -1148,21 +1179,31 @@ static int emulate_privileged_op(struct 1.139 op_bytes = 1; 1.140 case 0xe5: /* IN imm8,%eax */ 1.141 port = insn_fetch(u8, 1, cs, eip); 1.142 + io_emul_stub[7] = port; /* imm8 */ 1.143 exec_in: 1.144 if ( !guest_io_okay(port, op_bytes, v, regs) ) 1.145 goto fail; 1.146 switch ( op_bytes ) 1.147 { 1.148 case 1: 1.149 - regs->eax &= ~0xffUL; 1.150 - regs->eax |= (u8)inb_user(port, v, regs); 1.151 + res = regs->eax & ~0xffUL; 1.152 + if ( guest_inb_okay(port, v, regs) ) 1.153 + regs->eax = res | (u8)((io_emul_stub_t)io_emul_stub)(regs); 1.154 + else 1.155 + regs->eax = res | (u8)~0; 1.156 break; 1.157 case 2: 1.158 - regs->eax &= ~0xffffUL; 1.159 - regs->eax |= (u16)inw_user(port, v, regs); 1.160 + res = regs->eax & ~0xffffUL; 1.161 + if ( guest_inw_okay(port, v, regs) ) 1.162 + regs->eax = res | (u16)((io_emul_stub_t)io_emul_stub)(regs); 1.163 + else 1.164 + regs->eax = res | (u16)~0; 1.165 break; 1.166 case 4: 1.167 - regs->eax = (u32)inl_user(port, v, regs); 1.168 + if ( guest_inl_okay(port, v, regs) ) 1.169 + regs->eax = (u32)((io_emul_stub_t)io_emul_stub)(regs); 1.170 + else 1.171 + regs->eax = (u32)~0; 1.172 break; 1.173 } 1.174 goto done; 1.175 @@ -1177,19 +1218,23 @@ static int emulate_privileged_op(struct 1.176 op_bytes = 1; 1.177 case 0xe7: /* OUT %eax,imm8 */ 1.178 port = insn_fetch(u8, 1, cs, eip); 1.179 + io_emul_stub[7] = port; /* imm8 */ 1.180 exec_out: 1.181 if ( !guest_io_okay(port, op_bytes, v, regs) ) 1.182 goto fail; 1.183 switch ( op_bytes ) 1.184 { 1.185 case 1: 1.186 - outb_user((u8)regs->eax, port, v, regs); 1.187 + if ( guest_outb_okay(port, v, regs) ) 1.188 + ((io_emul_stub_t)io_emul_stub)(regs); 1.189 break; 1.190 case 2: 1.191 - outw_user((u16)regs->eax, port, v, regs); 1.192 + if ( guest_outw_okay(port, v, regs) ) 1.193 + ((io_emul_stub_t)io_emul_stub)(regs); 1.194 break; 1.195 case 4: 1.196 - outl_user((u32)regs->eax, port, v, regs); 1.197 + if ( guest_outl_okay(port, v, regs) ) 1.198 + ((io_emul_stub_t)io_emul_stub)(regs); 1.199 break; 1.200 } 1.201 goto done; 1.202 @@ -1212,15 +1257,13 @@ static int emulate_privileged_op(struct 1.203 */ 1.204 /*v->vcpu_info->evtchn_upcall_mask = (opcode == 0xfa);*/ 1.205 goto done; 1.206 - 1.207 - case 0x0f: /* Two-byte opcode */ 1.208 - break; 1.209 - 1.210 - default: 1.211 - goto fail; 1.212 } 1.213 1.214 - /* Remaining instructions only emulated from guest kernel. */ 1.215 + /* No decode of this single-byte opcode. */ 1.216 + goto fail; 1.217 + 1.218 + twobyte_opcode: 1.219 + /* Two-byte opcodes only emulated from guest kernel. */ 1.220 if ( !guest_kernel_mode(v, regs) ) 1.221 goto fail; 1.222
2.1 --- a/xen/arch/x86/x86_32/Makefile Mon Nov 13 14:25:48 2006 +0000 2.2 +++ b/xen/arch/x86/x86_32/Makefile Mon Nov 13 16:19:38 2006 +0000 2.3 @@ -1,5 +1,6 @@ 2.4 obj-y += domain_page.o 2.5 obj-y += entry.o 2.6 +obj-y += gpr_switch.o 2.7 obj-y += mm.o 2.8 obj-y += seg_fixup.o 2.9 obj-y += traps.o
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/xen/arch/x86/x86_32/gpr_switch.S Mon Nov 13 16:19:38 2006 +0000 3.3 @@ -0,0 +1,43 @@ 3.4 +/* 3.5 + * GPR context switch between host and guest. 3.6 + * Used by IO-port-access emulation stub. 3.7 + * 3.8 + * Copyright (c) 2006, Novell, Inc. 3.9 + */ 3.10 + 3.11 +#include <xen/config.h> 3.12 +#include <asm/asm_defns.h> 3.13 + 3.14 +ENTRY(host_to_guest_gpr_switch) 3.15 + movl (%esp), %ecx 3.16 + movl %eax, (%esp) 3.17 + movl UREGS_edx(%eax), %edx 3.18 + pushl %ebx 3.19 + movl UREGS_ebx(%eax), %ebx 3.20 + pushl %ebp 3.21 + movl UREGS_ebp(%eax), %ebp 3.22 + pushl %esi 3.23 + movl UREGS_esi(%eax), %esi 3.24 + pushl %edi 3.25 + movl UREGS_edi(%eax), %edi 3.26 + pushl %ecx 3.27 + movl UREGS_ecx(%eax), %ecx 3.28 + movl UREGS_eax(%eax), %eax 3.29 + ret 3.30 + 3.31 +ENTRY(guest_to_host_gpr_switch) 3.32 + pushl %edx 3.33 + movl 5*4(%esp), %edx 3.34 + movl %eax, UREGS_eax(%edx) 3.35 + popl UREGS_edx(%edx) 3.36 + movl %edi, UREGS_edi(%edx) 3.37 + popl %edi 3.38 + movl %esi, UREGS_esi(%edx) 3.39 + popl %esi 3.40 + movl %ebp, UREGS_ebp(%edx) 3.41 + popl %ebp 3.42 + movl %ebx, UREGS_ebx(%edx) 3.43 + popl %ebx 3.44 + movl %ecx, UREGS_ecx(%edx) 3.45 + popl %ecx 3.46 + ret
4.1 --- a/xen/arch/x86/x86_64/Makefile Mon Nov 13 14:25:48 2006 +0000 4.2 +++ b/xen/arch/x86/x86_64/Makefile Mon Nov 13 16:19:38 2006 +0000 4.3 @@ -1,3 +1,4 @@ 4.4 obj-y += entry.o 4.5 +obj-y += gpr_switch.o 4.6 obj-y += mm.o 4.7 obj-y += traps.o
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/xen/arch/x86/x86_64/gpr_switch.S Mon Nov 13 16:19:38 2006 +0000 5.3 @@ -0,0 +1,63 @@ 5.4 +/* 5.5 + * GPR context switch between host and guest. 5.6 + * Used by IO-port-access emulation stub. 5.7 + * 5.8 + * Copyright (c) 2006, Novell, Inc. 5.9 + */ 5.10 + 5.11 +#include <xen/config.h> 5.12 +#include <asm/asm_defns.h> 5.13 + 5.14 +ENTRY(host_to_guest_gpr_switch) 5.15 + movq (%rsp), %rcx 5.16 + movq %rdi, (%rsp) 5.17 + movq UREGS_rdx(%rdi), %rdx 5.18 + pushq %rbx 5.19 + movq UREGS_rax(%rdi), %rax 5.20 + movq UREGS_rbx(%rdi), %rbx 5.21 + pushq %rbp 5.22 + movq UREGS_rsi(%rdi), %rsi 5.23 + movq UREGS_rbp(%rdi), %rbp 5.24 + pushq %r12 5.25 + movq UREGS_r8(%rdi), %r8 5.26 + movq UREGS_r12(%rdi), %r12 5.27 + pushq %r13 5.28 + movq UREGS_r9(%rdi), %r9 5.29 + movq UREGS_r13(%rdi), %r13 5.30 + pushq %r14 5.31 + movq UREGS_r10(%rdi), %r10 5.32 + movq UREGS_r14(%rdi), %r14 5.33 + pushq %r15 5.34 + movq UREGS_r11(%rdi), %r11 5.35 + movq UREGS_r15(%rdi), %r15 5.36 + pushq %rcx 5.37 + movq UREGS_rcx(%rdi), %rcx 5.38 + movq UREGS_rdi(%rdi), %rdi 5.39 + ret 5.40 + 5.41 +ENTRY(guest_to_host_gpr_switch) 5.42 + pushq %rdi 5.43 + movq 7*8(%rsp), %rdi 5.44 + movq %rax, UREGS_rax(%rdi) 5.45 + popq UREGS_rdi(%rdi) 5.46 + movq %r15, UREGS_r15(%rdi) 5.47 + movq %r11, UREGS_r11(%rdi) 5.48 + popq %r15 5.49 + movq %r14, UREGS_r14(%rdi) 5.50 + movq %r10, UREGS_r10(%rdi) 5.51 + popq %r14 5.52 + movq %r13, UREGS_r13(%rdi) 5.53 + movq %r9, UREGS_r9(%rdi) 5.54 + popq %r13 5.55 + movq %r12, UREGS_r12(%rdi) 5.56 + movq %r8, UREGS_r8(%rdi) 5.57 + popq %r12 5.58 + movq %rbp, UREGS_rbp(%rdi) 5.59 + movq %rsi, UREGS_rsi(%rdi) 5.60 + popq %rbp 5.61 + movq %rbx, UREGS_rbx(%rdi) 5.62 + movq %rdx, UREGS_rdx(%rdi) 5.63 + popq %rbx 5.64 + movq %rcx, UREGS_rcx(%rdi) 5.65 + popq %rcx 5.66 + ret