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