2011-05-31 19:35:29 +02:00
|
|
|
References: FATE#309900
|
|
|
|
|
|
|
|
# HG changeset patch
|
|
|
|
# User Keir Fraser <keir@xen.org>
|
|
|
|
# Date 1303130170 -3600
|
|
|
|
# Node ID 60f5df2afcbbe1e8d8438c2b7b8223d9d2102e06
|
|
|
|
# Parent 381ab77db71a4739b8a4f4fdad4ef3504999f998
|
|
|
|
svm: implement instruction fetch part of DecodeAssist (on #PF/#NPF)
|
|
|
|
|
|
|
|
Newer SVM implementations (Bulldozer) copy up to 15 bytes from the
|
|
|
|
instruction stream into the VMCB when a #PF or #NPF exception is
|
|
|
|
intercepted. This patch makes use of this information if available.
|
|
|
|
This saves us from a) traversing the guest's page tables, b) mapping
|
|
|
|
the guest's memory and c) copy the instructions from there into the
|
|
|
|
hypervisor's address space.
|
|
|
|
This speeds up #NPF intercepts quite a lot and avoids cache and TLB
|
|
|
|
trashing.
|
|
|
|
|
|
|
|
Signed-off-by: Andre Przywara <andre.przywara@amd.com>
|
|
|
|
Signed-off-by: Keir Fraser <keir@xen.org>
|
|
|
|
|
|
|
|
Index: xen-4.1.1-testing/xen/arch/x86/hvm/emulate.c
|
|
|
|
===================================================================
|
|
|
|
--- xen-4.1.1-testing.orig/xen/arch/x86/hvm/emulate.c
|
|
|
|
+++ xen-4.1.1-testing/xen/arch/x86/hvm/emulate.c
|
|
|
|
@@ -996,6 +996,8 @@ int hvm_emulate_one(
|
|
|
|
|
|
|
|
hvmemul_ctxt->insn_buf_eip = regs->eip;
|
|
|
|
hvmemul_ctxt->insn_buf_bytes =
|
|
|
|
+ hvm_get_insn_bytes(curr, hvmemul_ctxt->insn_buf)
|
|
|
|
+ ? :
|
|
|
|
(hvm_virtual_to_linear_addr(
|
|
|
|
x86_seg_cs, &hvmemul_ctxt->seg_reg[x86_seg_cs],
|
|
|
|
regs->eip, sizeof(hvmemul_ctxt->insn_buf),
|
|
|
|
Index: xen-4.1.1-testing/xen/arch/x86/hvm/svm/svm.c
|
|
|
|
===================================================================
|
|
|
|
--- xen-4.1.1-testing.orig/xen/arch/x86/hvm/svm/svm.c
|
|
|
|
+++ xen-4.1.1-testing/xen/arch/x86/hvm/svm/svm.c
|
|
|
|
@@ -603,6 +603,21 @@ static void svm_set_rdtsc_exiting(struct
|
|
|
|
vmcb_set_general1_intercepts(vmcb, general1_intercepts);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static unsigned int svm_get_insn_bytes(struct vcpu *v, uint8_t *buf)
|
|
|
|
+{
|
|
|
|
+ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
|
|
|
+ unsigned int len = v->arch.hvm_svm.cached_insn_len;
|
|
|
|
+
|
|
|
|
+ if ( len != 0 )
|
|
|
|
+ {
|
|
|
|
+ /* Latch and clear the cached instruction. */
|
|
|
|
+ memcpy(buf, vmcb->guest_ins, 15);
|
|
|
|
+ v->arch.hvm_svm.cached_insn_len = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void svm_init_hypercall_page(struct domain *d, void *hypercall_page)
|
|
|
|
{
|
|
|
|
char *p;
|
2011-06-14 21:01:54 +02:00
|
|
|
@@ -1448,7 +1463,8 @@ static struct hvm_function_table __read_
|
2011-05-31 19:35:29 +02:00
|
|
|
.msr_read_intercept = svm_msr_read_intercept,
|
|
|
|
.msr_write_intercept = svm_msr_write_intercept,
|
|
|
|
.invlpg_intercept = svm_invlpg_intercept,
|
|
|
|
- .set_rdtsc_exiting = svm_set_rdtsc_exiting
|
|
|
|
+ .set_rdtsc_exiting = svm_set_rdtsc_exiting,
|
|
|
|
+ .get_insn_bytes = svm_get_insn_bytes
|
|
|
|
};
|
|
|
|
|
|
|
|
asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
2011-06-14 21:01:54 +02:00
|
|
|
@@ -1554,7 +1570,12 @@ asmlinkage void svm_vmexit_handler(struc
|
2011-05-31 19:35:29 +02:00
|
|
|
(unsigned long)regs->ecx, (unsigned long)regs->edx,
|
|
|
|
(unsigned long)regs->esi, (unsigned long)regs->edi);
|
|
|
|
|
|
|
|
- if ( paging_fault(va, regs) )
|
|
|
|
+ if ( cpu_has_svm_decode )
|
|
|
|
+ v->arch.hvm_svm.cached_insn_len = vmcb->guest_ins_len & 0xf;
|
|
|
|
+ rc = paging_fault(va, regs);
|
|
|
|
+ v->arch.hvm_svm.cached_insn_len = 0;
|
|
|
|
+
|
|
|
|
+ if ( rc )
|
|
|
|
{
|
|
|
|
if ( trace_will_trace_event(TRC_SHADOW) )
|
|
|
|
break;
|
2011-06-14 21:01:54 +02:00
|
|
|
@@ -1720,7 +1741,10 @@ asmlinkage void svm_vmexit_handler(struc
|
2011-05-31 19:35:29 +02:00
|
|
|
case VMEXIT_NPF:
|
|
|
|
perfc_incra(svmexits, VMEXIT_NPF_PERFC);
|
|
|
|
regs->error_code = vmcb->exitinfo1;
|
|
|
|
+ if ( cpu_has_svm_decode )
|
|
|
|
+ v->arch.hvm_svm.cached_insn_len = vmcb->guest_ins_len & 0xf;
|
|
|
|
svm_do_nested_pgfault(vmcb->exitinfo2);
|
|
|
|
+ v->arch.hvm_svm.cached_insn_len = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VMEXIT_IRET: {
|
|
|
|
Index: xen-4.1.1-testing/xen/include/asm-x86/hvm/hvm.h
|
|
|
|
===================================================================
|
|
|
|
--- xen-4.1.1-testing.orig/xen/include/asm-x86/hvm/hvm.h
|
|
|
|
+++ xen-4.1.1-testing/xen/include/asm-x86/hvm/hvm.h
|
|
|
|
@@ -132,6 +132,9 @@ struct hvm_function_table {
|
|
|
|
int (*cpu_up)(void);
|
|
|
|
void (*cpu_down)(void);
|
|
|
|
|
|
|
|
+ /* Copy up to 15 bytes from cached instruction bytes at current rIP. */
|
|
|
|
+ unsigned int (*get_insn_bytes)(struct vcpu *v, uint8_t *buf);
|
|
|
|
+
|
|
|
|
/* Instruction intercepts: non-void return values are X86EMUL codes. */
|
|
|
|
void (*cpuid_intercept)(
|
|
|
|
unsigned int *eax, unsigned int *ebx,
|
|
|
|
@@ -324,6 +327,11 @@ static inline void hvm_cpu_down(void)
|
|
|
|
hvm_funcs.cpu_down();
|
|
|
|
}
|
|
|
|
|
|
|
|
+static inline unsigned int hvm_get_insn_bytes(struct vcpu *v, uint8_t *buf)
|
|
|
|
+{
|
|
|
|
+ return (hvm_funcs.get_insn_bytes ? hvm_funcs.get_insn_bytes(v, buf) : 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
enum hvm_task_switch_reason { TSW_jmp, TSW_iret, TSW_call_or_int };
|
|
|
|
void hvm_task_switch(
|
|
|
|
uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
|
|
|
|
Index: xen-4.1.1-testing/xen/include/asm-x86/hvm/svm/vmcb.h
|
|
|
|
===================================================================
|
|
|
|
--- xen-4.1.1-testing.orig/xen/include/asm-x86/hvm/svm/vmcb.h
|
|
|
|
+++ xen-4.1.1-testing/xen/include/asm-x86/hvm/svm/vmcb.h
|
|
|
|
@@ -498,6 +498,9 @@ struct arch_svm_struct {
|
|
|
|
int launch_core;
|
|
|
|
bool_t vmcb_in_sync; /* VMCB sync'ed with VMSAVE? */
|
|
|
|
|
|
|
|
+ /* VMCB has a cached instruction from #PF/#NPF Decode Assist? */
|
|
|
|
+ uint8_t cached_insn_len; /* Zero if no cached instruction. */
|
|
|
|
+
|
|
|
|
/* Upper four bytes are undefined in the VMCB, therefore we can't
|
|
|
|
* use the fields in the VMCB. Write a 64bit value and then read a 64bit
|
|
|
|
* value is fine unless there's a VMRUN/VMEXIT in between which clears
|