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 void chrp_smp_probe_threads(platform_t plat);
82 static struct cpu_group *chrp_smp_topo(platform_t plat);
84 static void chrp_reset(platform_t);
86 #include "phyp-hvcall.h"
87 static void phyp_cpu_idle(sbintime_t sbt);
90 static struct cpuref platform_cpuref[MAXCPU];
91 static int platform_cpuref_cnt;
92 static int platform_cpuref_valid;
94 static platform_method_t chrp_methods[] = {
95 PLATFORMMETHOD(platform_probe, chrp_probe),
96 PLATFORMMETHOD(platform_attach, chrp_attach),
97 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
98 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
99 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
101 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
102 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
103 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
104 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
106 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
107 PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads),
108 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
111 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 /* XXX: check for /rtas/ibm,hypertas-functions? */
140 if (!(mfmsr() & PSL_HV)) {
141 struct mem_region *phys, *avail;
143 mem_regions(&phys, &nphys, &avail, &navail);
144 realmaxaddr = phys[0].mr_size;
146 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
147 cpu_idle_hook = phyp_cpu_idle;
149 /* Set up important VPA fields */
150 for (i = 0; i < MAXCPU; i++) {
151 /* First two: VPA size */
153 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
155 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
156 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
157 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
158 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
159 splpar_vpa[i][0xfd] = 0xff;
160 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
164 /* Set up hypervisor CPU stuff */
165 chrp_smp_ap_init(plat);
170 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
177 parse_drconf_memory(struct mem_region *ofmem, int *msz,
178 struct mem_region *ofavail, int *asz)
182 int i, idx, len, lasz, lmsz, res;
183 uint32_t flags, lmb_size[2];
189 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
191 /* No drconf node, return. */
194 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
198 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
200 /* Parse the /ibm,dynamic-memory.
201 The first position gives the # of entries. The next two words
202 reflect the address of the memory block. The next four words are
203 the DRC index, reserved, list index and flags.
204 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
206 #el Addr DRC-idx res list-idx flags
207 -------------------------------------------------
208 | 4 | 8 | 4 | 4 | 4 | 4 |....
209 -------------------------------------------------
212 len = OF_getproplen(phandle, "ibm,dynamic-memory");
215 /* We have to use a variable length array on the stack
216 since we have very limited stack space.
218 cell_t arr[len/sizeof(cell_t)];
220 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
225 /* Number of elements */
228 /* First address, in arr[1], arr[2]*/
231 for (i = 0; i < idx; i++) {
232 base = ((uint64_t)dmem[0] << 32) + dmem[1];
235 /* Use region only if available and not reserved. */
236 if ((flags & 0x8) && !(flags & 0x80)) {
237 ofmem[lmsz].mr_start = base;
238 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
239 ofavail[lasz].mr_start = base;
240 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
255 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
256 struct mem_region *avail, int *availsz)
258 vm_offset_t maxphysaddr;
261 ofw_mem_regions(phys, physsz, avail, availsz);
262 parse_drconf_memory(phys, physsz, avail, availsz);
265 * On some firmwares (SLOF), some memory may be marked available that
266 * doesn't actually exist. This manifests as an extension of the last
267 * available segment past the end of physical memory, so truncate that
271 for (i = 0; i < *physsz; i++)
272 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
273 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
275 for (i = 0; i < *availsz; i++)
276 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
277 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
281 chrp_real_maxaddr(platform_t plat)
283 return (realmaxaddr);
287 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
289 phandle_t cpus, cpunode;
294 cpus = OF_finddevice("/cpus");
296 panic("CPU tree not found on Open Firmware\n");
298 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
299 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
300 if (res > 0 && strcmp(buf, "cpu") == 0)
304 panic("CPU node not found on Open Firmware\n");
306 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
309 panic("Unable to determine timebase frequency!");
315 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
318 if (platform_cpuref_valid == 0)
321 cpuref->cr_cpuid = 0;
322 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
328 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
332 if (platform_cpuref_valid == 0)
335 id = cpuref->cr_cpuid + 1;
336 if (id >= platform_cpuref_cnt)
339 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
340 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
346 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
349 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
350 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
355 get_cpu_reg(phandle_t cpu, cell_t *reg)
359 res = OF_getproplen(cpu, "reg");
360 if (res != sizeof(cell_t))
361 panic("Unexpected length for CPU property reg on Open Firmware\n");
362 OF_getencprop(cpu, "reg", reg, res);
366 chrp_cpuref_init(void)
368 phandle_t cpu, dev, chosen, pbsp;
371 int a, bsp, res, res2, tmp_cpuref_cnt;
372 static struct cpuref tmp_cpuref[MAXCPU];
373 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
375 if (platform_cpuref_valid)
381 res = OF_getprop(dev, "name", buf, sizeof(buf));
382 if (res > 0 && strcmp(buf, "cpus") == 0)
387 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
388 res = OF_getproplen(dev, "#address-cells");
389 res2 = OF_getproplen(dev, "#size-cells");
390 if (res != res2 || res != sizeof(cell_t))
391 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
392 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
393 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
394 if (addr_cells != 1 || size_cells != 0)
395 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
397 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
399 chosen = OF_finddevice("/chosen");
401 panic("Device /chosen not found on Open Firmware\n");
406 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
407 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
408 pbsp = OF_instance_to_package(ibsp);
410 get_cpu_reg(pbsp, &bsp_reg);
413 /* /chosen/fdtbootcpu */
415 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
416 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
420 panic("Boot CPU not found on Open Firmware\n");
424 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
425 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
426 if (res > 0 && strcmp(buf, "cpu") == 0) {
427 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
429 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
430 interrupt_servers, res);
432 get_cpu_reg(cpu, ®);
434 bsp = tmp_cpuref_cnt;
436 for (a = 0; a < res/sizeof(cell_t); a++) {
437 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
438 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
446 panic("Boot CPU not found\n");
448 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
449 for (a = bsp; a < tmp_cpuref_cnt; a++) {
450 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
451 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
452 platform_cpuref_cnt++;
454 for (a = 0; a < bsp; a++) {
455 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
456 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
457 platform_cpuref_cnt++;
460 platform_cpuref_valid = 1;
468 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
471 int result, err, timeout;
473 if (!rtas_exists()) {
474 printf("RTAS uninitialized: unable to start AP %d\n",
479 start_cpu = rtas_token_lookup("start-cpu");
480 if (start_cpu == -1) {
481 printf("RTAS unknown method: unable to start AP %d\n",
489 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
491 if (result < 0 || err != 0) {
492 printf("RTAS error (%d/%d): unable to start AP %d\n",
493 result, err, pc->pc_cpuid);
498 while (!pc->pc_awake && timeout--)
501 return ((pc->pc_awake) ? 0 : EBUSY);
505 chrp_smp_probe_threads(platform_t plat)
507 struct pcpu *pc, *last_pc;
512 for (i = 0; i <= mp_maxid; i++) {
516 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
522 if (mp_ncpus % ncores == 0)
523 smp_threads_per_core = mp_ncpus / ncores;
526 static struct cpu_group *
527 chrp_smp_topo(platform_t plat)
530 if (mp_ncpus % mp_ncores != 0) {
531 printf("WARNING: Irregular SMP topology. Performance may be "
532 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
533 return (smp_topo_none());
536 /* Don't do anything fancier for non-threaded SMP */
537 if (mp_ncpus == mp_ncores)
538 return (smp_topo_none());
540 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
546 chrp_reset(platform_t platform)
553 phyp_cpu_idle(sbintime_t sbt)
559 mtmsr(msr & ~PSL_EE);
560 if (sched_runnable()) {
565 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
570 chrp_smp_ap_init(platform_t platform)
572 if (!(mfmsr() & PSL_HV)) {
574 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
575 splpar_vpa[PCPU_GET(hwref)]);
577 /* Set interrupt priority */
578 phyp_hcall(H_CPPR, 0xff);
583 chrp_smp_ap_init(platform_t platform)