1 /*- 2 * Copyright (c) 2008 Marcel Moolenaar 3 * Copyright (c) 2009 Nathan Whitehorn 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/bus.h> 35 #include <sys/pcpu.h> 36 #include <sys/proc.h> 37 #include <sys/smp.h> 38 #include <vm/vm.h> 39 #include <vm/pmap.h> 40 41 #include <machine/bus.h> 42 #include <machine/cpu.h> 43 #include <machine/hid.h> 44 #include <machine/platformvar.h> 45 #include <machine/pmap.h> 46 #include <machine/rtas.h> 47 #include <machine/smp.h> 48 #include <machine/spr.h> 49 #include <machine/trap.h> 50 51 #include <dev/ofw/openfirm.h> 52 #include <machine/ofw_machdep.h> 53 54 #include "platform_if.h" 55 56 #ifdef SMP 57 extern void *ap_pcpu; 58 #endif 59 60 #ifdef __powerpc64__ 61 static uint8_t splpar_vpa[640] __aligned(64); 62 #endif 63 64 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; 65 66 static int chrp_probe(platform_t); 67 static int chrp_attach(platform_t); 68 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz, 69 struct mem_region *avail, int *availsz); 70 static vm_offset_t chrp_real_maxaddr(platform_t); 71 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); 72 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); 73 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); 74 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); 75 static void chrp_smp_ap_init(platform_t); 76 #ifdef SMP 77 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); 78 static struct cpu_group *chrp_smp_topo(platform_t plat); 79 #endif 80 static void chrp_reset(platform_t); 81 #ifdef __powerpc64__ 82 #include "phyp-hvcall.h" 83 static void phyp_cpu_idle(sbintime_t sbt); 84 #endif 85 86 static platform_method_t chrp_methods[] = { 87 PLATFORMMETHOD(platform_probe, chrp_probe), 88 PLATFORMMETHOD(platform_attach, chrp_attach), 89 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), 90 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), 91 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), 92 93 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), 94 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), 95 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), 96 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), 97 #ifdef SMP 98 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), 99 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), 100 #endif 101 102 PLATFORMMETHOD(platform_reset, chrp_reset), 103 104 { 0, 0 } 105 }; 106 107 static platform_def_t chrp_platform = { 108 "chrp", 109 chrp_methods, 110 0 111 }; 112 113 PLATFORM_DEF(chrp_platform); 114 115 static int 116 chrp_probe(platform_t plat) 117 { 118 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) 119 return (BUS_PROBE_GENERIC); 120 121 return (ENXIO); 122 } 123 124 static int 125 chrp_attach(platform_t plat) 126 { 127 #ifdef __powerpc64__ 128 /* XXX: check for /rtas/ibm,hypertas-functions? */ 129 if (!(mfmsr() & PSL_HV)) { 130 struct mem_region *phys, *avail; 131 int nphys, navail; 132 mem_regions(&phys, &nphys, &avail, &navail); 133 realmaxaddr = phys[0].mr_size; 134 135 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); 136 cpu_idle_hook = phyp_cpu_idle; 137 138 /* Set up important VPA fields */ 139 bzero(splpar_vpa, sizeof(splpar_vpa)); 140 splpar_vpa[4] = (uint8_t)((sizeof(splpar_vpa) >> 8) & 0xff); 141 splpar_vpa[5] = (uint8_t)(sizeof(splpar_vpa) & 0xff); 142 splpar_vpa[0xba] = 1; /* Maintain FPRs */ 143 splpar_vpa[0xbb] = 1; /* Maintain PMCs */ 144 splpar_vpa[0xfc] = 0xff; /* Maintain full SLB */ 145 splpar_vpa[0xfd] = 0xff; 146 splpar_vpa[0xff] = 1; /* Maintain Altivec */ 147 mb(); 148 149 /* Set up hypervisor CPU stuff */ 150 chrp_smp_ap_init(plat); 151 } 152 #endif 153 154 /* Some systems (e.g. QEMU) need Open Firmware to stand down */ 155 ofw_quiesce(); 156 157 return (0); 158 } 159 160 static int 161 parse_drconf_memory(struct mem_region *ofmem, int *msz, 162 struct mem_region *ofavail, int *asz) 163 { 164 phandle_t phandle; 165 vm_offset_t base; 166 int i, idx, len, lasz, lmsz, res; 167 uint32_t flags, lmb_size[2]; 168 uint64_t *dmem; 169 170 lmsz = *msz; 171 lasz = *asz; 172 173 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); 174 if (phandle == -1) 175 /* No drconf node, return. */ 176 return (0); 177 178 res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size)); 179 if (res == -1) 180 return (0); 181 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20); 182 183 /* Parse the /ibm,dynamic-memory. 184 The first position gives the # of entries. The next two words 185 reflect the address of the memory block. The next four words are 186 the DRC index, reserved, list index and flags. 187 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory) 188 189 #el Addr DRC-idx res list-idx flags 190 ------------------------------------------------- 191 | 4 | 8 | 4 | 4 | 4 | 4 |.... 192 ------------------------------------------------- 193 */ 194 195 len = OF_getproplen(phandle, "ibm,dynamic-memory"); 196 if (len > 0) { 197 198 /* We have to use a variable length array on the stack 199 since we have very limited stack space. 200 */ 201 cell_t arr[len/sizeof(cell_t)]; 202 203 res = OF_getprop(phandle, "ibm,dynamic-memory", &arr, 204 sizeof(arr)); 205 if (res == -1) 206 return (0); 207 208 /* Number of elements */ 209 idx = arr[0]; 210 211 /* First address, in arr[1], arr[2]*/ 212 dmem = (uint64_t*)&arr[1]; 213 214 for (i = 0; i < idx; i++) { 215 base = *dmem; 216 dmem += 2; 217 flags = *dmem; 218 /* Use region only if available and not reserved. */ 219 if ((flags & 0x8) && !(flags & 0x80)) { 220 ofmem[lmsz].mr_start = base; 221 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1]; 222 ofavail[lasz].mr_start = base; 223 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1]; 224 lmsz++; 225 lasz++; 226 } 227 dmem++; 228 } 229 } 230 231 *msz = lmsz; 232 *asz = lasz; 233 234 return (1); 235 } 236 237 void 238 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, 239 struct mem_region *avail, int *availsz) 240 { 241 vm_offset_t maxphysaddr; 242 int i; 243 244 ofw_mem_regions(phys, physsz, avail, availsz); 245 parse_drconf_memory(phys, physsz, avail, availsz); 246 247 /* 248 * On some firmwares (SLOF), some memory may be marked available that 249 * doesn't actually exist. This manifests as an extension of the last 250 * available segment past the end of physical memory, so truncate that 251 * one. 252 */ 253 maxphysaddr = 0; 254 for (i = 0; i < *physsz; i++) 255 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr) 256 maxphysaddr = phys[i].mr_start + phys[i].mr_size; 257 258 for (i = 0; i < *availsz; i++) 259 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr) 260 avail[i].mr_size = maxphysaddr - avail[i].mr_start; 261 } 262 263 static vm_offset_t 264 chrp_real_maxaddr(platform_t plat) 265 { 266 return (realmaxaddr); 267 } 268 269 static u_long 270 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) 271 { 272 phandle_t phandle; 273 int32_t ticks = -1; 274 275 phandle = cpuref->cr_hwref; 276 277 OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); 278 279 if (ticks <= 0) 280 panic("Unable to determine timebase frequency!"); 281 282 return (ticks); 283 } 284 285 static int 286 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 287 { 288 char buf[8]; 289 phandle_t cpu, dev, root; 290 int res, cpuid; 291 292 root = OF_peer(0); 293 294 dev = OF_child(root); 295 while (dev != 0) { 296 res = OF_getprop(dev, "name", buf, sizeof(buf)); 297 if (res > 0 && strcmp(buf, "cpus") == 0) 298 break; 299 dev = OF_peer(dev); 300 } 301 if (dev == 0) { 302 /* 303 * psim doesn't have a name property on the /cpus node, 304 * but it can be found directly 305 */ 306 dev = OF_finddevice("/cpus"); 307 if (dev == 0) 308 return (ENOENT); 309 } 310 311 cpu = OF_child(dev); 312 313 while (cpu != 0) { 314 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 315 if (res > 0 && strcmp(buf, "cpu") == 0) 316 break; 317 cpu = OF_peer(cpu); 318 } 319 if (cpu == 0) 320 return (ENOENT); 321 322 cpuref->cr_hwref = cpu; 323 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 324 sizeof(cpuid)); 325 if (res <= 0) 326 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 327 if (res <= 0) 328 cpuid = 0; 329 cpuref->cr_cpuid = cpuid; 330 331 return (0); 332 } 333 334 static int 335 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 336 { 337 char buf[8]; 338 phandle_t cpu; 339 int i, res, cpuid; 340 341 /* Check for whether it should be the next thread */ 342 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); 343 if (res > 0) { 344 cell_t interrupt_servers[res/sizeof(cell_t)]; 345 OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", 346 interrupt_servers, res); 347 for (i = 0; i < res/sizeof(cell_t) - 1; i++) { 348 if (interrupt_servers[i] == cpuref->cr_cpuid) { 349 cpuref->cr_cpuid = interrupt_servers[i+1]; 350 return (0); 351 } 352 } 353 } 354 355 /* Next CPU core/package */ 356 cpu = OF_peer(cpuref->cr_hwref); 357 while (cpu != 0) { 358 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 359 if (res > 0 && strcmp(buf, "cpu") == 0) 360 break; 361 cpu = OF_peer(cpu); 362 } 363 if (cpu == 0) 364 return (ENOENT); 365 366 cpuref->cr_hwref = cpu; 367 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 368 sizeof(cpuid)); 369 if (res <= 0) 370 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 371 if (res <= 0) 372 cpuid = 0; 373 cpuref->cr_cpuid = cpuid; 374 375 return (0); 376 } 377 378 static int 379 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 380 { 381 ihandle_t inst; 382 phandle_t bsp, chosen; 383 int res, cpuid; 384 385 chosen = OF_finddevice("/chosen"); 386 if (chosen == 0) 387 return (ENXIO); 388 389 res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); 390 if (res < 0) 391 return (ENXIO); 392 393 bsp = OF_instance_to_package(inst); 394 395 /* Pick the primary thread. Can it be any other? */ 396 cpuref->cr_hwref = bsp; 397 res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, 398 sizeof(cpuid)); 399 if (res <= 0) 400 res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid)); 401 if (res <= 0) 402 cpuid = 0; 403 cpuref->cr_cpuid = cpuid; 404 405 return (0); 406 } 407 408 #ifdef SMP 409 static int 410 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) 411 { 412 cell_t start_cpu; 413 int result, err, timeout; 414 415 if (!rtas_exists()) { 416 printf("RTAS uninitialized: unable to start AP %d\n", 417 pc->pc_cpuid); 418 return (ENXIO); 419 } 420 421 start_cpu = rtas_token_lookup("start-cpu"); 422 if (start_cpu == -1) { 423 printf("RTAS unknown method: unable to start AP %d\n", 424 pc->pc_cpuid); 425 return (ENXIO); 426 } 427 428 ap_pcpu = pc; 429 powerpc_sync(); 430 431 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, 432 &err); 433 if (result < 0 || err != 0) { 434 printf("RTAS error (%d/%d): unable to start AP %d\n", 435 result, err, pc->pc_cpuid); 436 return (ENXIO); 437 } 438 439 timeout = 10000; 440 while (!pc->pc_awake && timeout--) 441 DELAY(100); 442 443 return ((pc->pc_awake) ? 0 : EBUSY); 444 } 445 446 static struct cpu_group * 447 chrp_smp_topo(platform_t plat) 448 { 449 struct pcpu *pc, *last_pc; 450 int i, ncores, ncpus; 451 452 ncores = ncpus = 0; 453 last_pc = NULL; 454 for (i = 0; i <= mp_maxid; i++) { 455 pc = pcpu_find(i); 456 if (pc == NULL) 457 continue; 458 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) 459 ncores++; 460 last_pc = pc; 461 ncpus++; 462 } 463 464 if (ncpus % ncores != 0) { 465 printf("WARNING: Irregular SMP topology. Performance may be " 466 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); 467 return (smp_topo_none()); 468 } 469 470 /* Don't do anything fancier for non-threaded SMP */ 471 if (ncpus == ncores) 472 return (smp_topo_none()); 473 474 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); 475 } 476 #endif 477 478 static void 479 chrp_reset(platform_t platform) 480 { 481 OF_reboot(); 482 } 483 484 #ifdef __powerpc64__ 485 static void 486 phyp_cpu_idle(sbintime_t sbt) 487 { 488 phyp_hcall(H_CEDE); 489 } 490 491 static void 492 chrp_smp_ap_init(platform_t platform) 493 { 494 if (!(mfmsr() & PSL_HV)) { 495 /* Set interrupt priority */ 496 phyp_hcall(H_CPPR, 0xff); 497 498 /* Register VPA */ 499 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa); 500 } 501 } 502 #else 503 static void 504 chrp_smp_ap_init(platform_t platform) 505 { 506 } 507 #endif 508 509