[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC PATCH 6/9] x86/SVM: Add AVIC vmexit handlers



AVIC introduces two #vmexit handlers:
  * VMEXIT_INCOMP_IPI
  * VMEXIT_DO_NOACCEL

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
---
 xen/arch/x86/hvm/svm/avic.c        | 279 +++++++++++++++++++++++++++++++++++++
 xen/arch/x86/hvm/svm/svm.c         |   8 ++
 xen/include/asm-x86/hvm/svm/avic.h |   3 +
 xen/include/asm-x86/hvm/svm/vmcb.h |   2 +
 4 files changed, 292 insertions(+)

diff --git a/xen/arch/x86/hvm/svm/avic.c b/xen/arch/x86/hvm/svm/avic.c
index 70bac69..90df181 100644
--- a/xen/arch/x86/hvm/svm/avic.c
+++ b/xen/arch/x86/hvm/svm/avic.c
@@ -18,6 +18,7 @@
 #define AVIC_DOORBELL           0xc001011b
 #define AVIC_HPA_MASK           ~((0xFFFULL << 52) || 0xFFF)
 #define AVIC_APIC_BAR_MASK      0xFFFFFFFFFF000ULL
+#define AVIC_UNACCEL_ACCESS_OFFSET_MASK    0xFF0
 
 bool_t svm_avic = 0;
 boolean_param("svm-avic", svm_avic);
@@ -215,3 +216,281 @@ int svm_avic_init_vmcb(struct vcpu *v)
 
     return 0;
 }
