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/rtas.h> 46 #include <machine/smp.h> 47 #include <machine/spr.h> 48 #include <machine/trap.h> 49 50 #include <dev/ofw/openfirm.h> 51 #include <machine/ofw_machdep.h> 52 53 #include "platform_if.h" 54 55 #ifdef SMP 56 extern void *ap_pcpu; 57 #endif 58 59 #ifdef __powerpc64__ 60 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */ 61 #endif 62 63 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; 64 65 static int chrp_probe(platform_t); 66 static int chrp_attach(platform_t); 67 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz, 68 struct mem_region *avail, int *availsz); 69 static vm_offset_t chrp_real_maxaddr(platform_t); 70 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); 71 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); 72 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); 73 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); 74 static void chrp_smp_ap_init(platform_t); 75 #ifdef SMP 76 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); 77 static struct cpu_group *chrp_smp_topo(platform_t plat); 78 #endif 79 static void chrp_reset(platform_t); 80 #ifdef __powerpc64__ 81 #include "phyp-hvcall.h" 82 static void phyp_cpu_idle(sbintime_t sbt); 83 #endif 84 85 static platform_method_t chrp_methods[] = { 86 PLATFORMMETHOD(platform_probe, chrp_probe), 87 PLATFORMMETHOD(platform_attach, chrp_attach), 88 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), 89 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), 90 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), 91 92 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), 93 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), 94 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), 95 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), 96 #ifdef SMP 97 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), 98 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), 99 #endif 100 101 PLATFORMMETHOD(platform_reset, chrp_reset), 102 103 { 0, 0 } 104 }; 105 106 static platform_def_t chrp_platform = { 107 "chrp", 108 chrp_methods, 109 0 110 }; 111 112 PLATFORM_DEF(chrp_platform); 113 114 static int 115 chrp_probe(platform_t plat) 116 { 117 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) 118 return (BUS_PROBE_GENERIC); 119 120 return (ENXIO); 121 } 122 123 static int 124 chrp_attach(platform_t plat) 125 { 126 #ifdef __powerpc64__ 127 int i; 128 129 /* XXX: check for /rtas/ibm,hypertas-functions? */ 130 if (!(mfmsr() & PSL_HV)) { 131 struct mem_region *phys, *avail; 132 int nphys, navail; 133 mem_regions(&phys, &nphys, &avail, &navail); 134 realmaxaddr = phys[0].mr_size; 135 136 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); 137 cpu_idle_hook = phyp_cpu_idle; 138 139 /* Set up important VPA fields */ 140 for (i = 0; i < MAXCPU; i++) { 141 bzero(splpar_vpa[i], sizeof(splpar_vpa)); 142 /* First two: VPA size */ 143 splpar_vpa[i][4] = 144 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff); 145 splpar_vpa[i][5] = 146 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff); 147 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */ 148 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */ 149 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */ 150 splpar_vpa[i][0xfd] = 0xff; 151 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */ 152 } 153 mb(); 154 155 /* Set up hypervisor CPU stuff */ 156 chrp_smp_ap_init(plat); 157 } 158 #endif 159 160 /* Some systems (e.g. QEMU) need Open Firmware to stand down */ 161 ofw_quiesce(); 162 163 return (0); 164 } 165 166 static int 167 parse_drconf_memory(struct mem_region *ofmem, int *msz, 168 struct mem_region *ofavail, int *asz) 169 { 170 phandle_t phandle; 171 vm_offset_t base; 172 int i, idx, len, lasz, lmsz, res; 173 uint32_t flags, lmb_size[2]; 174 uint32_t *dmem; 175 176 lmsz = *msz; 177 lasz = *asz; 178 179 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); 180 if (phandle == -1) 181 /* No drconf node, return. */ 182 return (0); 183 184 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size, 185 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_getencprop(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 = &arr[1]; 220 221 for (i = 0; i < idx; i++) { 222 base = ((uint64_t)dmem[0] << 32) + dmem[1]; 223 dmem += 4; 224 flags = dmem[1]; 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 += 2; 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_getencprop(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_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 331 sizeof(cpuid)); 332 if (res <= 0) 333 res = OF_getencprop(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_getencprop(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_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 375 sizeof(cpuid)); 376 if (res <= 0) 377 res = OF_getencprop(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_getencprop(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_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, 405 sizeof(cpuid)); 406 if (res <= 0) 407 res = OF_getencprop(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