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/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
39 #include <sys/sched.h>
44 #include <machine/bus.h>
45 #include <machine/cpu.h>
46 #include <machine/hid.h>
47 #include <machine/platformvar.h>
48 #include <machine/rtas.h>
49 #include <machine/smp.h>
50 #include <machine/spr.h>
51 #include <machine/trap.h>
53 #include <dev/ofw/openfirm.h>
54 #include <machine/ofw_machdep.h>
56 #include "platform_if.h"
63 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
66 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
68 static int chrp_probe(platform_t);
69 static int chrp_attach(platform_t);
70 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
71 struct mem_region *avail, int *availsz);
72 static vm_offset_t chrp_real_maxaddr(platform_t);
73 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
74 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
75 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
76 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
77 static void chrp_smp_ap_init(platform_t);
78 static int chrp_cpuref_init(void);
80 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
81 static struct cpu_group *chrp_smp_topo(platform_t plat);
83 static void chrp_reset(platform_t);
85 #include "phyp-hvcall.h"
86 static void phyp_cpu_idle(sbintime_t sbt);
89 static struct cpuref platform_cpuref[MAXCPU];
90 static int platform_cpuref_cnt;
91 static int platform_cpuref_valid;
93 static platform_method_t chrp_methods[] = {
94 PLATFORMMETHOD(platform_probe, chrp_probe),
95 PLATFORMMETHOD(platform_attach, chrp_attach),
96 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
97 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
98 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
100 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
101 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
102 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
103 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
105 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
106 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
109 PLATFORMMETHOD(platform_reset, chrp_reset),
114 static platform_def_t chrp_platform = {
120 PLATFORM_DEF(chrp_platform);
123 chrp_probe(platform_t plat)
125 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
126 return (BUS_PROBE_GENERIC);
132 chrp_attach(platform_t plat)
137 /* XXX: check for /rtas/ibm,hypertas-functions? */
138 if (!(mfmsr() & PSL_HV)) {
139 struct mem_region *phys, *avail;
141 mem_regions(&phys, &nphys, &avail, &navail);
142 realmaxaddr = phys[0].mr_size;
144 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
145 cpu_idle_hook = phyp_cpu_idle;
147 /* Set up important VPA fields */
148 for (i = 0; i < MAXCPU; i++) {
149 /* First two: VPA size */
151 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
153 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
154 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
155 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
156 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
157 splpar_vpa[i][0xfd] = 0xff;
158 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
162 /* Set up hypervisor CPU stuff */
163 chrp_smp_ap_init(plat);
168 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
175 parse_drconf_memory(struct mem_region *ofmem, int *msz,
176 struct mem_region *ofavail, int *asz)
180 int i, idx, len, lasz, lmsz, res;
181 uint32_t flags, lmb_size[2];
187 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
189 /* No drconf node, return. */
192 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
196 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
198 /* Parse the /ibm,dynamic-memory.
199 The first position gives the # of entries. The next two words
200 reflect the address of the memory block. The next four words are
201 the DRC index, reserved, list index and flags.
202 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
204 #el Addr DRC-idx res list-idx flags
205 -------------------------------------------------
206 | 4 | 8 | 4 | 4 | 4 | 4 |....
207 -------------------------------------------------
210 len = OF_getproplen(phandle, "ibm,dynamic-memory");
213 /* We have to use a variable length array on the stack
214 since we have very limited stack space.
216 cell_t arr[len/sizeof(cell_t)];
218 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
223 /* Number of elements */
226 /* First address, in arr[1], arr[2]*/
229 for (i = 0; i < idx; i++) {
230 base = ((uint64_t)dmem[0] << 32) + dmem[1];
233 /* Use region only if available and not reserved. */
234 if ((flags & 0x8) && !(flags & 0x80)) {
235 ofmem[lmsz].mr_start = base;
236 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
237 ofavail[lasz].mr_start = base;
238 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
253 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
254 struct mem_region *avail, int *availsz)
256 vm_offset_t maxphysaddr;
259 ofw_mem_regions(phys, physsz, avail, availsz);
260 parse_drconf_memory(phys, physsz, avail, availsz);
263 * On some firmwares (SLOF), some memory may be marked available that
264 * doesn't actually exist. This manifests as an extension of the last
265 * available segment past the end of physical memory, so truncate that
269 for (i = 0; i < *physsz; i++)
270 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
271 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
273 for (i = 0; i < *availsz; i++)
274 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
275 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
279 chrp_real_maxaddr(platform_t plat)
281 return (realmaxaddr);
285 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
287 phandle_t cpus, cpunode;
292 cpus = OF_finddevice("/cpus");
294 panic("CPU tree not found on Open Firmware\n");
296 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
297 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
298 if (res > 0 && strcmp(buf, "cpu") == 0)
302 panic("CPU node not found on Open Firmware\n");
304 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
307 panic("Unable to determine timebase frequency!");
313 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
316 if (platform_cpuref_valid == 0)
319 cpuref->cr_cpuid = 0;
320 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
326 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
330 if (platform_cpuref_valid == 0)
333 id = cpuref->cr_cpuid + 1;
334 if (id >= platform_cpuref_cnt)
337 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
338 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
344 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
347 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
348 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
353 get_cpu_reg(phandle_t cpu, cell_t *reg)
357 res = OF_getproplen(cpu, "reg");
358 if (res != sizeof(cell_t))
359 panic("Unexpected length for CPU property reg on Open Firmware\n");
360 OF_getencprop(cpu, "reg", reg, res);
364 chrp_cpuref_init(void)
366 phandle_t cpu, dev, chosen, pbsp;
369 int a, bsp, res, res2, tmp_cpuref_cnt;
370 static struct cpuref tmp_cpuref[MAXCPU];
371 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
373 if (platform_cpuref_valid)
379 res = OF_getprop(dev, "name", buf, sizeof(buf));
380 if (res > 0 && strcmp(buf, "cpus") == 0)
385 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
386 res = OF_getproplen(dev, "#address-cells");
387 res2 = OF_getproplen(dev, "#size-cells");
388 if (res != res2 || res != sizeof(cell_t))
389 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
390 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
391 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
392 if (addr_cells != 1 || size_cells != 0)
393 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
395 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
397 chosen = OF_finddevice("/chosen");
399 panic("Device /chosen not found on Open Firmware\n");
404 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
405 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
406 pbsp = OF_instance_to_package(ibsp);
408 get_cpu_reg(pbsp, &bsp_reg);
411 /* /chosen/fdtbootcpu */
413 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
414 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
418 panic("Boot CPU not found on Open Firmware\n");
422 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
423 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
424 if (res > 0 && strcmp(buf, "cpu") == 0) {
425 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
427 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
428 interrupt_servers, res);
430 get_cpu_reg(cpu, ®);
432 bsp = tmp_cpuref_cnt;
434 for (a = 0; a < res/sizeof(cell_t); a++) {
435 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
436 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
444 panic("Boot CPU not found\n");
446 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
447 for (a = bsp; a < tmp_cpuref_cnt; a++) {
448 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
449 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
450 platform_cpuref_cnt++;
452 for (a = 0; a < bsp; a++) {
453 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
454 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
455 platform_cpuref_cnt++;
458 platform_cpuref_valid = 1;
466 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
469 int result, err, timeout;
471 if (!rtas_exists()) {
472 printf("RTAS uninitialized: unable to start AP %d\n",
477 start_cpu = rtas_token_lookup("start-cpu");
478 if (start_cpu == -1) {
479 printf("RTAS unknown method: unable to start AP %d\n",
487 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
489 if (result < 0 || err != 0) {
490 printf("RTAS error (%d/%d): unable to start AP %d\n",
491 result, err, pc->pc_cpuid);
496 while (!pc->pc_awake && timeout--)
499 return ((pc->pc_awake) ? 0 : EBUSY);
502 static struct cpu_group *
503 chrp_smp_topo(platform_t plat)
505 struct pcpu *pc, *last_pc;
506 int i, ncores, ncpus;
510 for (i = 0; i <= mp_maxid; i++) {
514 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
520 if (ncpus % ncores != 0) {
521 printf("WARNING: Irregular SMP topology. Performance may be "
522 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
523 return (smp_topo_none());
526 /* Don't do anything fancier for non-threaded SMP */
528 return (smp_topo_none());
530 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
535 chrp_reset(platform_t platform)
542 phyp_cpu_idle(sbintime_t sbt)
548 mtmsr(msr & ~PSL_EE);
549 if (sched_runnable()) {
554 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
559 chrp_smp_ap_init(platform_t platform)
561 if (!(mfmsr() & PSL_HV)) {
563 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
564 splpar_vpa[PCPU_GET(hwref)]);
566 /* Set interrupt priority */
567 phyp_hcall(H_CPPR, 0xff);
572 chrp_smp_ap_init(platform_t platform)