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