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