xen/arch/x86/hvm/vmx/vmx.c | 77 ++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 43 deletions(-)
Xen's segment enumeration almost matches the VMCS encoding order, while the
VMCS encoding order has the system segments immediately following the user
segments for all relevant attributes.
Use a sneaky xor to hide the difference in encoding order to fold the switch
statements, dropping 10 __vmread() and 10 __vmwrite() calls. Bloat-o-meter
reports:
add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-433 (-433)
Function old new delta
vmx_set_segment_register 804 593 -211
vmx_get_segment_register 778 556 -222
showing that these wrappers aren't trivial. In addition, 20 BUGs worth of
metadata are dropped.
No functional change.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Wei Liu <wl@xen.org>
CC: Jun Nakajima <jun.nakajima@intel.com>
CC: Kevin Tian <kevin.tian@intel.com>
We could make x86_seg_* match the VMCS encoding order if we're willing to make
v->arch.hvm.vmx.vm86_saved_seg[] one entry longer, but I don't think the added
complexity to the vmx_realmode logic is worth it.
---
xen/arch/x86/hvm/vmx/vmx.c | 77 ++++++++++++++++++++--------------------------
1 file changed, 34 insertions(+), 43 deletions(-)
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index c44cf8f5d425..9765cfd90a0a 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -986,6 +986,7 @@ static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg,
struct segment_register *reg)
{
unsigned long attr = 0, sel = 0, limit;
+ unsigned int tmp_seg;
/*
* We may get here in the context of dump_execstate(), which may have
@@ -1009,34 +1010,34 @@ static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg,
return;
}
- switch ( seg )
+ /*
+ * Xen's x86_seg_* enumeration *almost* matches the VMCS encoding order.
+ *
+ * tr and ldtr are reversed, and other areas of code rely on this, so we
+ * can't just re-enumerate.
+ */
+ BUILD_BUG_ON(x86_seg_tr != 6);
+ BUILD_BUG_ON(x86_seg_ldtr != 7);
+ BUILD_BUG_ON(x86_seg_gdtr != 8);
+ BUILD_BUG_ON(x86_seg_idtr != 9);
+ switch ( tmp_seg = seg )
{
- case x86_seg_es ... x86_seg_gs:
- __vmread(GUEST_SEG_SELECTOR(seg), &sel);
- __vmread(GUEST_SEG_LIMIT(seg), &limit);
- __vmread(GUEST_SEG_BASE(seg), ®->base);
- __vmread(GUEST_SEG_AR_BYTES(seg), &attr);
- break;
case x86_seg_tr:
- __vmread(GUEST_TR_SELECTOR, &sel);
- __vmread(GUEST_TR_LIMIT, &limit);
- __vmread(GUEST_TR_BASE, ®->base);
- __vmread(GUEST_TR_AR_BYTES, &attr);
- break;
+ case x86_seg_ldtr:
+ tmp_seg ^= 1; /* Flip tr and ldtr so GUEST_SEG_*() works. */
+ fallthrough;
+
+ case x86_seg_es ... x86_seg_gs:
+ __vmread(GUEST_SEG_SELECTOR(tmp_seg), &sel);
+ __vmread(GUEST_SEG_AR_BYTES(tmp_seg), &attr);
+ fallthrough;
+
case x86_seg_gdtr:
- __vmread(GUEST_GDTR_LIMIT, &limit);
- __vmread(GUEST_GDTR_BASE, ®->base);
- break;
case x86_seg_idtr:
- __vmread(GUEST_IDTR_LIMIT, &limit);
- __vmread(GUEST_IDTR_BASE, ®->base);
- break;
- case x86_seg_ldtr:
- __vmread(GUEST_LDTR_SELECTOR, &sel);
- __vmread(GUEST_LDTR_LIMIT, &limit);
- __vmread(GUEST_LDTR_BASE, ®->base);
- __vmread(GUEST_LDTR_AR_BYTES, &attr);
+ __vmread(GUEST_SEG_LIMIT(tmp_seg), &limit);
+ __vmread(GUEST_SEG_BASE(tmp_seg), ®->base);
break;
+
default:
BUG();
return;
@@ -1150,32 +1151,22 @@ static void vmx_set_segment_register(struct vcpu *v, enum x86_segment seg,
switch ( seg )
{
+ case x86_seg_tr:
+ case x86_seg_ldtr:
+ seg ^= 1; /* Flip tr and ldtr so GUEST_SEG_*() works. */
+ fallthrough;
+
case x86_seg_es ... x86_seg_gs:
__vmwrite(GUEST_SEG_SELECTOR(seg), sel);
- __vmwrite(GUEST_SEG_LIMIT(seg), limit);
- __vmwrite(GUEST_SEG_BASE(seg), base);
__vmwrite(GUEST_SEG_AR_BYTES(seg), attr);
- break;
- case x86_seg_tr:
- __vmwrite(GUEST_TR_SELECTOR, sel);
- __vmwrite(GUEST_TR_LIMIT, limit);
- __vmwrite(GUEST_TR_BASE, base);
- __vmwrite(GUEST_TR_AR_BYTES, attr);
- break;
+ fallthrough;
+
case x86_seg_gdtr:
- __vmwrite(GUEST_GDTR_LIMIT, limit);
- __vmwrite(GUEST_GDTR_BASE, base);
- break;
case x86_seg_idtr:
- __vmwrite(GUEST_IDTR_LIMIT, limit);
- __vmwrite(GUEST_IDTR_BASE, base);
- break;
- case x86_seg_ldtr:
- __vmwrite(GUEST_LDTR_SELECTOR, sel);
- __vmwrite(GUEST_LDTR_LIMIT, limit);
- __vmwrite(GUEST_LDTR_BASE, base);
- __vmwrite(GUEST_LDTR_AR_BYTES, attr);
+ __vmwrite(GUEST_SEG_LIMIT(seg), limit);
+ __vmwrite(GUEST_SEG_BASE(seg), base);
break;
+
default:
BUG();
}
--
2.11.0
On 21.01.2022 12:08, Andrew Cooper wrote: > Xen's segment enumeration almost matches the VMCS encoding order, while the > VMCS encoding order has the system segments immediately following the user > segments for all relevant attributes. > > Use a sneaky xor to hide the difference in encoding order to fold the switch > statements, dropping 10 __vmread() and 10 __vmwrite() calls. Bloat-o-meter > reports: > > add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-433 (-433) > Function old new delta > vmx_set_segment_register 804 593 -211 > vmx_get_segment_register 778 556 -222 > > showing that these wrappers aren't trivial. In addition, 20 BUGs worth of > metadata are dropped. > > No functional change. > > Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
© 2016 - 2024 Red Hat, Inc.