[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |