return 0;
}
+/*
+ * TASK_SWITCH vmexits never provide an instruction length. We must always
+ * decode under %rip to find the answer.
+ */
+unsigned int svm_get_task_switch_insn_len(void)
+{
+ struct hvm_emulate_ctxt ctxt;
+ struct x86_emulate_state *state;
+ unsigned int emul_len, modrm_reg;
+
+ hvm_emulate_init_once(&ctxt, NULL, guest_cpu_user_regs());
+ hvm_emulate_init_per_insn(&ctxt, NULL, 0);
+ state = x86_decode_insn(&ctxt.ctxt, hvmemul_insn_fetch);
+ if ( IS_ERR_OR_NULL(state) )
+ return 0;
+
+ emul_len = x86_insn_length(state, &ctxt.ctxt);
+
+ /*
+ * Check for an instruction which can cause a task switch. Any far
+ * jmp/call/ret, any software interrupt/exception with trap semantics
+ * (except icebp - handled specially), and iret.
+ */
+ switch ( ctxt.ctxt.opcode )
+ {
+ case 0xff: /* Grp 5 */
+ /* call / jmp (far, absolute indirect) */
+ if ( (unsigned int)x86_insn_modrm(state, NULL, &modrm_reg) >= 3 ||
+ (modrm_reg != 3 && modrm_reg != 5) )
+ {
+ default:
+ printk(XENLOG_G_WARNING "Bad instruction for task switch\n");
+ hvm_dump_emulation_state(XENLOG_G_WARNING, "SVM Insn len",
+ &ctxt, X86EMUL_UNHANDLEABLE);
+ emul_len = 0;
+ break;
+ }
+ /* Fallthrough */
+ case 0x9a: /* call (far, absolute) */
+ case 0xca: /* ret imm16 (far) */
+ case 0xcb: /* ret (far) */
+ case 0xcc: /* int3 */
+ case 0xcd: /* int imm8 */
+ case 0xce: /* into */
+ case 0xcf: /* iret */
+ case 0xea: /* jmp (far, absolute) */
+ break;
+ }
+
+ x86_emulate_free_state(state);
+
+ return emul_len;
+}
+
/*
* Local variables:
* mode: C
case VMEXIT_TASK_SWITCH: {
enum hvm_task_switch_reason reason;
- int32_t errcode = -1;
+ int32_t errcode = -1, insn_len = -1;
+
+ /*
+ * All TASK_SWITCH intercepts have fault-like semantics. NRIP is
+ * never provided, even for instruction-induced task switches, but we
+ * need to know the instruction length in order to set %eip suitably
+ * in the outgoing TSS.
+ *
+ * For a task switch which vectored through the IDT, look at the type
+ * to distinguish interrupts/exceptions from instruction based
+ * switches.
+ */
+ if ( vmcb->exitintinfo.fields.v )
+ {
+ switch ( vmcb->exitintinfo.fields.type )
+ {
+ /*
+ * #BP and #OF are from INT3/INTO respectively. #DB from
+ * ICEBP is handled specially, and already has fault
+ * semantics.
+ */
+ case X86_EVENTTYPE_HW_EXCEPTION:
+ if ( vmcb->exitintinfo.fields.vector == TRAP_int3 ||
+ vmcb->exitintinfo.fields.vector == TRAP_overflow )
+ break;
+ /* Fallthrough */
+ case X86_EVENTTYPE_EXT_INTR:
+ case X86_EVENTTYPE_NMI:
+ insn_len = 0;
+ break;
+ }
+
+ /*
+ * The common logic above will have forwarded the vectoring
+ * information. Undo this as we are going to emulate.
+ */
+ vmcb->eventinj.bytes = 0;
+ }
+
+ /*
+ * insn_len being -1 indicates that we have an instruction-induced
+ * task switch. Decode under %rip to find its length.
+ */
+ if ( insn_len < 0 && (insn_len = svm_get_task_switch_insn_len()) == 0 )
+ goto crash_or_fault;
+
if ( (vmcb->exitinfo2 >> 36) & 1 )
reason = TSW_iret;
else if ( (vmcb->exitinfo2 >> 38) & 1 )
if ( (vmcb->exitinfo2 >> 44) & 1 )
errcode = (uint32_t)vmcb->exitinfo2;
- /*
- * Some processors set the EXITINTINFO field when the task switch
- * is caused by a task gate in the IDT. In this case we will be
- * emulating the event injection, so we do not want the processor
- * to re-inject the original event!
- */
- vmcb->eventinj.bytes = 0;
-
- hvm_task_switch(vmcb->exitinfo1, reason, errcode, 0);
+ hvm_task_switch(vmcb->exitinfo1, reason, errcode, insn_len);
break;
}
gprintk(XENLOG_ERR, "Unexpected vmexit: reason %#"PRIx64", "
"exitinfo1 %#"PRIx64", exitinfo2 %#"PRIx64"\n",
exit_reason, vmcb->exitinfo1, vmcb->exitinfo2);
+ crash_or_fault:
svm_crash_or_fault(v);
break;
}