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