All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/13] Big real mode fixes
@ 2012-08-21 14:06 Avi Kivity
  2012-08-21 14:06 ` [PATCH 01/13] KVM: VMX: Separate saving pre-realmode state from setting segments Avi Kivity
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:06 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

It turns out that our big real mode implementation was based on a miconception:
I believed that loading a segment register in real mode causes the limit to be
reset to 0xffff (thus undoing the effect), while in reality, the limit is preserved
across segment changes.  This bug was hidden by another bug: we didn't do limit checks
in real mode.  So even though segment loads corrupted the limit, the guest still
functioned because that limit isn't checked.

This patchset fixes both bugs, and introduces an optimization: we can now use
vm86 mode to virtualize big real mode (under usually-met conditions).  This
both speeds up big real mode, and makes it more robust, since fewer instructions
need to be emulated.

I think this patchset means we can remove emulate_invalid_guest_state=0, since it
offers no advantages now.

Avi Kivity (13):
  KVM: VMX: Separate saving pre-realmode state from setting segments
  KVM: VMX: Fix incorrect lookup of segment S flag in
    fix_pmode_dataseg()
  KVM: VMX: Use kvm_segment to save protected-mode segments when
    entering realmode
  KVM: VMX: Retain limit and attributes when entering protected mode
  KVM: VMX: Allow real mode emulation using vm86 with dpl=0
  KVM: VMX: Allow vm86 virtualization of big real mode
  KVM: x86 emulator: Leave segment limit and attributs alone in real
    mode
  KVM: x86 emulator: Check segment limits in real mode too
  KVM: x86 emulator: Fix #GP error code during linearization
  KVM: VMX: Return real real-mode segment data even if
    emulate_invalid_guest_state=1
  KVM: VMX: Preserve segment limit and access rights in real mode
  KVM: VMX: Save all segment data in real mode
  KVM: VMX: Ignore segment G and D bits when considering whether we can
    virtualize

 arch/x86/kvm/emulate.c |  19 ++++------
 arch/x86/kvm/vmx.c     | 100 ++++++++++++++++++-------------------------------
 2 files changed, 44 insertions(+), 75 deletions(-)

-- 
1.7.11.3


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 01/13] KVM: VMX: Separate saving pre-realmode state from setting segments
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
@ 2012-08-21 14:06 ` Avi Kivity
  2012-08-21 14:06 ` [PATCH 02/13] KVM: VMX: Fix incorrect lookup of segment S flag in fix_pmode_dataseg() Avi Kivity
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:06 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Commit b246dd5df139 ("KVM: VMX: Fix KVM_SET_SREGS with big real mode
segments") moved fix_rmode_seg() to vmx_set_segment(), so that it is
applied not just on transitions to real mode, but also on KVM_SET_SREGS
(migration).  However fix_rmode_seg() not only munges the vmcs segments,
it also sets up the save area for us to restore when returning to
protected mode or to return in vmx_get_segment().

Move saving the segment into a new function, save_rmode_seg(), and
call it just during the transition.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index c00f03d..bd6e06e 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2769,7 +2769,7 @@ static gva_t rmode_tss_base(struct kvm *kvm)
 	return kvm->arch.tss_addr;
 }
 
-static void fix_rmode_seg(int seg, struct kvm_save_segment *save)
+static void save_rmode_seg(int seg, struct kvm_save_segment *save)
 {
 	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
 
@@ -2777,6 +2777,12 @@ static void fix_rmode_seg(int seg, struct kvm_save_segment *save)
 	save->base = vmcs_readl(sf->base);
 	save->limit = vmcs_read32(sf->limit);
 	save->ar = vmcs_read32(sf->ar_bytes);
+}
+
+static void fix_rmode_seg(int seg, struct kvm_save_segment *save)
+{
+	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+
 	vmcs_write16(sf->selector, save->base >> 4);
 	vmcs_write32(sf->base, save->base & 0xffff0);
 	vmcs_write32(sf->limit, 0xffff);
@@ -2799,6 +2805,12 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
 	vmx->emulation_required = 1;
 	vmx->rmode.vm86_active = 1;
 
+	save_rmode_seg(VCPU_SREG_TR, &vmx->rmode.tr);
+	save_rmode_seg(VCPU_SREG_ES, &vmx->rmode.es);
+	save_rmode_seg(VCPU_SREG_DS, &vmx->rmode.ds);
+	save_rmode_seg(VCPU_SREG_FS, &vmx->rmode.fs);
+	save_rmode_seg(VCPU_SREG_GS, &vmx->rmode.gs);
+
 	/*
 	 * Very old userspace does not call KVM_SET_TSS_ADDR before entering
 	 * vcpu. Call it here with phys address pointing 16M below 4G.
@@ -2813,14 +2825,8 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
 
 	vmx_segment_cache_clear(vmx);
 
-	vmx->rmode.tr.selector = vmcs_read16(GUEST_TR_SELECTOR);
-	vmx->rmode.tr.base = vmcs_readl(GUEST_TR_BASE);
 	vmcs_writel(GUEST_TR_BASE, rmode_tss_base(vcpu->kvm));
-
-	vmx->rmode.tr.limit = vmcs_read32(GUEST_TR_LIMIT);
 	vmcs_write32(GUEST_TR_LIMIT, RMODE_TSS_SIZE - 1);
-
-	vmx->rmode.tr.ar = vmcs_read32(GUEST_TR_AR_BYTES);
 	vmcs_write32(GUEST_TR_AR_BYTES, 0x008b);
 
 	flags = vmcs_readl(GUEST_RFLAGS);
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/13] KVM: VMX: Fix incorrect lookup of segment S flag in fix_pmode_dataseg()
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
  2012-08-21 14:06 ` [PATCH 01/13] KVM: VMX: Separate saving pre-realmode state from setting segments Avi Kivity
