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