+
+/***************************************************************
+ * AVIC INCOMP IPI VMEXIT
+ */
+void svm_avic_vmexit_do_incomp_ipi(struct cpu_user_regs *regs)
+{
+    struct vcpu *v = current;
+    struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+    u32 icrh = vmcb->exitinfo1 >> 32;
+    u32 icrl = vmcb->exitinfo1;
+    u32 id = vmcb->exitinfo2 >> 32;
+    u32 index = vmcb->exitinfo2 && 0xFF;
+
+    dprintk(XENLOG_DEBUG, "SVM: %s: cpu=%#x, vcpu=%#x, "
+           "icrh:icrl=%#010x:%08x, id=%u, index=%u\n",
+           __func__, v->processor, v->vcpu_id, icrh, icrl, id, index);
+
+    switch ( id )
+    {
+    case AVIC_INCMP_IPI_ERR_INVALID_INT_TYPE:
+        /*
+         * AVIC hardware handles the generation of
+         * IPIs when the specified Message Type is Fixed
+         * (also known as fixed delivery mode) and
+         * the Trigger Mode is edge-triggered. The hardware
+         * also supports self and broadcast delivery modes
+         * specified via the Destination Shorthand(DSH)
+         * field of the ICRL. Logical and physical APIC ID
+         * formats are supported. All other IPI types cause
+         * a #VMEXIT, which needs to emulated.
+         */
+        vlapic_reg_write(v, APIC_ICR2, icrh);
+        vlapic_reg_write(v, APIC_ICR, icrl);
+        break;
+    case AVIC_INCMP_IPI_ERR_TARGET_NOT_RUN:
+    {
+        /*
+         * At this point, we expect that the AVIC HW has already
+         * set the appropriate IRR bits on the valid target
+         * vcpus. So, we just need to kick the appropriate vcpu.
+         */
+        struct vcpu *c;
+        struct domain *d = v->domain;
+        uint32_t dest = GET_xAPIC_DEST_FIELD(icrh);
+        uint32_t short_hand = icrl & APIC_SHORT_MASK;
+        bool_t dest_mode = !!(icrl & APIC_DEST_MASK);
+
+        for_each_vcpu ( d, c )
+        {
+            if ( vlapic_match_dest(vcpu_vlapic(c), vcpu_vlapic(v),
+                                   short_hand, dest, dest_mode) )
+            {
+                vcpu_kick(c);
+                break;
+            }
+        }
+        break;
+    }
+    case AVIC_INCMP_IPI_ERR_INV_TARGET:
+        dprintk(XENLOG_ERR,
+                "SVM: %s: Invalid IPI target (icr=%#08x:%08x, idx=%u)\n",
+                __func__, icrh, icrl, index);
+        break;
+    case AVIC_INCMP_IPI_ERR_INV_BK_PAGE:
+        dprintk(XENLOG_ERR,
+                "SVM: %s: Invalid bk page (icr=%#08x:%08x, idx=%u)\n",
+                __func__, icrh, icrl, index);
+        break;
+    default:
+        dprintk(XENLOG_ERR, "SVM: %s: Unknown IPI interception\n", __func__);
+    }
+}
+
+/***************************************************************
+ * AVIC NOACCEL VMEXIT
+ */
+#define GET_APIC_LOGICAL_ID(x)        (((x) >> 24) & 0xFFu)
+
+static struct svm_avic_log_ait_entry *
+avic_get_logical_id_entry(struct vcpu *v, u32 ldr, bool flat)
+{
+    int index;
+    struct svm_avic_log_ait_entry *avic_log_ait;
+    struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+    int dlid = GET_APIC_LOGICAL_ID(ldr);
+
+    if ( !dlid )
+        return NULL;
+
+    if ( flat )
+    {
+        index = ffs(dlid) - 1;
+        if ( index > 7 )
+            return NULL;
+    }
+    else
+    {
+        int cluster = (dlid & 0xf0) >> 4;
+        int apic = ffs(dlid & 0x0f) - 1;
+
+        if ((apic < 0) || (apic > 7) || (cluster >= 0xf))
+            return NULL;
+        index = (cluster << 2) + apic;
+    }
+
+    avic_log_ait = mfn_to_virt(d->avic_log_ait_mfn);
+
+    return &avic_log_ait[index];
+}
+
+static int avic_ldr_write(struct vcpu *v, u8 g_phy_id, u32 ldr, bool valid)
+{
+    bool flat;
+    struct svm_avic_log_ait_entry *entry, new_entry;
+
+    flat = *avic_get_bk_page_entry(v, APIC_DFR) == APIC_DFR_FLAT;
+    entry = avic_get_logical_id_entry(v, ldr, flat);
+    if (!entry)
+        return -EINVAL;
+
+    new_entry = *entry;
+    smp_rmb();
+    new_entry.guest_phy_apic_id = g_phy_id;
+    new_entry.valid = valid;
+    *entry = new_entry;
+    smp_wmb();
+
+    return 0;
+}
+
+static int avic_handle_ldr_update(struct vcpu *v)
+{
+    int ret = 0;
+    struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+    u32 ldr = *avic_get_bk_page_entry(v, APIC_LDR);
+    u32 apic_id = (*avic_get_bk_page_entry(v, APIC_ID) >> 24);
+
+    if ( !ldr )
+        return 1;
+
+    ret = avic_ldr_write(v, apic_id, ldr, true);
+    if (ret && d->ldr_reg)
+    {
+        avic_ldr_write(v, 0, d->ldr_reg, false);
+        d->ldr_reg = 0;
+    }
+    else
+    {
+        d->ldr_reg = ldr;
+    }
+
+    return ret;
+}
+
+static int avic_handle_apic_id_update(struct vcpu *v, bool init)
+{
+    struct arch_svm_struct *s = &v->arch.hvm_svm;
+    struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+    u32 apic_id_reg = *avic_get_bk_page_entry(v, APIC_ID);
+    u32 id = (apic_id_reg >> 24) & 0xff;
+   struct svm_avic_phy_ait_entry *old, *new;
+
+   old = s->avic_phy_id_cache; 
+   new = avic_get_phy_ait_entry(v, id);
+   if ( !new || !old )
+       return 0;
+
+   /* We need to move physical_id_entry to new offset */
+   *new = *old;
+   *((u64 *)old) = 0ULL;
+   s->avic_phy_id_cache = new;
+
+    /*
+     * Also update the guest physical APIC ID in the logical
+     * APIC ID table entry if already setup the LDR.
+     */
+    if ( d->ldr_reg )
+        avic_handle_ldr_update(v);
+
+    return 0;
+}
+
+static int avic_handle_dfr_update(struct vcpu *v)
+{
+    struct svm_domain *d = &v->domain->arch.hvm_domain.svm;
+    u32 dfr = *avic_get_bk_page_entry(v, APIC_DFR);
+    u32 mod = (dfr >> 28) & 0xf;
+
+    /*
+     * We assume that all local APICs are using the same type.
+     * If this changes, we need to flush the AVIC logical
+     * APID id table.
+     */
+    if ( d->ldr_mode == mod )
+        return 0;
+
+    clear_domain_page(_mfn(d->avic_log_ait_mfn));
+    d->ldr_mode = mod;
+    if (d->ldr_reg)
+        avic_handle_ldr_update(v);
+    return 0;
+}
+
+static int avic_unaccel_trap_write(struct vcpu *v)
+{
+    struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+    u32 offset = vmcb->exitinfo1 & AVIC_UNACCEL_ACCESS_OFFSET_MASK;
+    u32 reg = *avic_get_bk_page_entry(v, offset);
+
+    switch ( offset ) {
+    case APIC_ID:
+        if ( avic_handle_apic_id_update(v, false) )
+            return 0;
+        break;
+    case APIC_LDR:
+        if ( avic_handle_ldr_update(v) )
+            return 0;
+        break;
+    case APIC_DFR:
+        avic_handle_dfr_update(v);
+        break;
+    default:
+        break;
+    }
+
+    vlapic_reg_write(v, offset, reg);
+
+    return 1;
+}
+
+void svm_avic_vmexit_do_noaccel(struct cpu_user_regs *regs)
+{
+    struct vcpu *v = current;
+    struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+    u32 offset = vmcb->exitinfo1 & 0xFF0;
+    u32 rw = (vmcb->exitinfo1 >> 32) & 0x1;
+    u32 vector = vmcb->exitinfo2 & 0xFFFFFFFF;
+
+    dprintk(XENLOG_DEBUG,
+           "SVM: %s: offset=%#x, rw=%#x, vector=%#x, vcpu_id=%#x, cpu=%#x\n",
+           __func__, offset, rw, vector, v->vcpu_id, v->processor);
+
+    switch(offset)
+    {
+    case APIC_ID:
+    case APIC_EOI:
+    case APIC_RRR:
+    case APIC_LDR:
+    case APIC_DFR:
+    case APIC_SPIV:
+    case APIC_ESR:
+    case APIC_ICR:
+    case APIC_LVTT:
+    case APIC_LVTTHMR:
+    case APIC_LVTPC:
+    case APIC_LVT0:
+    case APIC_LVT1:
+    case APIC_LVTERR:
+    case APIC_TMICT:
+    case APIC_TDCR:
+        /* Handling Trap */
+        if ( !rw )
+            /* Trap read should never happens */
+            BUG();
+        avic_unaccel_trap_write(v);
+        break;
+    default:
+        /* Handling Fault */
+        if ( !rw )
+            *avic_get_bk_page_entry(v, offset) = vlapic_read_aligned(
+                                                        vcpu_vlapic(v), 
offset);
+
+        hvm_mem_access_emulate_one(EMUL_KIND_NORMAL, TRAP_invalid_op,
+                                       HVM_DELIVER_NO_ERROR_CODE);
+    }
+
+    return;
+}
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index bcb7df4..409096a 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -2710,6 +2710,14 @@ void svm_vmexit_handler(struct cpu_user_regs *regs)
         svm_vmexit_do_pause(regs);
         break;
 