@ 2012-08-21 14:06 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 03/13] KVM: VMX: Use kvm_segment to save protected-mode segments when entering realmode Avi Kivity
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:06 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

fix_pmode_dataseg() looks up S in ->base instead of ->ar_bytes.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index bd6e06e..6865ec5 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2694,11 +2694,11 @@ static __exit void hardware_unsetup(void)
 	free_kvm_area();
 }
 
-static void fix_pmode_dataseg(int seg, struct kvm_save_segment *save)
+static void fix_pmode_dataseg(int seg, struct kvm_segment *save)
 {
 	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
 
-	if (vmcs_readl(sf->base) == save->base && (save->base & AR_S_MASK)) {
+	if (vmcs_readl(sf->base) == save->base && (save->ar_bytes & AR_S_MASK)) {
 		vmcs_write16(sf->selector, save->selector);
 		vmcs_writel(sf->base, save->base);
 		vmcs_write32(sf->limit, save->limit);
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/13] KVM: VMX: Use kvm_segment to save protected-mode segments when entering realmode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
  2012-08-21 14:06 ` [PATCH 01/13] KVM: VMX: Separate saving pre-realmode state from setting segments Avi Kivity
  2012-08-21 14:06 ` [PATCH 02/13] KVM: VMX: Fix incorrect lookup of segment S flag in fix_pmode_dataseg() Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 04/13] KVM: VMX: Retain limit and attributes when entering protected mode Avi Kivity
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Instead of using struct kvm_save_segment, use struct kvm_segment, which is what
the other APIs use.  This leads to some simplification.

We replace save_rmode_seg() with a call to vmx_save_segment().  Since this depends
on rmode.vm86_active, we move the call to before setting the flag.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 85 +++++++++++++++---------------------------------------
 1 file changed, 24 insertions(+), 61 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 6865ec5..1c35095 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -405,16 +405,16 @@ struct vcpu_vmx {
 	struct {
 		int vm86_active;
 		ulong save_rflags;
+		struct kvm_segment segs[8];
+	} rmode;
+	struct {
+		u32 bitmask; /* 4 bits per segment (1 bit per field) */
 		struct kvm_save_segment {
 			u16 selector;
 			unsigned long base;
 			u32 limit;
 			u32 ar;
-		} tr, es, ds, fs, gs;
-	} rmode;
-	struct {
-		u32 bitmask; /* 4 bits per segment (1 bit per field) */
-		struct kvm_save_segment seg[8];
+		} seg[8];
 	} segment_cache;
 	int vpid;
 	bool emulation_required;
@@ -2694,15 +2694,12 @@ static __exit void hardware_unsetup(void)
 	free_kvm_area();
 }
 
