Skip to content

Commit

Permalink
xen: fix dispatching of NMIs
Browse files Browse the repository at this point in the history
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
  • Loading branch information
royger authored and royger committed Nov 12, 2019
1 parent 0c5c403 commit f106f64
Showing 1 changed file with 40 additions and 10 deletions.
50 changes: 40 additions & 10 deletions sys/x86/xen/xen_apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 ----------------------------------*/
Expand All @@ -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

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit f106f64

Please sign in to comment.