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