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)
140 /* XXX: check for /rtas/ibm,hypertas-functions? */
141 if (!(mfmsr() & PSL_HV)) {
142 struct mem_region *phys, *avail;
146 mem_regions(&phys, &nphys, &avail, &navail);
149 for (i = 0; i < nphys; i++) {
150 off = phys[i].mr_start + phys[i].mr_size;
151 realmaxaddr = MAX(off, realmaxaddr);
154 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
155 cpu_idle_hook = phyp_cpu_idle;
157 /* Set up important VPA fields */
158 for (i = 0; i < MAXCPU; i++) {
159 /* First two: VPA size */
161 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
163 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
164 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
165 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
166 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
167 splpar_vpa[i][0xfd] = 0xff;
168 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
172 /* Set up hypervisor CPU stuff */
173 chrp_smp_ap_init(plat);
178 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
180 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
188 parse_drconf_memory(struct mem_region *ofmem, int *msz,
189 struct mem_region *ofavail, int *asz)
193 int i, idx, len, lasz, lmsz, res;
194 uint32_t flags, lmb_size[2];
200 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
202 /* No drconf node, return. */
205 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
209 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
211 /* Parse the /ibm,dynamic-memory.
212 The first position gives the # of entries. The next two words
213 reflect the address of the memory block. The next four words are
214 the DRC index, reserved, list index and flags.
215 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
217 #el Addr DRC-idx res list-idx flags
218 -------------------------------------------------
219 | 4 | 8 | 4 | 4 | 4 | 4 |....
220 -------------------------------------------------
223 len = OF_getproplen(phandle, "ibm,dynamic-memory");
225 /* We have to use a variable length array on the stack
226 since we have very limited stack space.
228 cell_t arr[len/sizeof(cell_t)];
230 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
235 /* Number of elements */
238 /* First address, in arr[1], arr[2]*/
241 for (i = 0; i < idx; i++) {
242 base = ((uint64_t)dmem[0] << 32) + dmem[1];
245 /* Use region only if available and not reserved. */
246 if ((flags & 0x8) && !(flags & 0x80)) {
247 ofmem[lmsz].mr_start = base;
248 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
249 ofavail[lasz].mr_start = base;
250 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
265 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
266 struct mem_region *avail, int *availsz)
268 vm_offset_t maxphysaddr;
271 ofw_mem_regions(phys, physsz, avail, availsz);
272 parse_drconf_memory(phys, physsz, avail, availsz);
275 * On some firmwares (SLOF), some memory may be marked available that
276 * doesn't actually exist. This manifests as an extension of the last
277 * available segment past the end of physical memory, so truncate that
281 for (i = 0; i < *physsz; i++)
282 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
283 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
285 for (i = 0; i < *availsz; i++)
286 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
287 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
291 chrp_real_maxaddr(platform_t plat)
293 return (realmaxaddr);
297 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
299 phandle_t cpus, cpunode;
304 cpus = OF_finddevice("/cpus");
306 panic("CPU tree not found on Open Firmware\n");
308 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
309 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
310 if (res > 0 && strcmp(buf, "cpu") == 0)
314 panic("CPU node not found on Open Firmware\n");
316 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
319 panic("Unable to determine timebase frequency!");
325 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
328 if (platform_cpuref_valid == 0)
331 cpuref->cr_cpuid = 0;
332 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
338 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
342 if (platform_cpuref_valid == 0)
345 id = cpuref->cr_cpuid + 1;
346 if (id >= platform_cpuref_cnt)
349 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
350 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
356 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
359 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
360 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
365 get_cpu_reg(phandle_t cpu, cell_t *reg)
369 res = OF_getproplen(cpu, "reg");
370 if (res != sizeof(cell_t))
371 panic("Unexpected length for CPU property reg on Open Firmware\n");
372 OF_getencprop(cpu, "reg", reg, res);
376 chrp_cpuref_init(void)
378 phandle_t cpu, dev, chosen, pbsp;
381 int a, bsp, res, res2, tmp_cpuref_cnt;
382 static struct cpuref tmp_cpuref[MAXCPU];
383 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
385 if (platform_cpuref_valid)
391 res = OF_getprop(dev, "name", buf, sizeof(buf));
392 if (res > 0 && strcmp(buf, "cpus") == 0)
397 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
398 res = OF_getproplen(dev, "#address-cells");
399 res2 = OF_getproplen(dev, "#size-cells");
400 if (res != res2 || res != sizeof(cell_t))
401 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
402 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
403 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
404 if (addr_cells != 1 || size_cells != 0)
405 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
407 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
409 chosen = OF_finddevice("/chosen");
411 panic("Device /chosen not found on Open Firmware\n");
416 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
417 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
418 pbsp = OF_instance_to_package(be32toh(ibsp));
420 get_cpu_reg(pbsp, &bsp_reg);
423 /* /chosen/fdtbootcpu */
425 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
426 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
430 panic("Boot CPU not found on Open Firmware\n");
434 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
435 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
436 if (res > 0 && strcmp(buf, "cpu") == 0) {
437 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
439 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
440 interrupt_servers, res);
442 get_cpu_reg(cpu, ®);
444 bsp = tmp_cpuref_cnt;
446 for (a = 0; a < res/sizeof(cell_t); a++) {
447 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
448 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
456 panic("Boot CPU not found\n");
458 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
459 for (a = bsp; a < tmp_cpuref_cnt; a++) {
460 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
461 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
462 platform_cpuref_cnt++;
464 for (a = 0; a < bsp; a++) {
465 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
466 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
467 platform_cpuref_cnt++;
470 platform_cpuref_valid = 1;
477 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
480 int result, err, timeout;
482 if (!rtas_exists()) {
483 printf("RTAS uninitialized: unable to start AP %d\n",
488 start_cpu = rtas_token_lookup("start-cpu");
489 if (start_cpu == -1) {
490 printf("RTAS unknown method: unable to start AP %d\n",
498 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
500 if (result < 0 || err != 0) {
501 printf("RTAS error (%d/%d): unable to start AP %d\n",
502 result, err, pc->pc_cpuid);
507 while (!pc->pc_awake && timeout--)
510 return ((pc->pc_awake) ? 0 : EBUSY);
514 chrp_smp_probe_threads(platform_t plat)
516 struct pcpu *pc, *last_pc;
521 for (i = 0; i <= mp_maxid; i++) {
525 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
531 if (mp_ncpus % ncores == 0)
532 smp_threads_per_core = mp_ncpus / ncores;
535 static struct cpu_group *
536 chrp_smp_topo(platform_t plat)
539 if (mp_ncpus % mp_ncores != 0) {
540 printf("WARNING: Irregular SMP topology. Performance may be "
541 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
542 return (smp_topo_none());
545 /* Don't do anything fancier for non-threaded SMP */
546 if (mp_ncpus == mp_ncores)
547 return (smp_topo_none());
549 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
555 chrp_reset(platform_t platform)
562 phyp_cpu_idle(sbintime_t sbt)
568 mtmsr(msr & ~PSL_EE);
569 if (sched_runnable()) {
574 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
579 chrp_smp_ap_init(platform_t platform)
581 if (!(mfmsr() & PSL_HV)) {
583 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
584 splpar_vpa[PCPU_GET(hwref)]);
586 /* Set interrupt priority */
587 phyp_hcall(H_CPPR, 0xff);
592 chrp_smp_ap_init(platform_t platform)