-static void fix_pmode_dataseg(int seg, struct kvm_segment *save)
+static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save)
 {
 	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
 
-	if (vmcs_readl(sf->base) == save->base && (save->ar_bytes & AR_S_MASK)) {
-		vmcs_write16(sf->selector, save->selector);
-		vmcs_writel(sf->base, save->base);
-		vmcs_write32(sf->limit, save->limit);
-		vmcs_write32(sf->ar_bytes, save->ar);
+	if (vmcs_readl(sf->base) == save->base && save->s) {
+		vmx_set_segment(vcpu, save, seg);
 	} else {
 		u32 dpl = (vmcs_read16(sf->selector) & SELECTOR_RPL_MASK)
 			<< AR_DPL_SHIFT;
@@ -2720,10 +2717,7 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
 
 	vmx_segment_cache_clear(vmx);
 
-	vmcs_write16(GUEST_TR_SELECTOR, vmx->rmode.tr.selector);
-	vmcs_writel(GUEST_TR_BASE, vmx->rmode.tr.base);
-	vmcs_write32(GUEST_TR_LIMIT, vmx->rmode.tr.limit);
-	vmcs_write32(GUEST_TR_AR_BYTES, vmx->rmode.tr.ar);
+	vmx_set_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_TR], VCPU_SREG_TR);
 
 	flags = vmcs_readl(GUEST_RFLAGS);
 	flags &= RMODE_GUEST_OWNED_EFLAGS_BITS;
@@ -2738,10 +2732,10 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
 	if (emulate_invalid_guest_state)
 		return;
 
-	fix_pmode_dataseg(VCPU_SREG_ES, &vmx->rmode.es);
-	fix_pmode_dataseg(VCPU_SREG_DS, &vmx->rmode.ds);
-	fix_pmode_dataseg(VCPU_SREG_GS, &vmx->rmode.gs);
-	fix_pmode_dataseg(VCPU_SREG_FS, &vmx->rmode.fs);
+	fix_pmode_dataseg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]);
+	fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]);
+	fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]);
+	fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]);
 
 	vmx_segment_cache_clear(vmx);
 
@@ -2769,17 +2763,7 @@ static gva_t rmode_tss_base(struct kvm *kvm)
 	return kvm->arch.tss_addr;
 }
 
-static void save_rmode_seg(int seg, struct kvm_save_segment *save)
-{
-	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
-
-	save->selector = vmcs_read16(sf->selector);
-	save->base = vmcs_readl(sf->base);
-	save->limit = vmcs_read32(sf->limit);
-	save->ar = vmcs_read32(sf->ar_bytes);
-}
-
-static void fix_rmode_seg(int seg, struct kvm_save_segment *save)
+static void fix_rmode_seg(int seg, struct kvm_segment *save)
 {
 	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
 
@@ -2802,14 +2786,15 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
 	if (enable_unrestricted_guest)
 		return;
 
+	vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_TR], VCPU_SREG_TR);
+	vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_ES], VCPU_SREG_ES);
+	vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_DS], VCPU_SREG_DS);
+	vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_FS], VCPU_SREG_FS);
+	vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_GS], VCPU_SREG_GS);
+
 	vmx->emulation_required = 1;
 	vmx->rmode.vm86_active = 1;
 
