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:
parent
c082b391b0
commit
b2802351c1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=354637
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user