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