-	save_rmode_seg(VCPU_SREG_TR, &vmx->rmode.tr);
-	save_rmode_seg(VCPU_SREG_ES, &vmx->rmode.es);
-	save_rmode_seg(VCPU_SREG_DS, &vmx->rmode.ds);
-	save_rmode_seg(VCPU_SREG_FS, &vmx->rmode.fs);
-	save_rmode_seg(VCPU_SREG_GS, &vmx->rmode.gs);
 
 	/*
 	 * Very old userspace does not call KVM_SET_TSS_ADDR before entering
@@ -3119,7 +3104,6 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
 			    struct kvm_segment *var, int seg)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
-	struct kvm_save_segment *save;
 	u32 ar;
 
 	if (vmx->rmode.vm86_active
@@ -3127,27 +3111,15 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
 		|| seg == VCPU_SREG_DS || seg == VCPU_SREG_FS
 		|| seg == VCPU_SREG_GS)
 	    && !emulate_invalid_guest_state) {
-		switch (seg) {
-		case VCPU_SREG_TR: save = &vmx->rmode.tr; break;
-		case VCPU_SREG_ES: save = &vmx->rmode.es; break;
-		case VCPU_SREG_DS: save = &vmx->rmode.ds; break;
-		case VCPU_SREG_FS: save = &vmx->rmode.fs; break;
-		case VCPU_SREG_GS: save = &vmx->rmode.gs; break;
-		default: BUG();
-		}
-		var->selector = save->selector;
-		var->base = save->base;
-		var->limit = save->limit;
-		ar = save->ar;
+		*var = vmx->rmode.segs[seg];
 		if (seg == VCPU_SREG_TR
 		    || var->selector == vmx_read_guest_seg_selector(vmx, seg))
-			goto use_saved_rmode_seg;
+			return;
 	}
 	var->base = vmx_read_guest_seg_base(vmx, seg);
 	var->limit = vmx_read_guest_seg_limit(vmx, seg);
 	var->selector = vmx_read_guest_seg_selector(vmx, seg);
 	ar = vmx_read_guest_seg_ar(vmx, seg);
-use_saved_rmode_seg:
 	if ((ar & AR_UNUSABLE_MASK) && !emulate_invalid_guest_state)
 		ar = 0;
 	var->type = ar & 15;
@@ -3236,10 +3208,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
 
 	if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) {
 		vmcs_write16(sf->selector, var->selector);
-		vmx->rmode.tr.selector = var->selector;
-		vmx->rmode.tr.base = var->base;
-		vmx->rmode.tr.limit = var->limit;
-		vmx->rmode.tr.ar = vmx_segment_access_rights(var);
+		vmx->rmode.segs[VCPU_SREG_TR] = *var;
 		return;
 	}
 	vmcs_writel(sf->base, var->base);
@@ -3290,16 +3259,10 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
 				     vmcs_readl(GUEST_CS_BASE) >> 4);
 			break;
 		case VCPU_SREG_ES:
-			fix_rmode_seg(VCPU_SREG_ES, &vmx->rmode.es);
-			break;
 		case VCPU_SREG_DS:
-			fix_rmode_seg(VCPU_SREG_DS, &vmx->rmode.ds);
-			break;
 		case VCPU_SREG_GS:
-			fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.gs);
-			break;
 		case VCPU_SREG_FS:
-			fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.fs);
+			fix_rmode_seg(seg, &vmx->rmode.segs[seg]);
 			break;
 		case VCPU_SREG_SS:
 			vmcs_write16(GUEST_SS_SELECTOR,
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/13] KVM: VMX: Retain limit and attributes when entering protected mode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (2 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 03/13] KVM: VMX: Use kvm_segment to save protected-mode segments when entering realmode Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 05/13] KVM: VMX: Allow real mode emulation using vm86 with dpl=0 Avi Kivity
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Real processors don't change segment limits and attributes while in
real mode.  Mimic that behaviour.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 1c35095..e94b90b 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2697,14 +2697,14 @@ static __exit void hardware_unsetup(void)
 static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save)
 {
 	struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+	struct kvm_segment tmp = *save;
 
-	if (vmcs_readl(sf->base) == save->base && save->s) {
-		vmx_set_segment(vcpu, save, seg);
-	} else {
-		u32 dpl = (vmcs_read16(sf->selector) & SELECTOR_RPL_MASK)
-			<< AR_DPL_SHIFT;
-		vmcs_write32(sf->ar_bytes, 0x93 | dpl);
+	if (!(vmcs_readl(sf->base) == tmp.base && tmp.s)) {
+		tmp.base = vmcs_readl(sf->base);
+		tmp.selector = vmcs_read16(sf->selector);
+		tmp.s = 1;
 	}
+	vmx_set_segment(vcpu, &tmp, seg);
 }
 
 static void enter_pmode(struct kvm_vcpu *vcpu)
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/13] KVM: VMX: Allow real mode emulation using vm86 with dpl=0
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (3 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 04/13] KVM: VMX: Retain limit and attributes when entering protected mode Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 06/13] KVM: VMX: Allow vm86 virtualization of big real mode Avi Kivity
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Real mode is always entered from protected mode with dpl=0.  Since
the dpl doesn't affect execution, and we already override it to 3
in the vmcs (as vmx requires), we can allow execution in that state.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index e94b90b..4f3d6ac 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3318,7 +3318,7 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg)
 		return false;
 	if (var.limit != 0xffff)
 		return false;
-	if (ar != 0xf3)
+	if ((ar | (3 << AR_DPL_SHIFT)) != 0xf3)
 		return false;
 
 	return true;
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/13] KVM: VMX: Allow vm86 virtualization of big real mode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (4 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 05/13] KVM: VMX: Allow real mode emulation using vm86 with dpl=0 Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 07/13] KVM: x86 emulator: Leave segment limit and attributs alone in " Avi Kivity
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Usually, big real mode uses large (4GB) segments.  Currently we don't
virtualize this; if any segment has a limit other than 0xffff, we emulate.
But if we set the vmx-visible limit to 0xffff, we can use vm86 to virtualize
real mode; if an access overruns the segment limit, the guest will #GP, which
we will trap and forward to the emulator.  This results in significantly
faster execution, and less risk of hitting an unemulated instruction.

If the limit is less than 0xffff, we retain the existing behaviour.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 4f3d6ac..939e4a0 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3316,7 +3316,7 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg)
 
 	if (var.base != (var.selector << 4))
 		return false;
-	if (var.limit != 0xffff)
+	if (var.limit < 0xffff)
 		return false;
 	if ((ar | (3 << AR_DPL_SHIFT)) != 0xf3)
 		return false;
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/13] KVM: x86 emulator: Leave segment limit and attributs alone in real mode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (5 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 06/13] KVM: VMX: Allow vm86 virtualization of big real mode Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 08/13] KVM: x86 emulator: Check segment limits in real mode too Avi Kivity
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

When loading a segment in real mode, only the base and selector must
be modified.  The limit needs to be left alone, otherwise big real mode
users will hit a #GP due to limit checking (currently this is suppressed
because we don't check limits in real mode).

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/emulate.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index a3b57a2..7281ff8 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1355,19 +1355,15 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 	bool null_selector = !(selector & ~0x3); /* 0000-0003 are null */
 	ulong desc_addr;
 	int ret;
