diff mbox

[v13,1/3] x86emul: New return code for unimplemented instruction

Message ID 1506340992-5587-2-git-send-email-ppircalabu@bitdefender.com (mailing list archive)
State New, archived
Headers show

Commit Message

Petre Ovidiu PIRCALABU Sept. 25, 2017, 12:03 p.m. UTC
Enforce the distinction between an instruction not implemented by the
emulator and the failure to emulate that instruction by defining a new
return code, X86EMUL_UNIMPLEMENTED.

This value should only be returned by the core emulator only if it fails to
properly decode the current instruction's opcode, and not by any of other
functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.

e.g. hvm_process_io_intercept should not return X86EMUL_UNIMPLEMENTED.
The return value of this function depends on either the return code of
one of the hvm_io_ops handlers (read/write) or the value returned by
hvm_copy_guest_from_phys / hvm_copy_to_guest_phys.

Similary, none of this functions should return X86EMUL_UNIMPLEMENTED.
 - hvm_io_intercept
 - hvmemul_do_io
 - hvm_send_buffered_ioreq
 - hvm_send_ioreq
 - hvm_broadcast_ioreq
 - hvmemul_do_io_buffer
 - hvmemul_validate

Also the behavior of hvm_emulate_one_insn and vmx_realmode_emulate_one
was modified to generate an Invalid Opcode trap when X86EMUL_UNRECOGNIZED
is returned by the emulator instead of just crash the domain.

Signed-off-by: Petre Pircalabu <ppircalabu@bitdefender.com>

---
Changed since v10:
    * Added asserts to make sure the return code cannot be X86EMUL_UNIMPLEMENTED.
    * Add new return code (X86EMUL_UNRECOGNIZED) to be used when trying
    to emulate an instruction with an invalid opcode.

Changed since v11:
    * Fixed double negative in the patch description.
    * Move assertion into the switch and use ASSERT_UNREACHABLE() when
    applicable.
    * Changed the description of X86EMUL_UNIMPLEMENTED / X86EMUL_UNRECOGNIZED
    to reflect the differences between those 2 return codes.
    * Changed the returned value to X86EMUL_UNRECOGNIZED in the
    following cases:
        X86EMUL_OPC(0x0f, 0x73): /* Group 14 */
        X86EMUL_OPC_66(0x0f, 0x73):
        X86EMUL_OPC_VEX_66(0x0f, 0x73):
                - All valid opcodes are defined, so it should return
                X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.

        X86EMUL_OPC(0x0f, 0xc7) /* Group 9 */
                - For register type instructions all possible opcodes are
                defined, so it should return X86EMUL_UNRECOGNIZED if
                mod R/M bits are not matched.

        X86EMUL_OPC_VEX(0x0f38, 0xf3): /* Group 17 */
                - All valid opcodes are defined, so it should return
                X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.

        X86EMUL_OPC_XOP(09, 0x01): /* XOP Grp1 */
        X86EMUL_OPC_XOP(09, 0x02): /* XOP Grp2 */
                - All valid opcodes are defined, so it should return
                X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.

        X86EMUL_OPC(0x0f, 0x01): /* Grp7 */
                - Not all valid opcodes are defined so it should return
                X86EMUL_UNIMPLEMENTED if mod R/M bits are not matched.
                (e.g. XGETBV)

        X86EMUL_OPC_66(0x0f, 0x78):
                - All valid opcodes are defined, so it should return
                X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.

        X86EMUL_OPC(0x0f, 0xae):
        X86EMUL_OPC_66(0x0f, 0xae): /* Grp15 */
                - Not all valid opcodes are defined so it should return
                X86EMUL_UNIMPLEMENTED if mod R/M bits are not matched.
                (e.g. FXSAVE/FXRSTOR )

Changed since v12:
    * return X86EMUL_UNIMPLEMENTED if HAVE_GAS_RDRAND is not defined
    * create unrecognized_insn label
    * return X86EMUL_UNRECOGNIZED in case of failure to decode Group #13
    instructions.
    * add a "TODO:" comment to the description of X86EMUL_UNRECOGNIZED
    stating that for now it can be used interchangeably with
    X86EMUL_UNIMPLEMENTED.
---
 xen/arch/x86/hvm/emulate.c             | 12 ++++++++
 xen/arch/x86/hvm/hvm.c                 |  1 +
 xen/arch/x86/hvm/io.c                  |  5 ++++
 xen/arch/x86/hvm/vmx/realmode.c        |  9 ++++++
 xen/arch/x86/mm/shadow/multi.c         |  2 +-
 xen/arch/x86/x86_emulate/x86_emulate.c | 51 +++++++++++++++++++---------------
 xen/arch/x86/x86_emulate/x86_emulate.h | 17 ++++++++++++
 7 files changed, 74 insertions(+), 23 deletions(-)

Comments

Paul Durrant Sept. 25, 2017, 12:15 p.m. UTC | #1
> -----Original Message-----
> From: Petre Pircalabu [mailto:ppircalabu@bitdefender.com]
> Sent: 25 September 2017 13:03
> To: xen-devel@lists.xen.org
> Cc: Ian Jackson <Ian.Jackson@citrix.com>; Wei Liu <wei.liu2@citrix.com>;
> Andrew Cooper <Andrew.Cooper3@citrix.com>; George Dunlap
> <George.Dunlap@citrix.com>; jbeulich@suse.com; konrad.wilk@oracle.com;
> sstabellini@kernel.org; Tim (Xen.org) <tim@xen.org>; Paul Durrant
> <Paul.Durrant@citrix.com>; rcojocaru@bitdefender.com;
> tamas@tklengyel.com; jun.nakajima@intel.com; Kevin Tian
> <kevin.tian@intel.com>; Petre Pircalabu <ppircalabu@bitdefender.com>
> Subject: [PATCH v13 1/3] x86emul: New return code for unimplemented
> instruction
> 
> Enforce the distinction between an instruction not implemented by the
> emulator and the failure to emulate that instruction by defining a new
> return code, X86EMUL_UNIMPLEMENTED.
> 
> This value should only be returned by the core emulator only if it fails to
> properly decode the current instruction's opcode, and not by any of other
> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
> 
> e.g. hvm_process_io_intercept should not return
> X86EMUL_UNIMPLEMENTED.
> The return value of this function depends on either the return code of
> one of the hvm_io_ops handlers (read/write) or the value returned by
> hvm_copy_guest_from_phys / hvm_copy_to_guest_phys.
> 
> Similary, none of this functions should return X86EMUL_UNIMPLEMENTED.
>  - hvm_io_intercept
>  - hvmemul_do_io
>  - hvm_send_buffered_ioreq
>  - hvm_send_ioreq
>  - hvm_broadcast_ioreq
>  - hvmemul_do_io_buffer
>  - hvmemul_validate
> 
> Also the behavior of hvm_emulate_one_insn and
> vmx_realmode_emulate_one
> was modified to generate an Invalid Opcode trap when
> X86EMUL_UNRECOGNIZED
> is returned by the emulator instead of just crash the domain.
> 
> Signed-off-by: Petre Pircalabu <ppircalabu@bitdefender.com>
> 

