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