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