diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index f129f3ac4..93ee021b5 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -101,7 +101,7 @@ vm_lapic_from_vcpu_id(struct acrn_vm *vm, uint16_t vcpu_id) return vcpu_vlapic(vcpu); } -static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint8_t lapicid) +static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint32_t lapicid) { uint16_t i; struct acrn_vcpu *vcpu; @@ -117,7 +117,7 @@ static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint8_t lapicid) if (cpu_id == INVALID_CPU_ID) { cpu_id = get_pcpu_nums(); - pr_err("%s: bad lapicid %hhu", __func__, lapicid); + pr_err("%s: bad lapicid %lu", __func__, lapicid); } return cpu_id; @@ -1045,7 +1045,7 @@ vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, b * Physical mode: destination is LAPIC ID. */ *dmask = 0UL; - vcpu_id = vm_apicid2vcpu_id(vm, (uint8_t)dest); + vcpu_id = vm_apicid2vcpu_id(vm, dest); if (vcpu_id < vm->hw.created_vcpus) { bitmap_set_lock(vcpu_id, dmask); } @@ -1135,6 +1135,57 @@ vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, b } } +/* + * This function populates 'dmask' with the set of vcpus that is the possible destination + * when lapic is passthru. + */ +void +vlapic_calcdest_lapic_pt(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys) +{ + struct acrn_vlapic *vlapic; + struct acrn_vcpu *vcpu; + uint32_t ldr, logical_id, dest_logical_id, dest_cluster_id; + uint16_t vcpu_id; + + if (dest == 0xffU) { + /* + * Broadcast in both logical and physical modes. + */ + *dmask = vm_active_cpus(vm); + } else if (phys) { + /* + * Physical mode: destination is LAPIC ID. + */ + *dmask = 0UL; + vcpu_id = vm_apicid2vcpu_id(vm, dest); + if (vcpu_id < vm->hw.created_vcpus) { + bitmap_set_lock(vcpu_id, dmask); + } + dev_dbg(ACRN_DBG_LAPICPT, "%s: phys destmod, dmask: 0x%016llx", __func__, *dmask); + } else { + /* + * Logical mode: match each APIC that has a bit set + * in its LDR that matches a bit in the ldest. + */ + foreach_vcpu(vcpu_id, vm, vcpu) { + vlapic = vm_lapic_from_vcpu_id(vm, vcpu_id); + + ldr = vlapic->apic_page.ldr.v; + logical_id = ldr & 0xFFFFU; + + dest_cluster_id = (dest >> 16U) & 0xFFFFU; + dest_logical_id = dest & 0xFFFFU; + if (dest_cluster_id != ((ldr >> 16U) & 0xFFFFU)) { + continue; + } + if ((dest_logical_id & logical_id) != 0U) { + bitmap_set_lock(vcpu_id, dmask); + } + } + dev_dbg(ACRN_DBG_LAPICPT, "%s: logical destmod, dmask: 0x%016llx", __func__, *dmask); + } +} + static void vlapic_set_tpr(struct acrn_vlapic *vlapic, uint32_t val) { diff --git a/hypervisor/arch/x86/lapic.c b/hypervisor/arch/x86/lapic.c index 380f895ad..798551386 100644 --- a/hypervisor/arch/x86/lapic.c +++ b/hypervisor/arch/x86/lapic.c @@ -6,27 +6,6 @@ #include -/* x2APIC Interrupt Command Register (ICR) structure */ -union apic_icr { - uint64_t value; - struct { - uint32_t lo_32; - uint32_t hi_32; - } value_32; - struct { - uint32_t vector:8; - uint32_t delivery_mode:3; - uint32_t destination_mode:1; - uint32_t rsvd_1:2; - uint32_t level:1; - uint32_t trigger_mode:1; - uint32_t rsvd_2:2; - uint32_t shorthand:2; - uint32_t rsvd_3:12; - uint32_t dest_field:32; - } bits; -}; - union lapic_base_msr { uint64_t value; struct { diff --git a/hypervisor/common/hypercall.c b/hypervisor/common/hypercall.c index ddb143a32..d29f83c9f 100644 --- a/hypervisor/common/hypercall.c +++ b/hypervisor/common/hypercall.c @@ -375,6 +375,53 @@ int32_t hcall_set_irqline(const struct acrn_vm *vm, uint16_t vmid, return ret; } +static void inject_msi_lapic_pt(struct acrn_vm *vm, const struct acrn_msi_entry *vmsi) +{ + union apic_icr icr; + struct acrn_vcpu *vcpu; + union msi_addr_reg vmsi_addr; + union msi_data_reg vmsi_data; + uint64_t vdmask = 0UL; + uint32_t vdest, dest = 0U; + uint16_t vcpu_id; + bool phys; + + vmsi_addr.full = vmsi->msi_addr; + vmsi_data.full = (uint32_t)vmsi->msi_data; + + dev_dbg(ACRN_DBG_LAPICPT, "%s: msi_addr 0x%016llx, msi_data 0x%016llx", + __func__, vmsi->msi_addr, vmsi->msi_data); + + if (vmsi_addr.bits.addr_base == MSI_ADDR_BASE) { + vdest = vmsi_addr.bits.dest_field; + phys = (vmsi_addr.bits.dest_mode == MSI_ADDR_DESTMODE_PHYS); + /* + * calculate all reachable destination vcpu. + * the delivery mode of vmsi will be forwarded to ICR delievry field + * and handled by hardware. + */ + vlapic_calcdest_lapic_pt(vm, &vdmask, vdest, phys); + dev_dbg(ACRN_DBG_LAPICPT, "%s: vcpu destination mask 0x%016llx", __func__, vdmask); + + vcpu_id = ffs64(vdmask); + while (vcpu_id != INVALID_BIT_INDEX) { + bitmap_clear_nolock(vcpu_id, &vdmask); + vcpu = vcpu_from_vid(vm, vcpu_id); + dest |= per_cpu(lapic_ldr, vcpu->pcpu_id); + vcpu_id = ffs64(vdmask); + } + + icr.value = 0UL; + icr.bits.dest_field = dest; + icr.bits.vector = vmsi_data.bits.vector; + icr.bits.delivery_mode = vmsi_data.bits.delivery_mode; + icr.bits.destination_mode = MSI_ADDR_DESTMODE_LOGICAL; + + msr_write(MSR_IA32_EXT_APIC_ICR, icr.value); + dev_dbg(ACRN_DBG_LAPICPT, "%s: icr.value 0x%016llx", __func__, icr.value); + } +} + /** * @brief inject MSI interrupt * @@ -390,7 +437,7 @@ int32_t hcall_set_irqline(const struct acrn_vm *vm, uint16_t vmid, */ int32_t hcall_inject_msi(struct acrn_vm *vm, uint16_t vmid, uint64_t param) { - int32_t ret; + int32_t ret = -1; struct acrn_msi_entry msi; struct acrn_vm *target_vm = get_vm_from_vmid(vmid); @@ -400,10 +447,14 @@ int32_t hcall_inject_msi(struct acrn_vm *vm, uint16_t vmid, uint64_t param) pr_err("%s: Unable copy param to vm\n", __func__); ret = -1; } else { - ret = vlapic_intr_msi(target_vm, msi.msi_addr, msi.msi_data); + /* For target cpu with lapic pt, send ipi instead of injection via vlapic */ + if (is_lapic_pt(target_vm)) { + inject_msi_lapic_pt(target_vm, &msi); + ret = 0; + } else { + ret = vlapic_intr_msi(target_vm, msi.msi_addr, msi.msi_data); + } } - } else { - ret = -1; } return ret; diff --git a/hypervisor/include/arch/x86/guest/vlapic.h b/hypervisor/include/arch/x86/guest/vlapic.h index 148f97f94..c6d5c876b 100644 --- a/hypervisor/include/arch/x86/guest/vlapic.h +++ b/hypervisor/include/arch/x86/guest/vlapic.h @@ -249,6 +249,7 @@ int32_t apic_write_vmexit_handler(struct acrn_vcpu *vcpu); int32_t veoi_vmexit_handler(struct acrn_vcpu *vcpu); int32_t tpr_below_threshold_vmexit_handler(__unused struct acrn_vcpu *vcpu); void vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, bool lowprio); +void vlapic_calcdest_lapic_pt(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys); /** * @} diff --git a/hypervisor/include/arch/x86/lapic.h b/hypervisor/include/arch/x86/lapic.h index cff933c97..77be03ce5 100644 --- a/hypervisor/include/arch/x86/lapic.h +++ b/hypervisor/include/arch/x86/lapic.h @@ -51,6 +51,26 @@ enum intr_cpu_startup_shorthand { INTR_CPU_STARTUP_UNKNOWN, }; +/* x2APIC Interrupt Command Register (ICR) structure */ +union apic_icr { + uint64_t value; + struct { + uint32_t lo_32; + uint32_t hi_32; + } value_32; + struct { + uint32_t vector:8; + uint32_t delivery_mode:3; + uint32_t destination_mode:1; + uint32_t rsvd_1:2; + uint32_t level:1; + uint32_t trigger_mode:1; + uint32_t rsvd_2:2; + uint32_t shorthand:2; + uint32_t rsvd_3:12; + uint32_t dest_field:32; + } bits; +}; /** * @brief Save context of lapic diff --git a/hypervisor/include/debug/logmsg.h b/hypervisor/include/debug/logmsg.h index cb45889aa..75d057d2d 100644 --- a/hypervisor/include/debug/logmsg.h +++ b/hypervisor/include/debug/logmsg.h @@ -27,6 +27,7 @@ */ #define LOG_MESSAGE_MAX_SIZE (4U * LOG_ENTRY_SIZE) +#define ACRN_DBG_LAPICPT 5U #if defined(HV_DEBUG) extern uint16_t console_loglevel;