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