xref: /freebsd/sys/powerpc/pseries/platform_chrp.c (revision 52c2bb75163559a6e2866ad374a7de67a4ea1273)
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 void chrp_smp_probe_threads(platform_t plat);
82 static struct cpu_group *chrp_smp_topo(platform_t plat);
83 #endif
84 static void chrp_reset(platform_t);
85 #ifdef __powerpc64__
86 #include "phyp-hvcall.h"
87 static void phyp_cpu_idle(sbintime_t sbt);
88 #endif
89 
90 static struct cpuref platform_cpuref[MAXCPU];
91 static int platform_cpuref_cnt;
92 static int platform_cpuref_valid;
93 
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),
100 
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),
105 #ifdef SMP
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),
109 #endif
110 
111 	PLATFORMMETHOD(platform_reset,		chrp_reset),
112 
113 	{ 0, 0 }
114 };
115 
116 static platform_def_t chrp_platform = {
117 	"chrp",
118 	chrp_methods,
119 	0
120 };
121 
122 PLATFORM_DEF(chrp_platform);
123 
124 static int
125 chrp_probe(platform_t plat)
126 {
127 	if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
128 		return (BUS_PROBE_GENERIC);
129 
130 	return (ENXIO);
131 }
132 
133 static int
134 chrp_attach(platform_t plat)
135 {
136 #ifdef __powerpc64__
137 	int i;
138 
139 	/* XXX: check for /rtas/ibm,hypertas-functions? */
140 	if (!(mfmsr() & PSL_HV)) {
141 		struct mem_region *phys, *avail;
142 		int nphys, navail;
143 		mem_regions(&phys, &nphys, &avail, &navail);
144 		realmaxaddr = phys[0].mr_size;
145 
146 		pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
147 		cpu_idle_hook = phyp_cpu_idle;
148 
149 		/* Set up important VPA fields */
150 		for (i = 0; i < MAXCPU; i++) {
151 			/* First two: VPA size */
152 			splpar_vpa[i][4] =
153 			    (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
154 			splpar_vpa[i][5] =
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 */
161 		}
162 		mb();
163 
164 		/* Set up hypervisor CPU stuff */
165 		chrp_smp_ap_init(plat);
166 	}
167 #endif
168 	chrp_cpuref_init();
169 
170 	/* Some systems (e.g. QEMU) need Open Firmware to stand down */
171 	ofw_quiesce();
172 
173 	return (0);
174 }
175 
176 static int
177 parse_drconf_memory(struct mem_region *ofmem, int *msz,
178 		    struct mem_region *ofavail, int *asz)
179 {
180 	phandle_t phandle;
181 	vm_offset_t base;
182 	int i, idx, len, lasz, lmsz, res;
183 	uint32_t flags, lmb_size[2];
184 	uint32_t *dmem;
185 
186 	lmsz = *msz;
187 	lasz = *asz;
188 
189 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
190 	if (phandle == -1)
191 		/* No drconf node, return. */
192 		return (0);
193 
194 	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
195 	    sizeof(lmb_size));
196 	if (res == -1)
197 		return (0);
198 	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
199 
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)
205 
206 	    #el  Addr   DRC-idx  res   list-idx  flags
207 	   -------------------------------------------------
208 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
209 	   -------------------------------------------------
210 	*/
211 
212 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
213 	if (len > 0) {
214 
215 		/* We have to use a variable length array on the stack
216 		   since we have very limited stack space.
217 		*/
218 		cell_t arr[len/sizeof(cell_t)];
219 
220 		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
221 		    sizeof(arr));
222 		if (res == -1)
223 			return (0);
224 
225 		/* Number of elements */
226 		idx = arr[0];
227 
228 		/* First address, in arr[1], arr[2]*/
229 		dmem = &arr[1];
230 
231 		for (i = 0; i < idx; i++) {
232 			base = ((uint64_t)dmem[0] << 32) + dmem[1];
233 			dmem += 4;
234 			flags = 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];
241 				lmsz++;
242 				lasz++;
243 			}
244 			dmem += 2;
245 		}
246 	}
247 
248 	*msz = lmsz;
249 	*asz = lasz;
250 
251 	return (1);
252 }
253 
254 void
255 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
256     struct mem_region *avail, int *availsz)
257 {
258 	vm_offset_t maxphysaddr;
259 	int i;
260 
261 	ofw_mem_regions(phys, physsz, avail, availsz);
262 	parse_drconf_memory(phys, physsz, avail, availsz);
263 
264 	/*
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
268 	 * one.
269 	 */
270 	maxphysaddr = 0;
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;
274 
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;
278 }
279 
280 static vm_offset_t
281 chrp_real_maxaddr(platform_t plat)
282 {
283 	return (realmaxaddr);
284 }
285 
286 static u_long
287 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
288 {
289 	phandle_t cpus, cpunode;
290 	int32_t ticks = -1;
291 	int res;
292 	char buf[8];
293 
294 	cpus = OF_finddevice("/cpus");
295 	if (cpus == -1)
296 		panic("CPU tree not found on Open Firmware\n");
297 
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)
301 			break;
302 	}
303 	if (cpunode <= 0)
304 		panic("CPU node not found on Open Firmware\n");
305 
306 	OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
307 
308 	if (ticks <= 0)
309 		panic("Unable to determine timebase frequency!");
310 
311 	return (ticks);
312 }
313 
314 static int
315 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
316 {
317 
318 	if (platform_cpuref_valid == 0)
319 		return (EINVAL);
320 
321 	cpuref->cr_cpuid = 0;
322 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
323 
324 	return (0);
325 }
326 
327 static int
328 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
329 {
330 	int id;
331 
332 	if (platform_cpuref_valid == 0)
333 		return (EINVAL);
334 
335 	id = cpuref->cr_cpuid + 1;
336 	if (id >= platform_cpuref_cnt)
337 		return (ENOENT);
338 
339 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
340 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
341 
342 	return (0);
343 }
344 
345 static int
346 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
347 {
348 
349 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
350 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
351 	return (0);
352 }
353 
354 static void
355 get_cpu_reg(phandle_t cpu, cell_t *reg)
356 {
357 	int res;
358 
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);
363 }
364 
365 static int
366 chrp_cpuref_init(void)
367 {
368 	phandle_t cpu, dev, chosen, pbsp;
369 	ihandle_t ibsp;
370 	char buf[32];
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;
374 
375 	if (platform_cpuref_valid)
376 		return (0);
377 
378 	dev = OF_peer(0);
379 	dev = OF_child(dev);
380 	while (dev != 0) {
381 		res = OF_getprop(dev, "name", buf, sizeof(buf));
382 		if (res > 0 && strcmp(buf, "cpus") == 0)
383 			break;
384 		dev = OF_peer(dev);
385 	}
386 
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");
396 
397 	/* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
398 
399 	chosen = OF_finddevice("/chosen");
400 	if (chosen == -1)
401 		panic("Device /chosen not found on Open Firmware\n");
402 
403 	bsp_reg = -1;
404 
405 	/* /chosen/cpu */
406 	if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
407 		OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
408 		pbsp = OF_instance_to_package(ibsp);
409 		if (pbsp != -1)
410 			get_cpu_reg(pbsp, &bsp_reg);
411 	}
412 
413 	/* /chosen/fdtbootcpu */
414 	if (bsp_reg == -1) {
415 		if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
416 			OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
417 	}
418 
419 	if (bsp_reg == -1)
420 		panic("Boot CPU not found on Open Firmware\n");
421 
422 	bsp = -1;
423 	tmp_cpuref_cnt = 0;
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");
428 			if (res > 0) {
429 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
430 				    interrupt_servers, res);
431 
432 				get_cpu_reg(cpu, &reg);
433 				if (reg == bsp_reg)
434 					bsp = tmp_cpuref_cnt;
435 
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;
439 					tmp_cpuref_cnt++;
440 				}
441 			}
442 		}
443 	}
444 
445 	if (bsp == -1)
446 		panic("Boot CPU not found\n");
447 
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++;
453 	}
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++;
458 	}
459 
460 	platform_cpuref_valid = 1;
461 
462 	return (0);
463 }
464 
465 
466 #ifdef SMP
467 static int
468 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
469 {
470 	cell_t start_cpu;
471 	int result, err, timeout;
472 
473 	if (!rtas_exists()) {
474 		printf("RTAS uninitialized: unable to start AP %d\n",
475 		    pc->pc_cpuid);
476 		return (ENXIO);
477 	}
478 
479 	start_cpu = rtas_token_lookup("start-cpu");
480 	if (start_cpu == -1) {
481 		printf("RTAS unknown method: unable to start AP %d\n",
482 		    pc->pc_cpuid);
483 		return (ENXIO);
484 	}
485 
486 	ap_pcpu = pc;
487 	powerpc_sync();
488 
489 	result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
490 	    &err);
491 	if (result < 0 || err != 0) {
492 		printf("RTAS error (%d/%d): unable to start AP %d\n",
493 		    result, err, pc->pc_cpuid);
494 		return (ENXIO);
495 	}
496 
497 	timeout = 10000;
498 	while (!pc->pc_awake && timeout--)
499 		DELAY(100);
500 
501 	return ((pc->pc_awake) ? 0 : EBUSY);
502 }
503 
504 static void
505 chrp_smp_probe_threads(platform_t plat)
506 {
507 	struct pcpu *pc, *last_pc;
508 	int i, ncores;
509 
510 	ncores = 0;
511 	last_pc = NULL;
512 	for (i = 0; i <= mp_maxid; i++) {
513 		pc = pcpu_find(i);
514 		if (pc == NULL)
515 			continue;
516 		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
517 			ncores++;
518 		last_pc = pc;
519 	}
520 
521 	mp_ncores = ncores;
522 	if (mp_ncpus % ncores == 0)
523 		smp_threads_per_core = mp_ncpus / ncores;
524 }
525 
526 static struct cpu_group *
527 chrp_smp_topo(platform_t plat)
528 {
529 
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());
534 	}
535 
536 	/* Don't do anything fancier for non-threaded SMP */
537 	if (mp_ncpus == mp_ncores)
538 		return (smp_topo_none());
539 
540 	return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
541 	    CG_FLAG_SMT));
542 }
543 #endif
544 
545 static void
546 chrp_reset(platform_t platform)
547 {
548 	OF_reboot();
549 }
550 
551 #ifdef __powerpc64__
552 static void
553 phyp_cpu_idle(sbintime_t sbt)
554 {
555 	register_t msr;
556 
557 	msr = mfmsr();
558 
559 	mtmsr(msr & ~PSL_EE);
560 	if (sched_runnable()) {
561 		mtmsr(msr);
562 		return;
563 	}
564 
565 	phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
566 	mtmsr(msr);
567 }
568 
569 static void
570 chrp_smp_ap_init(platform_t platform)
571 {
572 	if (!(mfmsr() & PSL_HV)) {
573 		/* Register VPA */
574 		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
575 		    splpar_vpa[PCPU_GET(hwref)]);
576 
577 		/* Set interrupt priority */
578 		phyp_hcall(H_CPPR, 0xff);
579 	}
580 }
581 #else
582 static void
583 chrp_smp_ap_init(platform_t platform)
584 {
585 }
586 #endif
587 
588