mp_machdep.c revision 183060
1/*- 2 * Copyright (c) 2008 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/powerpc/powerpc/mp_machdep.c 183060 2008-09-16 01:05:54Z marcel $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/bus.h> 34#include <sys/pcpu.h> 35#include <sys/proc.h> 36#include <sys/sched.h> 37#include <sys/smp.h> 38 39#include <machine/bus.h> 40#include <machine/cpu.h> 41#include <machine/intr_machdep.h> 42#include <machine/smp.h> 43 44#include "pic_if.h" 45 46extern struct pcpu __pcpu[MAXCPU]; 47 48volatile static int ap_awake; 49volatile static u_int ap_state; 50volatile static uint32_t ap_decr; 51 52int mp_ipi_test = 0; 53 54void 55machdep_ap_bootstrap(void) 56{ 57 58 // __asm __volatile("mtspr 1023,%0" :: "r"(PCPU_GET(cpuid))); 59 __asm __volatile("mfspr %0,1023" : "=r"(pcpup->pc_pir)); 60 pcpup->pc_awake = 1; 61 62 while (ap_state == 0) 63 ; 64 65 __asm __volatile("mtdec %0" :: "r"(ap_decr)); 66 67 ap_awake++; 68 69 /* Initialize curthread. */ 70 PCPU_SET(curthread, PCPU_GET(idlethread)); 71 72 mtmsr(mfmsr() | PSL_EE); 73 sched_throw(NULL); 74} 75 76struct cpu_group * 77cpu_topo(void) 78{ 79 80 return (smp_topo_none()); 81} 82 83void 84cpu_mp_setmaxid(void) 85{ 86 struct cpuref cpuref; 87 int error; 88 89 mp_ncpus = 0; 90 error = powerpc_smp_first_cpu(&cpuref); 91 while (!error) { 92 mp_ncpus++; 93 error = powerpc_smp_next_cpu(&cpuref); 94 } 95 /* Sanity. */ 96 if (mp_ncpus == 0) 97 mp_ncpus = 1; 98 99 /* 100 * Set the largest cpuid we're going to use. This is necessary 101 * for VM initialization. 102 */ 103 mp_maxid = min(mp_ncpus, MAXCPU) - 1; 104} 105 106int 107cpu_mp_probe(void) 108{ 109 110 /* 111 * We're not going to enable SMP if there's only 1 processor. 112 */ 113 return (mp_ncpus > 1); 114} 115 116void 117cpu_mp_start(void) 118{ 119 struct cpuref bsp, cpu; 120 struct pcpu *pc; 121 int error; 122 123 error = powerpc_smp_get_bsp(&bsp); 124 KASSERT(error == 0, ("Don't know BSP")); 125 KASSERT(bsp.cr_cpuid == 0, ("%s: cpuid != 0", __func__)); 126 127 error = powerpc_smp_first_cpu(&cpu); 128 while (!error) { 129 if (cpu.cr_cpuid >= MAXCPU) { 130 printf("SMP: cpu%d: skipped -- ID out of range\n", 131 cpu.cr_cpuid); 132 goto next; 133 } 134 if (all_cpus & (1 << cpu.cr_cpuid)) { 135 printf("SMP: cpu%d: skipped - duplicate ID\n", 136 cpu.cr_cpuid); 137 goto next; 138 } 139 if (cpu.cr_cpuid != bsp.cr_cpuid) { 140 pc = &__pcpu[cpu.cr_cpuid]; 141 pcpu_init(pc, cpu.cr_cpuid, sizeof(*pc)); 142 } else { 143 pc = pcpup; 144 pc->pc_cpuid = bsp.cr_cpuid; 145 pc->pc_bsp = 1; 146 } 147 pc->pc_cpumask = 1 << pc->pc_cpuid; 148 pc->pc_hwref = cpu.cr_hwref; 149 all_cpus |= pc->pc_cpumask; 150 151 next: 152 error = powerpc_smp_next_cpu(&cpu); 153 } 154} 155 156void 157cpu_mp_announce(void) 158{ 159 struct pcpu *pc; 160 int i; 161 162 for (i = 0; i <= mp_maxid; i++) { 163 pc = pcpu_find(i); 164 if (pc == NULL) 165 continue; 166 printf("cpu%d: dev=%x", i, pc->pc_hwref); 167 if (pc->pc_bsp) 168 printf(" (BSP)"); 169 printf("\n"); 170 } 171} 172 173static void 174cpu_mp_unleash(void *dummy) 175{ 176 struct pcpu *pc; 177 int cpus; 178 179 if (mp_ncpus <= 1) 180 return; 181 182 if (mp_ipi_test != 1) { 183 printf("SMP: ERROR: sending of a test IPI failed\n"); 184 return; 185 } 186 187 cpus = 0; 188 smp_cpus = 0; 189 SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { 190 cpus++; 191 pc->pc_other_cpus = all_cpus & ~pc->pc_cpumask; 192 if (!pc->pc_bsp) { 193 printf("Waking up CPU %d (dev=%x)\n", pc->pc_cpuid, 194 pc->pc_hwref); 195 powerpc_smp_start_cpu(pc); 196 } else { 197 __asm __volatile("mfspr %0,1023" : "=r"(pc->pc_pir)); 198 pc->pc_awake = 1; 199 } 200 if (pc->pc_awake) 201 smp_cpus++; 202 } 203 204 ap_awake = 1; 205 __asm __volatile("mfdec %0" : "=r"(ap_decr)); 206 ap_state++; 207 208 while (ap_awake < smp_cpus) 209 ; 210 211 if (smp_cpus != cpus || cpus != mp_ncpus) { 212 printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n", 213 mp_ncpus, cpus, smp_cpus); 214 } 215 216 smp_active = 1; 217 smp_started = 1; 218} 219 220SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL); 221 222static u_int ipi_msg_cnt[32]; 223 224int 225powerpc_ipi_handler(void *arg) 226{ 227 cpumask_t self; 228 uint32_t ipimask; 229 int msg; 230 231 ipimask = atomic_readandclear_32(&(pcpup->pc_ipimask)); 232 if (ipimask == 0) 233 return (FILTER_STRAY); 234 while ((msg = ffs(ipimask) - 1) != -1) { 235 ipimask &= ~(1u << msg); 236 ipi_msg_cnt[msg]++; 237 switch (msg) { 238 case IPI_AST: 239 break; 240 case IPI_PREEMPT: 241 sched_preempt(curthread); 242 break; 243 case IPI_RENDEZVOUS: 244 smp_rendezvous_action(); 245 break; 246 case IPI_STOP: 247 self = PCPU_GET(cpumask); 248 savectx(PCPU_GET(curpcb)); 249 atomic_set_int(&stopped_cpus, self); 250 while ((started_cpus & self) == 0) 251 cpu_spinwait(); 252 atomic_clear_int(&started_cpus, self); 253 atomic_clear_int(&stopped_cpus, self); 254 break; 255 case IPI_PPC_TEST: 256 mp_ipi_test++; 257 break; 258 } 259 } 260 261 return (FILTER_HANDLED); 262} 263 264static void 265ipi_send(struct pcpu *pc, int ipi) 266{ 267 268 atomic_set_32(&pc->pc_ipimask, (1 << ipi)); 269 PIC_IPI(pic, pc->pc_cpuid); 270} 271 272/* Send an IPI to a set of cpus. */ 273void 274ipi_selected(cpumask_t cpus, int ipi) 275{ 276 struct pcpu *pc; 277 278 SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { 279 if (cpus & pc->pc_cpumask) 280 ipi_send(pc, ipi); 281 } 282} 283 284/* Send an IPI to all CPUs, including myself. */ 285void 286ipi_all(int ipi) 287{ 288 struct pcpu *pc; 289 290 SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { 291 ipi_send(pc, ipi); 292 } 293} 294 295/* Send an IPI to all CPUs EXCEPT myself. */ 296void 297ipi_all_but_self(int ipi) 298{ 299 struct pcpu *pc; 300 301 SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { 302 if (pc != pcpup) 303 ipi_send(pc, ipi); 304 } 305} 306 307/* Send an IPI to myself. */ 308void 309ipi_self(int ipi) 310{ 311 312 ipi_send(pcpup, ipi); 313} 314