1 /* $NetBSD: ipifuncs.c,v 1.31 2001/09/28 11:59:51 chs 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 41 42 __KERNEL_RCSID(0, "$NetBSD: ipifuncs.c,v 1.31 2001/09/28 11:59:51 chs Exp $"); 43 44 /* 45 * Interprocessor interrupt handlers. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/device.h> 50 #include <sys/proc.h> 51 #include <sys/systm.h> 52 #include <sys/reboot.h> 53 54 #include <uvm/uvm_extern.h> 55 56 #include <machine/atomic.h> 57 #include <machine/alpha_cpu.h> 58 #include <machine/alpha.h> 59 #include <machine/cpu.h> 60 #include <machine/cpuvar.h> 61 #include <machine/intr.h> 62 #include <machine/rpb.h> 63 #include <machine/prom.h> 64 65 typedef void (*ipifunc_t)(struct cpu_info *, struct trapframe *); 66 67 void alpha_ipi_halt(struct cpu_info *, struct trapframe *); 68 void alpha_ipi_imb(struct cpu_info *, struct trapframe *); 69 void alpha_ipi_ast(struct cpu_info *, struct trapframe *); 70 void alpha_ipi_synch_fpu(struct cpu_info *, struct trapframe *); 71 void alpha_ipi_discard_fpu(struct cpu_info *, struct trapframe *); 72 void alpha_ipi_pause(struct cpu_info *, struct trapframe *); 73 74 /* 75 * NOTE: This table must be kept in order with the bit definitions 76 * in <machine/intr.h>. 77 */ 78 ipifunc_t ipifuncs[ALPHA_NIPIS] = { 79 alpha_ipi_halt, 80 microset, 81 pmap_do_tlb_shootdown, 82 alpha_ipi_imb, 83 alpha_ipi_ast, 84 alpha_ipi_synch_fpu, 85 alpha_ipi_discard_fpu, 86 alpha_ipi_pause, 87 pmap_do_reactivate, 88 }; 89 90 const char *ipinames[ALPHA_NIPIS] = { 91 "halt ipi", 92 "microset ipi", 93 "shootdown ipi", 94 "imb ipi", 95 "ast ipi", 96 "synch fpu ipi", 97 "discard fpu ipi", 98 "pause ipi", 99 "pmap reactivate ipi", 100 }; 101 102 /* 103 * Initialize IPI state for a CPU. 104 * 105 * Note: the cpu_info softc pointer must be valid. 106 */ 107 void 108 alpha_ipi_init(struct cpu_info *ci) 109 { 110 struct cpu_softc *sc = ci->ci_softc; 111 int i; 112 113 evcnt_attach_dynamic(&sc->sc_evcnt_ipi, EVCNT_TYPE_INTR, 114 NULL, sc->sc_dev.dv_xname, "ipi"); 115 116 for (i = 0; i < ALPHA_NIPIS; i++) { 117 evcnt_attach_dynamic(&sc->sc_evcnt_which_ipi[i], 118 EVCNT_TYPE_INTR, NULL, sc->sc_dev.dv_xname, 119 ipinames[i]); 120 } 121 } 122 123 /* 124 * Process IPIs for a CPU. 125 */ 126 void 127 alpha_ipi_process(struct cpu_info *ci, struct trapframe *framep) 128 { 129 struct cpu_softc *sc = ci->ci_softc; 130 u_long pending_ipis, bit; 131 132 #ifdef DIAGNOSTIC 133 if (sc == NULL) { 134 /* XXX panic? */ 135 printf("WARNING: no softc for ID %lu\n", ci->ci_cpuid); 136 return; 137 } 138 #endif 139 140 pending_ipis = atomic_loadlatch_ulong(&ci->ci_ipis, 0); 141 142 /* 143 * For various reasons, it is possible to have spurious calls 144 * to this routine, so just bail out now if there are none 145 * pending. 146 */ 147 if (pending_ipis == 0) 148 return; 149 150 sc->sc_evcnt_ipi.ev_count++; 151 152 for (bit = 0; bit < ALPHA_NIPIS; bit++) { 153 if (pending_ipis & (1UL << bit)) { 154 sc->sc_evcnt_which_ipi[bit].ev_count++; 155 (*ipifuncs[bit])(ci, framep); 156 } 157 } 158 } 159 160 /* 161 * Send an interprocessor interrupt. 162 */ 163 void 164 alpha_send_ipi(u_long cpu_id, u_long ipimask) 165 { 166 167 #ifdef DIAGNOSTIC 168 if (cpu_id >= hwrpb->rpb_pcs_cnt || 169 cpu_info[cpu_id] == NULL) 170 panic("alpha_send_ipi: bogus cpu_id"); 171 if (((1UL << cpu_id) & cpus_running) == 0) 172 panic("alpha_send_ipi: CPU %ld not running", cpu_id); 173 #endif 174 175 atomic_setbits_ulong(&cpu_info[cpu_id]->ci_ipis, ipimask); 176 alpha_pal_wripir(cpu_id); 177 } 178 179 /* 180 * Broadcast an IPI to all but ourselves. 181 */ 182 void 183 alpha_broadcast_ipi(u_long ipimask) 184 { 185 struct cpu_info *ci; 186 CPU_INFO_ITERATOR cii; 187 u_long cpu_id = cpu_number(); 188 u_long cpumask; 189 190 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 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 void 221 alpha_ipi_halt(struct cpu_info *ci, struct trapframe *framep) 222 { 223 u_long cpu_id = ci->ci_cpuid; 224 u_long wait_mask = (1UL << cpu_id); 225 226 /* Disable interrupts. */ 227 (void) splhigh(); 228 229 if (cpu_id != hwrpb->rpb_primary_cpu_id) { 230 /* 231 * If we're not the primary, we just halt now. 232 */ 233 cpu_halt(); 234 } 235 236 /* 237 * We're the primary. We need to wait for all the other 238 * secondary CPUs to halt, then we can drop back to the 239 * console. 240 */ 241 printf("%s: waiting for secondary CPUs to halt...\n", 242 ci->ci_softc->sc_dev.dv_xname); 243 alpha_mb(); 244 for (;;) { 245 alpha_mb(); 246 if (cpus_running == wait_mask) 247 break; 248 delay(1000); 249 } 250 251 prom_halt(boothowto & RB_HALT); 252 /* NOTREACHED */ 253 } 254 255 void 256 alpha_ipi_imb(struct cpu_info *ci, struct trapframe *framep) 257 { 258 259 alpha_pal_imb(); 260 } 261 262 void 263 alpha_ipi_ast(struct cpu_info *ci, struct trapframe *framep) 264 { 265 266 if (ci->ci_curproc != NULL) 267 aston(ci->ci_curproc); 268 } 269 270 void 271 alpha_ipi_synch_fpu(struct cpu_info *ci, struct trapframe *framep) 272 { 273 274 if (ci->ci_flags & CPUF_FPUSAVE) 275 return; 276 fpusave_cpu(ci, 1); 277 } 278 279 void 280 alpha_ipi_discard_fpu(struct cpu_info *ci, struct trapframe *framep) 281 { 282 283 if (ci->ci_flags & CPUF_FPUSAVE) 284 return; 285 fpusave_cpu(ci, 0); 286 } 287 288 void 289 alpha_ipi_pause(struct cpu_info *ci, struct trapframe *framep) 290 { 291 u_long cpumask = (1UL << ci->ci_cpuid); 292 int s; 293 294 s = splhigh(); 295 296 /* Point debuggers at our trapframe for register state. */ 297 ci->ci_db_regs = framep; 298 299 atomic_setbits_ulong(&ci->ci_flags, CPUF_PAUSED); 300 301 /* Spin with interrupts disabled until we're resumed. */ 302 do { 303 alpha_mb(); 304 } while (cpus_paused & cpumask); 305 306 atomic_clearbits_ulong(&ci->ci_flags, CPUF_PAUSED); 307 308 ci->ci_db_regs = NULL; 309 310 splx(s); 311 312 /* Do an IMB on the way out, in case the kernel text was changed. */ 313 alpha_pal_imb(); 314 } 315