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