+	u16 dummy;
 
 	memset(&seg_desc, 0, sizeof seg_desc);
 
 	if ((seg <= VCPU_SREG_GS && ctxt->mode == X86EMUL_MODE_VM86)
 	    || ctxt->mode == X86EMUL_MODE_REAL) {
 		/* set real mode segment descriptor */
+		ctxt->ops->get_segment(ctxt, &dummy, &seg_desc, NULL, seg);
 		set_desc_base(&seg_desc, selector << 4);
-		set_desc_limit(&seg_desc, 0xffff);
-		seg_desc.type = 3;
-		seg_desc.p = 1;
-		seg_desc.s = 1;
-		if (ctxt->mode == X86EMUL_MODE_VM86)
-			seg_desc.dpl = 3;
 		goto load;
 	}
 
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/13] KVM: x86 emulator: Check segment limits in real mode too
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (6 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 07/13] KVM: x86 emulator: Leave segment limit and attributs alone in " Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 09/13] KVM: x86 emulator: Fix #GP error code during linearization Avi Kivity
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Segment limits are verified in real mode, not just protected mode.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/emulate.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 7281ff8..67da0ac 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -632,8 +632,6 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
 
 	la = seg_base(ctxt, addr.seg) + addr.ea;
 	switch (ctxt->mode) {
-	case X86EMUL_MODE_REAL:
-		break;
 	case X86EMUL_MODE_PROT64:
 		if (((signed long)la << 16) >> 16 != la)
 			return emulate_gp(ctxt, 0);
@@ -663,7 +661,10 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
 				goto bad;
 		}
 		cpl = ctxt->ops->cpl(ctxt);
-		rpl = sel & 3;
+		if (ctxt->mode == X86EMUL_MODE_REAL)
+			rpl = 0;
+		else
+			rpl = sel & 3;
 		cpl = max(cpl, rpl);
 		if (!(desc.type & 8)) {
 			/* data segment */
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/13] KVM: x86 emulator: Fix #GP error code during linearization
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (7 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 08/13] KVM: x86 emulator: Check segment limits in real mode too Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 10/13] KVM: VMX: Return real real-mode segment data even if emulate_invalid_guest_state=1 Avi Kivity
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

We want the segment selector, nor segment number.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/emulate.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 67da0ac..9001b1c 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -689,9 +689,9 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
 	return X86EMUL_CONTINUE;
 bad:
 	if (addr.seg == VCPU_SREG_SS)
-		return emulate_ss(ctxt, addr.seg);
+		return emulate_ss(ctxt, sel);
 	else
