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