xen: fix dispatching of NMIs

Currently NMIs are sent over event channels, but that defeats the
purpose of NMIs since event channels can be masked. Fix this by
issuing NMIs using a hypercall, which injects a NMI (vector #2) to the
desired vCPU.

Note that NMIs could also be triggered using the emulated local APIC,
but using a hypercall is better from a performance point of view
since it doesn't involve instruction decoding when not using x2APIC
mode.

Reported and Tested by:	avg
Sponsored by:		Citrix Systems R&D
This commit is contained in:
Roger Pau Monné 2019-11-12 10:31:28 +00:00
parent c082b391b0
commit b2802351c1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354637

View File

@ -72,7 +72,6 @@ static driver_filter_t xen_invlcache;
static driver_filter_t xen_ipi_bitmap_handler;
static driver_filter_t xen_cpustop_handler;
static driver_filter_t xen_cpususpend_handler;
static driver_filter_t xen_cpustophard_handler;
#endif
/*---------------------------------- Macros ----------------------------------*/
@ -96,7 +95,6 @@ static struct xen_ipi_handler xen_ipis[] =
[IPI_TO_IDX(IPI_BITMAP_VECTOR)] = { xen_ipi_bitmap_handler, "b" },
[IPI_TO_IDX(IPI_STOP)] = { xen_cpustop_handler, "st" },
[IPI_TO_IDX(IPI_SUSPEND)] = { xen_cpususpend_handler, "sp" },
[IPI_TO_IDX(IPI_STOP_HARD)] = { xen_cpustophard_handler, "sth" },
};
#endif
@ -259,12 +257,52 @@ xen_pv_lapic_ipi_raw(register_t icrlo, u_int dest)
XEN_APIC_UNSUPPORTED;
}
#define PCPU_ID_GET(id, field) (pcpu_find(id)->pc_##field)
static void
send_nmi(int dest)
{
unsigned int cpu;
/*
* NMIs are not routed over event channels, and instead delivered as on
* native using the exception vector (#2). Triggering them can be done
* using the local APIC, or an hypercall as a shortcut like it's done
* below.
*/
switch(dest) {
case APIC_IPI_DEST_SELF:
HYPERVISOR_vcpu_op(VCPUOP_send_nmi, PCPU_GET(vcpu_id), NULL);
break;
case APIC_IPI_DEST_ALL:
CPU_FOREACH(cpu)
HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
PCPU_ID_GET(cpu, vcpu_id), NULL);
break;
case APIC_IPI_DEST_OTHERS:
CPU_FOREACH(cpu)
if (cpu != PCPU_GET(cpuid))
HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
PCPU_ID_GET(cpu, vcpu_id), NULL);
break;
default:
HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
PCPU_ID_GET(apic_cpuid(dest), vcpu_id), NULL);
break;
}
}
#undef PCPU_ID_GET
static void
xen_pv_lapic_ipi_vectored(u_int vector, int dest)
{
xen_intr_handle_t *ipi_handle;
int ipi_idx, to_cpu, self;
if (vector >= IPI_NMI_FIRST) {
send_nmi(dest);
return;
}
ipi_idx = IPI_TO_IDX(vector);
if (ipi_idx >= nitems(xen_ipis))
panic("IPI out of range");
@ -522,14 +560,6 @@ xen_cpususpend_handler(void *arg)
return (FILTER_HANDLED);
}
static int
xen_cpustophard_handler(void *arg)
{
ipi_nmi_handler();
return (FILTER_HANDLED);
}
/*----------------------------- XEN PV IPI setup -----------------------------*/
/*
* Those functions are provided outside of the Xen PV APIC implementation