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