-		return emulate_gp(ctxt, addr.seg);
+		return emulate_gp(ctxt, sel);
 }
 
 static int linearize(struct x86_emulate_ctxt *ctxt,
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/13] KVM: VMX: Return real real-mode segment data even if emulate_invalid_guest_state=1
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (8 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 09/13] KVM: x86 emulator: Fix #GP error code during linearization Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 11/13] KVM: VMX: Preserve segment limit and access rights in real mode Avi Kivity
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

emulate_invalid_guest_state=1 doesn't mean we don't munge the segments in the
vmcs; we do.  So we need to return the real ones (maintained by vmx_set_segment).

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 939e4a0..d4b1b16 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3109,8 +3109,7 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
 	if (vmx->rmode.vm86_active
 	    && (seg == VCPU_SREG_TR || seg == VCPU_SREG_ES
 		|| seg == VCPU_SREG_DS || seg == VCPU_SREG_FS
-		|| seg == VCPU_SREG_GS)
-	    && !emulate_invalid_guest_state) {
+		|| seg == VCPU_SREG_GS)) {
 		*var = vmx->rmode.segs[seg];
 		if (seg == VCPU_SREG_TR
 		    || var->selector == vmx_read_guest_seg_selector(vmx, seg))
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/13] KVM: VMX: Preserve segment limit and access rights in real mode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (9 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 10/13] KVM: VMX: Return real real-mode segment data even if emulate_invalid_guest_state=1 Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 12/13] KVM: VMX: Save all segment data " Avi Kivity
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

While this is undocumented, real processors do not reload the segment
limit and access rights when loading a segment register in real mode.
Real programs rely on it so we need to comply with this behaviour.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index d4b1b16..c16b375 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3114,6 +3114,9 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
 		if (seg == VCPU_SREG_TR
 		    || var->selector == vmx_read_guest_seg_selector(vmx, seg))
 			return;
+		var->base = vmx_read_guest_seg_base(vmx, seg);
+		var->selector = vmx_read_guest_seg_selector(vmx, seg);
+		return;
 	}
 	var->base = vmx_read_guest_seg_base(vmx, seg);
 	var->limit = vmx_read_guest_seg_limit(vmx, seg);
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/13] KVM: VMX: Save all segment data in real mode
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (10 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 11/13] KVM: VMX: Preserve segment limit and access rights in real mode Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-21 14:07 ` [PATCH 13/13] KVM: VMX: Ignore segment G and D bits when considering whether we can virtualize Avi Kivity
  2012-08-28  0:08 ` [PATCH 00/13] Big real mode fixes Marcelo Tosatti
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index c16b375..4649618 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3217,6 +3217,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
 	vmcs_write32(sf->limit, var->limit);
 	vmcs_write16(sf->selector, var->selector);
 	if (vmx->rmode.vm86_active && var->s) {
+		vmx->rmode.segs[seg] = *var;
 		/*
 		 * Hack real-mode segments into vm86 compatibility.
 		 */
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 13/13] KVM: VMX: Ignore segment G and D bits when considering whether we can virtualize
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (11 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 12/13] KVM: VMX: Save all segment data " Avi Kivity
@ 2012-08-21 14:07 ` Avi Kivity
  2012-08-28  0:08 ` [PATCH 00/13] Big real mode fixes Marcelo Tosatti
  13 siblings, 0 replies; 15+ messages in thread
From: Avi Kivity @ 2012-08-21 14:07 UTC (permalink / raw)
  To: Marcelo Tosatti; +Cc: kvm

We will enter the guest with G and D cleared; as real hardware ignores D in
real mode, and G is taken care of by the limit test, we allow more code to
run in vm86 mode.

Signed-off-by: Avi Kivity <avi@redhat.com>
---
 arch/x86/kvm/vmx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 4649618..a35e05e 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3321,7 +3321,7 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg)
 		return false;
 	if (var.limit < 0xffff)
 		return false;
-	if ((ar | (3 << AR_DPL_SHIFT)) != 0xf3)
+	if (((ar | (3 << AR_DPL_SHIFT)) & ~(AR_G_MASK | AR_DB_MASK)) != 0xf3)
 		return false;
 
 	return true;
-- 
1.7.11.3


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 00/13] Big real mode fixes
  2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
                   ` (12 preceding siblings ...)
  2012-08-21 14:07 ` [PATCH 13/13] KVM: VMX: Ignore segment G and D bits when considering whether we can virtualize Avi Kivity
