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 int quiesce; 137 #ifdef __powerpc64__ 138 int i; 139 140 /* XXX: check for /rtas/ibm,hypertas-functions? */ 141 if (!(mfmsr() & PSL_HV)) { 142 struct mem_region *phys, *avail; 143 int nphys, navail; 144 vm_offset_t off; 145 146 mem_regions(&phys, &nphys, &avail, &navail); 147 148 realmaxaddr = 0; 149 for (i = 0; i < nphys; i++) { 150 off = phys[i].mr_start + phys[i].mr_size; 151 realmaxaddr = MAX(off, realmaxaddr); 152 } 153 154 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); 155 cpu_idle_hook = phyp_cpu_idle; 156 157 /* Set up important VPA fields */ 158 for (i = 0; i < MAXCPU; i++) { 159 /* First two: VPA size */ 160 splpar_vpa[i][4] = 161 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff); 162 splpar_vpa[i][5] = 163 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff); 164 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */ 165 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */ 166 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */ 167 splpar_vpa[i][0xfd] = 0xff; 168 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */ 169 } 170 mb(); 171 172 /* Set up hypervisor CPU stuff */ 173 chrp_smp_ap_init(plat); 174 } 175 #endif 176 chrp_cpuref_init(); 177 178 /* Some systems (e.g. QEMU) need Open Firmware to stand down */ 179 quiesce = 1; 180 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce); 181 if (quiesce) 182 ofw_quiesce(); 183 184 return (0); 185 } 186 187 static int 188 parse_drconf_memory(struct mem_region *ofmem, int *msz, 189 struct mem_region *ofavail, int *asz) 190 { 191 phandle_t phandle; 192 vm_offset_t base; 193 int i, idx, len, lasz, lmsz, res; 194 uint32_t flags, lmb_size[2]; 195 uint32_t *dmem; 196 197 lmsz = *msz; 198 lasz = *asz; 199 200 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); 201 if (phandle == -1) 202 /* No drconf node, return. */ 203 return (0); 204 205 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size, 206 sizeof(lmb_size)); 207 if (res == -1) 208 return (0); 209 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20); 210 211 /* Parse the /ibm,dynamic-memory. 212 The first position gives the # of entries. The next two words 213 reflect the address of the memory block. The next four words are 214 the DRC index, reserved, list index and flags. 215 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory) 216 217 #el Addr DRC-idx res list-idx flags 218 ------------------------------------------------- 219 | 4 | 8 | 4 | 4 | 4 | 4 |.... 220 ------------------------------------------------- 221 */ 222 223 len = OF_getproplen(phandle, "ibm,dynamic-memory"); 224 if (len > 0) { 225 226 /* We have to use a variable length array on the stack 227 since we have very limited stack space. 228 */ 229 cell_t arr[len/sizeof(cell_t)]; 230 231 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr, 232 sizeof(arr)); 233 if (res == -1) 234 return (0); 235 236 /* Number of elements */ 237 idx = arr[0]; 238 239 /* First address, in arr[1], arr[2]*/ 240 dmem = &arr[1]; 241 242 for (i = 0; i < idx; i++) { 243 base = ((uint64_t)dmem[0] << 32) + dmem[1]; 244 dmem += 4; 245 flags = dmem[1]; 246 /* Use region only if available and not reserved. */ 247 if ((flags & 0x8) && !(flags & 0x80)) { 248 ofmem[lmsz].mr_start = base; 249 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1]; 250 ofavail[lasz].mr_start = base; 251 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1]; 252 lmsz++; 253 lasz++; 254 } 255 dmem += 2; 256 } 257 } 258 259 *msz = lmsz; 260 *asz = lasz; 261 262 return (1); 263 } 264 265 void 266 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, 267 struct mem_region *avail, int *availsz) 268 { 269 vm_offset_t maxphysaddr; 270 int i; 271 272 ofw_mem_regions(phys, physsz, avail, availsz); 273 parse_drconf_memory(phys, physsz, avail, availsz); 274 275 /* 276 * On some firmwares (SLOF), some memory may be marked available that 277 * doesn't actually exist. This manifests as an extension of the last 278 * available segment past the end of physical memory, so truncate that 279 * one. 280 */ 281 maxphysaddr = 0; 282 for (i = 0; i < *physsz; i++) 283 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr) 284 maxphysaddr = phys[i].mr_start + phys[i].mr_size; 285 286 for (i = 0; i < *availsz; i++) 287 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr) 288 avail[i].mr_size = maxphysaddr - avail[i].mr_start; 289 } 290 291 static vm_offset_t 292 chrp_real_maxaddr(platform_t plat) 293 { 294 return (realmaxaddr); 295 } 296 297 static u_long 298 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) 299 { 300 phandle_t cpus, cpunode; 301 int32_t ticks = -1; 302 int res; 303 char buf[8]; 304 305 cpus = OF_finddevice("/cpus"); 306 if (cpus == -1) 307 panic("CPU tree not found on Open Firmware\n"); 308 309 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) { 310 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf)); 311 if (res > 0 && strcmp(buf, "cpu") == 0) 312 break; 313 } 314 if (cpunode <= 0) 315 panic("CPU node not found on Open Firmware\n"); 316 317 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks)); 318 319 if (ticks <= 0) 320 panic("Unable to determine timebase frequency!"); 321 322 return (ticks); 323 } 324 325 static int 326 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 327 { 328 329 if (platform_cpuref_valid == 0) 330 return (EINVAL); 331 332 cpuref->cr_cpuid = 0; 333 cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 334 335 return (0); 336 } 337 338 static int 339 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 340 { 341 int id; 342 343 if (platform_cpuref_valid == 0) 344 return (EINVAL); 345 346 id = cpuref->cr_cpuid + 1; 347 if (id >= platform_cpuref_cnt) 348 return (ENOENT); 349 350 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid; 351 cpuref->cr_hwref = platform_cpuref[id].cr_hwref; 352 353 return (0); 354 } 355 356 static int 357 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 358 { 359 360 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid; 361 cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 362 return (0); 363 } 364 365 static void 366 get_cpu_reg(phandle_t cpu, cell_t *reg) 367 { 368 int res; 369 370 res = OF_getproplen(cpu, "reg"); 371 if (res != sizeof(cell_t)) 372 panic("Unexpected length for CPU property reg on Open Firmware\n"); 373 OF_getencprop(cpu, "reg", reg, res); 374 } 375 376 static int 377 chrp_cpuref_init(void) 378 { 379 phandle_t cpu, dev, chosen, pbsp; 380 ihandle_t ibsp; 381 char buf[32]; 382 int a, bsp, res, res2, tmp_cpuref_cnt; 383 static struct cpuref tmp_cpuref[MAXCPU]; 384 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg; 385 386 if (platform_cpuref_valid) 387 return (0); 388 389 dev = OF_peer(0); 390 dev = OF_child(dev); 391 while (dev != 0) { 392 res = OF_getprop(dev, "name", buf, sizeof(buf)); 393 if (res > 0 && strcmp(buf, "cpus") == 0) 394 break; 395 dev = OF_peer(dev); 396 } 397 398 /* Make sure that cpus reg property have 1 address cell and 0 size cells */ 399 res = OF_getproplen(dev, "#address-cells"); 400 res2 = OF_getproplen(dev, "#size-cells"); 401 if (res != res2 || res != sizeof(cell_t)) 402 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n"); 403 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells)); 404 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells)); 405 if (addr_cells != 1 || size_cells != 0) 406 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n"); 407 408 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */ 409 410 chosen = OF_finddevice("/chosen"); 411 if (chosen == -1) 412 panic("Device /chosen not found on Open Firmware\n"); 413 414 bsp_reg = -1; 415 416 /* /chosen/cpu */ 417 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) { 418 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp)); 419 pbsp = OF_instance_to_package(ibsp); 420 if (pbsp != -1) 421 get_cpu_reg(pbsp, &bsp_reg); 422 } 423 424 /* /chosen/fdtbootcpu */ 425 if (bsp_reg == -1) { 426 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t)) 427 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg)); 428 } 429 430 if (bsp_reg == -1) 431 panic("Boot CPU not found on Open Firmware\n"); 432 433 bsp = -1; 434 tmp_cpuref_cnt = 0; 435 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 436 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 437 if (res > 0 && strcmp(buf, "cpu") == 0) { 438 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); 439 if (res > 0) { 440 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", 441 interrupt_servers, res); 442 443 get_cpu_reg(cpu, ®); 444 if (reg == bsp_reg) 445 bsp = tmp_cpuref_cnt; 446 447 for (a = 0; a < res/sizeof(cell_t); a++) { 448 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a]; 449 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt; 450 tmp_cpuref_cnt++; 451 } 452 } 453 } 454 } 455 456 if (bsp == -1) 457 panic("Boot CPU not found\n"); 458 459 /* Map IDs, so BSP has CPUID 0 regardless of hwref */ 460 for (a = bsp; a < tmp_cpuref_cnt; a++) { 461 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 462 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 463 platform_cpuref_cnt++; 464 } 465 for (a = 0; a < bsp; a++) { 466 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 467 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 468 platform_cpuref_cnt++; 469 } 470 471 platform_cpuref_valid = 1; 472 473 return (0); 474 } 475 476 477 #ifdef SMP 478 static int 479 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) 480 { 481 cell_t start_cpu; 482 int result, err, timeout; 483 484 if (!rtas_exists()) { 485 printf("RTAS uninitialized: unable to start AP %d\n", 486 pc->pc_cpuid); 487 return (ENXIO); 488 } 489 490 start_cpu = rtas_token_lookup("start-cpu"); 491 if (start_cpu == -1) { 492 printf("RTAS unknown method: unable to start AP %d\n", 493 pc->pc_cpuid); 494 return (ENXIO); 495 } 496 497 ap_pcpu = pc; 498 powerpc_sync(); 499 500 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc, 501 &err); 502 if (result < 0 || err != 0) { 503 printf("RTAS error (%d/%d): unable to start AP %d\n", 504 result, err, pc->pc_cpuid); 505 return (ENXIO); 506 } 507 508 timeout = 10000; 509 while (!pc->pc_awake && timeout--) 510 DELAY(100); 511 512 return ((pc->pc_awake) ? 0 : EBUSY); 513 } 514 515 static void 516 chrp_smp_probe_threads(platform_t plat) 517 { 518 struct pcpu *pc, *last_pc; 519 int i, ncores; 520 521 ncores = 0; 522 last_pc = NULL; 523 for (i = 0; i <= mp_maxid; i++) { 524 pc = pcpu_find(i); 525 if (pc == NULL) 526 continue; 527 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) 528 ncores++; 529 last_pc = pc; 530 } 531 532 mp_ncores = ncores; 533 if (mp_ncpus % ncores == 0) 534 smp_threads_per_core = mp_ncpus / ncores; 535 } 536 537 static struct cpu_group * 538 chrp_smp_topo(platform_t plat) 539 { 540 541 if (mp_ncpus % mp_ncores != 0) { 542 printf("WARNING: Irregular SMP topology. Performance may be " 543 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores); 544 return (smp_topo_none()); 545 } 546 547 /* Don't do anything fancier for non-threaded SMP */ 548 if (mp_ncpus == mp_ncores) 549 return (smp_topo_none()); 550 551 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core, 552 CG_FLAG_SMT)); 553 } 554 #endif 555 556 static void 557 chrp_reset(platform_t platform) 558 { 559 OF_reboot(); 560 } 561 562 #ifdef __powerpc64__ 563 static void 564 phyp_cpu_idle(sbintime_t sbt) 565 { 566 register_t msr; 567 568 msr = mfmsr(); 569 570 mtmsr(msr & ~PSL_EE); 571 if (sched_runnable()) { 572 mtmsr(msr); 573 return; 574 } 575 576 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */ 577 mtmsr(msr); 578 } 579 580 static void 581 chrp_smp_ap_init(platform_t platform) 582 { 583 if (!(mfmsr() & PSL_HV)) { 584 /* Register VPA */ 585 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref), 586 splpar_vpa[PCPU_GET(hwref)]); 587 588 /* Set interrupt priority */ 589 phyp_hcall(H_CPPR, 0xff); 590 } 591 } 592 #else 593 static void 594 chrp_smp_ap_init(platform_t platform) 595 { 596 } 597 #endif 598 599