Re: [PATCH v2 3/3] KVM: x86 emulator: Allow PM/VM86 switch during task switch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 01/27/2012 09:23 PM, Kevin Wolf wrote:
> Task switches can switch between Protected Mode and VM86. The current
> mode must be updated during the task switch emulation so that the new
> segment selectors are interpreted correctly and privilege checks
> succeed.
>
> VMX code calculates the CPL from the code segment selector and rflags,
> so it needs rflags to be updated in the vcpu struct. SVM stores the DPL
> of the code segment instead, so we must be sure to give the right one
> when updating the selector.

svm has vmcb_save_area::cpl; it's independent of CS.RPL.

>
> Signed-off-by: Kevin Wolf <kwolf@xxxxxxxxxx>
> ---
>  arch/x86/include/asm/kvm_emulate.h |    1 +
>  arch/x86/kvm/emulate.c             |   26 ++++++++++++++++++++++++++
>  arch/x86/kvm/x86.c                 |    6 ++++++
>  3 files changed, 33 insertions(+), 0 deletions(-)
>
> diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
> index c8a9cf3..4a21c7d 100644
> --- a/arch/x86/include/asm/kvm_emulate.h
> +++ b/arch/x86/include/asm/kvm_emulate.h
> @@ -176,6 +176,7 @@ struct x86_emulate_ops {
>  	void (*set_idt)(struct x86_emulate_ctxt *ctxt, struct desc_ptr *dt);
>  	ulong (*get_cr)(struct x86_emulate_ctxt *ctxt, int cr);
>  	int (*set_cr)(struct x86_emulate_ctxt *ctxt, int cr, ulong val);
> +	void (*set_rflags)(struct x86_emulate_ctxt *ctxt, ulong val);
>  	int (*cpl)(struct x86_emulate_ctxt *ctxt);
>  	int (*get_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong *dest);
>  	int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value);
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index 833969e..143ce8e 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -552,6 +552,14 @@ static void set_segment_selector(struct x86_emulate_ctxt *ctxt, u16 selector,
>  	struct desc_struct desc;
>  
>  	ctxt->ops->get_segment(ctxt, &dummy, &desc, &base3, seg);
> +
> +	if (ctxt->mode == X86EMUL_MODE_REAL)
> +		desc.dpl = 0;

Can't happen.

> +	else if (ctxt->mode == X86EMUL_MODE_VM86)
> +		desc.dpl = 3;
> +	else
> +		desc.dpl = selector & 0x3;

I don't think this is right.  The SDM explicitly says that just the
selectors are loaded, yet you're modifying the DPL here too.  Unless
there's an abort, we'll be loading the descriptors later anyway (with
their DPL).  If there's an abort, I think we should continue with the
mismatched DPL.

> +
>  	ctxt->ops->set_segment(ctxt, selector, &desc, base3, seg);
>  }
>  
> @@ -2273,6 +2281,24 @@ static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
>  		return emulate_gp(ctxt, 0);
>  	ctxt->_eip = tss->eip;
>  	ctxt->eflags = tss->eflags | 2;
> +
> +	/*
> +	 * If we're switching between Protected Mode and VM86, we need to make
> +	 * sure to update the mode before loading the segment descriptors so
> +	 * that the selectors are interpreted correctly.
> +	 *
> +	 * Need to get it to the vcpu struct immediately because it influences
> +	 * the CPL which is checked at least when loading the segment
> +	 * descriptors and when pushing an error code to the new kernel stack.
> +	 */
> +	if (ctxt->eflags & X86_EFLAGS_VM)
> +		ctxt->mode = X86EMUL_MODE_VM86;
> +	else
> +		ctxt->mode = X86EMUL_MODE_PROT32;
> +

Shouldn't this be done after the set_segment_selector() block?  My
interpretation of the SDM is that if a fault happens while loading
descriptors the fault happens with old segment cache, that is, it needs
to be interpreted according to the old mode.

> +	ctxt->ops->set_rflags(ctxt, ctxt->eflags);
> +
> +	/* General purpose registers */
>  	ctxt->regs[VCPU_REGS_RAX] = tss->eax;
>  	ctxt->regs[VCPU_REGS_RCX] = tss->ecx;
>  	ctxt->regs[VCPU_REGS_RDX] = tss->edx;
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index dc3e945..502b5c3 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4040,6 +4040,11 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val)
>  	return res;
>  }
>  
> +static void emulator_set_rflags(struct x86_emulate_ctxt *ctxt, ulong val)
> +{
> +	kvm_set_rflags(emul_to_vcpu(ctxt), val);
> +}
> +
>  static int emulator_get_cpl(struct x86_emulate_ctxt *ctxt)
>  {
>  	return kvm_x86_ops->get_cpl(emul_to_vcpu(ctxt));
> @@ -4199,6 +4204,7 @@ static struct x86_emulate_ops emulate_ops = {
>  	.set_idt	     = emulator_set_idt,
>  	.get_cr              = emulator_get_cr,
>  	.set_cr              = emulator_set_cr,
> +	.set_rflags          = emulator_set_rflags,
>  	.cpl                 = emulator_get_cpl,
>  	.get_dr              = emulator_get_dr,
>  	.set_dr              = emulator_set_dr,

It would be good to switch the entire emulator to use ->set_rflags()
instead of sometimes letting the caller do it.

-- 
error compiling committee.c: too many arguments to function

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux