HV: bug fix on emulating guest IPI

With current code, the INIT-STARTUP IPI with EXCLUDING_SELF shorthand
cannot be handled.

This patch is to correct hypervisor to emulate IPI with different
delivery_mode & shorthand.

Signed-off-by: Zheng, Gen <gen.zheng@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Zheng, Gen 2018-07-02 09:26:37 +08:00 committed by lijinxia
parent 61cd6946d0
commit 8b1c5a7cba

View File

@ -1024,10 +1024,9 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic)
bool phys; bool phys;
uint64_t dmask = 0; uint64_t dmask = 0;
uint64_t icrval; uint64_t icrval;
uint32_t dest, vec, mode; uint32_t dest, vec, mode, shorthand;
struct lapic_regs *lapic; struct lapic_regs *lapic;
struct vcpu *target_vcpu; struct vcpu *target_vcpu;
uint32_t target_vcpu_id;
lapic = vlapic->apic_page; lapic = vlapic->apic_page;
lapic->icr_lo &= ~APIC_DELSTAT_PEND; lapic->icr_lo &= ~APIC_DELSTAT_PEND;
@ -1037,6 +1036,7 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic)
vec = icrval & APIC_VECTOR_MASK; vec = icrval & APIC_VECTOR_MASK;
mode = icrval & APIC_DELMODE_MASK; mode = icrval & APIC_DELMODE_MASK;
phys = ((icrval & APIC_DESTMODE_LOG) == 0); phys = ((icrval & APIC_DESTMODE_LOG) == 0);
shorthand = icrval & APIC_DEST_MASK;
if (mode == APIC_DELMODE_FIXED && vec < 16) { if (mode == APIC_DELMODE_FIXED && vec < 16) {
vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR); vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR);
@ -1047,65 +1047,52 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic)
dev_dbg(ACRN_DBG_LAPIC, dev_dbg(ACRN_DBG_LAPIC,
"icrlo 0x%016llx triggered ipi %d", icrval, vec); "icrlo 0x%016llx triggered ipi %d", icrval, vec);
if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) { if ((shorthand == APIC_DEST_SELF || shorthand == APIC_DEST_ALLISELF)
switch (icrval & APIC_DEST_MASK) { && (mode == APIC_DELMODE_NMI || mode == APIC_DELMODE_INIT
case APIC_DEST_DESTFLD: || mode == APIC_DELMODE_STARTUP)) {
vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false); dev_dbg(ACRN_DBG_LAPIC, "Invalid ICR value");
break; return 0;
case APIC_DEST_SELF:
bitmap_set(vlapic->vcpu->vcpu_id, &dmask);
break;
case APIC_DEST_ALLISELF:
dmask = vm_active_cpus(vlapic->vm);
break;
case APIC_DEST_ALLESELF:
dmask = vm_active_cpus(vlapic->vm);
bitmap_clear(vlapic->vcpu->vcpu_id, &dmask);
break;
default:
break;
}
while ((i = ffs64(dmask)) >= 0) {
bitmap_clear(i, &dmask);
target_vcpu = vcpu_from_vid(vlapic->vm, i);
if (target_vcpu == NULL)
return 0;
if (mode == APIC_DELMODE_FIXED) {
vlapic_set_intr(target_vcpu, vec,
LAPIC_TRIG_EDGE);
dev_dbg(ACRN_DBG_LAPIC,
"vlapic sending ipi %d to vcpu_id %d",
vec, i);
} else {
vcpu_inject_nmi(target_vcpu);
dev_dbg(ACRN_DBG_LAPIC,
"vlapic send ipi nmi to vcpu_id %d", i);
}
}
return 0; /* handled completely in the kernel */
} }
if (phys) { switch (shorthand) {
/* INIT/SIPI is sent in Physical mode with LAPIC ID as its case APIC_DEST_DESTFLD:
* destination, so the dest need to be changed to VCPU ID; vlapic_calcdest(vlapic->vm, &dmask, dest, phys, false);
*/ break;
target_vcpu_id = vm_apicid2vcpu_id(vlapic->vm, dest); case APIC_DEST_SELF:
target_vcpu = vcpu_from_vid(vlapic->vm, target_vcpu_id); bitmap_set(vlapic->vcpu->vcpu_id, &dmask);
if (target_vcpu == NULL) { break;
pr_err("Target VCPU not found"); case APIC_DEST_ALLISELF:
return 0; dmask = vm_active_cpus(vlapic->vm);
} break;
case APIC_DEST_ALLESELF:
dmask = vm_active_cpus(vlapic->vm);
bitmap_clear(vlapic->vcpu->vcpu_id, &dmask);
break;
}
if (mode == APIC_DELMODE_INIT) { while ((i = ffs64(dmask)) >= 0) {
bitmap_clear(i, &dmask);
target_vcpu = vcpu_from_vid(vlapic->vm, i);
if (target_vcpu == NULL)
continue;
if (mode == APIC_DELMODE_FIXED) {
vlapic_set_intr(target_vcpu, vec,
LAPIC_TRIG_EDGE);
dev_dbg(ACRN_DBG_LAPIC,
"vlapic sending ipi %d to vcpu_id %d",
vec, i);
} else if (mode == APIC_DELMODE_NMI){
vcpu_inject_nmi(target_vcpu);
dev_dbg(ACRN_DBG_LAPIC,
"vlapic send ipi nmi to vcpu_id %d", i);
} else if (mode == APIC_DELMODE_INIT) {
if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT) if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
return 0; continue;
dev_dbg(ACRN_DBG_LAPIC, dev_dbg(ACRN_DBG_LAPIC,
"Sending INIT from VCPU %d to %d", "Sending INIT from VCPU %d to %d",
vlapic->vcpu->vcpu_id, target_vcpu_id); vlapic->vcpu->vcpu_id, i);
/* put target vcpu to INIT state and wait for SIPI */ /* put target vcpu to INIT state and wait for SIPI */
pause_vcpu(target_vcpu, VCPU_PAUSED); pause_vcpu(target_vcpu, VCPU_PAUSED);
@ -1115,23 +1102,18 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic)
* wait-for-SIPI state. * wait-for-SIPI state.
*/ */
target_vcpu->arch_vcpu.nr_sipi = 1; target_vcpu->arch_vcpu.nr_sipi = 1;
} else if (mode == APIC_DELMODE_STARTUP) {
return 0;
}
if (mode == APIC_DELMODE_STARTUP) {
/* Ignore SIPIs in any state other than wait-for-SIPI */ /* Ignore SIPIs in any state other than wait-for-SIPI */
if ((target_vcpu->state != VCPU_INIT) || if ((target_vcpu->state != VCPU_INIT) ||
(target_vcpu->arch_vcpu.nr_sipi == 0)) (target_vcpu->arch_vcpu.nr_sipi == 0))
return 0; continue;
dev_dbg(ACRN_DBG_LAPIC, dev_dbg(ACRN_DBG_LAPIC,
"Sending SIPI from VCPU %d to %d with vector %d", "Sending SIPI from VCPU %d to %d with vector %d",
vlapic->vcpu->vcpu_id, target_vcpu_id, vec); vlapic->vcpu->vcpu_id, i, vec);
if (--target_vcpu->arch_vcpu.nr_sipi > 0) if (--target_vcpu->arch_vcpu.nr_sipi > 0)
return 0; continue;
target_vcpu->arch_vcpu.cpu_mode = CPU_MODE_REAL; target_vcpu->arch_vcpu.cpu_mode = CPU_MODE_REAL;
target_vcpu->arch_vcpu.sipi_vector = vec; target_vcpu->arch_vcpu.sipi_vector = vec;
@ -1139,15 +1121,10 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic)
target_vcpu->vcpu_id, target_vcpu->vcpu_id,
target_vcpu->vm->attr.id); target_vcpu->vm->attr.id);
schedule_vcpu(target_vcpu); schedule_vcpu(target_vcpu);
return 0;
} }
} }
/* return 0; /* handled completely in the kernel */
* This will cause a return to userland.
*/
return 1;
} }
int int