xref: /freebsd/sys/powerpc/pseries/platform_chrp.c (revision 488ab515d6cc02f6f743f0badfc8e94eb553cd30)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Marcel Moolenaar
5  * Copyright (c) 2009 Nathan Whitehorn
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
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.
17  *
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.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/pcpu.h>
38 #include <sys/proc.h>
39 #include <sys/sched.h>
40 #include <sys/smp.h>
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43 
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>
52 
53 #include <dev/ofw/openfirm.h>
54 #include <machine/ofw_machdep.h>
55 
56 #include "platform_if.h"
57 
58 #ifdef SMP
59 extern void *ap_pcpu;
60 #endif
61 
62 #ifdef __powerpc64__
63 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
64 #endif
65 
66 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
67 
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);
79 #ifdef SMP
80 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
81 static struct cpu_group *chrp_smp_topo(platform_t plat);
82 #endif
83 static void chrp_reset(platform_t);
84 #ifdef __powerpc64__
85 #include "phyp-hvcall.h"
86 static void phyp_cpu_idle(sbintime_t sbt);
87 #endif
88 
89 static struct cpuref platform_cpuref[MAXCPU];
90 static int platform_cpuref_cnt;
91 static int platform_cpuref_valid;
92 
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),
99 
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),
104 #ifdef SMP
105 	PLATFORMMETHOD(platform_smp_start_cpu,	chrp_smp_start_cpu),
106 	PLATFORMMETHOD(platform_smp_topo,	chrp_smp_topo),
107 #endif
108 
109 	PLATFORMMETHOD(platform_reset,		chrp_reset),
110 
111 	{ 0, 0 }
112 };
113 
114 static platform_def_t chrp_platform = {
115 	"chrp",
116 	chrp_methods,
117 	0
118 };
119 
120 PLATFORM_DEF(chrp_platform);
121 
122 static int
123 chrp_probe(platform_t plat)
124 {
125 	if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
126 		return (BUS_PROBE_GENERIC);
127 
128 	return (ENXIO);
129 }
130 
131 static int
132 chrp_attach(platform_t plat)
133 {
134 #ifdef __powerpc64__
135 	int i;
136 
137 	/* XXX: check for /rtas/ibm,hypertas-functions? */
138 	if (!(mfmsr() & PSL_HV)) {
139 		struct mem_region *phys, *avail;
140 		int nphys, navail;
141 		mem_regions(&phys, &nphys, &avail, &navail);
142 		realmaxaddr = phys[0].mr_size;
143 
144 		pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
145 		cpu_idle_hook = phyp_cpu_idle;
146 
147 		/* Set up important VPA fields */
148 		for (i = 0; i < MAXCPU; i++) {
149 			bzero(splpar_vpa[i], sizeof(splpar_vpa));
150 			/* First two: VPA size */
151 			splpar_vpa[i][4] =
152 			    (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
153 			splpar_vpa[i][5] =
154 			    (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
155 			splpar_vpa[i][0xba] = 1;	/* Maintain FPRs */
156 			splpar_vpa[i][0xbb] = 1;	/* Maintain PMCs */
157 			splpar_vpa[i][0xfc] = 0xff;	/* Maintain full SLB */
158 			splpar_vpa[i][0xfd] = 0xff;
159 			splpar_vpa[i][0xff] = 1;	/* Maintain Altivec */
160 		}
161 		mb();
162 
163 		/* Set up hypervisor CPU stuff */
164 		chrp_smp_ap_init(plat);
165 	}
166 #endif
167 	chrp_cpuref_init();
168 
169 	/* Some systems (e.g. QEMU) need Open Firmware to stand down */
170 	ofw_quiesce();
171 
172 	return (0);
173 }
174 
175 static int
176 parse_drconf_memory(struct mem_region *ofmem, int *msz,
177 		    struct mem_region *ofavail, int *asz)
178 {
179 	phandle_t phandle;
180 	vm_offset_t base;
181 	int i, idx, len, lasz, lmsz, res;
182 	uint32_t flags, lmb_size[2];
183 	uint32_t *dmem;
184 
185 	lmsz = *msz;
186 	lasz = *asz;
187 
188 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
189 	if (phandle == -1)
190 		/* No drconf node, return. */
191 		return (0);
192 
193 	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
194 	    sizeof(lmb_size));
195 	if (res == -1)
196 		return (0);
197 	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
198 
199 	/* Parse the /ibm,dynamic-memory.
200 	   The first position gives the # of entries. The next two words
201  	   reflect the address of the memory block. The next four words are
202 	   the DRC index, reserved, list index and flags.
203 	   (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
204 
205 	    #el  Addr   DRC-idx  res   list-idx  flags
206 	   -------------------------------------------------
207 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
208 	   -------------------------------------------------
209 	*/
210 
211 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
212 	if (len > 0) {
213 
214 		/* We have to use a variable length array on the stack
215 		   since we have very limited stack space.
216 		*/
217 		cell_t arr[len/sizeof(cell_t)];
218 
219 		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
220 		    sizeof(arr));
221 		if (res == -1)
222 			return (0);
223 
224 		/* Number of elements */
225 		idx = arr[0];
226 
227 		/* First address, in arr[1], arr[2]*/
228 		dmem = &arr[1];
229 
230 		for (i = 0; i < idx; i++) {
231 			base = ((uint64_t)dmem[0] << 32) + dmem[1];
232 			dmem += 4;
233 			flags = dmem[1];
234 			/* Use region only if available and not reserved. */
235 			if ((flags & 0x8) && !(flags & 0x80)) {
236 				ofmem[lmsz].mr_start = base;
237 				ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
238 				ofavail[lasz].mr_start = base;
239 				ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
240 				lmsz++;
241 				lasz++;
242 			}
243 			dmem += 2;
244 		}
245 	}
246 
247 	*msz = lmsz;
248 	*asz = lasz;
249 
250 	return (1);
251 }
252 
253 void
254 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
255     struct mem_region *avail, int *availsz)
256 {
257 	vm_offset_t maxphysaddr;
258 	int i;
259 
260 	ofw_mem_regions(phys, physsz, avail, availsz);
261 	parse_drconf_memory(phys, physsz, avail, availsz);
262 
263 	/*
264 	 * On some firmwares (SLOF), some memory may be marked available that
265 	 * doesn't actually exist. This manifests as an extension of the last
266 	 * available segment past the end of physical memory, so truncate that
267 	 * one.
268 	 */
269 	maxphysaddr = 0;
270 	for (i = 0; i < *physsz; i++)
271 		if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
272 			maxphysaddr = phys[i].mr_start + phys[i].mr_size;
273 
274 	for (i = 0; i < *availsz; i++)
275 		if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
276 			avail[i].mr_size = maxphysaddr - avail[i].mr_start;
277 }
278 
279 static vm_offset_t
280 chrp_real_maxaddr(platform_t plat)
281 {
282 	return (realmaxaddr);
283 }
284 
285 static u_long
286 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
287 {
288 	phandle_t cpus, cpunode;
289 	int32_t ticks = -1;
290 	int res;
291 	char buf[8];
292 
293 	cpus = OF_finddevice("/cpus");
294 	if (cpus == -1)
295 		panic("CPU tree not found on Open Firmware\n");
296 
297 	for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
298 		res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
299 		if (res > 0 && strcmp(buf, "cpu") == 0)
300 			break;
301 	}
302 	if (cpunode <= 0)
303 		panic("CPU node not found on Open Firmware\n");
304 
305 	OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
306 
307 	if (ticks <= 0)
308 		panic("Unable to determine timebase frequency!");
309 
310 	return (ticks);
311 }
312 
313 static int
314 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
315 {
316 
317 	if (platform_cpuref_valid == 0)
318 		return (EINVAL);
319 
320 	cpuref->cr_cpuid = 0;
321 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
322 
323 	return (0);
324 }
325 
326 static int
327 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
328 {
329 	int id;
330 
331 	if (platform_cpuref_valid == 0)
332 		return (EINVAL);
333 
334 	id = cpuref->cr_cpuid + 1;
335 	if (id >= platform_cpuref_cnt)
336 		return (ENOENT);
337 
338 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
339 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
340 
341 	return (0);
342 }
343 
344 static int
345 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
346 {
347 
348 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
349 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
350 	return (0);
351 }
352 
353 static void
354 get_cpu_reg(phandle_t cpu, cell_t *reg)
355 {
356 	int res;
357 
358 	res = OF_getproplen(cpu, "reg");
359 	if (res != sizeof(cell_t))
360 		panic("Unexpected length for CPU property reg on Open Firmware\n");
361 	OF_getencprop(cpu, "reg", reg, res);
362 }
363 
364 static int
365 chrp_cpuref_init(void)
366 {
367 	phandle_t cpu, dev, chosen, pbsp;
368 	ihandle_t ibsp;
369 	char buf[32];
370 	int a, bsp, res, res2, tmp_cpuref_cnt;
371 	static struct cpuref tmp_cpuref[MAXCPU];
372 	cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
373 
374 	if (platform_cpuref_valid)
375 		return (0);
376 
377 	dev = OF_peer(0);
378 	dev = OF_child(dev);
379 	while (dev != 0) {
380 		res = OF_getprop(dev, "name", buf, sizeof(buf));
381 		if (res > 0 && strcmp(buf, "cpus") == 0)
382 			break;
383 		dev = OF_peer(dev);
384 	}
385 
386 	/* Make sure that cpus reg property have 1 address cell and 0 size cells */
387 	res = OF_getproplen(dev, "#address-cells");
388 	res2 = OF_getproplen(dev, "#size-cells");
389 	if (res != res2 || res != sizeof(cell_t))
390 		panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
391 	OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
392 	OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
393 	if (addr_cells != 1 || size_cells != 0)
394 		panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
395 
396 	/* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
397 
398 	chosen = OF_finddevice("/chosen");
399 	if (chosen == -1)
400 		panic("Device /chosen not found on Open Firmware\n");
401 
402 	bsp_reg = -1;
403 
404 	/* /chosen/cpu */
405 	if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
406 		OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
407 		pbsp = OF_instance_to_package(ibsp);
408 		if (pbsp != -1)
409 			get_cpu_reg(pbsp, &bsp_reg);
410 	}
411 
412 	/* /chosen/fdtbootcpu */
413 	if (bsp_reg == -1) {
414 		if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
415 			OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
416 	}
417 
418 	if (bsp_reg == -1)
419 		panic("Boot CPU not found on Open Firmware\n");
420 
421 	bsp = -1;
422 	tmp_cpuref_cnt = 0;
423 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
424 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
425 		if (res > 0 && strcmp(buf, "cpu") == 0) {
426 			res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
427 			if (res > 0) {
428 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
429 				    interrupt_servers, res);
430 
431 				get_cpu_reg(cpu, &reg);
432 				if (reg == bsp_reg)
433 					bsp = tmp_cpuref_cnt;
434 
435 				for (a = 0; a < res/sizeof(cell_t); a++) {
436 					tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
437 					tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
438 					tmp_cpuref_cnt++;
439 				}
440 			}
441 		}
442 	}
443 
444 	if (bsp == -1)
445 		panic("Boot CPU not found\n");
446 
447 	/* Map IDs, so BSP has CPUID 0 regardless of hwref */
448 	for (a = bsp; a < tmp_cpuref_cnt; a++) {
449 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
450 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
451 		platform_cpuref_cnt++;
452 	}
453 	for (a = 0; a < bsp; a++) {
454 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
455 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
456 		platform_cpuref_cnt++;
457 	}
458 
459 	platform_cpuref_valid = 1;
460 
461 	return (0);
462 }
463 
464 
465 #ifdef SMP
466 static int
467 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
468 {
469 	cell_t start_cpu;
470 	int result, err, timeout;
471 
472 	if (!rtas_exists()) {
473 		printf("RTAS uninitialized: unable to start AP %d\n",
474 		    pc->pc_cpuid);
475 		return (ENXIO);
476 	}
477 
478 	start_cpu = rtas_token_lookup("start-cpu");
479 	if (start_cpu == -1) {
480 		printf("RTAS unknown method: unable to start AP %d\n",
481 		    pc->pc_cpuid);
482 		return (ENXIO);
483 	}
484 
485 	ap_pcpu = pc;
486 	powerpc_sync();
487 
488 	result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
489 	    &err);
490 	if (result < 0 || err != 0) {
491 		printf("RTAS error (%d/%d): unable to start AP %d\n",
492 		    result, err, pc->pc_cpuid);
493 		return (ENXIO);
494 	}
495 
496 	timeout = 10000;
497 	while (!pc->pc_awake && timeout--)
498 		DELAY(100);
499 
500 	return ((pc->pc_awake) ? 0 : EBUSY);
501 }
502 
503 static struct cpu_group *
504 chrp_smp_topo(platform_t plat)
505 {
506 	struct pcpu *pc, *last_pc;
507 	int i, ncores, ncpus;
508 
509 	ncores = ncpus = 0;
510 	last_pc = NULL;
511 	for (i = 0; i <= mp_maxid; i++) {
512 		pc = pcpu_find(i);
513 		if (pc == NULL)
514 			continue;
515 		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
516 			ncores++;
517 		last_pc = pc;
518 		ncpus++;
519 	}
520 
521 	if (ncpus % ncores != 0) {
522 		printf("WARNING: Irregular SMP topology. Performance may be "
523 		     "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
524 		return (smp_topo_none());
525 	}
526 
527 	/* Don't do anything fancier for non-threaded SMP */
528 	if (ncpus == ncores)
529 		return (smp_topo_none());
530 
531 	return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
532 }
533 #endif
534 
535 static void
536 chrp_reset(platform_t platform)
537 {
538 	OF_reboot();
539 }
540 
541 #ifdef __powerpc64__
542 static void
543 phyp_cpu_idle(sbintime_t sbt)
544 {
545 	register_t msr;
546 
547 	msr = mfmsr();
548 
549 	mtmsr(msr & ~PSL_EE);
550 	if (sched_runnable()) {
551 		mtmsr(msr);
552 		return;
553 	}
554 
555 	phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
556 	mtmsr(msr);
557 }
558 
559 static void
560 chrp_smp_ap_init(platform_t platform)
561 {
562 	if (!(mfmsr() & PSL_HV)) {
563 		/* Register VPA */
564 		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
565 		    splpar_vpa[PCPU_GET(hwref)]);
566 
567 		/* Set interrupt priority */
568 		phyp_hcall(H_CPPR, 0xff);
569 	}
570 }
571 #else
572 static void
573 chrp_smp_ap_init(platform_t platform)
574 {
575 }
576 #endif
577 
578