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);
158 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
159 cpu_idle_hook = phyp_cpu_idle;
161 /* Set up important VPA fields */
162 for (i = 0; i < MAXCPU; i++) {
163 /* First two: VPA size */
165 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
167 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
168 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
169 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
170 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
171 splpar_vpa[i][0xfd] = 0xff;
172 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
176 /* Set up hypervisor CPU stuff */
177 chrp_smp_ap_init(plat);
179 #if BYTE_ORDER == LITTLE_ENDIAN
181 * Ask the hypervisor to update the LPAR ILE bit.
183 * This involves all processors reentering the hypervisor
184 * so the change appears simultaneously in all processors.
185 * This can take a long time.
188 result = phyp_hcall(H_SET_MODE, 1UL,
189 H_SET_MODE_RSRC_ILE, 0, 0);
190 if (result == H_SUCCESS)
200 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
202 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
210 parse_drconf_memory(struct mem_region *ofmem, int *msz,
211 struct mem_region *ofavail, int *asz)
215 int i, idx, len, lasz, lmsz, res;
216 uint32_t flags, lmb_size[2];
222 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
224 /* No drconf node, return. */
227 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
231 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
233 /* Parse the /ibm,dynamic-memory.
234 The first position gives the # of entries. The next two words
235 reflect the address of the memory block. The next four words are
236 the DRC index, reserved, list index and flags.
237 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
239 #el Addr DRC-idx res list-idx flags
240 -------------------------------------------------
241 | 4 | 8 | 4 | 4 | 4 | 4 |....
242 -------------------------------------------------
245 len = OF_getproplen(phandle, "ibm,dynamic-memory");
247 /* We have to use a variable length array on the stack
248 since we have very limited stack space.
250 cell_t arr[len/sizeof(cell_t)];
252 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
257 /* Number of elements */
260 /* First address, in arr[1], arr[2]*/
263 for (i = 0; i < idx; i++) {
264 base = ((uint64_t)dmem[0] << 32) + dmem[1];
267 /* Use region only if available and not reserved. */
268 if ((flags & 0x8) && !(flags & 0x80)) {
269 ofmem[lmsz].mr_start = base;
270 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
271 ofavail[lasz].mr_start = base;
272 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
287 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
288 struct mem_region *avail, int *availsz)
290 vm_offset_t maxphysaddr;
293 ofw_mem_regions(phys, physsz, avail, availsz);
294 parse_drconf_memory(phys, physsz, avail, availsz);
297 * On some firmwares (SLOF), some memory may be marked available that
298 * doesn't actually exist. This manifests as an extension of the last
299 * available segment past the end of physical memory, so truncate that
303 for (i = 0; i < *physsz; i++)
304 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
305 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
307 for (i = 0; i < *availsz; i++)
308 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
309 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
313 chrp_real_maxaddr(platform_t plat)
315 return (realmaxaddr);
319 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
321 phandle_t cpus, cpunode;
326 cpus = OF_finddevice("/cpus");
328 panic("CPU tree not found on Open Firmware\n");
330 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
331 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
332 if (res > 0 && strcmp(buf, "cpu") == 0)
336 panic("CPU node not found on Open Firmware\n");
338 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
341 panic("Unable to determine timebase frequency!");
347 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
350 if (platform_cpuref_valid == 0)
353 cpuref->cr_cpuid = 0;
354 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
360 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
364 if (platform_cpuref_valid == 0)
367 id = cpuref->cr_cpuid + 1;
368 if (id >= platform_cpuref_cnt)
371 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
372 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
378 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
381 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
382 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
387 get_cpu_reg(phandle_t cpu, cell_t *reg)
391 res = OF_getproplen(cpu, "reg");
392 if (res != sizeof(cell_t))
393 panic("Unexpected length for CPU property reg on Open Firmware\n");
394 OF_getencprop(cpu, "reg", reg, res);
398 chrp_cpuref_init(void)
400 phandle_t cpu, dev, chosen, pbsp;
403 int a, bsp, res, res2, tmp_cpuref_cnt;
404 static struct cpuref tmp_cpuref[MAXCPU];
405 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
407 if (platform_cpuref_valid)
413 res = OF_getprop(dev, "name", buf, sizeof(buf));
414 if (res > 0 && strcmp(buf, "cpus") == 0)
419 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
420 res = OF_getproplen(dev, "#address-cells");
421 res2 = OF_getproplen(dev, "#size-cells");
422 if (res != res2 || res != sizeof(cell_t))
423 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
424 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
425 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
426 if (addr_cells != 1 || size_cells != 0)
427 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
429 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
431 chosen = OF_finddevice("/chosen");
433 panic("Device /chosen not found on Open Firmware\n");
438 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
439 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
440 pbsp = OF_instance_to_package(be32toh(ibsp));
442 get_cpu_reg(pbsp, &bsp_reg);
445 /* /chosen/fdtbootcpu */
447 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
448 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
452 panic("Boot CPU not found on Open Firmware\n");
456 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
457 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
458 if (res > 0 && strcmp(buf, "cpu") == 0) {
459 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
461 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
462 interrupt_servers, res);
464 get_cpu_reg(cpu, ®);
466 bsp = tmp_cpuref_cnt;
468 for (a = 0; a < res/sizeof(cell_t); a++) {
469 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
470 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
478 panic("Boot CPU not found\n");
480 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
481 for (a = bsp; a < tmp_cpuref_cnt; a++) {
482 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
483 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
484 platform_cpuref_cnt++;
486 for (a = 0; a < bsp; a++) {
487 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
488 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
489 platform_cpuref_cnt++;
492 platform_cpuref_valid = 1;
499 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
502 int result, err, timeout;
504 if (!rtas_exists()) {
505 printf("RTAS uninitialized: unable to start AP %d\n",
510 start_cpu = rtas_token_lookup("start-cpu");
511 if (start_cpu == -1) {
512 printf("RTAS unknown method: unable to start AP %d\n",
520 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
522 if (result < 0 || err != 0) {
523 printf("RTAS error (%d/%d): unable to start AP %d\n",
524 result, err, pc->pc_cpuid);
529 while (!pc->pc_awake && timeout--)
532 return ((pc->pc_awake) ? 0 : EBUSY);
536 chrp_smp_probe_threads(platform_t plat)
538 struct pcpu *pc, *last_pc;
543 for (i = 0; i <= mp_maxid; i++) {
547 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
553 if (mp_ncpus % ncores == 0)
554 smp_threads_per_core = mp_ncpus / ncores;
557 static struct cpu_group *
558 chrp_smp_topo(platform_t plat)
561 if (mp_ncpus % mp_ncores != 0) {
562 printf("WARNING: Irregular SMP topology. Performance may be "
563 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
564 return (smp_topo_none());
567 /* Don't do anything fancier for non-threaded SMP */
568 if (mp_ncpus == mp_ncores)
569 return (smp_topo_none());
571 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
577 chrp_reset(platform_t platform)
584 phyp_cpu_idle(sbintime_t sbt)
590 mtmsr(msr & ~PSL_EE);
591 if (sched_runnable()) {
596 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
601 chrp_smp_ap_init(platform_t platform)
603 if (!(mfmsr() & PSL_HV)) {
605 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
606 splpar_vpa[PCPU_GET(hwref)]);
608 /* Set interrupt priority */
609 phyp_hcall(H_CPPR, 0xff);
614 chrp_smp_ap_init(platform_t platform)