x86emul: generalize exception handling for rep_* hooks If any of those hooks returns X86EMUL_EXCEPTION, some register state should still get updated if some iterations have been performed (but the rIP update will get suppressed if not all of them did get handled). This updating is done by register_address_increment() and __put_rep_prefix() (which hence must no longer be bypassed). As a result put_rep_prefix() can then skip most of the writeback, but needs to ensure proper completion of the executed instruction. While on the HVM side the VA -> LA -> PA translation process ensures that an exception would be raised on the first iteration only, doing so would unduly complicate the PV side code about to be added. Signed-off-by: Jan Beulich --- v3: Broken out from a later patch. --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -960,7 +960,11 @@ static void __put_rep_prefix( #define put_rep_prefix(reps_completed) ({ \ if ( rep_prefix() ) \ + { \ __put_rep_prefix(&_regs, ctxt->regs, ad_bytes, reps_completed); \ + if ( unlikely(rc == X86EMUL_EXCEPTION) ) \ + goto no_writeback; \ + } \ }) /* Clip maximum repetitions so that the index register at most just wraps. */ @@ -2896,14 +2900,9 @@ x86_emulate( dst.mem.off = truncate_ea_and_reps(_regs.edi, nr_reps, dst.bytes); if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 ) goto done; - if ( (nr_reps > 1) && (ops->rep_ins != NULL) && + if ( (nr_reps == 1) || !ops->rep_ins || ((rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes, - &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) ) - { - if ( rc != 0 ) - goto done; - } - else + &nr_reps, ctxt)) == X86EMUL_UNHANDLEABLE) ) { fail_if(ops->read_io == NULL); if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 ) @@ -2915,6 +2914,8 @@ x86_emulate( _regs.edi, nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes)); put_rep_prefix(nr_reps); + if ( rc != X86EMUL_OKAY ) + goto done; break; } @@ -2925,14 +2926,9 @@ x86_emulate( ea.mem.off = truncate_ea_and_reps(_regs.esi, nr_reps, dst.bytes); if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 ) goto done; - if ( (nr_reps > 1) && (ops->rep_outs != NULL) && + if ( (nr_reps == 1) || !ops->rep_outs || ((rc = ops->rep_outs(ea.mem.seg, ea.mem.off, port, dst.bytes, - &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) ) - { - if ( rc != 0 ) - goto done; - } - else + &nr_reps, ctxt)) == X86EMUL_UNHANDLEABLE) ) { if ( (rc = read_ulong(ea.mem.seg, truncate_ea(_regs.esi), &dst.val, dst.bytes, ctxt, ops)) != 0 ) @@ -2946,6 +2942,8 @@ x86_emulate( _regs.esi, nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes)); put_rep_prefix(nr_reps); + if ( rc != X86EMUL_OKAY ) + goto done; break; } @@ -3161,15 +3159,10 @@ x86_emulate( dst.mem.seg = x86_seg_es; dst.mem.off = truncate_ea_and_reps(_regs.edi, nr_reps, dst.bytes); src.mem.off = truncate_ea_and_reps(_regs.esi, nr_reps, dst.bytes); - if ( (nr_reps > 1) && (ops->rep_movs != NULL) && + if ( (nr_reps == 1) || !ops->rep_movs || ((rc = ops->rep_movs(ea.mem.seg, src.mem.off, dst.mem.seg, dst.mem.off, dst.bytes, - &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) ) - { - if ( rc != 0 ) - goto done; - } - else + &nr_reps, ctxt)) == X86EMUL_UNHANDLEABLE) ) { if ( (rc = read_ulong(ea.mem.seg, src.mem.off, &dst.val, dst.bytes, ctxt, ops)) != 0 ) @@ -3184,6 +3177,8 @@ x86_emulate( _regs.edi, nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes)); put_rep_prefix(nr_reps); + if ( rc != X86EMUL_OKAY ) + goto done; break; } @@ -3222,13 +3217,14 @@ x86_emulate( dst.val = _regs.eax; dst.type = OP_MEM; nr_reps = 1; + rc = X86EMUL_OKAY; } - else if ( rc != X86EMUL_OKAY ) - goto done; register_address_increment( _regs.edi, nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes)); put_rep_prefix(nr_reps); + if ( rc != X86EMUL_OKAY ) + goto done; break; }