Skip to content

Commit

Permalink
KVM: task switch: use seg regs provided by subarch instead of reading…
Browse files Browse the repository at this point in the history
… from GDT

There is no guarantee that the old TSS descriptor in the GDT contains
the proper base address. This is the case for Windows installation's
reboot-via-triplefault.

Use guest registers instead. Also translate the address properly.

Signed-off-by: Marcelo Tosatti <[email protected]>
Signed-off-by: Avi Kivity <[email protected]>
  • Loading branch information
matosatti authored and avikivity committed Jul 27, 2008
1 parent 98899aa commit 34198bf
Showing 1 changed file with 30 additions and 63 deletions.
93 changes: 30 additions & 63 deletions arch/x86/kvm/x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -3267,54 +3267,6 @@ static u32 get_tss_base_addr(struct kvm_vcpu *vcpu,
return vcpu->arch.mmu.gva_to_gpa(vcpu, base_addr);
}

static int load_tss_segment32(struct kvm_vcpu *vcpu,
struct desc_struct *seg_desc,
struct tss_segment_32 *tss)
{
u32 base_addr;

base_addr = get_tss_base_addr(vcpu, seg_desc);

return kvm_read_guest(vcpu->kvm, base_addr, tss,
sizeof(struct tss_segment_32));
}

static int save_tss_segment32(struct kvm_vcpu *vcpu,
struct desc_struct *seg_desc,
struct tss_segment_32 *tss)
{
u32 base_addr;

base_addr = get_tss_base_addr(vcpu, seg_desc);

return kvm_write_guest(vcpu->kvm, base_addr, tss,
sizeof(struct tss_segment_32));
}

static int load_tss_segment16(struct kvm_vcpu *vcpu,
struct desc_struct *seg_desc,
struct tss_segment_16 *tss)
{
u32 base_addr;

base_addr = get_tss_base_addr(vcpu, seg_desc);

return kvm_read_guest(vcpu->kvm, base_addr, tss,
sizeof(struct tss_segment_16));
}

static int save_tss_segment16(struct kvm_vcpu *vcpu,
struct desc_struct *seg_desc,
struct tss_segment_16 *tss)
{
u32 base_addr;

base_addr = get_tss_base_addr(vcpu, seg_desc);

return kvm_write_guest(vcpu->kvm, base_addr, tss,
sizeof(struct tss_segment_16));
}

static u16 get_segment_selector(struct kvm_vcpu *vcpu, int seg)
{
struct kvm_segment kvm_seg;
Expand Down Expand Up @@ -3472,20 +3424,26 @@ static int load_state_from_tss16(struct kvm_vcpu *vcpu,
}

static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
struct desc_struct *cseg_desc,
u32 old_tss_base,
struct desc_struct *nseg_desc)
{
struct tss_segment_16 tss_segment_16;
int ret = 0;

if (load_tss_segment16(vcpu, cseg_desc, &tss_segment_16))
if (kvm_read_guest(vcpu->kvm, old_tss_base, &tss_segment_16,
sizeof tss_segment_16))
goto out;

save_state_to_tss16(vcpu, &tss_segment_16);
save_tss_segment16(vcpu, cseg_desc, &tss_segment_16);

if (load_tss_segment16(vcpu, nseg_desc, &tss_segment_16))
if (kvm_write_guest(vcpu->kvm, old_tss_base, &tss_segment_16,
sizeof tss_segment_16))
goto out;

if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
&tss_segment_16, sizeof tss_segment_16))
goto out;

if (load_state_from_tss16(vcpu, &tss_segment_16))
goto out;

Expand All @@ -3495,20 +3453,26 @@ static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
}

static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
struct desc_struct *cseg_desc,
u32 old_tss_base,
struct desc_struct *nseg_desc)
{
struct tss_segment_32 tss_segment_32;
int ret = 0;

if (load_tss_segment32(vcpu, cseg_desc, &tss_segment_32))
if (kvm_read_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
sizeof tss_segment_32))
goto out;

save_state_to_tss32(vcpu, &tss_segment_32);
save_tss_segment32(vcpu, cseg_desc, &tss_segment_32);

if (load_tss_segment32(vcpu, nseg_desc, &tss_segment_32))
if (kvm_write_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
sizeof tss_segment_32))
goto out;

if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
&tss_segment_32, sizeof tss_segment_32))
goto out;

if (load_state_from_tss32(vcpu, &tss_segment_32))
goto out;

Expand All @@ -3523,16 +3487,20 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
struct desc_struct cseg_desc;
struct desc_struct nseg_desc;
int ret = 0;
u32 old_tss_base = get_segment_base(vcpu, VCPU_SREG_TR);
u16 old_tss_sel = get_segment_selector(vcpu, VCPU_SREG_TR);

kvm_get_segment(vcpu, &tr_seg, VCPU_SREG_TR);
old_tss_base = vcpu->arch.mmu.gva_to_gpa(vcpu, old_tss_base);

/* FIXME: Handle errors. Failure to read either TSS or their
* descriptors should generate a pagefault.
*/
if (load_guest_segment_descriptor(vcpu, tss_selector, &nseg_desc))
goto out;

if (load_guest_segment_descriptor(vcpu, tr_seg.selector, &cseg_desc))
if (load_guest_segment_descriptor(vcpu, old_tss_sel, &cseg_desc))
goto out;


if (reason != TASK_SWITCH_IRET) {
int cpl;

Expand All @@ -3550,8 +3518,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)

if (reason == TASK_SWITCH_IRET || reason == TASK_SWITCH_JMP) {
cseg_desc.type &= ~(1 << 1); //clear the B flag
save_guest_segment_descriptor(vcpu, tr_seg.selector,
&cseg_desc);
save_guest_segment_descriptor(vcpu, old_tss_sel, &cseg_desc);
}

if (reason == TASK_SWITCH_IRET) {
Expand All @@ -3563,10 +3530,10 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
kvm_x86_ops->cache_regs(vcpu);

if (nseg_desc.type & 8)
ret = kvm_task_switch_32(vcpu, tss_selector, &cseg_desc,
ret = kvm_task_switch_32(vcpu, tss_selector, old_tss_base,
&nseg_desc);
else
ret = kvm_task_switch_16(vcpu, tss_selector, &cseg_desc,
ret = kvm_task_switch_16(vcpu, tss_selector, old_tss_base,
&nseg_desc);

if (reason == TASK_SWITCH_CALL || reason == TASK_SWITCH_GATE) {
Expand Down

0 comments on commit 34198bf

Please sign in to comment.