Reviewed-by: Paul Durrant <paul.durrant@citrix.com>

> ---
> Changed since v10:
>     * Added asserts to make sure the return code cannot be
> X86EMUL_UNIMPLEMENTED.
>     * Add new return code (X86EMUL_UNRECOGNIZED) to be used when
> trying
>     to emulate an instruction with an invalid opcode.
> 
> Changed since v11:
>     * Fixed double negative in the patch description.
>     * Move assertion into the switch and use ASSERT_UNREACHABLE() when
>     applicable.
>     * Changed the description of X86EMUL_UNIMPLEMENTED /
> X86EMUL_UNRECOGNIZED
>     to reflect the differences between those 2 return codes.
>     * Changed the returned value to X86EMUL_UNRECOGNIZED in the
>     following cases:
>         X86EMUL_OPC(0x0f, 0x73): /* Group 14 */
>         X86EMUL_OPC_66(0x0f, 0x73):
>         X86EMUL_OPC_VEX_66(0x0f, 0x73):
>                 - All valid opcodes are defined, so it should return
>                 X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.
> 
>         X86EMUL_OPC(0x0f, 0xc7) /* Group 9 */
>                 - For register type instructions all possible opcodes are
>                 defined, so it should return X86EMUL_UNRECOGNIZED if
>                 mod R/M bits are not matched.
> 
>         X86EMUL_OPC_VEX(0x0f38, 0xf3): /* Group 17 */
>                 - All valid opcodes are defined, so it should return
>                 X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.
> 
>         X86EMUL_OPC_XOP(09, 0x01): /* XOP Grp1 */
>         X86EMUL_OPC_XOP(09, 0x02): /* XOP Grp2 */
>                 - All valid opcodes are defined, so it should return
>                 X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.
> 
>         X86EMUL_OPC(0x0f, 0x01): /* Grp7 */
>                 - Not all valid opcodes are defined so it should return
>                 X86EMUL_UNIMPLEMENTED if mod R/M bits are not matched.
>                 (e.g. XGETBV)
> 
>         X86EMUL_OPC_66(0x0f, 0x78):
>                 - All valid opcodes are defined, so it should return
>                 X86EMUL_UNRECOGNIZED if mod R/M bits are not matched.
> 
>         X86EMUL_OPC(0x0f, 0xae):
>         X86EMUL_OPC_66(0x0f, 0xae): /* Grp15 */
>                 - Not all valid opcodes are defined so it should return
>                 X86EMUL_UNIMPLEMENTED if mod R/M bits are not matched.
>                 (e.g. FXSAVE/FXRSTOR )
> 
> Changed since v12:
>     * return X86EMUL_UNIMPLEMENTED if HAVE_GAS_RDRAND is not
> defined
>     * create unrecognized_insn label
>     * return X86EMUL_UNRECOGNIZED in case of failure to decode Group #13
>     instructions.
>     * add a "TODO:" comment to the description of X86EMUL_UNRECOGNIZED
>     stating that for now it can be used interchangeably with
>     X86EMUL_UNIMPLEMENTED.
> ---
>  xen/arch/x86/hvm/emulate.c             | 12 ++++++++
>  xen/arch/x86/hvm/hvm.c                 |  1 +
>  xen/arch/x86/hvm/io.c                  |  5 ++++
>  xen/arch/x86/hvm/vmx/realmode.c        |  9 ++++++
>  xen/arch/x86/mm/shadow/multi.c         |  2 +-
>  xen/arch/x86/x86_emulate/x86_emulate.c | 51 +++++++++++++++++++----
> -----------
>  xen/arch/x86/x86_emulate/x86_emulate.h | 17 ++++++++++++
>  7 files changed, 74 insertions(+), 23 deletions(-)
> 
> diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
> index cc874ce..385fe1e 100644
> --- a/xen/arch/x86/hvm/emulate.c
> +++ b/xen/arch/x86/hvm/emulate.c
> @@ -284,10 +284,15 @@ static int hvmemul_do_io(
>          }
>          break;
>      }
> +    case X86EMUL_UNIMPLEMENTED:
> +        ASSERT_UNREACHABLE();
> +        /* Fall-through */
>      default:
>          BUG();
>      }
> 
> +    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
> +
>      if ( rc != X86EMUL_OKAY )
>          return rc;
> 
> @@ -313,6 +318,9 @@ static int hvmemul_do_io_buffer(
> 
>      rc = hvmemul_do_io(is_mmio, addr, reps, size, dir, df, 0,
>                         (uintptr_t)buffer);
> +
> +    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
> +
>      if ( rc == X86EMUL_UNHANDLEABLE && dir == IOREQ_READ )
>          memset(buffer, 0xff, size);
> 
> @@ -405,6 +413,8 @@ static int hvmemul_do_io_addr(
>      rc = hvmemul_do_io(is_mmio, addr, &count, size, dir, df, 1,
>                         ram_gpa);
> 
> +    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
> +
>      if ( rc == X86EMUL_OKAY )
>          v->arch.hvm_vcpu.hvm_io.mmio_retry = (count < *reps);
> 
> @@ -2045,6 +2055,7 @@ int hvm_emulate_one_mmio(unsigned long mfn,
> unsigned long gla)
>      switch ( rc )
>      {
>      case X86EMUL_UNHANDLEABLE:
> +    case X86EMUL_UNIMPLEMENTED:
>          hvm_dump_emulation_state(XENLOG_G_WARNING, "MMCFG",
> &ctxt);
>          break;
>      case X86EMUL_EXCEPTION:
> @@ -2102,6 +2113,7 @@ void hvm_emulate_one_vm_event(enum
> emul_kind kind, unsigned int trapnr,
>           * consistent with X86EMUL_RETRY.
>           */
>          return;
> +    case X86EMUL_UNIMPLEMENTED:
>      case X86EMUL_UNHANDLEABLE:
>          hvm_dump_emulation_state(XENLOG_G_DEBUG, "Mem event", &ctx);
>          hvm_inject_hw_exception(trapnr, errcode);
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index d99f4b4..57f3f76 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -3735,6 +3735,7 @@ void hvm_ud_intercept(struct cpu_user_regs
> *regs)
>      switch ( hvm_emulate_one(&ctxt) )
>      {
>      case X86EMUL_UNHANDLEABLE:
> +    case X86EMUL_UNIMPLEMENTED:
>          hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
>          break;
>      case X86EMUL_EXCEPTION:
> diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
> index bf41954..b8c0ae7 100644
> --- a/xen/arch/x86/hvm/io.c
> +++ b/xen/arch/x86/hvm/io.c
> @@ -99,6 +99,11 @@ bool
> hvm_emulate_one_insn(hvm_emulate_validate_t *validate, const char
> *descr)
>          hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt);
>          return false;
> 
> +    case X86EMUL_UNRECOGNIZED:
> +        hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt);
> +        hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
> +        break;
> +
>      case X86EMUL_EXCEPTION:
>          hvm_inject_event(&ctxt.ctxt.event);
>          break;
> diff --git a/xen/arch/x86/hvm/vmx/realmode.c
> b/xen/arch/x86/hvm/vmx/realmode.c
> index 12d43ad..b73fc80 100644
> --- a/xen/arch/x86/hvm/vmx/realmode.c
> +++ b/xen/arch/x86/hvm/vmx/realmode.c
> @@ -112,6 +112,15 @@ void vmx_realmode_emulate_one(struct
> hvm_emulate_ctxt *hvmemul_ctxt)
>          goto fail;
>      }
> 
> +    if ( rc == X86EMUL_UNRECOGNIZED )
> +    {
> +        gdprintk(XENLOG_ERR, "Unrecognized insn.\n");
> +        if ( curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE )
> +            goto fail;
> +
> +        realmode_deliver_exception(TRAP_invalid_op, 0, hvmemul_ctxt);
> +    }
> +
>      if ( rc == X86EMUL_EXCEPTION )
>      {
>          if ( unlikely(curr->domain->debugger_attached) &&
> diff --git a/xen/arch/x86/mm/shadow/multi.c
> b/xen/arch/x86/mm/shadow/multi.c
> index 8d4f244..2557e21 100644
> --- a/xen/arch/x86/mm/shadow/multi.c
> +++ b/xen/arch/x86/mm/shadow/multi.c
> @@ -3488,7 +3488,7 @@ static int sh_page_fault(struct vcpu *v,
>       * would be a good unshadow hint. If we *do* decide to unshadow-on-
> fault
>       * then it must be 'failable': we cannot require the unshadow to succeed.
>       */
> -    if ( r == X86EMUL_UNHANDLEABLE )
> +    if ( r == X86EMUL_UNHANDLEABLE || r == X86EMUL_UNIMPLEMENTED )
>      {
>          perfc_incr(shadow_fault_emulate_failed);
>  #if SHADOW_OPTIMIZATIONS & SHOPT_FAST_EMULATION
> diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c
> b/xen/arch/x86/x86_emulate/x86_emulate.c
> index c1e2300..5be33d8 100644
> --- a/xen/arch/x86/x86_emulate/x86_emulate.c
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.c
> @@ -848,7 +848,8 @@ do{ asm volatile (                                                      \
>                  stub.func);                                             \
>          generate_exception_if(res_.fields.trapnr == EXC_UD, EXC_UD);    \
>          domain_crash(current->domain);                                  \
> -        goto cannot_emulate;                                            \
> +        rc = X86EMUL_UNHANDLEABLE;                                      \
> +        goto done;                                                      \
>      }                                                                   \
>  } while (0)
>  #else
> @@ -2585,7 +2586,7 @@ x86_decode(
>                          d = twobyte_table[0x3a].desc;
>                          break;
>                      default:
> -                        rc = X86EMUL_UNHANDLEABLE;
> +                        rc = X86EMUL_UNRECOGNIZED;
>                          goto done;
>                      }
>                  }
> @@ -2599,7 +2600,7 @@ x86_decode(
>                  }
>                  else
>                  {
> -                    rc = X86EMUL_UNHANDLEABLE;
> +                    rc = X86EMUL_UNRECOGNIZED;
>                      goto done;
>                  }
> 
> @@ -2879,7 +2880,7 @@ x86_decode(
> 
>      default:
>          ASSERT_UNREACHABLE();
> -        return X86EMUL_UNHANDLEABLE;
> +        return X86EMUL_UNIMPLEMENTED;
>      }
> 
>      if ( ea.type == OP_MEM )
> @@ -4191,7 +4192,7 @@ x86_emulate(
>                  break;
>              case 4: /* fldenv - TODO */
>                  state->fpu_ctrl = true;
> -                goto cannot_emulate;
> +                goto unimplemented_insn;
>              case 5: /* fldcw m2byte */
>                  state->fpu_ctrl = true;
>                  if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
> @@ -4202,7 +4203,7 @@ x86_emulate(
>                  break;
>              case 6: /* fnstenv - TODO */
>                  state->fpu_ctrl = true;
> -                goto cannot_emulate;
> +                goto unimplemented_insn;
>              case 7: /* fnstcw m2byte */
>                  state->fpu_ctrl = true;
>                  emulate_fpu_insn_memdst("fnstcw", dst.val);
> @@ -4438,7 +4439,7 @@ x86_emulate(
>              case 4: /* frstor - TODO */
>              case 6: /* fnsave - TODO */
>                  state->fpu_ctrl = true;
> -                goto cannot_emulate;
> +                goto unimplemented_insn;
>              case 7: /* fnstsw m2byte */
>                  state->fpu_ctrl = true;
>                  emulate_fpu_insn_memdst("fnstsw", dst.val);
> @@ -5197,7 +5198,7 @@ x86_emulate(
>  #undef _GRP7
> 
>          default:
> -            goto cannot_emulate;
> +            goto unimplemented_insn;
>          }
>          break;
>      }
> @@ -6195,7 +6196,7 @@ x86_emulate(
>                  /* vpsll{w,d} $imm8,{x,y}mm,{x,y}mm */
>              break;
>          default:
> -            goto cannot_emulate;
> +            goto unrecognized_insn;
>          }
>      simd_0f_shift_imm:
>          generate_exception_if(ea.type != OP_REG, EXC_UD);
> @@ -6243,7 +6244,7 @@ x86_emulate(
>          case 6: /* psllq $imm8,mm */
>              goto simd_0f_shift_imm;
>          }
> -        goto cannot_emulate;
> +        goto unrecognized_insn;
> 
>      case X86EMUL_OPC_66(0x0f, 0x73):
>      case X86EMUL_OPC_VEX_66(0x0f, 0x73):
> @@ -6259,7 +6260,7 @@ x86_emulate(
>                  /* vpslldq $imm8,{x,y}mm,{x,y}mm */
>              goto simd_0f_shift_imm;
>          }
> -        goto cannot_emulate;
> +        goto unrecognized_insn;
> 
>      case X86EMUL_OPC(0x0f, 0x77):        /* emms */
>      case X86EMUL_OPC_VEX(0x0f, 0x77):    /* vzero{all,upper} */
> @@ -6323,7 +6324,7 @@ x86_emulate(
>          case 0: /* extrq $imm8,$imm8,xmm */
>              break;
>          default:
> -            goto cannot_emulate;
> +            goto unrecognized_insn;
>          }
>          /* fall through */
>      case X86EMUL_OPC_F2(0x0f, 0x78):     /* insertq $imm8,$imm8,xmm,xmm
> */
> @@ -6518,7 +6519,7 @@ x86_emulate(
>                  goto done;
>              break;
>          default:
> -            goto cannot_emulate;
> +            goto unimplemented_insn;
>          }
>          break;
> 
> @@ -6534,7 +6535,7 @@ x86_emulate(
>              vcpu_must_have(avx);
>              goto stmxcsr;
>          }
> -        goto cannot_emulate;
> +        goto unrecognized_insn;
> 
>      case X86EMUL_OPC_F3(0x0f, 0xae): /* Grp15 */
>          fail_if(modrm_mod != 3);
> @@ -6777,10 +6778,10 @@ x86_emulate(
>              switch ( modrm_reg & 7 )
>              {
>              default:
> -                goto cannot_emulate;
> +                goto unrecognized_insn;
> 
> -#ifdef HAVE_GAS_RDRAND
>              case 6: /* rdrand */
> +#ifdef HAVE_GAS_RDRAND
>                  generate_exception_if(rep_prefix(), EXC_UD);
>                  host_and_vcpu_must_have(rdrand);
>                  dst = ea;
> @@ -6805,6 +6806,8 @@ x86_emulate(
>                  if ( carry )
>                      _regs.eflags |= X86_EFLAGS_CF;
>                  break;
> +#else
> +                goto unimplemented_insn;
>  #endif
> 
>              case 7: /* rdseed / rdpid */
> @@ -7359,7 +7362,7 @@ x86_emulate(
>              host_and_vcpu_must_have(bmi1);
>              break;
>          default:
> -            goto cannot_emulate;
> +            goto unrecognized_insn;
>          }
> 
>          generate_exception_if(vex.l, EXC_UD);
> @@ -7670,7 +7673,7 @@ x86_emulate(
>              host_and_vcpu_must_have(tbm);
>              break;
>          default:
> -            goto cannot_emulate;
> +            goto unrecognized_insn;
>          }
> 
>      xop_09_rm_rv:
> @@ -7704,7 +7707,7 @@ x86_emulate(
>              host_and_vcpu_must_have(tbm);
>              goto xop_09_rm_rv;
>          }
> -        goto cannot_emulate;
> +        goto unrecognized_insn;
> 
>      case X86EMUL_OPC_XOP(0a, 0x10): /* bextr imm,r/m,r */
>      {
> @@ -7736,8 +7739,11 @@ x86_emulate(
>      }
> 
>      default:
> -    cannot_emulate:
> -        rc = X86EMUL_UNHANDLEABLE;
> +    unimplemented_insn:
> +        rc = X86EMUL_UNIMPLEMENTED;
> +        goto done;
> +    unrecognized_insn:
> +        rc = X86EMUL_UNRECOGNIZED;
>          goto done;
>      }
> 
> @@ -7789,7 +7795,8 @@ x86_emulate(
>                  if ( (d & DstMask) != DstMem )
>                  {
>                      ASSERT_UNREACHABLE();
> -                    goto cannot_emulate;
> +                    rc = X86EMUL_UNHANDLEABLE;
> +                    goto done;
>                  }
>                  break;
>              }
> diff --git a/xen/arch/x86/x86_emulate/x86_emulate.h
> b/xen/arch/x86/x86_emulate/x86_emulate.h
> index 4ddf111..0c8c80a 100644
> --- a/xen/arch/x86/x86_emulate/x86_emulate.h
> +++ b/xen/arch/x86/x86_emulate/x86_emulate.h
> @@ -133,6 +133,23 @@ struct x86_emul_fpu_aux {
>    * Undefined behavior when used anywhere else.
>    */
>  #define X86EMUL_DONE           4
> + /*
> +  * Current instruction is not implemented by the emulator.
> +  * This value should only be returned by the core emulator when a valid
> +  * opcode is found but the execution logic for that instruction is missing.
> +  * It should NOT be returned by any of the x86_emulate_ops callbacks.
> +  */
> +#define X86EMUL_UNIMPLEMENTED  5
> + /*
> +  * The current instruction's opcode is not valid.
> +  * If this error code is returned by a function, an #UD trap should be
> +  * raised by the final consumer of it.
> +  *
> +  * TODO: For the moment X86EMUL_UNRECOGNIZED and
> X86EMUL_UNIMPLEMENTED
> +  * can be used interchangeably therefore raising an #UD trap is not
> +  * strictly expected for now.
> + */
> +#define X86EMUL_UNRECOGNIZED   X86EMUL_UNIMPLEMENTED
> 
>  /* FPU sub-types which may be requested via ->get_fpu(). */
>  enum x86_emulate_fpu_type {
> --
> 2.7.4
Jan Beulich Sept. 26, 2017, 1:53 p.m. UTC | #2
>>> On 25.09.17 at 14:03, <ppircalabu@bitdefender.com> wrote:
> Enforce the distinction between an instruction not implemented by the
> emulator and the failure to emulate that instruction by defining a new
> return code, X86EMUL_UNIMPLEMENTED.
> 
> This value should only be returned by the core emulator only if it fails to
> properly decode the current instruction's opcode, and not by any of other
> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
> 
> e.g. hvm_process_io_intercept should not return X86EMUL_UNIMPLEMENTED.
> The return value of this function depends on either the return code of
> one of the hvm_io_ops handlers (read/write) or the value returned by
> hvm_copy_guest_from_phys / hvm_copy_to_guest_phys.
> 
> Similary, none of this functions should return X86EMUL_UNIMPLEMENTED.
>  - hvm_io_intercept
>  - hvmemul_do_io
>  - hvm_send_buffered_ioreq
>  - hvm_send_ioreq
>  - hvm_broadcast_ioreq
>  - hvmemul_do_io_buffer
>  - hvmemul_validate
> 
> Also the behavior of hvm_emulate_one_insn and vmx_realmode_emulate_one
> was modified to generate an Invalid Opcode trap when X86EMUL_UNRECOGNIZED
> is returned by the emulator instead of just crash the domain.
> 
> Signed-off-by: Petre Pircalabu <ppircalabu@bitdefender.com>

Reviewed-by: Jan Beulich <jbeulich@suse.com>
George Dunlap Oct. 2, 2017, 1:39 p.m. UTC | #3
On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
> Enforce the distinction between an instruction not implemented by the
> emulator and the failure to emulate that instruction by defining a new
> return code, X86EMUL_UNIMPLEMENTED.
> 
> This value should only be returned by the core emulator only if it fails to
> properly decode the current instruction's opcode, and not by any of other
> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
> 
> e.g. hvm_process_io_intercept should not return X86EMUL_UNIMPLEMENTED.
> The return value of this function depends on either the return code of
> one of the hvm_io_ops handlers (read/write) or the value returned by
> hvm_copy_guest_from_phys / hvm_copy_to_guest_phys.
> 
> Similary, none of this functions should return X86EMUL_UNIMPLEMENTED.
>  - hvm_io_intercept
>  - hvmemul_do_io
>  - hvm_send_buffered_ioreq
>  - hvm_send_ioreq
>  - hvm_broadcast_ioreq
>  - hvmemul_do_io_buffer
>  - hvmemul_validate
> 
> Also the behavior of hvm_emulate_one_insn and vmx_realmode_emulate_one
> was modified to generate an Invalid Opcode trap when X86EMUL_UNRECOGNIZED
> is returned by the emulator instead of just crash the domain.
> 
> Signed-off-by: Petre Pircalabu <ppircalabu@bitdefender.com>

Looks good, thanks:

Reviewed-by: George Dunlap <george.dunlap@citrix.com>
George Dunlap Oct. 2, 2017, 1:43 p.m. UTC | #4
On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
> Enforce the distinction between an instruction not implemented by the
> emulator and the failure to emulate that instruction by defining a new
> return code, X86EMUL_UNIMPLEMENTED.
> 
> This value should only be returned by the core emulator only if it fails to
> properly decode the current instruction's opcode, and not by any of other
> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.

Oh, minor comment:  Should this paragraph be changed to match the
comment in the patch itself?  I.e.:

"This value should only be returned by the core emulator when a valid
opcode is found but the execution logic for that instruction is missing.
It should NOT be returned by any of the x86_emulate_ops callbacks."

 -George
George Dunlap Oct. 2, 2017, 2:09 p.m. UTC | #5
On 10/02/2017 02:43 PM, George Dunlap wrote:
> On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
>> Enforce the distinction between an instruction not implemented by the
>> emulator and the failure to emulate that instruction by defining a new
>> return code, X86EMUL_UNIMPLEMENTED.
>>
>> This value should only be returned by the core emulator only if it fails to
>> properly decode the current instruction's opcode, and not by any of other
>> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
> 
> Oh, minor comment:  Should this paragraph be changed to match the
> comment in the patch itself?  I.e.:
> 
> "This value should only be returned by the core emulator when a valid
> opcode is found but the execution logic for that instruction is missing.
> It should NOT be returned by any of the x86_emulate_ops callbacks."

I'll do this on check-in if I don't hear any objections by tomorrow.

 -George
Petre Ovidiu PIRCALABU Oct. 2, 2017, 2:42 p.m. UTC | #6
On Lu, 2017-10-02 at 15:09 +0100, George Dunlap wrote:
> On 10/02/2017 02:43 PM, George Dunlap wrote:

> > 

> > On 09/25/2017 01:03 PM, Petre Pircalabu wrote:

> > > 

> > > Enforce the distinction between an instruction not implemented by

> > > the

> > > emulator and the failure to emulate that instruction by defining

> > > a new

> > > return code, X86EMUL_UNIMPLEMENTED.

> > > 

> > > This value should only be returned by the core emulator only if

> > > it fails to

> > > properly decode the current instruction's opcode, and not by any

> > > of other

> > > functions, such as the x86_emulate_ops or the hvm_io_ops

> > > callbacks.

> > Oh, minor comment:  Should this paragraph be changed to match the

> > comment in the patch itself?  I.e.:

> > 

> > "This value should only be returned by the core emulator when a

> > valid

> > opcode is found but the execution logic for that instruction is

> > missing.

> > It should NOT be returned by any of the x86_emulate_ops callbacks."

I've changed the comment from the definition multiple times and I
forgot to update the patch comment. Indeed, the newest description
better reflects the current usage of X86EMUL_UNIMPLEMENTED.
> I'll do this on check-in if I don't hear any objections by tomorrow.

> 

>  -George

Many thanks for your support,
Petre
> 

> ________________________

> This email was scanned by Bitdefender
George Dunlap Oct. 2, 2017, 3 p.m. UTC | #7
On 10/02/2017 03:42 PM, Petre Ovidiu PIRCALABU wrote:
> On Lu, 2017-10-02 at 15:09 +0100, George Dunlap wrote:
>> On 10/02/2017 02:43 PM, George Dunlap wrote:
>>>
>>> On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
>>>>
>>>> Enforce the distinction between an instruction not implemented by
>>>> the
>>>> emulator and the failure to emulate that instruction by defining
>>>> a new
>>>> return code, X86EMUL_UNIMPLEMENTED.
>>>>
>>>> This value should only be returned by the core emulator only if
>>>> it fails to
>>>> properly decode the current instruction's opcode, and not by any
>>>> of other
>>>> functions, such as the x86_emulate_ops or the hvm_io_ops
>>>> callbacks.
>>> Oh, minor comment:  Should this paragraph be changed to match the
>>> comment in the patch itself?  I.e.:
>>>
>>> "This value should only be returned by the core emulator when a
>>> valid
>>> opcode is found but the execution logic for that instruction is
>>> missing.
>>> It should NOT be returned by any of the x86_emulate_ops callbacks."
> I've changed the comment from the definition multiple times and I
> forgot to update the patch comment. Indeed, the newest description
> better reflects the current usage of X86EMUL_UNIMPLEMENTED.

No worries, it happens. :-) Thanks again for all your effort with this,
and sorry for the late review.

 -George
Jan Beulich Oct. 4, 2017, 11:11 a.m. UTC | #8
>>> On 02.10.17 at 16:09, <george.dunlap@citrix.com> wrote:
> On 10/02/2017 02:43 PM, George Dunlap wrote:
>> On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
>>> Enforce the distinction between an instruction not implemented by the
>>> emulator and the failure to emulate that instruction by defining a new
>>> return code, X86EMUL_UNIMPLEMENTED.
>>>
>>> This value should only be returned by the core emulator only if it fails to
>>> properly decode the current instruction's opcode, and not by any of other
>>> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
>> 
>> Oh, minor comment:  Should this paragraph be changed to match the
>> comment in the patch itself?  I.e.:
>> 
>> "This value should only be returned by the core emulator when a valid
>> opcode is found but the execution logic for that instruction is missing.
>> It should NOT be returned by any of the x86_emulate_ops callbacks."
> 
> I'll do this on check-in if I don't hear any objections by tomorrow.

This shouldn't really have gone in without a VMX maintainer ack.

Jan
George Dunlap Oct. 4, 2017, 11:14 a.m. UTC | #9
On 10/04/2017 12:11 PM, Jan Beulich wrote:
>>>> On 02.10.17 at 16:09, <george.dunlap@citrix.com> wrote:
>> On 10/02/2017 02:43 PM, George Dunlap wrote:
>>> On 09/25/2017 01:03 PM, Petre Pircalabu wrote:
>>>> Enforce the distinction between an instruction not implemented by the
>>>> emulator and the failure to emulate that instruction by defining a new
>>>> return code, X86EMUL_UNIMPLEMENTED.
>>>>
>>>> This value should only be returned by the core emulator only if it fails to
>>>> properly decode the current instruction's opcode, and not by any of other
>>>> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.
>>>
>>> Oh, minor comment:  Should this paragraph be changed to match the
>>> comment in the patch itself?  I.e.:
>>>
>>> "This value should only be returned by the core emulator when a valid
>>> opcode is found but the execution logic for that instruction is missing.
>>> It should NOT be returned by any of the x86_emulate_ops callbacks."
>>
>> I'll do this on check-in if I don't hear any objections by tomorrow.
> 
> This shouldn't really have gone in without a VMX maintainer ack.

Indeed -- sorry I missed that.

 -George
Tian, Kevin Oct. 10, 2017, 5:23 a.m. UTC | #10
> From: George Dunlap [mailto:george.dunlap@citrix.com]

> Sent: Wednesday, October 4, 2017 7:14 PM

> 

> On 10/04/2017 12:11 PM, Jan Beulich wrote:

> >>>> On 02.10.17 at 16:09, <george.dunlap@citrix.com> wrote:

> >> On 10/02/2017 02:43 PM, George Dunlap wrote:

> >>> On 09/25/2017 01:03 PM, Petre Pircalabu wrote:

> >>>> Enforce the distinction between an instruction not implemented by

> the

> >>>> emulator and the failure to emulate that instruction by defining a new

> >>>> return code, X86EMUL_UNIMPLEMENTED.

> >>>>

> >>>> This value should only be returned by the core emulator only if it fails

> to

> >>>> properly decode the current instruction's opcode, and not by any of

> other

> >>>> functions, such as the x86_emulate_ops or the hvm_io_ops callbacks.

> >>>

> >>> Oh, minor comment:  Should this paragraph be changed to match the

> >>> comment in the patch itself?  I.e.:

> >>>

> >>> "This value should only be returned by the core emulator when a valid

> >>> opcode is found but the execution logic for that instruction is missing.

> >>> It should NOT be returned by any of the x86_emulate_ops callbacks."

> >>

> >> I'll do this on check-in if I don't hear any objections by tomorrow.

> >

> > This shouldn't really have gone in without a VMX maintainer ack.

> 

> Indeed -- sorry I missed that.

> 


here comes: 

Reviewed-by: Kevin Tian <kevin.tian@intel.com>
diff mbox

Patch

diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
index cc874ce..385fe1e 100644
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -284,10 +284,15 @@  static int hvmemul_do_io(
         }
         break;
     }
+    case X86EMUL_UNIMPLEMENTED:
+        ASSERT_UNREACHABLE();
+        /* Fall-through */
     default:
         BUG();
     }
 
+    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
+
     if ( rc != X86EMUL_OKAY )
         return rc;
 
@@ -313,6 +318,9 @@  static int hvmemul_do_io_buffer(
 
     rc = hvmemul_do_io(is_mmio, addr, reps, size, dir, df, 0,
                        (uintptr_t)buffer);
+
+    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
+
     if ( rc == X86EMUL_UNHANDLEABLE && dir == IOREQ_READ )
         memset(buffer, 0xff, size);
 
@@ -405,6 +413,8 @@  static int hvmemul_do_io_addr(
     rc = hvmemul_do_io(is_mmio, addr, &count, size, dir, df, 1,
                        ram_gpa);
 
+    ASSERT(rc != X86EMUL_UNIMPLEMENTED);
+
     if ( rc == X86EMUL_OKAY )
         v->arch.hvm_vcpu.hvm_io.mmio_retry = (count < *reps);
 
@@ -2045,6 +2055,7 @@  int hvm_emulate_one_mmio(unsigned long mfn, unsigned long gla)
     switch ( rc )
     {
     case X86EMUL_UNHANDLEABLE:
+    case X86EMUL_UNIMPLEMENTED:
         hvm_dump_emulation_state(XENLOG_G_WARNING, "MMCFG", &ctxt);
         break;
     case X86EMUL_EXCEPTION:
@@ -2102,6 +2113,7 @@  void hvm_emulate_one_vm_event(enum emul_kind kind, unsigned int trapnr,
          * consistent with X86EMUL_RETRY.
          */
         return;
+    case X86EMUL_UNIMPLEMENTED:
     case X86EMUL_UNHANDLEABLE:
         hvm_dump_emulation_state(XENLOG_G_DEBUG, "Mem event", &ctx);
         hvm_inject_hw_exception(trapnr, errcode);
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index d99f4b4..57f3f76 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3735,6 +3735,7 @@  void hvm_ud_intercept(struct cpu_user_regs *regs)
     switch ( hvm_emulate_one(&ctxt) )
     {
     case X86EMUL_UNHANDLEABLE:
+    case X86EMUL_UNIMPLEMENTED:
         hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
         break;
     case X86EMUL_EXCEPTION:
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
index bf41954..b8c0ae7 100644
--- a/xen/arch/x86/hvm/io.c
+++ b/xen/arch/x86/hvm/io.c
@@ -99,6 +99,11 @@  bool hvm_emulate_one_insn(hvm_emulate_validate_t *validate, const char *descr)
         hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt);
         return false;
 
+    case X86EMUL_UNRECOGNIZED:
+        hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt);
+        hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
+        break;
+
     case X86EMUL_EXCEPTION:
         hvm_inject_event(&ctxt.ctxt.event);
         break;
diff --git a/xen/arch/x86/hvm/vmx/realmode.c b/xen/arch/x86/hvm/vmx/realmode.c
index 12d43ad..b73fc80 100644
--- a/xen/arch/x86/hvm/vmx/realmode.c
+++ b/xen/arch/x86/hvm/vmx/realmode.c
@@ -112,6 +112,15 @@  void vmx_realmode_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt)
         goto fail;
     }
 
+    if ( rc == X86EMUL_UNRECOGNIZED )
+    {
+        gdprintk(XENLOG_ERR, "Unrecognized insn.\n");
+        if ( curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE )
+            goto fail;
+
+        realmode_deliver_exception(TRAP_invalid_op, 0, hvmemul_ctxt);
+    }
+
     if ( rc == X86EMUL_EXCEPTION )
     {
         if ( unlikely(curr->domain->debugger_attached) &&
diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c
index 8d4f244..2557e21 100644
--- a/xen/arch/x86/mm/shadow/multi.c
+++ b/xen/arch/x86/mm/shadow/multi.c
@@ -3488,7 +3488,7 @@  static int sh_page_fault(struct vcpu *v,
      * would be a good unshadow hint. If we *do* decide to unshadow-on-fault
      * then it must be 'failable': we cannot require the unshadow to succeed.
      */
-    if ( r == X86EMUL_UNHANDLEABLE )
+    if ( r == X86EMUL_UNHANDLEABLE || r == X86EMUL_UNIMPLEMENTED )
     {
         perfc_incr(shadow_fault_emulate_failed);
 #if SHADOW_OPTIMIZATIONS & SHOPT_FAST_EMULATION
diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c
index c1e2300..5be33d8 100644
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -848,7 +848,8 @@  do{ asm volatile (                                                      \
                 stub.func);                                             \
         generate_exception_if(res_.fields.trapnr == EXC_UD, EXC_UD);    \
         domain_crash(current->domain);                                  \
-        goto cannot_emulate;                                            \
+        rc = X86EMUL_UNHANDLEABLE;                                      \
+        goto done;                                                      \
     }                                                                   \
 } while (0)
 #else
@@ -2585,7 +2586,7 @@  x86_decode(
                         d = twobyte_table[0x3a].desc;
                         break;
                     default:
-                        rc = X86EMUL_UNHANDLEABLE;
+                        rc = X86EMUL_UNRECOGNIZED;
                         goto done;
                     }
                 }
@@ -2599,7 +2600,7 @@  x86_decode(
                 }
                 else
                 {
-                    rc = X86EMUL_UNHANDLEABLE;
+                    rc = X86EMUL_UNRECOGNIZED;
                     goto done;
                 }
 
@@ -2879,7 +2880,7 @@  x86_decode(
 
     default:
         ASSERT_UNREACHABLE();
-        return X86EMUL_UNHANDLEABLE;
+        return X86EMUL_UNIMPLEMENTED;
     }
 
     if ( ea.type == OP_MEM )
@@ -4191,7 +4192,7 @@  x86_emulate(
                 break;
             case 4: /* fldenv - TODO */
                 state->fpu_ctrl = true;
-                goto cannot_emulate;
+                goto unimplemented_insn;
             case 5: /* fldcw m2byte */
                 state->fpu_ctrl = true;
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
@@ -4202,7 +4203,7 @@  x86_emulate(
                 break;
             case 6: /* fnstenv - TODO */
                 state->fpu_ctrl = true;
-                goto cannot_emulate;
+                goto unimplemented_insn;
             case 7: /* fnstcw m2byte */
                 state->fpu_ctrl = true;
                 emulate_fpu_insn_memdst("fnstcw", dst.val);
@@ -4438,7 +4439,7 @@  x86_emulate(
             case 4: /* frstor - TODO */
             case 6: /* fnsave - TODO */
                 state->fpu_ctrl = true;
-                goto cannot_emulate;
+                goto unimplemented_insn;
             case 7: /* fnstsw m2byte */
                 state->fpu_ctrl = true;
                 emulate_fpu_insn_memdst("fnstsw", dst.val);
@@ -5197,7 +5198,7 @@  x86_emulate(
 #undef _GRP7
 
         default:
-            goto cannot_emulate;
+            goto unimplemented_insn;
         }
         break;
     }
@@ -6195,7 +6196,7 @@  x86_emulate(
                 /* vpsll{w,d} $imm8,{x,y}mm,{x,y}mm */
             break;
         default:
-            goto cannot_emulate;
+            goto unrecognized_insn;
         }
     simd_0f_shift_imm:
         generate_exception_if(ea.type != OP_REG, EXC_UD);
@@ -6243,7 +6244,7 @@  x86_emulate(
         case 6: /* psllq $imm8,mm */
             goto simd_0f_shift_imm;
         }
-        goto cannot_emulate;
+        goto unrecognized_insn;
 
     case X86EMUL_OPC_66(0x0f, 0x73):
     case X86EMUL_OPC_VEX_66(0x0f, 0x73):
@@ -6259,7 +6260,7 @@  x86_emulate(
                 /* vpslldq $imm8,{x,y}mm,{x,y}mm */
             goto simd_0f_shift_imm;
         }
-        goto cannot_emulate;
+        goto unrecognized_insn;
 
     case X86EMUL_OPC(0x0f, 0x77):        /* emms */
     case X86EMUL_OPC_VEX(0x0f, 0x77):    /* vzero{all,upper} */
@@ -6323,7 +6324,7 @@  x86_emulate(
         case 0: /* extrq $imm8,$imm8,xmm */
             break;
         default:
-            goto cannot_emulate;
+            goto unrecognized_insn;
         }
         /* fall through */
     case X86EMUL_OPC_F2(0x0f, 0x78):     /* insertq $imm8,$imm8,xmm,xmm */
@@ -6518,7 +6519,7 @@  x86_emulate(
                 goto done;
             break;
         default:
-            goto cannot_emulate;
+            goto unimplemented_insn;
         }
         break;
 
@@ -6534,7 +6535,7 @@  x86_emulate(
             vcpu_must_have(avx);
             goto stmxcsr;
         }
-        goto cannot_emulate;
+        goto unrecognized_insn;
 
     case X86EMUL_OPC_F3(0x0f, 0xae): /* Grp15 */
         fail_if(modrm_mod != 3);
@@ -6777,10 +6778,10 @@  x86_emulate(
             switch ( modrm_reg & 7 )
             {
             default:
-                goto cannot_emulate;
+                goto unrecognized_insn;
 
-#ifdef HAVE_GAS_RDRAND
             case 6: /* rdrand */
+#ifdef HAVE_GAS_RDRAND
                 generate_exception_if(rep_prefix(), EXC_UD);
                 host_and_vcpu_must_have(rdrand);
                 dst = ea;
@@ -6805,6 +6806,8 @@  x86_emulate(
                 if ( carry )
                     _regs.eflags |= X86_EFLAGS_CF;
                 break;
+#else
+                goto unimplemented_insn;
 #endif
 
             case 7: /* rdseed / rdpid */
@@ -7359,7 +7362,7 @@  x86_emulate(
             host_and_vcpu_must_have(bmi1);
             break;
         default:
-            goto cannot_emulate;
+            goto unrecognized_insn;
         }
 
         generate_exception_if(vex.l, EXC_UD);
@@ -7670,7 +7673,7 @@  x86_emulate(
             host_and_vcpu_must_have(tbm);
             break;
         default:
-            goto cannot_emulate;
+            goto unrecognized_insn;
         }
 
     xop_09_rm_rv:
@@ -7704,7 +7707,7 @@  x86_emulate(
             host_and_vcpu_must_have(tbm);
             goto xop_09_rm_rv;
         }
-        goto cannot_emulate;
+        goto unrecognized_insn;
 
     case X86EMUL_OPC_XOP(0a, 0x10): /* bextr imm,r/m,r */
     {
@@ -7736,8 +7739,11 @@  x86_emulate(
     }
 
     default:
-    cannot_emulate:
-        rc = X86EMUL_UNHANDLEABLE;
+    unimplemented_insn:
+        rc = X86EMUL_UNIMPLEMENTED;
+        goto done;
+    unrecognized_insn:
+        rc = X86EMUL_UNRECOGNIZED;
         goto done;
     }
 
@@ -7789,7 +7795,8 @@  x86_emulate(
                 if ( (d & DstMask) != DstMem )
                 {
                     ASSERT_UNREACHABLE();
-                    goto cannot_emulate;
+                    rc = X86EMUL_UNHANDLEABLE;
+                    goto done;
                 }
                 break;
             }
diff --git a/xen/arch/x86/x86_emulate/x86_emulate.h b/xen/arch/x86/x86_emulate/x86_emulate.h
index 4ddf111..0c8c80a 100644
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
@@ -133,6 +133,23 @@  struct x86_emul_fpu_aux {
   * Undefined behavior when used anywhere else.
   */
 #define X86EMUL_DONE           4
+ /*
+  * Current instruction is not implemented by the emulator.
+  * This value should only be returned by the core emulator when a valid
+  * opcode is found but the execution logic for that instruction is missing.
+  * It should NOT be returned by any of the x86_emulate_ops callbacks.
+  */
+#define X86EMUL_UNIMPLEMENTED  5
+ /*
+  * The current instruction's opcode is not valid.
+  * If this error code is returned by a function, an #UD trap should be
+  * raised by the final consumer of it.
+  *
+  * TODO: For the moment X86EMUL_UNRECOGNIZED and X86EMUL_UNIMPLEMENTED
+  * can be used interchangeably therefore raising an #UD trap is not
+  * strictly expected for now.
+ */
+#define X86EMUL_UNRECOGNIZED   X86EMUL_UNIMPLEMENTED
 
 /* FPU sub-types which may be requested via ->get_fpu(). */
 enum x86_emulate_fpu_type {