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;
145 mem_regions(&phys, &nphys, &avail, &navail);
148 for (i = 0; i < nphys; i++) {
149 off = phys[i].mr_start + phys[i].mr_size;
150 realmaxaddr = MAX(off, realmaxaddr);
153 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
154 cpu_idle_hook = phyp_cpu_idle;
156 /* Set up important VPA fields */
157 for (i = 0; i < MAXCPU; i++) {
158 /* First two: VPA size */
160 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
162 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
163 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
164 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
165 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
166 splpar_vpa[i][0xfd] = 0xff;
167 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
171 /* Set up hypervisor CPU stuff */
172 chrp_smp_ap_init(plat);
177 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
184 parse_drconf_memory(struct mem_region *ofmem, int *msz,
185 struct mem_region *ofavail, int *asz)
189 int i, idx, len, lasz, lmsz, res;
190 uint32_t flags, lmb_size[2];
196 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
198 /* No drconf node, return. */
201 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
205 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
207 /* Parse the /ibm,dynamic-memory.
208 The first position gives the # of entries. The next two words
209 reflect the address of the memory block. The next four words are
210 the DRC index, reserved, list index and flags.
211 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
213 #el Addr DRC-idx res list-idx flags
214 -------------------------------------------------
215 | 4 | 8 | 4 | 4 | 4 | 4 |....
216 -------------------------------------------------
219 len = OF_getproplen(phandle, "ibm,dynamic-memory");
222 /* We have to use a variable length array on the stack
223 since we have very limited stack space.
225 cell_t arr[len/sizeof(cell_t)];
227 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
232 /* Number of elements */
235 /* First address, in arr[1], arr[2]*/
238 for (i = 0; i < idx; i++) {
239 base = ((uint64_t)dmem[0] << 32) + dmem[1];
242 /* Use region only if available and not reserved. */
243 if ((flags & 0x8) && !(flags & 0x80)) {
244 ofmem[lmsz].mr_start = base;
245 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
246 ofavail[lasz].mr_start = base;
247 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
262 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
263 struct mem_region *avail, int *availsz)
265 vm_offset_t maxphysaddr;
268 ofw_mem_regions(phys, physsz, avail, availsz);
269 parse_drconf_memory(phys, physsz, avail, availsz);
272 * On some firmwares (SLOF), some memory may be marked available that
273 * doesn't actually exist. This manifests as an extension of the last
274 * available segment past the end of physical memory, so truncate that
278 for (i = 0; i < *physsz; i++)
279 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
280 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
282 for (i = 0; i < *availsz; i++)
283 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
284 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
288 chrp_real_maxaddr(platform_t plat)
290 return (realmaxaddr);
294 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
296 phandle_t cpus, cpunode;
301 cpus = OF_finddevice("/cpus");
303 panic("CPU tree not found on Open Firmware\n");
305 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
306 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
307 if (res > 0 && strcmp(buf, "cpu") == 0)
311 panic("CPU node not found on Open Firmware\n");
313 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
316 panic("Unable to determine timebase frequency!");
322 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
325 if (platform_cpuref_valid == 0)
328 cpuref->cr_cpuid = 0;
329 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
335 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
339 if (platform_cpuref_valid == 0)
342 id = cpuref->cr_cpuid + 1;
343 if (id >= platform_cpuref_cnt)
346 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
347 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
353 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
356 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
357 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
362 get_cpu_reg(phandle_t cpu, cell_t *reg)
366 res = OF_getproplen(cpu, "reg");
367 if (res != sizeof(cell_t))
368 panic("Unexpected length for CPU property reg on Open Firmware\n");
369 OF_getencprop(cpu, "reg", reg, res);
373 chrp_cpuref_init(void)
375 phandle_t cpu, dev, chosen, pbsp;
378 int a, bsp, res, res2, tmp_cpuref_cnt;
379 static struct cpuref tmp_cpuref[MAXCPU];
380 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
382 if (platform_cpuref_valid)
388 res = OF_getprop(dev, "name", buf, sizeof(buf));
389 if (res > 0 && strcmp(buf, "cpus") == 0)
394 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
395 res = OF_getproplen(dev, "#address-cells");
396 res2 = OF_getproplen(dev, "#size-cells");
397 if (res != res2 || res != sizeof(cell_t))
398 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
399 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
400 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
401 if (addr_cells != 1 || size_cells != 0)
402 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
404 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
406 chosen = OF_finddevice("/chosen");
408 panic("Device /chosen not found on Open Firmware\n");
413 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
414 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
415 pbsp = OF_instance_to_package(ibsp);
417 get_cpu_reg(pbsp, &bsp_reg);
420 /* /chosen/fdtbootcpu */
422 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
423 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
427 panic("Boot CPU not found on Open Firmware\n");
431 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
432 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
433 if (res > 0 && strcmp(buf, "cpu") == 0) {
434 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
436 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
437 interrupt_servers, res);
439 get_cpu_reg(cpu, ®);
441 bsp = tmp_cpuref_cnt;
443 for (a = 0; a < res/sizeof(cell_t); a++) {
444 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
445 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
453 panic("Boot CPU not found\n");
455 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
456 for (a = bsp; a < tmp_cpuref_cnt; a++) {
457 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
458 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
459 platform_cpuref_cnt++;
461 for (a = 0; a < bsp; a++) {
462 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
463 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
464 platform_cpuref_cnt++;
467 platform_cpuref_valid = 1;
475 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
478 int result, err, timeout;
480 if (!rtas_exists()) {
481 printf("RTAS uninitialized: unable to start AP %d\n",
486 start_cpu = rtas_token_lookup("start-cpu");
487 if (start_cpu == -1) {
488 printf("RTAS unknown method: unable to start AP %d\n",
496 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
498 if (result < 0 || err != 0) {
499 printf("RTAS error (%d/%d): unable to start AP %d\n",
500 result, err, pc->pc_cpuid);
505 while (!pc->pc_awake && timeout--)
508 return ((pc->pc_awake) ? 0 : EBUSY);
512 chrp_smp_probe_threads(platform_t plat)
514 struct pcpu *pc, *last_pc;
519 for (i = 0; i <= mp_maxid; i++) {
523 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
529 if (mp_ncpus % ncores == 0)
530 smp_threads_per_core = mp_ncpus / ncores;
533 static struct cpu_group *
534 chrp_smp_topo(platform_t plat)
537 if (mp_ncpus % mp_ncores != 0) {
538 printf("WARNING: Irregular SMP topology. Performance may be "
539 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
540 return (smp_topo_none());
543 /* Don't do anything fancier for non-threaded SMP */
544 if (mp_ncpus == mp_ncores)
545 return (smp_topo_none());
547 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
553 chrp_reset(platform_t platform)
560 phyp_cpu_idle(sbintime_t sbt)
566 mtmsr(msr & ~PSL_EE);
567 if (sched_runnable()) {
572 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
577 chrp_smp_ap_init(platform_t platform)
579 if (!(mfmsr() & PSL_HV)) {
581 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
582 splpar_vpa[PCPU_GET(hwref)]);
584 /* Set interrupt priority */
585 phyp_hcall(H_CPPR, 0xff);
590 chrp_smp_ap_init(platform_t platform)