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