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