The VT-x task switch handler adds inst_len to %rip before calling
hvm_task_switch(), which is problematic in two ways:
1) Early faults (i.e. ones delivered in the context of the old task) get
delivered with trap semantics, and break restartibility.
2) The addition isn't truncated to 32 bits. In the corner case of a task
switch instruction crossing the 4G->0 boundary taking an early fault (with
trap semantics), a VMEntry failure will occur due to %rip being out of
range.
Instead, pass the instruction length into hvm_task_switch() and write it into
the outgoing TSS only, leaving %rip in its original location.
For now, pass 0 on the SVM side. This highlights a separate preexisting bug
which will be addressed in the following patch.
While adjusting call sites, drop the unnecessary uint16_t cast.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Release-acked-by: Juergen Gross <jgross@suse.com>
void hvm_task_switch(
uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
- int32_t errcode)
+ int32_t errcode, unsigned int insn_len)
{
struct vcpu *v = current;
struct cpu_user_regs *regs = guest_cpu_user_regs();
if ( taskswitch_reason == TSW_iret )
eflags &= ~X86_EFLAGS_NT;
- tss.eip = regs->eip;
+ tss.eip = regs->eip + insn_len;
tss.eflags = eflags;
tss.eax = regs->eax;
tss.ecx = regs->ecx;
*/
vmcb->eventinj.bytes = 0;
- hvm_task_switch((uint16_t)vmcb->exitinfo1, reason, errcode);
+ hvm_task_switch(vmcb->exitinfo1, reason, errcode, 0);
break;
}
__vmread(IDT_VECTORING_ERROR_CODE, &ecode);
else
ecode = -1;
- regs->rip += inst_len;
- hvm_task_switch((uint16_t)exit_qualification, reasons[source], ecode);
+
+ hvm_task_switch(exit_qualification, reasons[source], ecode, inst_len);
break;
}
case EXIT_REASON_CPUID:
enum hvm_task_switch_reason { TSW_jmp, TSW_iret, TSW_call_or_int };
void hvm_task_switch(
uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
- int32_t errcode);
+ int32_t errcode, unsigned int insn_len);
enum hvm_access_type {
hvm_access_insn_fetch,