#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipi.c,v 1.30 2019/12/01 15:34:46 ad Exp $");
#include "opt_mtrr.h"
#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/intr.h>
#include <sys/ipi.h>
#include <sys/cpu.h>
#include <sys/xcall.h>
#ifdef MULTIPROCESSOR
#include <machine/cpufunc.h>
#include <machine/cpuvar.h>
#include <machine/i82093var.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#include <machine/mtrr.h>
#include <machine/gdt.h>
#include "acpica.h"
#include <x86/fpu.h>
static void x86_ipi_ast(struct cpu_info *);
static void x86_ipi_halt(struct cpu_info *);
static void x86_ipi_kpreempt(struct cpu_info *);
static void x86_ipi_xcall(struct cpu_info *);
static void x86_ipi_generic(struct cpu_info *);
#ifdef MTRR
static void x86_ipi_reload_mtrr(struct cpu_info *);
#else
#define x86_ipi_reload_mtrr NULL
#endif
#if NACPICA > 0
void acpi_cpu_sleep(struct cpu_info *);
#else
#define acpi_cpu_sleep NULL
#endif
static void x86_ipi_synch_fpu(struct cpu_info *);
void (* const ipifunc[X86_NIPI])(struct cpu_info *) =
{
x86_ipi_halt,
x86_ipi_ast,
x86_ipi_generic,
x86_ipi_synch_fpu,
x86_ipi_reload_mtrr,
NULL,
x86_ipi_xcall,
acpi_cpu_sleep,
x86_ipi_kpreempt
};
int
x86_send_ipi(struct cpu_info *ci, int ipimask)
{
uint32_t o, n;
int ret = 0;
if (__predict_false((ci->ci_flags & CPUF_RUNNING) == 0))
return ENOENT;
for (o = 0;; o = n) {
n = atomic_cas_32(&ci->ci_ipis, o, o | ipimask);
if (__predict_true(o == n)) {
break;
}
}
if (o == 0) {
ret = x86_ipi(LAPIC_IPI_VECTOR, ci->ci_cpuid, LAPIC_DLMODE_FIXED);
if (ret != 0) {
printf("ipi of %x from %s to %s failed\n",
ipimask,
device_xname(curcpu()->ci_dev),
device_xname(ci->ci_dev));
}
}
return ret;
}
void
x86_broadcast_ipi(int ipimask)
{
struct cpu_info *ci, *self = curcpu();
int count = 0;
CPU_INFO_ITERATOR cii;
for (CPU_INFO_FOREACH(cii, ci)) {
if (ci == self)
continue;
if ((ci->ci_flags & CPUF_RUNNING) == 0)
continue;
atomic_or_32(&ci->ci_ipis, ipimask);
count++;
}
if (!count)
return;
x86_ipi(LAPIC_IPI_VECTOR, LAPIC_DEST_ALLEXCL, LAPIC_DLMODE_FIXED);
}
void
x86_ipi_handler(void)
{
struct cpu_info *ci = curcpu();
uint32_t pending;
int bit;
pending = atomic_swap_32(&ci->ci_ipis, 0);
KDASSERT((pending >> X86_NIPI) == 0);
while ((bit = ffs(pending)) != 0) {
bit--;
pending &= ~(1 << bit);
ci->ci_ipi_events[bit].ev_count++;
(*ipifunc[bit])(ci);
}
}
static void
x86_ipi_halt(struct cpu_info *ci)
{
x86_disable_intr();
atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING);
for (;;) {
x86_hlt();
}
}
static void
x86_ipi_synch_fpu(struct cpu_info *ci)
{
panic("%s: impossible", __func__);
}
#ifdef MTRR
static void
x86_ipi_reload_mtrr(struct cpu_info *ci)
{
if (mtrr_funcs != NULL) {
mtrr_reload_cpu(ci);
}
}
#endif
static void
x86_ipi_kpreempt(struct cpu_info *ci)
{
softint_trigger(1 << SIR_PREEMPT);
}
static void
x86_ipi_ast(struct cpu_info *ci)
{
aston(ci->ci_onproc);
}
static void
x86_ipi_xcall(struct cpu_info *ci)
{
xc_ipi_handler();
}
static void
x86_ipi_generic(struct cpu_info *ci)
{
ipi_cpu_handler();
}
void
xc_send_ipi(struct cpu_info *ci)
{
KASSERT(kpreempt_disabled());
KASSERT(curcpu() != ci);
if (ci) {
x86_send_ipi(ci, X86_IPI_XCALL);
} else {
x86_broadcast_ipi(X86_IPI_XCALL);
}
}
void
cpu_ipi(struct cpu_info *ci)
{
KASSERT(kpreempt_disabled());
KASSERT(curcpu() != ci);
if (ci) {
x86_send_ipi(ci, X86_IPI_GENERIC);
} else {
x86_broadcast_ipi(X86_IPI_GENERIC);
}
}
#else
int
x86_send_ipi(struct cpu_info *ci, int ipimask)
{
return 0;
}
void
x86_broadcast_ipi(int ipimask)
{
}
void
cpu_ipi(struct cpu_info *ci)
{
}
#endif