xref: /freebsd/sys/powerpc/pseries/platform_chrp.c (revision 86aa9539fef591a363b06a0ebd3aa7a07f4c1579)
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 		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 	ofw_quiesce();
179 
180 	return (0);
181 }
182 
183 static int
184 parse_drconf_memory(struct mem_region *ofmem, int *msz,
185 		    struct mem_region *ofavail, int *asz)
186 {
187 	phandle_t phandle;
188 	vm_offset_t base;
189 	int i, idx, len, lasz, lmsz, res;
190 	uint32_t flags, lmb_size[2];
191 	uint32_t *dmem;
192 
193 	lmsz = *msz;
194 	lasz = *asz;
195 
196 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
197 	if (phandle == -1)
198 		/* No drconf node, return. */
199 		return (0);
200 
201 	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
202 	    sizeof(lmb_size));
203 	if (res == -1)
204 		return (0);
205 	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
206 
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)
212 
213 	    #el  Addr   DRC-idx  res   list-idx  flags
214 	   -------------------------------------------------
215 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
216 	   -------------------------------------------------
217 	*/
218 
219 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
220 	if (len > 0) {
221 
222 		/* We have to use a variable length array on the stack
223 		   since we have very limited stack space.
224 		*/
225 		cell_t arr[len/sizeof(cell_t)];
226 
227 		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
228 		    sizeof(arr));
229 		if (res == -1)
230 			return (0);
231 
232 		/* Number of elements */
233 		idx = arr[0];
234 
235 		/* First address, in arr[1], arr[2]*/
236 		dmem = &arr[1];
237 
238 		for (i = 0; i < idx; i++) {
239 			base = ((uint64_t)dmem[0] << 32) + dmem[1];
240 			dmem += 4;
241 			flags = 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];
248 				lmsz++;
249 				lasz++;
250 			}
251 			dmem += 2;
252 		}
253 	}
254 
255 	*msz = lmsz;
256 	*asz = lasz;
257 
258 	return (1);
259 }
260 
261 void
262 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
263     struct mem_region *avail, int *availsz)
264 {
265 	vm_offset_t maxphysaddr;
266 	int i;
267 
268 	ofw_mem_regions(phys, physsz, avail, availsz);
269 	parse_drconf_memory(phys, physsz, avail, availsz);
270 
271 	/*
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
275 	 * one.
276 	 */
277 	maxphysaddr = 0;
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;
281 
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;
285 }
286 
287 static vm_offset_t
288 chrp_real_maxaddr(platform_t plat)
289 {
290 	return (realmaxaddr);
291 }
292 
293 static u_long
294 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
295 {
296 	phandle_t cpus, cpunode;
297 	int32_t ticks = -1;
298 	int res;
299 	char buf[8];
300 
301 	cpus = OF_finddevice("/cpus");
302 	if (cpus == -1)
303 		panic("CPU tree not found on Open Firmware\n");
304 
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)
308 			break;
309 	}
310 	if (cpunode <= 0)
311 		panic("CPU node not found on Open Firmware\n");
312 
313 	OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
314 
315 	if (ticks <= 0)
316 		panic("Unable to determine timebase frequency!");
317 
318 	return (ticks);
319 }
320 
321 static int
322 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
323 {
324 
325 	if (platform_cpuref_valid == 0)
326 		return (EINVAL);
327 
328 	cpuref->cr_cpuid = 0;
329 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
330 
331 	return (0);
332 }
333 
334 static int
335 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
336 {
337 	int id;
338 
339 	if (platform_cpuref_valid == 0)
340 		return (EINVAL);
341 
342 	id = cpuref->cr_cpuid + 1;
343 	if (id >= platform_cpuref_cnt)
344 		return (ENOENT);
345 
346 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
347 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
348 
349 	return (0);
350 }
351 
352 static int
353 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
354 {
355 
356 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
357 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
358 	return (0);
359 }
360 
361 static void
362 get_cpu_reg(phandle_t cpu, cell_t *reg)
363 {
364 	int res;
365 
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);
370 }
371 
372 static int
373 chrp_cpuref_init(void)
374 {
375 	phandle_t cpu, dev, chosen, pbsp;
376 	ihandle_t ibsp;
377 	char buf[32];
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;
381 
382 	if (platform_cpuref_valid)
383 		return (0);
384 
385 	dev = OF_peer(0);
386 	dev = OF_child(dev);
387 	while (dev != 0) {
388 		res = OF_getprop(dev, "name", buf, sizeof(buf));
389 		if (res > 0 && strcmp(buf, "cpus") == 0)
390 			break;
391 		dev = OF_peer(dev);
392 	}
393 
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");
403 
404 	/* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
405 
406 	chosen = OF_finddevice("/chosen");
407 	if (chosen == -1)
408 		panic("Device /chosen not found on Open Firmware\n");
409 
410 	bsp_reg = -1;
411 
412 	/* /chosen/cpu */
413 	if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
414 		OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
415 		pbsp = OF_instance_to_package(ibsp);
416 		if (pbsp != -1)
417 			get_cpu_reg(pbsp, &bsp_reg);
418 	}
419 
420 	/* /chosen/fdtbootcpu */
421 	if (bsp_reg == -1) {
422 		if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
423 			OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
424 	}
425 
426 	if (bsp_reg == -1)
427 		panic("Boot CPU not found on Open Firmware\n");
428 
429 	bsp = -1;
430 	tmp_cpuref_cnt = 0;
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");
435 			if (res > 0) {
436 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
437 				    interrupt_servers, res);
438 
439 				get_cpu_reg(cpu, &reg);
440 				if (reg == bsp_reg)
441 					bsp = tmp_cpuref_cnt;
442 
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;
446 					tmp_cpuref_cnt++;
447 				}
448 			}
449 		}
450 	}
451 
452 	if (bsp == -1)
453 		panic("Boot CPU not found\n");
454 
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++;
460 	}
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++;
465 	}
466 
467 	platform_cpuref_valid = 1;
468 
469 	return (0);
470 }
471 
472 
473 #ifdef SMP
474 static int
475 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
476 {
477 	cell_t start_cpu;
478 	int result, err, timeout;
479 
480 	if (!rtas_exists()) {
481 		printf("RTAS uninitialized: unable to start AP %d\n",
482 		    pc->pc_cpuid);
483 		return (ENXIO);
484 	}
485 
486 	start_cpu = rtas_token_lookup("start-cpu");
487 	if (start_cpu == -1) {
488 		printf("RTAS unknown method: unable to start AP %d\n",
489 		    pc->pc_cpuid);
490 		return (ENXIO);
491 	}
492 
493 	ap_pcpu = pc;
494 	powerpc_sync();
495 
496 	result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
497 	    &err);
498 	if (result < 0 || err != 0) {
499 		printf("RTAS error (%d/%d): unable to start AP %d\n",
500 		    result, err, pc->pc_cpuid);
501 		return (ENXIO);
502 	}
503 
504 	timeout = 10000;
505 	while (!pc->pc_awake && timeout--)
506 		DELAY(100);
507 
508 	return ((pc->pc_awake) ? 0 : EBUSY);
509 }
510 
511 static void
512 chrp_smp_probe_threads(platform_t plat)
513 {
514 	struct pcpu *pc, *last_pc;
515 	int i, ncores;
516 
517 	ncores = 0;
518 	last_pc = NULL;
519 	for (i = 0; i <= mp_maxid; i++) {
520 		pc = pcpu_find(i);
521 		if (pc == NULL)
522 			continue;
523 		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
524 			ncores++;
525 		last_pc = pc;
526 	}
527 
528 	mp_ncores = ncores;
529 	if (mp_ncpus % ncores == 0)
530 		smp_threads_per_core = mp_ncpus / ncores;
531 }
532 
533 static struct cpu_group *
534 chrp_smp_topo(platform_t plat)
535 {
536 
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());
541 	}
542 
543 	/* Don't do anything fancier for non-threaded SMP */
544 	if (mp_ncpus == mp_ncores)
545 		return (smp_topo_none());
546 
547 	return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
548 	    CG_FLAG_SMT));
549 }
550 #endif
551 
552 static void
553 chrp_reset(platform_t platform)
554 {
555 	OF_reboot();
556 }
557 
558 #ifdef __powerpc64__
559 static void
560 phyp_cpu_idle(sbintime_t sbt)
561 {
562 	register_t msr;
563 
564 	msr = mfmsr();
565 
566 	mtmsr(msr & ~PSL_EE);
567 	if (sched_runnable()) {
568 		mtmsr(msr);
569 		return;
570 	}
571 
572 	phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
573 	mtmsr(msr);
574 }
575 
576 static void
577 chrp_smp_ap_init(platform_t platform)
578 {
579 	if (!(mfmsr() & PSL_HV)) {
580 		/* Register VPA */
581 		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
582 		    splpar_vpa[PCPU_GET(hwref)]);
583 
584 		/* Set interrupt priority */
585 		phyp_hcall(H_CPPR, 0xff);
586 	}
587 }
588 #else
589 static void
590 chrp_smp_ap_init(platform_t platform)
591 {
592 }
593 #endif
594 
595