@ 2012-08-28  0:08 ` Marcelo Tosatti
  13 siblings, 0 replies; 15+ messages in thread
From: Marcelo Tosatti @ 2012-08-28  0:08 UTC (permalink / raw)
  To: Avi Kivity; +Cc: kvm

On Tue, Aug 21, 2012 at 05:06:57PM +0300, Avi Kivity wrote:
> It turns out that our big real mode implementation was based on a miconception:
> I believed that loading a segment register in real mode causes the limit to be
> reset to 0xffff (thus undoing the effect), while in reality, the limit is preserved
> across segment changes.  This bug was hidden by another bug: we didn't do limit checks
> in real mode.  So even though segment loads corrupted the limit, the guest still
> functioned because that limit isn't checked.
> 
> This patchset fixes both bugs, and introduces an optimization: we can now use
> vm86 mode to virtualize big real mode (under usually-met conditions).  This
> both speeds up big real mode, and makes it more robust, since fewer instructions
> need to be emulated.
> 
> I think this patchset means we can remove emulate_invalid_guest_state=0, since it
> offers no advantages now.
> 
> Avi Kivity (13):
>   KVM: VMX: Separate saving pre-realmode state from setting segments
>   KVM: VMX: Fix incorrect lookup of segment S flag in
>     fix_pmode_dataseg()
>   KVM: VMX: Use kvm_segment to save protected-mode segments when
>     entering realmode
>   KVM: VMX: Retain limit and attributes when entering protected mode
>   KVM: VMX: Allow real mode emulation using vm86 with dpl=0
>   KVM: VMX: Allow vm86 virtualization of big real mode
>   KVM: x86 emulator: Leave segment limit and attributs alone in real
>     mode
>   KVM: x86 emulator: Check segment limits in real mode too
>   KVM: x86 emulator: Fix #GP error code during linearization
>   KVM: VMX: Return real real-mode segment data even if
>     emulate_invalid_guest_state=1
>   KVM: VMX: Preserve segment limit and access rights in real mode
>   KVM: VMX: Save all segment data in real mode
>   KVM: VMX: Ignore segment G and D bits when considering whether we can
>     virtualize
> 
>  arch/x86/kvm/emulate.c |  19 ++++------
>  arch/x86/kvm/vmx.c     | 100 ++++++++++++++++++-------------------------------
>  2 files changed, 44 insertions(+), 75 deletions(-)

Applied, thanks.


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2012-08-28 16:25 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-21 14:06 [PATCH 00/13] Big real mode fixes Avi Kivity
2012-08-21 14:06 ` [PATCH 01/13] KVM: VMX: Separate saving pre-realmode state from setting segments Avi Kivity
2012-08-21 14:06 ` [PATCH 02/13] KVM: VMX: Fix incorrect lookup of segment S flag in fix_pmode_dataseg() Avi Kivity
2012-08-21 14:07 ` [PATCH 03/13] KVM: VMX: Use kvm_segment to save protected-mode segments when entering realmode Avi Kivity
2012-08-21 14:07 ` [PATCH 04/13] KVM: VMX: Retain limit and attributes when entering protected mode Avi Kivity
2012-08-21 14:07 ` [PATCH 05/13] KVM: VMX: Allow real mode emulation using vm86 with dpl=0 Avi Kivity
2012-08-21 14:07 ` [PATCH 06/13] KVM: VMX: Allow vm86 virtualization of big real mode Avi Kivity
2012-08-21 14:07 ` [PATCH 07/13] KVM: x86 emulator: Leave segment limit and attributs alone in " Avi Kivity
2012-08-21 14:07 ` [PATCH 08/13] KVM: x86 emulator: Check segment limits in real mode too Avi Kivity
2012-08-21 14:07 ` [PATCH 09/13] KVM: x86 emulator: Fix #GP error code during linearization Avi Kivity
2012-08-21 14:07 ` [PATCH 10/13] KVM: VMX: Return real real-mode segment data even if emulate_invalid_guest_state=1 Avi Kivity
2012-08-21 14:07 ` [PATCH 11/13] KVM: VMX: Preserve segment limit and access rights in real mode Avi Kivity
2012-08-21 14:07 ` [PATCH 12/13] KVM: VMX: Save all segment data " Avi Kivity
2012-08-21 14:07 ` [PATCH 13/13] KVM: VMX: Ignore segment G and D bits when considering whether we can virtualize Avi Kivity
2012-08-28  0:08 ` [PATCH 00/13] Big real mode fixes Marcelo Tosatti

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.