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