Home | History | Annotate | Line # | Download | only in alpha
      1 /* $NetBSD: ipifuncs.c,v 1.55 2022/04/09 23:42:56 riastradh Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
      9  * NASA Ames Research Center.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
     34 
     35 __KERNEL_RCSID(0, "$NetBSD: ipifuncs.c,v 1.55 2022/04/09 23:42:56 riastradh Exp $");
     36 
     37 /*
     38  * Interprocessor interrupt handlers.
     39  */
     40 
     41 #include <sys/param.h>
     42 #include <sys/device.h>
     43 #include <sys/proc.h>
     44 #include <sys/systm.h>
     45 #include <sys/reboot.h>
     46 #include <sys/atomic.h>
     47 #include <sys/cpu.h>
     48 #include <sys/ipi.h>
     49 #include <sys/intr.h>
     50 #include <sys/xcall.h>
     51 #include <sys/bitops.h>
     52 
     53 #include <uvm/uvm_extern.h>
     54 
     55 #include <machine/alpha_cpu.h>
     56 #include <machine/alpha.h>
     57 #include <machine/cpuvar.h>
     58 #include <machine/rpb.h>
     59 #include <machine/prom.h>
     60 
     61 typedef void (*ipifunc_t)(struct cpu_info *, struct trapframe *);
     62 
     63 static void	alpha_ipi_halt(struct cpu_info *, struct trapframe *);
     64 static void	alpha_ipi_primary_cc(struct cpu_info *, struct trapframe *);
     65 static void	alpha_ipi_ast(struct cpu_info *, struct trapframe *);
     66 static void	alpha_ipi_pause(struct cpu_info *, struct trapframe *);
     67 static void	alpha_ipi_xcall(struct cpu_info *, struct trapframe *);
     68 static void	alpha_ipi_generic(struct cpu_info *, struct trapframe *);
     69 
     70 const ipifunc_t ipifuncs[ALPHA_NIPIS] = {
     71 	[ilog2(ALPHA_IPI_HALT)] =	alpha_ipi_halt,
     72 	[ilog2(ALPHA_IPI_PRIMARY_CC)] =	alpha_ipi_primary_cc,
     73 	[ilog2(ALPHA_IPI_SHOOTDOWN)] =	pmap_tlb_shootdown_ipi,
     74 	[ilog2(ALPHA_IPI_AST)] =	alpha_ipi_ast,
     75 	[ilog2(ALPHA_IPI_PAUSE)] =	alpha_ipi_pause,
     76 	[ilog2(ALPHA_IPI_XCALL)] =	alpha_ipi_xcall,
     77 	[ilog2(ALPHA_IPI_GENERIC)] =	alpha_ipi_generic
     78 };
     79 
     80 const char * const ipinames[ALPHA_NIPIS] = {
     81 	[ilog2(ALPHA_IPI_HALT)] =	"halt ipi",
     82 	[ilog2(ALPHA_IPI_PRIMARY_CC)] =	"primary cc ipi",
     83 	[ilog2(ALPHA_IPI_SHOOTDOWN)] =	"shootdown ipi",
     84 	[ilog2(ALPHA_IPI_AST)] =	"ast ipi",
     85 	[ilog2(ALPHA_IPI_PAUSE)] =	"pause ipi",
     86 	[ilog2(ALPHA_IPI_XCALL)] =	"xcall ipi",
     87 	[ilog2(ALPHA_IPI_GENERIC)] =	"generic ipi",
     88 };
     89 
     90 /*
     91  * Initialize IPI state for a CPU.
     92  *
     93  * Note: the cpu_info softc pointer must be valid.
     94  */
     95 void
     96 alpha_ipi_init(struct cpu_info *ci)
     97 {
     98 	struct cpu_softc * const sc = ci->ci_softc;
     99 	const char * const xname = device_xname(sc->sc_dev);
    100 	int i;
    101 
    102 	evcnt_attach_dynamic(&sc->sc_evcnt_ipi, EVCNT_TYPE_INTR,
    103 	    NULL, xname, "ipi");
    104 
    105 	for (i = 0; i < ALPHA_NIPIS; i++) {
    106 		evcnt_attach_dynamic(&sc->sc_evcnt_which_ipi[i],
    107 		    EVCNT_TYPE_INTR, NULL, xname, ipinames[i]);
    108 	}
    109 }
    110 
    111 /*
    112  * Process IPIs for a CPU.
    113  */
    114 void
    115 alpha_ipi_process(struct cpu_info *ci, struct trapframe *framep)
    116 {
    117 	struct cpu_softc * const sc = ci->ci_softc;
    118 	u_long pending_ipis, bit;
    119 
    120 #ifdef DIAGNOSTIC
    121 	if (sc == NULL) {
    122 		/* XXX panic? */
    123 		printf("WARNING: no softc for ID %lu\n", ci->ci_cpuid);
    124 		return;
    125 	}
    126 #endif
    127 
    128 	while ((pending_ipis = atomic_swap_ulong(&ci->ci_ipis, 0)) != 0) {
    129 		/*
    130 		 * Ensure everything prior to setting ci_ipis in
    131 		 * alpha_send_ipi happens-before everything after
    132 		 * reading ci_ipis here so we're not working on stale
    133 		 * inputs.
    134 		 */
    135 		membar_acquire();
    136 
    137 		sc->sc_evcnt_ipi.ev_count++;
    138 
    139 		for (bit = 0; bit < ALPHA_NIPIS; bit++) {
    140 			if (pending_ipis & (1UL << bit)) {
    141 				sc->sc_evcnt_which_ipi[bit].ev_count++;
    142 				(*ipifuncs[bit])(ci, framep);
    143 			}
    144 		}
    145 	}
    146 }
    147 
    148 /*
    149  * Send an interprocessor interrupt.
    150  */
    151 void
    152 alpha_send_ipi(u_long const cpu_id, u_long const ipimask)
    153 {
    154 
    155 	KASSERT(cpu_id < hwrpb->rpb_pcs_cnt);
    156 	KASSERT(cpu_info[cpu_id] != NULL);
    157 	KASSERT(cpus_running & (1UL << cpu_id));
    158 
    159 	/*
    160 	 * Make sure all loads and stores prior to calling
    161 	 * alpha_send_ipi() have completed before informing
    162 	 * the CPU of the work we are asking it to do.
    163 	 */
    164 	membar_release();
    165 	atomic_or_ulong(&cpu_info[cpu_id]->ci_ipis, ipimask);
    166 
    167 	/*
    168 	 * Ensure that the store of ipimask completes before actually
    169 	 * writing to the IPIR.
    170 	 *
    171 	 * Note: we use MB rather than WMB because how the IPIR
    172 	 * is implemented is not architecturally specified, and
    173 	 * WMB is only guaranteed to provide ordering for stores
    174 	 * to regions of the same memory-likeness.
    175 	 */
    176 	alpha_mb();
    177 	alpha_pal_wripir(cpu_id);
    178 }
    179 
    180 /*
    181  * Broadcast an IPI to all but ourselves.
    182  */
    183 void
    184 alpha_broadcast_ipi(u_long const ipimask)
    185 {
    186 	struct cpu_info *ci;
    187 	CPU_INFO_ITERATOR cii;
    188 
    189 	const u_long cpu_id = cpu_number();
    190 	const u_long cpumask = cpus_running & ~(1UL << cpu_id);
    191 
    192 	for (CPU_INFO_FOREACH(cii, ci)) {
    193 		if ((cpumask & (1UL << ci->ci_cpuid)) == 0)
    194 			continue;
    195 		alpha_send_ipi(ci->ci_cpuid, ipimask);
    196 	}
    197 }
    198 
    199 /*
    200  * Send an IPI to all in the list but ourselves.
    201  */
    202 void
    203 alpha_multicast_ipi(u_long cpumask, u_long const ipimask)
    204 {
    205 	struct cpu_info *ci;
    206 	CPU_INFO_ITERATOR cii;
    207 
    208 	cpumask &= cpus_running;
    209 	cpumask &= ~(1UL << cpu_number());
    210 	if (cpumask == 0)
    211 		return;
    212 
    213 	for (CPU_INFO_FOREACH(cii, ci)) {
    214 		if ((cpumask & (1UL << ci->ci_cpuid)) == 0)
    215 			continue;
    216 		alpha_send_ipi(ci->ci_cpuid, ipimask);
    217 	}
    218 }
    219 
    220 static void
    221 alpha_ipi_halt(struct cpu_info * const ci,
    222     struct trapframe * const framep __unused)
    223 {
    224 	const u_long cpu_id = ci->ci_cpuid;
    225 	const u_long wait_mask = (1UL << cpu_id);
    226 
    227 	/* Disable interrupts. */
    228 	(void) splhigh();
    229 
    230 	if (cpu_id != hwrpb->rpb_primary_cpu_id) {
    231 		/*
    232 		 * If we're not the primary, we just halt now.
    233 		 */
    234 		cpu_halt();
    235 	}
    236 
    237 	/*
    238 	 * We're the primary.  We need to wait for all the other
    239 	 * secondary CPUs to halt, then we can drop back to the
    240 	 * console.
    241 	 */
    242 	alpha_mb();
    243 	for (;;) {
    244 		alpha_mb();
    245 		if (cpus_running == wait_mask)
    246 			break;
    247 		delay(1000);
    248 	}
    249 
    250 	prom_halt(boothowto & RB_HALT);
    251 	/* NOTREACHED */
    252 }
    253 
    254 static void
    255 alpha_ipi_primary_cc(struct cpu_info * const ci __unused,
    256     struct trapframe * const framep __unused)
    257 {
    258 	int const s = splhigh();
    259 	cc_primary_cc();
    260 	splx(s);
    261 }
    262 
    263 static void
    264 alpha_ipi_ast(struct cpu_info * const ci,
    265     struct trapframe * const framep __unused)
    266 {
    267 
    268 	if (ci->ci_onproc != ci->ci_data.cpu_idlelwp)
    269 		aston(ci->ci_onproc);
    270 }
    271 
    272 static void
    273 alpha_ipi_pause(struct cpu_info * const ci, struct trapframe * const framep)
    274 {
    275 	const u_long cpumask = (1UL << ci->ci_cpuid);
    276 	int s;
    277 
    278 	s = splhigh();
    279 
    280 	/* Point debuggers at our trapframe for register state. */
    281 	ci->ci_db_regs = framep;
    282 	alpha_wmb();
    283 	atomic_or_ulong(&ci->ci_flags, CPUF_PAUSED);
    284 
    285 	/* Spin with interrupts disabled until we're resumed. */
    286 	do {
    287 		alpha_mb();
    288 	} while (cpus_paused & cpumask);
    289 
    290 	atomic_and_ulong(&ci->ci_flags, ~CPUF_PAUSED);
    291 	alpha_wmb();
    292 	ci->ci_db_regs = NULL;
    293 
    294 	splx(s);
    295 
    296 	/* Do a TBIA+IMB on the way out, in case things have changed. */
    297 	ALPHA_TBIA();
    298 	alpha_pal_imb();
    299 }
    300 
    301 /*
    302  * MD support for xcall(9) interface.
    303  */
    304 
    305 static void
    306 alpha_ipi_xcall(struct cpu_info * const ci __unused,
    307     struct trapframe * const framep __unused)
    308 {
    309 	xc_ipi_handler();
    310 }
    311 
    312 void
    313 xc_send_ipi(struct cpu_info * const ci)
    314 {
    315 	KASSERT(kpreempt_disabled());
    316 	KASSERT(curcpu() != ci);
    317 
    318 	if (ci) {
    319 		/* Unicast: remote CPU. */
    320 		alpha_send_ipi(ci->ci_cpuid, ALPHA_IPI_XCALL);
    321 	} else {
    322 		/* Broadcast: all, but local CPU (caller will handle it). */
    323 		alpha_broadcast_ipi(ALPHA_IPI_XCALL);
    324 	}
    325 }
    326 
    327 static void
    328 alpha_ipi_generic(struct cpu_info * const ci __unused,
    329     struct trapframe * const framep __unused)
    330 {
    331 	ipi_cpu_handler();
    332 }
    333 
    334 void
    335 cpu_ipi(struct cpu_info * const ci)
    336 {
    337 	KASSERT(kpreempt_disabled());
    338 	KASSERT(curcpu() != ci);
    339 
    340 	if (ci) {
    341 		/* Unicast: remote CPU. */
    342 		alpha_send_ipi(ci->ci_cpuid, ALPHA_IPI_GENERIC);
    343 	} else {
    344 		/* Broadcast: all, but local CPU (caller will handle it). */
    345 		alpha_broadcast_ipi(ALPHA_IPI_GENERIC);
    346 	}
    347 }
    348