* [PATCH 0/2] Segment cleanups and optimizations
@ 2011-04-28 9:23 Avi Kivity
2011-04-28 9:23 ` [PATCH 1/2] KVM: x86 emulator: consolidate segment accessors Avi Kivity
2011-04-28 9:23 ` [PATCH 2/2] KVM: VMX: Cache vmcs segment fields Avi Kivity
0 siblings, 2 replies; 3+ messages in thread
From: Avi Kivity @ 2011-04-28 9:23 UTC (permalink / raw)
To: Marcelo Tosatti, kvm
The first patch in this segment-themed patchset cleans up segment handling
in the emulator; while the second patch undoes some of the performance hit
taken by the emulator segment limit checks.
Avi Kivity (2):
KVM: x86 emulator: consolidate segment accessors
KVM: VMX: Cache vmcs segment fields
arch/x86/include/asm/kvm_emulate.h | 13 +---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/emulate.c | 122 ++++++++++++++++++++----------------
arch/x86/kvm/vmx.c | 102 +++++++++++++++++++++++++++---
arch/x86/kvm/x86.c | 41 +++---------
5 files changed, 176 insertions(+), 103 deletions(-)
--
1.7.4.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] KVM: x86 emulator: consolidate segment accessors
2011-04-28 9:23 [PATCH 0/2] Segment cleanups and optimizations Avi Kivity
@ 2011-04-28 9:23 ` Avi Kivity
2011-04-28 9:23 ` [PATCH 2/2] KVM: VMX: Cache vmcs segment fields Avi Kivity
1 sibling, 0 replies; 3+ messages in thread
From: Avi Kivity @ 2011-04-28 9:23 UTC (permalink / raw)
To: Marcelo Tosatti, kvm
Instead of separate accessors for the segment selector and cached descriptor,
use one accessor for both. This simplifies the code somewhat.
Signed-off-by: Avi Kivity <avi@redhat.com>
---
arch/x86/include/asm/kvm_emulate.h | 13 +---
arch/x86/kvm/emulate.c | 122 ++++++++++++++++++++----------------
arch/x86/kvm/x86.c | 41 +++---------
3 files changed, 83 insertions(+), 93 deletions(-)
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index 28114f5..0049211 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -164,15 +164,10 @@ struct x86_emulate_ops {
int size, unsigned short port, const void *val,
unsigned int count);
- bool (*get_cached_descriptor)(struct x86_emulate_ctxt *ctxt,
- struct desc_struct *desc, u32 *base3,
- int seg);
- void (*set_cached_descriptor)(struct x86_emulate_ctxt *ctxt,
- struct desc_struct *desc, u32 base3,
- int seg);
- u16 (*get_segment_selector)(struct x86_emulate_ctxt *ctxt, int seg);
- void (*set_segment_selector)(struct x86_emulate_ctxt *ctxt,
- u16 sel, int seg);
+ bool (*get_segment)(struct x86_emulate_ctxt *ctxt, u16 *selector,
+ struct desc_struct *desc, u32 *base3, int seg);
+ void (*set_segment)(struct x86_emulate_ctxt *ctxt, u16 selector,
+ struct desc_struct *desc, u32 base3, int seg);
unsigned long (*get_cached_segment_base)(struct x86_emulate_ctxt *ctxt,
int seg);
void (*get_gdt)(struct x86_emulate_ctxt *ctxt, struct desc_ptr *dt);
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 1568c49..a8faf8d 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -553,6 +553,26 @@ static int emulate_nm(struct x86_emulate_ctxt *ctxt)
return emulate_exception(ctxt, NM_VECTOR, 0, false);
}
+static u16 get_segment_selector(struct x86_emulate_ctxt *ctxt, unsigned seg)
+{
+ u16 selector;
+ struct desc_struct desc;
+
+ ctxt->ops->get_segment(ctxt, &selector, &desc, NULL, seg);
+ return selector;
+}
+
+static void set_segment_selector(struct x86_emulate_ctxt *ctxt, u16 selector,
+ unsigned seg)
+{
+ u16 dummy;
+ u32 base3;
+ struct desc_struct desc;
+
+ ctxt->ops->get_segment(ctxt, &dummy, &desc, &base3, seg);
+ ctxt->ops->set_segment(ctxt, selector, &desc, base3, seg);
+}
+
static int __linearize(struct x86_emulate_ctxt *ctxt,
struct segmented_address addr,
unsigned size, bool write, bool fetch,
@@ -563,6 +583,7 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
bool usable;
ulong la;
u32 lim;
+ u16 sel;
unsigned cpl, rpl;
la = seg_base(ctxt, ctxt->ops, addr.seg) + addr.ea;
@@ -574,8 +595,8 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
return emulate_gp(ctxt, 0);
break;
default:
- usable = ctxt->ops->get_cached_descriptor(ctxt, &desc, NULL,
- addr.seg);
+ usable = ctxt->ops->get_segment(ctxt, &sel, &desc, NULL,
+ addr.seg);
if (!usable)
goto bad;
/* code segment or read-only data segment */
@@ -598,7 +619,7 @@ static int __linearize(struct x86_emulate_ctxt *ctxt,
goto bad;
}
cpl = ctxt->ops->cpl(ctxt);
- rpl = ctxt->ops->get_segment_selector(ctxt, addr.seg) & 3;
+ rpl = sel & 3;
cpl = max(cpl, rpl);
if (!(desc.type & 8)) {
/* data segment */
@@ -1142,9 +1163,10 @@ static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt,
{
if (selector & 1 << 2) {
struct desc_struct desc;
+ u16 sel;
+
memset (dt, 0, sizeof *dt);
- if (!ops->get_cached_descriptor(ctxt, &desc, NULL,
- VCPU_SREG_LDTR));
+ if (!ops->get_segment(ctxt, &sel, &desc, NULL, VCPU_SREG_LDTR));
return;
dt->size = desc_limit_scaled(&desc); /* what if limit > 65535? */
@@ -1305,8 +1327,7 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
return ret;
}
load:
- ops->set_segment_selector(ctxt, selector, seg);
- ops->set_cached_descriptor(ctxt, &seg_desc, 0, seg);
+ ops->set_segment(ctxt, selector, &seg_desc, 0, seg);
return X86EMUL_CONTINUE;
exception:
emulate_exception(ctxt, err_vec, err_code, true);
@@ -1464,7 +1485,7 @@ static int emulate_push_sreg(struct x86_emulate_ctxt *ctxt,
{
struct decode_cache *c = &ctxt->decode;
- c->src.val = ops->get_segment_selector(ctxt, seg);
+ c->src.val = get_segment_selector(ctxt, seg);
return em_push(ctxt);
}
@@ -1552,7 +1573,7 @@ int emulate_int_real(struct x86_emulate_ctxt *ctxt,
ctxt->eflags &= ~(EFLG_IF | EFLG_TF | EFLG_AC);
- c->src.val = ops->get_segment_selector(ctxt, VCPU_SREG_CS);
+ c->src.val = get_segment_selector(ctxt, VCPU_SREG_CS);
rc = em_push(ctxt);
if (rc != X86EMUL_CONTINUE)
return rc;
@@ -1838,8 +1859,10 @@ setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
struct x86_emulate_ops *ops, struct desc_struct *cs,
struct desc_struct *ss)
{
+ u16 selector;
+
memset(cs, 0, sizeof(struct desc_struct));
- ops->get_cached_descriptor(ctxt, cs, NULL, VCPU_SREG_CS);
+ ops->get_segment(ctxt, &selector, cs, NULL, VCPU_SREG_CS);
memset(ss, 0, sizeof(struct desc_struct));
cs->l = 0; /* will be adjusted later */
@@ -1888,10 +1911,8 @@ emulate_syscall(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
cs.d = 0;
cs.l = 1;
}
- ops->set_cached_descriptor(ctxt, &cs, 0, VCPU_SREG_CS);
- ops->set_segment_selector(ctxt, cs_sel, VCPU_SREG_CS);
- ops->set_cached_descriptor(ctxt, &ss, 0, VCPU_SREG_SS);
- ops->set_segment_selector(ctxt, ss_sel, VCPU_SREG_SS);
+ ops->set_segment(ctxt, cs_sel, &cs, 0, VCPU_SREG_CS);
+ ops->set_segment(ctxt, ss_sel, &ss, 0, VCPU_SREG_SS);
c->regs[VCPU_REGS_RCX] = c->eip;
if (efer & EFER_LMA) {
@@ -1961,10 +1982,8 @@ emulate_sysenter(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
cs.l = 1;
}
- ops->set_cached_descriptor(ctxt, &cs, 0, VCPU_SREG_CS);
- ops->set_segment_selector(ctxt, cs_sel, VCPU_SREG_CS);
- ops->set_cached_descriptor(ctxt, &ss, 0, VCPU_SREG_SS);
- ops->set_segment_selector(ctxt, ss_sel, VCPU_SREG_SS);
+ ops->set_segment(ctxt, cs_sel, &cs, 0, VCPU_SREG_CS);
+ ops->set_segment(ctxt, ss_sel, &ss, 0, VCPU_SREG_SS);
ops->get_msr(ctxt, MSR_IA32_SYSENTER_EIP, &msr_data);
c->eip = msr_data;
@@ -2018,10 +2037,8 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
cs_sel |= SELECTOR_RPL_MASK;
ss_sel |= SELECTOR_RPL_MASK;
- ops->set_cached_descriptor(ctxt, &cs, 0, VCPU_SREG_CS);
- ops->set_segment_selector(ctxt, cs_sel, VCPU_SREG_CS);
- ops->set_cached_descriptor(ctxt, &ss, 0, VCPU_SREG_SS);
- ops->set_segment_selector(ctxt, ss_sel, VCPU_SREG_SS);
+ ops->set_segment(ctxt, cs_sel, &cs, 0, VCPU_SREG_CS);
+ ops->set_segment(ctxt, ss_sel, &ss, 0, VCPU_SREG_SS);
c->eip = c->regs[VCPU_REGS_RDX];
c->regs[VCPU_REGS_RSP] = c->regs[VCPU_REGS_RCX];
@@ -2048,11 +2065,11 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt,
struct desc_struct tr_seg;
u32 base3;
int r;
- u16 io_bitmap_ptr, perm, bit_idx = port & 0x7;
+ u16 tr, io_bitmap_ptr, perm, bit_idx = port & 0x7;
unsigned mask = (1 << len) - 1;
unsigned long base;
- ops->get_cached_descriptor(ctxt, &tr_seg, &base3, VCPU_SREG_TR);
+ ops->get_segment(ctxt, &tr, &tr_seg, &base3, VCPU_SREG_TR);
if (!tr_seg.p)
return false;
if (desc_limit_scaled(&tr_seg) < 103)
@@ -2107,11 +2124,11 @@ static void save_state_to_tss16(struct x86_emulate_ctxt *ctxt,
tss->si = c->regs[VCPU_REGS_RSI];
tss->di = c->regs[VCPU_REGS_RDI];
- tss->es = ops->get_segment_selector(ctxt, VCPU_SREG_ES);
- tss->cs = ops->get_segment_selector(ctxt, VCPU_SREG_CS);
- tss->ss = ops->get_segment_selector(ctxt, VCPU_SREG_SS);
- tss->ds = ops->get_segment_selector(ctxt, VCPU_SREG_DS);
- tss->ldt = ops->get_segment_selector(ctxt, VCPU_SREG_LDTR);
+ tss->es = get_segment_selector(ctxt, VCPU_SREG_ES);
+ tss->cs = get_segment_selector(ctxt, VCPU_SREG_CS);
+ tss->ss = get_segment_selector(ctxt, VCPU_SREG_SS);
+ tss->ds = get_segment_selector(ctxt, VCPU_SREG_DS);
+ tss->ldt = get_segment_selector(ctxt, VCPU_SREG_LDTR);
}
static int load_state_from_tss16(struct x86_emulate_ctxt *ctxt,
@@ -2136,11 +2153,11 @@ static int load_state_from_tss16(struct x86_emulate_ctxt *ctxt,
* SDM says that segment selectors are loaded before segment
* descriptors
*/
- ops->set_segment_selector(ctxt, tss->ldt, VCPU_SREG_LDTR);
- ops->set_segment_selector(ctxt, tss->es, VCPU_SREG_ES);
- ops->set_segment_selector(ctxt, tss->cs, VCPU_SREG_CS);
- ops->set_segment_selector(ctxt, tss->ss, VCPU_SREG_SS);
- ops->set_segment_selector(ctxt, tss->ds, VCPU_SREG_DS);
+ set_segment_selector(ctxt, tss->ldt, VCPU_SREG_LDTR);
+ set_segment_selector(ctxt, tss->es, VCPU_SREG_ES);
+ set_segment_selector(ctxt, tss->cs, VCPU_SREG_CS);
+ set_segment_selector(ctxt, tss->ss, VCPU_SREG_SS);
+ set_segment_selector(ctxt, tss->ds, VCPU_SREG_DS);
/*
* Now load segment descriptors. If fault happenes at this stage
@@ -2227,13 +2244,13 @@ static void save_state_to_tss32(struct x86_emulate_ctxt *ctxt,
tss->esi = c->regs[VCPU_REGS_RSI];
tss->edi = c->regs[VCPU_REGS_RDI];
- tss->es = ops->get_segment_selector(ctxt, VCPU_SREG_ES);
- tss->cs = ops->get_segment_selector(ctxt, VCPU_SREG_CS);
- tss->ss = ops->get_segment_selector(ctxt, VCPU_SREG_SS);
- tss->ds = ops->get_segment_selector(ctxt, VCPU_SREG_DS);
- tss->fs = ops->get_segment_selector(ctxt, VCPU_SREG_FS);
- tss->gs = ops->get_segment_selector(ctxt, VCPU_SREG_GS);
- tss->ldt_selector = ops->get_segment_selector(ctxt, VCPU_SREG_LDTR);
+ tss->es = get_segment_selector(ctxt, VCPU_SREG_ES);
+ tss->cs = get_segment_selector(ctxt, VCPU_SREG_CS);
+ tss->ss = get_segment_selector(ctxt, VCPU_SREG_SS);
+ tss->ds = get_segment_selector(ctxt, VCPU_SREG_DS);
+ tss->fs = get_segment_selector(ctxt, VCPU_SREG_FS);
+ tss->gs = get_segment_selector(ctxt, VCPU_SREG_GS);
+ tss->ldt_selector = get_segment_selector(ctxt, VCPU_SREG_LDTR);
}
static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
@@ -2260,13 +2277,13 @@ static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
* SDM says that segment selectors are loaded before segment
* descriptors
*/
- ops->set_segment_selector(ctxt, tss->ldt_selector, VCPU_SREG_LDTR);
- ops->set_segment_selector(ctxt, tss->es, VCPU_SREG_ES);
- ops->set_segment_selector(ctxt, tss->cs, VCPU_SREG_CS);
- ops->set_segment_selector(ctxt, tss->ss, VCPU_SREG_SS);
- ops->set_segment_selector(ctxt, tss->ds, VCPU_SREG_DS);
- ops->set_segment_selector(ctxt, tss->fs, VCPU_SREG_FS);
- ops->set_segment_selector(ctxt, tss->gs, VCPU_SREG_GS);
+ set_segment_selector(ctxt, tss->ldt_selector, VCPU_SREG_LDTR);
+ set_segment_selector(ctxt, tss->es, VCPU_SREG_ES);
+ set_segment_selector(ctxt, tss->cs, VCPU_SREG_CS);
+ set_segment_selector(ctxt, tss->ss, VCPU_SREG_SS);
+ set_segment_selector(ctxt, tss->ds, VCPU_SREG_DS);
+ set_segment_selector(ctxt, tss->fs, VCPU_SREG_FS);
+ set_segment_selector(ctxt, tss->gs, VCPU_SREG_GS);
/*
* Now load segment descriptors. If fault happenes at this stage
@@ -2348,7 +2365,7 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
{
struct desc_struct curr_tss_desc, next_tss_desc;
int ret;
- u16 old_tss_sel = ops->get_segment_selector(ctxt, VCPU_SREG_TR);
+ u16 old_tss_sel = get_segment_selector(ctxt, VCPU_SREG_TR);
ulong old_tss_base =
ops->get_cached_segment_base(ctxt, VCPU_SREG_TR);
u32 desc_limit;
@@ -2411,8 +2428,7 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
}
ops->set_cr(ctxt, 0, ops->get_cr(ctxt, 0) | X86_CR0_TS);
- ops->set_cached_descriptor(ctxt, &next_tss_desc, 0, VCPU_SREG_TR);
- ops->set_segment_selector(ctxt, tss_selector, VCPU_SREG_TR);
+ ops->set_segment(ctxt, tss_selector, &next_tss_desc, 0, VCPU_SREG_TR);
if (has_error_code) {
struct decode_cache *c = &ctxt->decode;
@@ -2503,7 +2519,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
ulong old_eip;
int rc;
- old_cs = ctxt->ops->get_segment_selector(ctxt, VCPU_SREG_CS);
+ old_cs = get_segment_selector(ctxt, VCPU_SREG_CS);
old_eip = c->eip;
memcpy(&sel, c->src.valptr + c->op_bytes, 2);
@@ -3881,7 +3897,7 @@ special_insn:
rc = emulate_ud(ctxt);
goto done;
}
- c->dst.val = ops->get_segment_selector(ctxt, c->modrm_reg);
+ c->dst.val = get_segment_selector(ctxt, c->modrm_reg);
break;
case 0x8d: /* lea r16/r32, m */
c->dst.val = c->src.addr.mem.ea;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 22bc69c..77c9d867 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4304,13 +4304,14 @@ static unsigned long emulator_get_cached_segment_base(
return get_segment_base(emul_to_vcpu(ctxt), seg);
}
-static bool emulator_get_cached_descriptor(struct x86_emulate_ctxt *ctxt,
- struct desc_struct *desc, u32 *base3,
- int seg)
+static bool emulator_get_segment(struct x86_emulate_ctxt *ctxt, u16 *selector,
+ struct desc_struct *desc, u32 *base3,
+ int seg)
{
struct kvm_segment var;
kvm_get_segment(emul_to_vcpu(ctxt), &var, seg);
+ *selector = var.selector;
if (var.unusable)
return false;
@@ -4335,16 +4336,14 @@ static bool emulator_get_cached_descriptor(struct x86_emulate_ctxt *ctxt,
return true;
}
-static void emulator_set_cached_descriptor(struct x86_emulate_ctxt *ctxt,
- struct desc_struct *desc, u32 base3,
- int seg)
+static void emulator_set_segment(struct x86_emulate_ctxt *ctxt, u16 selector,
+ struct desc_struct *desc, u32 base3,
+ int seg)
{
struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
struct kvm_segment var;
- /* needed to preserve selector */
- kvm_get_segment(vcpu, &var, seg);
-
+ var.selector = selector;
var.base = get_desc_base(desc);
#ifdef CONFIG_X86_64
var.base |= ((u64)base3) << 32;
@@ -4368,24 +4367,6 @@ static void emulator_set_cached_descriptor(struct x86_emulate_ctxt *ctxt,
return;
}
-static u16 emulator_get_segment_selector(struct x86_emulate_ctxt *ctxt, int seg)
-{
- struct kvm_segment kvm_seg;
-
- kvm_get_segment(emul_to_vcpu(ctxt), &kvm_seg, seg);
- return kvm_seg.selector;
-}
-
-static void emulator_set_segment_selector(struct x86_emulate_ctxt *ctxt,
- u16 sel, int seg)
-{
- struct kvm_segment kvm_seg;
-
- kvm_get_segment(emul_to_vcpu(ctxt), &kvm_seg, seg);
- kvm_seg.selector = sel;
- kvm_set_segment(emul_to_vcpu(ctxt), &kvm_seg, seg);
-}
-
static int emulator_get_msr(struct x86_emulate_ctxt *ctxt,
u32 msr_index, u64 *pdata)
{
@@ -4436,10 +4417,8 @@ static struct x86_emulate_ops emulate_ops = {
.invlpg = emulator_invlpg,
.pio_in_emulated = emulator_pio_in_emulated,
.pio_out_emulated = emulator_pio_out_emulated,
- .get_cached_descriptor = emulator_get_cached_descriptor,
- .set_cached_descriptor = emulator_set_cached_descriptor,
- .get_segment_selector = emulator_get_segment_selector,
- .set_segment_selector = emulator_set_segment_selector,
+ .get_segment = emulator_get_segment,
+ .set_segment = emulator_set_segment,
.get_cached_segment_base = emulator_get_cached_segment_base,
.get_gdt = emulator_get_gdt,
.get_idt = emulator_get_idt,
--
1.7.4.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] KVM: VMX: Cache vmcs segment fields
2011-04-28 9:23 [PATCH 0/2] Segment cleanups and optimizations Avi Kivity
2011-04-28 9:23 ` [PATCH 1/2] KVM: x86 emulator: consolidate segment accessors Avi Kivity
@ 2011-04-28 9:23 ` Avi Kivity
1 sibling, 0 replies; 3+ messages in thread
From: Avi Kivity @ 2011-04-28 9:23 UTC (permalink / raw)
To: Marcelo Tosatti, kvm
Since the emulator now checks segment limits and access rights, it
generates a lot more accesses to the vmcs segment fields. Undo some
of the performance hit by cacheing those fields in a read-only cache
(the entire cache is invalidated on any write, or on guest exit).
Signed-off-by: Avi Kivity <avi@redhat.com>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/vmx.c | 102 +++++++++++++++++++++++++++++++++++----
2 files changed, 93 insertions(+), 10 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index afb0e69..d2ac8e2 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -136,6 +136,7 @@ enum kvm_reg_ex {
VCPU_EXREG_CR3,
VCPU_EXREG_RFLAGS,
VCPU_EXREG_CPL,
+ VCPU_EXREG_SEGMENTS,
};
enum {
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 3f6e9bf..d7463c3 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -162,6 +162,10 @@ struct vcpu_vmx {
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];
+ } segment_cache;
int vpid;
bool emulation_required;
@@ -174,6 +178,15 @@ struct vcpu_vmx {
bool rdtscp_enabled;
};
+enum segment_cache_field {
+ SEG_FIELD_SEL = 0,
+ SEG_FIELD_BASE = 1,
+ SEG_FIELD_LIMIT = 2,
+ SEG_FIELD_AR = 3,
+
+ SEG_FIELD_NR = 4
+};
+
static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
{
return container_of(vcpu, struct vcpu_vmx, vcpu);
@@ -646,6 +659,62 @@ static void vmcs_set_bits(unsigned long field, u32 mask)
vmcs_writel(field, vmcs_readl(field) | mask);
}
+static void vmx_segment_cache_clear(struct vcpu_vmx *vmx)
+{
+ vmx->segment_cache.bitmask = 0;
+}
+
+static bool vmx_segment_cache_test_set(struct vcpu_vmx *vmx, unsigned seg,
+ unsigned field)
+{
+ bool ret;
+ u32 mask = 1 << (seg * SEG_FIELD_NR + field);
+
+ if (!(vmx->vcpu.arch.regs_avail & (1 << VCPU_EXREG_SEGMENTS))) {
+ vmx->vcpu.arch.regs_avail |= (1 << VCPU_EXREG_SEGMENTS);
+ vmx->segment_cache.bitmask = 0;
+ }
+ ret = vmx->segment_cache.bitmask & mask;
+ vmx->segment_cache.bitmask |= mask;
+ return ret;
+}
+
+static u16 vmx_read_guest_seg_selector(struct vcpu_vmx *vmx, unsigned seg)
+{
+ u16 *p = &vmx->segment_cache.seg[seg].selector;
+
+ if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_SEL))
+ *p = vmcs_read16(kvm_vmx_segment_fields[seg].selector);
+ return *p;
+}
+
+static ulong vmx_read_guest_seg_base(struct vcpu_vmx *vmx, unsigned seg)
+{
+ ulong *p = &vmx->segment_cache.seg[seg].base;
+
+ if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_BASE))
+ *p = vmcs_readl(kvm_vmx_segment_fields[seg].base);
+ return *p;
+}
+
+static u32 vmx_read_guest_seg_limit(struct vcpu_vmx *vmx, unsigned seg)
+{
+ u32 *p = &vmx->segment_cache.seg[seg].limit;
+
+ if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_LIMIT))
+ *p = vmcs_read32(kvm_vmx_segment_fields[seg].limit);
+ return *p;
+}
+
+static u32 vmx_read_guest_seg_ar(struct vcpu_vmx *vmx, unsigned seg)
+{
+ u32 *p = &vmx->segment_cache.seg[seg].ar;
+
+ if (!vmx_segment_cache_test_set(vmx, seg, SEG_FIELD_AR))
+ *p = vmcs_read32(kvm_vmx_segment_fields[seg].ar_bytes);
+ return *p;
+}
+
static void update_exception_bitmap(struct kvm_vcpu *vcpu)
{
u32 eb;
@@ -1271,9 +1340,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
break;
#ifdef CONFIG_X86_64
case MSR_FS_BASE:
+ vmx_segment_cache_clear(vmx);
vmcs_writel(GUEST_FS_BASE, data);
break;
case MSR_GS_BASE:
+ vmx_segment_cache_clear(vmx);
vmcs_writel(GUEST_GS_BASE, data);
break;
case MSR_KERNEL_GS_BASE:
@@ -1717,6 +1788,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
vmx->emulation_required = 1;
vmx->rmode.vm86_active = 0;
+ 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);
@@ -1740,6 +1813,8 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
fix_pmode_dataseg(VCPU_SREG_GS, &vmx->rmode.gs);
fix_pmode_dataseg(VCPU_SREG_FS, &vmx->rmode.fs);
+ vmx_segment_cache_clear(vmx);
+
vmcs_write16(GUEST_SS_SELECTOR, 0);
vmcs_write32(GUEST_SS_AR_BYTES, 0x93);
@@ -1803,6 +1878,8 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
}
+ 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));
@@ -1879,6 +1956,8 @@ static void enter_lmode(struct kvm_vcpu *vcpu)
{
u32 guest_tr_ar;
+ vmx_segment_cache_clear(to_vmx(vcpu));
+
guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES);
if ((guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) {
printk(KERN_DEBUG "%s: tss fixup for long mode. \n",
@@ -2082,7 +2161,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_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
struct kvm_save_segment *save;
u32 ar;
@@ -2104,13 +2182,13 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
var->limit = save->limit;
ar = save->ar;
if (seg == VCPU_SREG_TR
- || var->selector == vmcs_read16(sf->selector))
+ || var->selector == vmx_read_guest_seg_selector(vmx, seg))
goto use_saved_rmode_seg;
}
- var->base = vmcs_readl(sf->base);
- var->limit = vmcs_read32(sf->limit);
- var->selector = vmcs_read16(sf->selector);
- ar = vmcs_read32(sf->ar_bytes);
+ 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;
@@ -2127,14 +2205,13 @@ use_saved_rmode_seg:
static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg)
{
- struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
struct kvm_segment s;
if (to_vmx(vcpu)->rmode.vm86_active) {
vmx_get_segment(vcpu, &s, seg);
return s.base;
}
- return vmcs_readl(sf->base);
+ return vmx_read_guest_seg_base(to_vmx(vcpu), seg);
}
static int __vmx_get_cpl(struct kvm_vcpu *vcpu)
@@ -2146,7 +2223,7 @@ static int __vmx_get_cpl(struct kvm_vcpu *vcpu)
&& (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */
return 3;
- return vmcs_read16(GUEST_CS_SELECTOR) & 3;
+ return vmx_read_guest_seg_selector(to_vmx(vcpu), VCPU_SREG_CS) & 3;
}
static int vmx_get_cpl(struct kvm_vcpu *vcpu)
@@ -2188,6 +2265,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
u32 ar;
+ vmx_segment_cache_clear(vmx);
+
if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) {
vmcs_write16(sf->selector, var->selector);
vmx->rmode.tr.selector = var->selector;
@@ -2229,7 +2308,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l)
{
- u32 ar = vmcs_read32(GUEST_CS_AR_BYTES);
+ u32 ar = vmx_read_guest_seg_ar(to_vmx(vcpu), VCPU_SREG_CS);
*db = (ar >> 14) & 1;
*l = (ar >> 13) & 1;
@@ -2816,6 +2895,8 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
if (ret != 0)
goto out;
+ vmx_segment_cache_clear(vmx);
+
seg_setup(VCPU_SREG_CS);
/*
* GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode
@@ -4188,6 +4269,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
| (1 << VCPU_EXREG_RFLAGS)
| (1 << VCPU_EXREG_CPL)
| (1 << VCPU_EXREG_PDPTR)
+ | (1 << VCPU_EXREG_SEGMENTS)
| (1 << VCPU_EXREG_CR3));
vcpu->arch.regs_dirty = 0;
--
1.7.4.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2011-04-28 9:23 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-04-28 9:23 [PATCH 0/2] Segment cleanups and optimizations Avi Kivity
2011-04-28 9:23 ` [PATCH 1/2] KVM: x86 emulator: consolidate segment accessors Avi Kivity
2011-04-28 9:23 ` [PATCH 2/2] KVM: VMX: Cache vmcs segment fields Avi Kivity
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox