Skip to content

Commit

Permalink
KVM: x86: hardware task switching support
Browse files Browse the repository at this point in the history
This emulates the x86 hardware task switch mechanism in software, as it is
unsupported by either vmx or svm.  It allows operating systems which use it,
like freedos, to run as kvm guests.

Signed-off-by: Izik Eidus <izike@qumranet.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
  • Loading branch information
Izik Eidus authored and Avi Kivity committed Apr 27, 2008
1 parent 2e4d265 commit 37817f2
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 3 deletions.
15 changes: 12 additions & 3 deletions arch/x86/kvm/svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1112,9 +1112,18 @@ static int invalid_op_interception(struct vcpu_svm *svm,
static int task_switch_interception(struct vcpu_svm *svm,
struct kvm_run *kvm_run)
{
pr_unimpl(&svm->vcpu, "%s: task switch is unsupported\n", __func__);
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
return 0;
u16 tss_selector;

tss_selector = (u16)svm->vmcb->control.exit_info_1;
if (svm->vmcb->control.exit_info_2 &
(1ULL << SVM_EXITINFOSHIFT_TS_REASON_IRET))
return kvm_task_switch(&svm->vcpu, tss_selector,
TASK_SWITCH_IRET);
if (svm->vmcb->control.exit_info_2 &
(1ULL << SVM_EXITINFOSHIFT_TS_REASON_JMP))
return kvm_task_switch(&svm->vcpu, tss_selector,
TASK_SWITCH_JMP);
return kvm_task_switch(&svm->vcpu, tss_selector, TASK_SWITCH_CALL);
}

static int cpuid_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/kvm/svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ struct __attribute__ ((__packed__)) vmcb {
#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID
#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR

#define SVM_EXITINFOSHIFT_TS_REASON_IRET 36
#define SVM_EXITINFOSHIFT_TS_REASON_JMP 38

#define SVM_EXIT_READ_CR0 0x000
#define SVM_EXIT_READ_CR3 0x003
#define SVM_EXIT_READ_CR4 0x004
Expand Down
59 changes: 59 additions & 0 deletions arch/x86/kvm/tss.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#ifndef __TSS_SEGMENT_H
#define __TSS_SEGMENT_H

struct tss_segment_32 {
u32 prev_task_link;
u32 esp0;
u32 ss0;
u32 esp1;
u32 ss1;
u32 esp2;
u32 ss2;
u32 cr3;
u32 eip;
u32 eflags;
u32 eax;
u32 ecx;
u32 edx;
u32 ebx;
u32 esp;
u32 ebp;
u32 esi;
u32 edi;
u32 es;
u32 cs;
u32 ss;
u32 ds;
u32 fs;
u32 gs;
u32 ldt_selector;
u16 t;
u16 io_map;
};

struct tss_segment_16 {
u16 prev_task_link;
u16 sp0;
u16 ss0;
u16 sp1;
u16 ss1;
u16 sp2;
u16 ss2;
u16 ip;
u16 flag;
u16 ax;
u16 cx;
u16 dx;
u16 bx;
u16 sp;
u16 bp;
u16 si;
u16 di;
u16 es;
u16 cs;
u16 ss;
u16 ds;
u16 ldt;
};

#endif
15 changes: 15 additions & 0 deletions arch/x86/kvm/vmx.c
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,20 @@ static int handle_apic_access(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
return 1;
}

static int handle_task_switch(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
unsigned long exit_qualification;
u16 tss_selector;
int reason;

exit_qualification = vmcs_readl(EXIT_QUALIFICATION);

reason = (u32)exit_qualification >> 30;
tss_selector = exit_qualification;

return kvm_task_switch(vcpu, tss_selector, reason);
}

/*
* The exit handlers return 1 if the exit was handled fully and guest execution
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
Expand All @@ -2271,6 +2285,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
[EXIT_REASON_TPR_BELOW_THRESHOLD] = handle_tpr_below_threshold,
[EXIT_REASON_APIC_ACCESS] = handle_apic_access,
[EXIT_REASON_WBINVD] = handle_wbinvd,
[EXIT_REASON_TASK_SWITCH] = handle_task_switch,
};

static const int kvm_vmx_max_exit_handlers =
Expand Down
Loading

0 comments on commit 37817f2

Please sign in to comment.