2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2008 Marcel Moolenaar
5 * Copyright (c) 2009 Nathan Whitehorn
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/endian.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
40 #include <sys/sched.h>
45 #include <machine/bus.h>
46 #include <machine/cpu.h>
47 #include <machine/hid.h>
48 #include <machine/platformvar.h>
49 #include <machine/rtas.h>
50 #include <machine/smp.h>
51 #include <machine/spr.h>
52 #include <machine/trap.h>
54 #include <dev/ofw/openfirm.h>
55 #include <machine/ofw_machdep.h>
57 #include "platform_if.h"
64 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
67 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
69 static int chrp_probe(platform_t);
70 static int chrp_attach(platform_t);
71 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
72 struct mem_region *avail, int *availsz);
73 static vm_offset_t chrp_real_maxaddr(platform_t);
74 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
75 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
76 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
77 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
78 static void chrp_smp_ap_init(platform_t);
79 static int chrp_cpuref_init(void);
81 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
82 static void chrp_smp_probe_threads(platform_t plat);
83 static struct cpu_group *chrp_smp_topo(platform_t plat);
85 static void chrp_reset(platform_t);
87 #include "phyp-hvcall.h"
88 static void phyp_cpu_idle(sbintime_t sbt);
91 static struct cpuref platform_cpuref[MAXCPU];
92 static int platform_cpuref_cnt;
93 static int platform_cpuref_valid;
95 static platform_method_t chrp_methods[] = {
96 PLATFORMMETHOD(platform_probe, chrp_probe),
97 PLATFORMMETHOD(platform_attach, chrp_attach),
98 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
99 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
100 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
102 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
103 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
104 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
105 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
107 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
108 PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads),
109 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
112 PLATFORMMETHOD(platform_reset, chrp_reset),
116 static platform_def_t chrp_platform = {
122 PLATFORM_DEF(chrp_platform);
125 chrp_probe(platform_t plat)
127 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
128 return (BUS_PROBE_GENERIC);
134 chrp_attach(platform_t plat)
139 #if BYTE_ORDER == LITTLE_ENDIAN
143 /* XXX: check for /rtas/ibm,hypertas-functions? */
144 if (!(mfmsr() & PSL_HV)) {
145 struct mem_region *phys, *avail;
149 mem_regions(&phys, &nphys, &avail, &navail);
152 for (i = 0; i < nphys; i++) {
153 off = phys[i].mr_start + phys[i].mr_size;
154 realmaxaddr = MAX(off, realmaxaddr);
157 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
158 cpu_idle_hook = phyp_cpu_idle;
160 /* Set up important VPA fields */
161 for (i = 0; i < MAXCPU; i++) {
162 /* First two: VPA size */
164 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
166 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
167 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
168 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
169 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
170 splpar_vpa[i][0xfd] = 0xff;
171 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
175 /* Set up hypervisor CPU stuff */
176 chrp_smp_ap_init(plat);
178 #if BYTE_ORDER == LITTLE_ENDIAN
180 * Ask the hypervisor to update the LPAR ILE bit.
182 * This involves all processors reentering the hypervisor
183 * so the change appears simultaneously in all processors.
184 * This can take a long time.
187 result = phyp_hcall(H_SET_MODE, 1UL,
188 H_SET_MODE_RSRC_ILE, 0, 0);
189 if (result == H_SUCCESS)
199 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
201 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
209 parse_drconf_memory(struct mem_region *ofmem, int *msz,
210 struct mem_region *ofavail, int *asz)
214 int i, idx, len, lasz, lmsz, res;
215 uint32_t flags, lmb_size[2];
221 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
223 /* No drconf node, return. */
226 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
230 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
232 /* Parse the /ibm,dynamic-memory.
233 The first position gives the # of entries. The next two words
234 reflect the address of the memory block. The next four words are
235 the DRC index, reserved, list index and flags.
236 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
238 #el Addr DRC-idx res list-idx flags
239 -------------------------------------------------
240 | 4 | 8 | 4 | 4 | 4 | 4 |....
241 -------------------------------------------------
244 len = OF_getproplen(phandle, "ibm,dynamic-memory");
246 /* We have to use a variable length array on the stack
247 since we have very limited stack space.
249 cell_t arr[len/sizeof(cell_t)];
251 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
256 /* Number of elements */
259 /* First address, in arr[1], arr[2]*/
262 for (i = 0; i < idx; i++) {
263 base = ((uint64_t)dmem[0] << 32) + dmem[1];
266 /* Use region only if available and not reserved. */
267 if ((flags & 0x8) && !(flags & 0x80)) {
268 ofmem[lmsz].mr_start = base;
269 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
270 ofavail[lasz].mr_start = base;
271 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
286 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
287 struct mem_region *avail, int *availsz)
289 vm_offset_t maxphysaddr;
292 ofw_mem_regions(phys, physsz, avail, availsz);
293 parse_drconf_memory(phys, physsz, avail, availsz);
296 * On some firmwares (SLOF), some memory may be marked available that
297 * doesn't actually exist. This manifests as an extension of the last
298 * available segment past the end of physical memory, so truncate that
302 for (i = 0; i < *physsz; i++)
303 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
304 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
306 for (i = 0; i < *availsz; i++)
307 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
308 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
312 chrp_real_maxaddr(platform_t plat)
314 return (realmaxaddr);
318 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
320 phandle_t cpus, cpunode;
325 cpus = OF_finddevice("/cpus");
327 panic("CPU tree not found on Open Firmware\n");
329 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
330 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
331 if (res > 0 && strcmp(buf, "cpu") == 0)
335 panic("CPU node not found on Open Firmware\n");
337 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
340 panic("Unable to determine timebase frequency!");
346 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
349 if (platform_cpuref_valid == 0)
352 cpuref->cr_cpuid = 0;
353 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
359 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
363 if (platform_cpuref_valid == 0)
366 id = cpuref->cr_cpuid + 1;
367 if (id >= platform_cpuref_cnt)
370 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
371 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
377 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
380 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
381 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
386 get_cpu_reg(phandle_t cpu, cell_t *reg)
390 res = OF_getproplen(cpu, "reg");
391 if (res != sizeof(cell_t))
392 panic("Unexpected length for CPU property reg on Open Firmware\n");
393 OF_getencprop(cpu, "reg", reg, res);
397 chrp_cpuref_init(void)
399 phandle_t cpu, dev, chosen, pbsp;
402 int a, bsp, res, res2, tmp_cpuref_cnt;
403 static struct cpuref tmp_cpuref[MAXCPU];
404 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
406 if (platform_cpuref_valid)
412 res = OF_getprop(dev, "name", buf, sizeof(buf));
413 if (res > 0 && strcmp(buf, "cpus") == 0)
418 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
419 res = OF_getproplen(dev, "#address-cells");
420 res2 = OF_getproplen(dev, "#size-cells");
421 if (res != res2 || res != sizeof(cell_t))
422 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
423 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
424 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
425 if (addr_cells != 1 || size_cells != 0)
426 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
428 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
430 chosen = OF_finddevice("/chosen");
432 panic("Device /chosen not found on Open Firmware\n");
437 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
438 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
439 pbsp = OF_instance_to_package(be32toh(ibsp));
441 get_cpu_reg(pbsp, &bsp_reg);
444 /* /chosen/fdtbootcpu */
446 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
447 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
451 panic("Boot CPU not found on Open Firmware\n");
455 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
456 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
457 if (res > 0 && strcmp(buf, "cpu") == 0) {
458 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
460 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
461 interrupt_servers, res);
463 get_cpu_reg(cpu, ®);
465 bsp = tmp_cpuref_cnt;
467 for (a = 0; a < res/sizeof(cell_t); a++) {
468 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
469 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
477 panic("Boot CPU not found\n");
479 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
480 for (a = bsp; a < tmp_cpuref_cnt; a++) {
481 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
482 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
483 platform_cpuref_cnt++;
485 for (a = 0; a < bsp; a++) {
486 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
487 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
488 platform_cpuref_cnt++;
491 platform_cpuref_valid = 1;
498 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
501 int result, err, timeout;
503 if (!rtas_exists()) {
504 printf("RTAS uninitialized: unable to start AP %d\n",
509 start_cpu = rtas_token_lookup("start-cpu");
510 if (start_cpu == -1) {
511 printf("RTAS unknown method: unable to start AP %d\n",
519 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
521 if (result < 0 || err != 0) {
522 printf("RTAS error (%d/%d): unable to start AP %d\n",
523 result, err, pc->pc_cpuid);
528 while (!pc->pc_awake && timeout--)
531 return ((pc->pc_awake) ? 0 : EBUSY);
535 chrp_smp_probe_threads(platform_t plat)
537 struct pcpu *pc, *last_pc;
542 for (i = 0; i <= mp_maxid; i++) {
546 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
552 if (mp_ncpus % ncores == 0)
553 smp_threads_per_core = mp_ncpus / ncores;
556 static struct cpu_group *
557 chrp_smp_topo(platform_t plat)
560 if (mp_ncpus % mp_ncores != 0) {
561 printf("WARNING: Irregular SMP topology. Performance may be "
562 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
563 return (smp_topo_none());
566 /* Don't do anything fancier for non-threaded SMP */
567 if (mp_ncpus == mp_ncores)
568 return (smp_topo_none());
570 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
576 chrp_reset(platform_t platform)
583 phyp_cpu_idle(sbintime_t sbt)
589 mtmsr(msr & ~PSL_EE);
590 if (sched_runnable()) {
595 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
600 chrp_smp_ap_init(platform_t platform)
602 if (!(mfmsr() & PSL_HV)) {
604 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
605 splpar_vpa[PCPU_GET(hwref)]);
607 /* Set interrupt priority */
608 phyp_hcall(H_CPPR, 0xff);
613 chrp_smp_ap_init(platform_t platform)