x86emul: make write and cmpxchg hooks optional While the read and fetch hooks are basically unavoidable, write and cmpxchg aren't really needed by that many insns. Signed-off-by: Jan Beulich --- v4: Don't open-code fail_if(). Add ASSERT()s for fetch and read hooks. Move a write hook check closer to its use, and add ASSERT()s and comments where this is not desirable. Re-base. --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -1520,6 +1520,7 @@ protmode_load_seg( { uint32_t new_desc_b = desc.b | a_flag; + fail_if(!ops->cmpxchg); switch ( (rc = ops->cmpxchg(sel_seg, (sel & 0xfff8) + 4, &desc.b, &new_desc_b, sizeof(desc.b), ctxt)) ) { @@ -2017,6 +2018,8 @@ x86_decode( bool pc_rel = false; int rc = X86EMUL_OKAY; + ASSERT(ops->insn_fetch); + memset(state, 0, sizeof(*state)); ea.type = OP_NONE; ea.mem.seg = x86_seg_ds; @@ -2494,6 +2497,8 @@ x86_emulate( struct x86_emulate_stub stub = {}; DECLARE_ALIGNED(mmval_t, mmval); + ASSERT(ops->read); + rc = x86_decode(&state, ctxt, ops); if ( rc != X86EMUL_OKAY ) return rc; @@ -2658,13 +2663,18 @@ x86_emulate( } else if ( !(d & Mov) ) /* optimisation - avoid slow emulated read */ { + fail_if(lock_prefix ? !ops->cmpxchg : !ops->write); if ( (rc = read_ulong(dst.mem.seg, dst.mem.off, &dst.val, dst.bytes, ctxt, ops)) ) goto done; dst.orig_val = dst.val; } - else /* Lock prefix is allowed only on RMW instructions. */ + else + { + /* Lock prefix is allowed only on RMW instructions. */ generate_exception_if(lock_prefix, EXC_UD); + fail_if(!ops->write); + } break; } @@ -2813,7 +2823,9 @@ x86_emulate( unsigned long regs[] = { _regs.eax, _regs.ecx, _regs.edx, _regs.ebx, _regs.esp, _regs.ebp, _regs.esi, _regs.edi }; + generate_exception_if(mode_64bit(), EXC_UD); + fail_if(!ops->write); for ( i = 0; i < 8; i++ ) if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes), ®s[i], op_bytes, ctxt)) != 0 ) @@ -3111,7 +3123,7 @@ x86_emulate( case 0x9a: /* call (far, absolute) */ ASSERT(!mode_64bit()); far_call: - fail_if(ops->read_segment == NULL); + fail_if(!ops->read_segment || !ops->write); if ( (rc = ops->read_segment(x86_seg_cs, &sreg, ctxt)) || (rc = load_seg(x86_seg_cs, imm2, 0, &cs, ctxt, ops)) || @@ -3350,6 +3362,7 @@ x86_emulate( dst.type = OP_REG; dst.bytes = (mode_64bit() && (op_bytes == 4)) ? 8 : op_bytes; dst.reg = (unsigned long *)&_regs.ebp; + fail_if(!ops->write); if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(dst.bytes), &_regs.ebp, dst.bytes, ctxt)) ) goto done; @@ -4446,6 +4459,7 @@ x86_emulate( else if ( rc != X86EMUL_UNHANDLEABLE ) goto done; } + fail_if(limit && !ops->write); while ( limit ) { rc = ops->write(ea.mem.seg, base, &zero, sizeof(zero), ctxt); @@ -4466,7 +4480,7 @@ x86_emulate( case 1: /* sidt */ generate_exception_if(ea.type != OP_MEM, EXC_UD); generate_exception_if(umip_active(ctxt, ops), EXC_GP, 0); - fail_if(ops->read_segment == NULL); + fail_if(!ops->read_segment || !ops->write); if ( (rc = ops->read_segment(seg, &sreg, ctxt)) ) goto done; if ( mode_64bit() ) @@ -4503,12 +4517,18 @@ x86_emulate( break; case 4: /* smsw */ generate_exception_if(umip_active(ctxt, ops), EXC_GP, 0); - ea.bytes = (ea.type == OP_MEM) ? 2 : op_bytes; + if ( ea.type == OP_MEM ) + { + fail_if(!ops->write); + d |= Mov; /* force writeback */ + ea.bytes = 2; + } + else + ea.bytes = op_bytes; dst = ea; fail_if(ops->read_cr == NULL); if ( (rc = ops->read_cr(0, &dst.val, ctxt)) ) goto done; - d |= Mov; /* force writeback */ break; case 6: /* lmsw */ fail_if(ops->read_cr == NULL); @@ -4710,6 +4730,8 @@ x86_emulate( if ( !(b & 1) ) rc = ops->read(ea.mem.seg, ea.mem.off+0, mmvalp, ea.bytes, ctxt); + else + fail_if(!ops->write); /* Check before running the stub. */ /* convert memory operand to (%rAX) */ rex_prefix &= ~REX_B; vex.b = 1; @@ -4724,8 +4746,11 @@ x86_emulate( put_fpu(&fic); put_stub(stub); if ( !rc && (b & 1) && (ea.type == OP_MEM) ) + { + ASSERT(ops->write); /* See the fail_if() above. */ rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp, ea.bytes, ctxt); + } if ( rc ) goto done; dst.type = OP_NONE; @@ -4994,6 +5019,8 @@ x86_emulate( if ( b == 0x6f ) rc = ops->read(ea.mem.seg, ea.mem.off+0, mmvalp, ea.bytes, ctxt); + else + fail_if(!ops->write); /* Check before running the stub. */ } if ( ea.type == OP_MEM || b == 0x7e ) { @@ -5015,8 +5042,11 @@ x86_emulate( put_fpu(&fic); put_stub(stub); if ( !rc && (b != 0x6f) && (ea.type == OP_MEM) ) + { + ASSERT(ops->write); /* See the fail_if() above. */ rc = ops->write(ea.mem.seg, ea.mem.off, mmvalp, ea.bytes, ctxt); + } if ( rc ) goto done; dst.type = OP_NONE; @@ -5264,6 +5294,7 @@ x86_emulate( generate_exception_if((modrm_reg & 7) != 1, EXC_UD); generate_exception_if(ea.type != OP_MEM, EXC_UD); + fail_if(!ops->cmpxchg); if ( op_bytes == 8 ) host_and_vcpu_must_have(cx16); op_bytes *= 2; @@ -5396,12 +5427,18 @@ x86_emulate( !ctxt->force_writeback ) /* nothing to do */; else if ( lock_prefix ) + { + fail_if(!ops->cmpxchg); rc = ops->cmpxchg( dst.mem.seg, dst.mem.off, &dst.orig_val, &dst.val, dst.bytes, ctxt); + } else + { + fail_if(!ops->write); rc = ops->write( dst.mem.seg, dst.mem.off, &dst.val, dst.bytes, ctxt); + } if ( rc != 0 ) goto done; default: @@ -5485,8 +5522,6 @@ x86_decode_insn( const struct x86_emulate_ops ops = { .insn_fetch = insn_fetch, .read = x86emul_unhandleable_rw, - .write = PTR_POISON, - .cmpxchg = PTR_POISON, }; int rc = x86_decode(state, ctxt, &ops);