+    case VMEXIT_AVIC_INCOMP_IPI:
+        svm_avic_vmexit_do_incomp_ipi(regs);
+        break;
+
+    case VMEXIT_AVIC_NOACCEL:
+        svm_avic_vmexit_do_noaccel(regs);
+        break;
+
     default:
     unexpected_exit_type:
         gdprintk(XENLOG_ERR, "unexpected VMEXIT: exit reason = %#"PRIx64", "
diff --git a/xen/include/asm-x86/hvm/svm/avic.h 
b/xen/include/asm-x86/hvm/svm/avic.h
index 9508486..2c501d4 100644
--- a/xen/include/asm-x86/hvm/svm/avic.h
+++ b/xen/include/asm-x86/hvm/svm/avic.h
@@ -37,4 +37,7 @@ bool_t svm_avic_vcpu_enabled(struct vcpu *v);
 void svm_avic_update_vapic_bar(struct vcpu *v,uint64_t data);
 int svm_avic_init_vmcb(struct vcpu *v);
 
+void svm_avic_vmexit_do_incomp_ipi(struct cpu_user_regs *regs);
+void svm_avic_vmexit_do_noaccel(struct cpu_user_regs *regs);
+
 #endif /* _SVM_AVIC_H_ */
diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h 
b/xen/include/asm-x86/hvm/svm/vmcb.h
index a42c034..23eb86b 100644
--- a/xen/include/asm-x86/hvm/svm/vmcb.h
+++ b/xen/include/asm-x86/hvm/svm/vmcb.h
@@ -302,6 +302,8 @@ enum VMEXIT_EXITCODE
     VMEXIT_MWAIT_CONDITIONAL= 140, /* 0x8c */
     VMEXIT_XSETBV           = 141, /* 0x8d */
     VMEXIT_NPF              = 1024, /* 0x400, nested paging fault */
+    VMEXIT_AVIC_INCOMP_IPI  = 1025, /* 0x401 */
+    VMEXIT_AVIC_NOACCEL     = 1026, /* 0x402 */
     VMEXIT_INVALID          =  -1
 };
 
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.