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