1 /* $NetBSD: ipifuncs.c,v 1.45 2010/12/17 02:36:35 joerg 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.45 2010/12/17 02:36:35 joerg 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/intr.h> 49 #include <sys/xcall.h> 50 51 #include <uvm/uvm_extern.h> 52 53 #include <machine/alpha_cpu.h> 54 #include <machine/alpha.h> 55 #include <machine/cpuvar.h> 56 #include <machine/rpb.h> 57 #include <machine/prom.h> 58 59 typedef void (*ipifunc_t)(struct cpu_info *, struct trapframe *); 60 61 void alpha_ipi_halt(struct cpu_info *, struct trapframe *); 62 void alpha_ipi_microset(struct cpu_info *, struct trapframe *); 63 void alpha_ipi_imb(struct cpu_info *, struct trapframe *); 64 void alpha_ipi_ast(struct cpu_info *, struct trapframe *); 65 void alpha_ipi_synch_fpu(struct cpu_info *, struct trapframe *); 66 void alpha_ipi_discard_fpu(struct cpu_info *, struct trapframe *); 67 void alpha_ipi_pause(struct cpu_info *, struct trapframe *); 68 void alpha_ipi_xcall(struct cpu_info *, struct trapframe *); 69 70 /* 71 * NOTE: This table must be kept in order with the bit definitions 72 * in <machine/intr.h>. 73 */ 74 ipifunc_t ipifuncs[ALPHA_NIPIS] = { 75 alpha_ipi_halt, 76 alpha_ipi_microset, 77 pmap_do_tlb_shootdown, 78 alpha_ipi_imb, 79 alpha_ipi_ast, 80 alpha_ipi_synch_fpu, 81 alpha_ipi_discard_fpu, 82 alpha_ipi_pause, 83 alpha_ipi_xcall 84 }; 85 86 const char *ipinames[ALPHA_NIPIS] = { 87 "halt ipi", 88 "microset ipi", 89 "shootdown ipi", 90 "imb ipi", 91 "ast ipi", 92 "synch fpu ipi", 93 "discard fpu ipi", 94 "pause ipi", 95 "xcall ipi" 96 }; 97 98 /* 99 * Initialize IPI state for a CPU. 100 * 101 * Note: the cpu_info softc pointer must be valid. 102 */ 103 void 104 alpha_ipi_init(struct cpu_info *ci) 105 { 106 struct cpu_softc *sc = ci->ci_softc; 107 int i; 108 109 evcnt_attach_dynamic(&sc->sc_evcnt_ipi, EVCNT_TYPE_INTR, 110 NULL, sc->sc_dev.dv_xname, "ipi"); 111 112 for (i = 0; i < ALPHA_NIPIS; i++) { 113 evcnt_attach_dynamic(&sc->sc_evcnt_which_ipi[i], 114 EVCNT_TYPE_INTR, NULL, sc->sc_dev.dv_xname, 115 ipinames[i]); 116 } 117 } 118 119 /* 120 * Process IPIs for a CPU. 121 */ 122 void 123 alpha_ipi_process(struct cpu_info *ci, struct trapframe *framep) 124 { 125 struct cpu_softc *sc = ci->ci_softc; 126 u_long pending_ipis, bit; 127 128 #ifdef DIAGNOSTIC 129 if (sc == NULL) { 130 /* XXX panic? */ 131 printf("WARNING: no softc for ID %lu\n", ci->ci_cpuid); 132 return; 133 } 134 #endif 135 136 pending_ipis = atomic_swap_ulong(&ci->ci_ipis, 0); 137 138 /* 139 * For various reasons, it is possible to have spurious calls 140 * to this routine, so just bail out now if there are none 141 * pending. 142 */ 143 if (pending_ipis == 0) 144 return; 145 146 sc->sc_evcnt_ipi.ev_count++; 147 148 for (bit = 0; bit < ALPHA_NIPIS; bit++) { 149 if (pending_ipis & (1UL << bit)) { 150 sc->sc_evcnt_which_ipi[bit].ev_count++; 151 (*ipifuncs[bit])(ci, framep); 152 } 153 } 154 } 155 156 /* 157 * Send an interprocessor interrupt. 158 */ 159 void 160 alpha_send_ipi(u_long cpu_id, u_long ipimask) 161 { 162 163 #ifdef DIAGNOSTIC 164 if (cpu_id >= hwrpb->rpb_pcs_cnt || 165 cpu_info[cpu_id] == NULL) 166 panic("alpha_send_ipi: bogus cpu_id"); 167 if (((1UL << cpu_id) & cpus_running) == 0) 168 panic("alpha_send_ipi: CPU %ld not running", cpu_id); 169 #endif 170 171 atomic_or_ulong(&cpu_info[cpu_id]->ci_ipis, ipimask); 172 alpha_pal_wripir(cpu_id); 173 } 174 175 /* 176 * Broadcast an IPI to all but ourselves. 177 */ 178 void 179 alpha_broadcast_ipi(u_long ipimask) 180 { 181 struct cpu_info *ci; 182 CPU_INFO_ITERATOR cii; 183 u_long cpu_id = cpu_number(); 184 u_long cpumask; 185 186 cpumask = cpus_running & ~(1UL << cpu_id); 187 188 for (CPU_INFO_FOREACH(cii, ci)) { 189 if ((cpumask & (1UL << ci->ci_cpuid)) == 0) 190 continue; 191 alpha_send_ipi(ci->ci_cpuid, ipimask); 192 } 193 } 194 195 /* 196 * Send an IPI to all in the list but ourselves. 197 */ 198 void 199 alpha_multicast_ipi(u_long cpumask, u_long ipimask) 200 { 201 struct cpu_info *ci; 202 CPU_INFO_ITERATOR cii; 203 204 cpumask &= cpus_running; 205 cpumask &= ~(1UL << cpu_number()); 206 if (cpumask == 0) 207 return; 208 209 for (CPU_INFO_FOREACH(cii, ci)) { 210 if ((cpumask & (1UL << ci->ci_cpuid)) == 0) 211 continue; 212 alpha_send_ipi(ci->ci_cpuid, ipimask); 213 } 214 } 215 216 void 217 alpha_ipi_halt(struct cpu_info *ci, struct trapframe *framep) 218 { 219 u_long cpu_id = ci->ci_cpuid; 220 u_long wait_mask = (1UL << cpu_id); 221 222 /* Disable interrupts. */ 223 (void) splhigh(); 224 225 if (cpu_id != hwrpb->rpb_primary_cpu_id) { 226 /* 227 * If we're not the primary, we just halt now. 228 */ 229 cpu_halt(); 230 } 231 232 /* 233 * We're the primary. We need to wait for all the other 234 * secondary CPUs to halt, then we can drop back to the 235 * console. 236 */ 237 alpha_mb(); 238 for (;;) { 239 alpha_mb(); 240 if (cpus_running == wait_mask) 241 break; 242 delay(1000); 243 } 244 245 prom_halt(boothowto & RB_HALT); 246 /* NOTREACHED */ 247 } 248 249 void 250 alpha_ipi_microset(struct cpu_info *ci, struct trapframe *framep) 251 { 252 253 cc_calibrate_cpu(ci); 254 } 255 256 void 257 alpha_ipi_imb(struct cpu_info *ci, struct trapframe *framep) 258 { 259 260 alpha_pal_imb(); 261 } 262 263 void 264 alpha_ipi_ast(struct cpu_info *ci, struct trapframe *framep) 265 { 266 267 if (ci->ci_curlwp != ci->ci_data.cpu_idlelwp) 268 aston(ci->ci_curlwp); 269 } 270 271 void 272 alpha_ipi_synch_fpu(struct cpu_info *ci, struct trapframe *framep) 273 { 274 275 if (ci->ci_flags & CPUF_FPUSAVE) 276 return; 277 fpusave_cpu(ci, 1); 278 } 279 280 void 281 alpha_ipi_discard_fpu(struct cpu_info *ci, struct trapframe *framep) 282 { 283 284 if (ci->ci_flags & CPUF_FPUSAVE) 285 return; 286 fpusave_cpu(ci, 0); 287 } 288 289 void 290 alpha_ipi_pause(struct cpu_info *ci, struct trapframe *framep) 291 { 292 u_long cpumask = (1UL << ci->ci_cpuid); 293 int s; 294 295 s = splhigh(); 296 297 /* Point debuggers at our trapframe for register state. */ 298 ci->ci_db_regs = framep; 299 300 atomic_or_ulong(&ci->ci_flags, CPUF_PAUSED); 301 302 /* Spin with interrupts disabled until we're resumed. */ 303 do { 304 alpha_mb(); 305 } while (cpus_paused & cpumask); 306 307 atomic_and_ulong(&ci->ci_flags, ~CPUF_PAUSED); 308 309 ci->ci_db_regs = NULL; 310 311 splx(s); 312 313 /* Do an IMB on the way out, in case the kernel text was changed. */ 314 alpha_pal_imb(); 315 } 316 317 /* 318 * MD support for xcall(9) interface. 319 */ 320 321 void 322 alpha_ipi_xcall(struct cpu_info *ci, struct trapframe *framep) 323 { 324 325 xc_ipi_handler(); 326 } 327 328 void 329 xc_send_ipi(struct cpu_info *ci) 330 { 331 332 KASSERT(kpreempt_disabled()); 333 KASSERT(curcpu() != ci); 334 335 if (ci) { 336 /* Unicast: remote CPU. */ 337 alpha_send_ipi(ci->ci_cpuid, ALPHA_IPI_XCALL); 338 } else { 339 /* Broadcast: all, but local CPU (caller will handle it). */ 340 alpha_broadcast_ipi(ALPHA_IPI_XCALL); 341 } 342 } 343