ia64/xen-unstable

changeset 15676:66055f773d19

[HVM] Inject #PF when mmio instruction fetch fails
instead of crashing the guest. This can happen if one vcpu pages out
another vcpu's kernel text page while the other is performing an mmio op.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Tue Jul 31 11:37:27 2007 +0100 (2007-07-31)
parents 66147ca8f9c4
children 7c5c3aa858cc
files xen/arch/x86/hvm/instrlen.c xen/arch/x86/hvm/platform.c xen/arch/x86/hvm/vmx/vmx.c xen/include/asm-x86/hvm/hvm.h
line diff
     1.1 --- a/xen/arch/x86/hvm/instrlen.c	Tue Jul 31 10:11:47 2007 +0100
     1.2 +++ b/xen/arch/x86/hvm/instrlen.c	Tue Jul 31 11:37:27 2007 +0100
     1.3 @@ -9,14 +9,6 @@
     1.4   * x86_emulate.c.  Used for MMIO.
     1.5   */
     1.6  
     1.7 -/*
     1.8 - * TODO: The way in which we use hvm_instruction_length is very inefficient as
     1.9 - * it now stands. It will be worthwhile to return the actual instruction buffer
    1.10 - * along with the instruction length since one of the reasons we are getting
    1.11 - * the instruction length is to know how many instruction bytes we need to
    1.12 - * fetch.
    1.13 - */
    1.14 -
    1.15  #include <xen/config.h>
    1.16  #include <xen/sched.h>
    1.17  #include <xen/mm.h>
    1.18 @@ -194,31 +186,51 @@ static uint8_t twobyte_table[256] = {
    1.19  /* 
    1.20   * insn_fetch - fetch the next byte from instruction stream
    1.21   */
    1.22 -#define insn_fetch()                                                    \
    1.23 -({ uint8_t _x;                                                          \
    1.24 -   if ( length >= 15 )                                                  \
    1.25 -       return -1;                                                       \
    1.26 -   if ( inst_copy_from_guest(&_x, pc, 1) != 1 ) {                       \
    1.27 -       gdprintk(XENLOG_WARNING,                                         \
    1.28 -                "Cannot read from address %lx (eip %lx, mode %d)\n",    \
    1.29 -                pc, org_pc, address_bytes);                             \
    1.30 -       return -1;                                                       \
    1.31 -   }                                                                    \
    1.32 -   pc += 1;                                                             \
    1.33 -   length += 1;                                                         \
    1.34 -   _x;                                                                  \
    1.35 +#define insn_fetch()                                                      \
    1.36 +({ uint8_t _x;                                                            \
    1.37 +   if ( length >= 15 )                                                    \
    1.38 +       return -1;                                                         \
    1.39 +   if ( inst_copy_from_guest(&_x, pc, 1) != 1 ) {                         \
    1.40 +       unsigned long err;                                                 \
    1.41 +       struct segment_register cs;                                        \
    1.42 +       gdprintk(XENLOG_WARNING,                                           \
    1.43 +                "Cannot read from address %lx (eip %lx, mode %d)\n",      \
    1.44 +                pc, org_pc, address_bytes);                               \
    1.45 +       err = 0; /* Must be not-present: we don't enforce reserved bits */ \
    1.46 +       if ( hvm_nx_enabled(current) )                                     \
    1.47 +           err |= PFEC_insn_fetch;                                        \
    1.48 +       hvm_get_segment_register(current, x86_seg_cs, &cs);                \
    1.49 +       if ( cs.attr.fields.dpl != 0 )                                     \
    1.50 +           err |= PFEC_user_mode;                                         \
    1.51 +       hvm_inject_exception(TRAP_page_fault, err, pc);                    \
    1.52 +       return -1;                                                         \
    1.53 +   }                                                                      \
    1.54 +   if ( buf )                                                             \
    1.55 +       buf[length] = _x;                                                  \
    1.56 +   length += 1;                                                           \
    1.57 +   pc += 1;                                                               \
    1.58 +   _x;                                                                    \
    1.59  })
    1.60  
    1.61 +#define insn_skip(_n) do {                      \
    1.62 +    int _i;                                     \
    1.63 +    for ( _i = 0; _i < (_n); _i++) {            \
    1.64 +        (void) insn_fetch();                    \
    1.65 +    }                                           \
    1.66 +} while (0)
    1.67 +
    1.68  /**
    1.69 - * hvm_instruction_length - returns the current instructions length
    1.70 + * hvm_instruction_fetch - read the current instruction and return its length
    1.71   *
    1.72   * @org_pc: guest instruction pointer
    1.73 - * @mode: guest operating mode
    1.74 + * @address_bytes: guest address width
    1.75 + * @buf: (optional) buffer to load actual instruction bytes into
    1.76   *
    1.77 - * EXTERNAL this routine calculates the length of the current instruction
    1.78 - * pointed to by org_pc.  The guest state is _not_ changed by this routine.
    1.79 + * Doesn't increment the guest's instruction pointer, but may
    1.80 + * issue faults to the guest.  Returns -1 on failure.
    1.81   */
    1.82 -int hvm_instruction_length(unsigned long org_pc, int address_bytes)
    1.83 +int hvm_instruction_fetch(unsigned long org_pc, int address_bytes,
    1.84 +                          unsigned char *buf)
    1.85  {
    1.86      uint8_t b, d, twobyte = 0, rex_prefix = 0, modrm_reg = 0;
    1.87      unsigned int op_default, op_bytes, ad_default, ad_bytes, tmp;
    1.88 @@ -317,18 +329,13 @@ done_prefixes:
    1.89              {
    1.90              case 0:
    1.91                  if ( modrm_rm == 6 ) 
    1.92 -                {
    1.93 -                    length += 2;
    1.94 -                    pc += 2; /* skip disp16 */
    1.95 -                }
    1.96 +                    insn_skip(2); /* skip disp16 */
    1.97                  break;
    1.98              case 1:
    1.99 -                length += 1;
   1.100 -                pc += 1; /* skip disp8 */
   1.101 +                insn_skip(1); /* skip disp8 */
   1.102                  break;
   1.103              case 2:
   1.104 -                length += 2;
   1.105 -                pc += 2; /* skip disp16 */
   1.106 +                insn_skip(2); /* skip disp16 */
   1.107                  break;
   1.108              }
   1.109          }
   1.110 @@ -340,33 +347,19 @@ done_prefixes:
   1.111              case 0:
   1.112                  if ( (modrm_rm == 4) && 
   1.113                       ((insn_fetch() & 7) == 5) )
   1.114 -                {
   1.115 -                    length += 4;
   1.116 -                    pc += 4; /* skip disp32 specified by SIB.base */
   1.117 -                }
   1.118 +                    insn_skip(4); /* skip disp32 specified by SIB.base */
   1.119                  else if ( modrm_rm == 5 )
   1.120 -                {
   1.121 -                    length += 4;
   1.122 -                    pc += 4; /* skip disp32 */
   1.123 -                }
   1.124 +                    insn_skip(4); /* skip disp32 */
   1.125                  break;
   1.126              case 1:
   1.127                  if ( modrm_rm == 4 )
   1.128 -                {
   1.129 -                    length += 1;
   1.130 -                    pc += 1;
   1.131 -                }
   1.132 -                length += 1;
   1.133 -                pc += 1; /* skip disp8 */
   1.134 +                    insn_skip(1);
   1.135 +                insn_skip(1); /* skip disp8 */
   1.136                  break;
   1.137              case 2:
   1.138                  if ( modrm_rm == 4 )
   1.139 -                {
   1.140 -                    length += 1;
   1.141 -                    pc += 1;
   1.142 -                }
   1.143 -                length += 4;
   1.144 -                pc += 4; /* skip disp32 */
   1.145 +                    insn_skip(1);
   1.146 +                insn_skip(4); /* skip disp32 */
   1.147                  break;
   1.148              }
   1.149          }
   1.150 @@ -387,12 +380,10 @@ done_prefixes:
   1.151          tmp = (d & ByteOp) ? 1 : op_bytes;
   1.152          if ( tmp == 8 ) tmp = 4;
   1.153          /* NB. Immediates are sign-extended as necessary. */
   1.154 -        length += tmp;
   1.155 -        pc += tmp;
   1.156 +        insn_skip(tmp);
   1.157          break;
   1.158      case SrcImmByte:
   1.159 -        length += 1;
   1.160 -        pc += 1;
   1.161 +        insn_skip(1);
   1.162          break;
   1.163      }
   1.164  
   1.165 @@ -402,8 +393,7 @@ done_prefixes:
   1.166      switch ( b )
   1.167      {
   1.168      case 0xa0 ... 0xa3: /* mov */
   1.169 -        length += ad_bytes;
   1.170 -        pc += ad_bytes; /* skip src/dst displacement */
   1.171 +        insn_skip(ad_bytes); /* skip src/dst displacement */
   1.172          break;
   1.173      case 0xf6 ... 0xf7: /* Grp3 */
   1.174          switch ( modrm_reg )
   1.175 @@ -412,8 +402,7 @@ done_prefixes:
   1.176              /* Special case in Grp3: test has an immediate source operand. */
   1.177              tmp = (d & ByteOp) ? 1 : op_bytes;
   1.178              if ( tmp == 8 ) tmp = 4;
   1.179 -            length += tmp;
   1.180 -            pc += tmp;
   1.181 +            insn_skip(tmp);
   1.182              break;
   1.183          }
   1.184          break;
     2.1 --- a/xen/arch/x86/hvm/platform.c	Tue Jul 31 10:11:47 2007 +0100
     2.2 +++ b/xen/arch/x86/hvm/platform.c	Tue Jul 31 11:37:27 2007 +0100
     2.3 @@ -1041,17 +1041,13 @@ void handle_mmio(unsigned long gpa)
     2.4          /* real or vm86 modes */
     2.5          address_bytes = 2;
     2.6      inst_addr = hvm_get_segment_base(v, x86_seg_cs) + regs->eip;
     2.7 -    inst_len = hvm_instruction_length(inst_addr, address_bytes);
     2.8 +    memset(inst, 0, MAX_INST_LEN);
     2.9 +    inst_len = hvm_instruction_fetch(inst_addr, address_bytes, inst);
    2.10      if ( inst_len <= 0 )
    2.11      {
    2.12 -        printk("handle_mmio: failed to get instruction length\n");
    2.13 -        domain_crash_synchronous();
    2.14 -    }
    2.15 -
    2.16 -    memset(inst, 0, MAX_INST_LEN);
    2.17 -    if ( inst_copy_from_guest(inst, inst_addr, inst_len) != inst_len ) {
    2.18 -        printk("handle_mmio: failed to copy instruction\n");
    2.19 -        domain_crash_synchronous();
    2.20 +        gdprintk(XENLOG_DEBUG, "handle_mmio: failed to get instruction\n");
    2.21 +        /* hvm_instruction_fetch() will have injected a #PF; get out now */
    2.22 +        return;
    2.23      }
    2.24  
    2.25      if ( mmio_decode(address_bytes, inst, mmio_op, &ad_size,
     3.1 --- a/xen/arch/x86/hvm/vmx/vmx.c	Tue Jul 31 10:11:47 2007 +0100
     3.2 +++ b/xen/arch/x86/hvm/vmx/vmx.c	Tue Jul 31 11:37:27 2007 +0100
     3.3 @@ -777,7 +777,7 @@ int vmx_vmcs_restore(struct vcpu *v, str
     3.4              else
     3.5                  addrbytes = 2;
     3.6  
     3.7 -            ilen = hvm_instruction_length(c->rip, addrbytes);
     3.8 +            ilen = hvm_instruction_fetch(c->rip, addrbytes, NULL);
     3.9              __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen);
    3.10          }
    3.11  
     4.1 --- a/xen/include/asm-x86/hvm/hvm.h	Tue Jul 31 10:11:47 2007 +0100
     4.2 +++ b/xen/include/asm-x86/hvm/hvm.h	Tue Jul 31 11:37:27 2007 +0100
     4.3 @@ -229,7 +229,8 @@ hvm_guest_x86_mode(struct vcpu *v)
     4.4      return hvm_funcs.guest_x86_mode(v);
     4.5  }
     4.6  
     4.7 -int hvm_instruction_length(unsigned long pc, int address_bytes);
     4.8 +int hvm_instruction_fetch(unsigned long pc, int address_bytes,
     4.9 +                          unsigned char *buf);
    4.10  
    4.11  static inline void
    4.12  hvm_update_host_cr3(struct vcpu *v)