svm: implement instruction fetch part of DecodeAssist (on #PF/#NPF)
authorKeir Fraser <keir@xen.org>
Mon, 18 Apr 2011 12:36:10 +0000 (13:36 +0100)
committerKeir Fraser <keir@xen.org>
Mon, 18 Apr 2011 12:36:10 +0000 (13:36 +0100)
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>
xen/arch/x86/hvm/emulate.c
xen/arch/x86/hvm/svm/svm.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/svm/vmcb.h

index af903c9b7207a035f9f778656ea11c859dcc4e6c..1230de6031b6df285cd9177761c24010d915731b 100644 (file)
@@ -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 8753cafa3e2d7df61cb251d4cdb0f47705bdbb65..a0db4c0c6c8fcdd591c8b1f6a053f979e8a42c88 100644 (file)
@@ -660,6 +660,21 @@ static void svm_set_rdtsc_exiting(struct vcpu *v, bool_t enable)
     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;
@@ -1650,6 +1665,7 @@ static struct hvm_function_table __read_mostly svm_function_table = {
     .msr_write_intercept  = svm_msr_write_intercept,
     .invlpg_intercept     = svm_invlpg_intercept,
     .set_rdtsc_exiting    = svm_set_rdtsc_exiting,
+    .get_insn_bytes       = svm_get_insn_bytes,
 
     .nhvm_vcpu_initialise = nsvm_vcpu_initialise,
     .nhvm_vcpu_destroy = nsvm_vcpu_destroy,
@@ -1836,7 +1852,12 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
                     (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;
@@ -2013,7 +2034,10 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
     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(v, regs, vmcb->exitinfo2);
+        v->arch.hvm_svm.cached_insn_len = 0;
         break;
 
     case VMEXIT_IRET: {
index 12bd8a82b1c5f1ff3e74c6c10efc3b435fa70379..1e5f7ec5fcc90b9122b9e1f386892e77134968d8 100644 (file)
@@ -133,6 +133,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,
@@ -342,6 +345,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 f589bdf5ac2a64ce56078c5609b58ba4ed580319..eecec70ce748bba3287dce4026b74f2794fbbc97 100644 (file)
@@ -501,6 +501,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