platform_chrp.c revision 330897
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: stable/11/sys/powerpc/pseries/platform_chrp.c 330897 2018-03-14 03:19:51Z eadler $");
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/smp.h>
40#include <vm/vm.h>
41#include <vm/pmap.h>
42
43#include <machine/bus.h>
44#include <machine/cpu.h>
45#include <machine/hid.h>
46#include <machine/platformvar.h>
47#include <machine/rtas.h>
48#include <machine/smp.h>
49#include <machine/spr.h>
50#include <machine/trap.h>
51
52#include <dev/ofw/openfirm.h>
53#include <machine/ofw_machdep.h>
54
55#include "platform_if.h"
56
57#ifdef SMP
58extern void *ap_pcpu;
59#endif
60
61#ifdef __powerpc64__
62static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
63#endif
64
65static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
66
67static int chrp_probe(platform_t);
68static int chrp_attach(platform_t);
69void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
70    struct mem_region *avail, int *availsz);
71static vm_offset_t chrp_real_maxaddr(platform_t);
72static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
73static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
74static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
75static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
76static void chrp_smp_ap_init(platform_t);
77#ifdef SMP
78static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
79static struct cpu_group *chrp_smp_topo(platform_t plat);
80#endif
81static void chrp_reset(platform_t);
82#ifdef __powerpc64__
83#include "phyp-hvcall.h"
84static void phyp_cpu_idle(sbintime_t sbt);
85#endif
86
87static platform_method_t chrp_methods[] = {
88	PLATFORMMETHOD(platform_probe, 		chrp_probe),
89	PLATFORMMETHOD(platform_attach,		chrp_attach),
90	PLATFORMMETHOD(platform_mem_regions,	chrp_mem_regions),
91	PLATFORMMETHOD(platform_real_maxaddr,	chrp_real_maxaddr),
92	PLATFORMMETHOD(platform_timebase_freq,	chrp_timebase_freq),
93
94	PLATFORMMETHOD(platform_smp_ap_init,	chrp_smp_ap_init),
95	PLATFORMMETHOD(platform_smp_first_cpu,	chrp_smp_first_cpu),
96	PLATFORMMETHOD(platform_smp_next_cpu,	chrp_smp_next_cpu),
97	PLATFORMMETHOD(platform_smp_get_bsp,	chrp_smp_get_bsp),
98#ifdef SMP
99	PLATFORMMETHOD(platform_smp_start_cpu,	chrp_smp_start_cpu),
100	PLATFORMMETHOD(platform_smp_topo,	chrp_smp_topo),
101#endif
102
103	PLATFORMMETHOD(platform_reset,		chrp_reset),
104
105	{ 0, 0 }
106};
107
108static platform_def_t chrp_platform = {
109	"chrp",
110	chrp_methods,
111	0
112};
113
114PLATFORM_DEF(chrp_platform);
115
116static int
117chrp_probe(platform_t plat)
118{
119	if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
120		return (BUS_PROBE_GENERIC);
121
122	return (ENXIO);
123}
124
125static int
126chrp_attach(platform_t plat)
127{
128#ifdef __powerpc64__
129	int i;
130
131	/* XXX: check for /rtas/ibm,hypertas-functions? */
132	if (!(mfmsr() & PSL_HV)) {
133		struct mem_region *phys, *avail;
134		int nphys, navail;
135		mem_regions(&phys, &nphys, &avail, &navail);
136		realmaxaddr = phys[0].mr_size;
137
138		pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
139		cpu_idle_hook = phyp_cpu_idle;
140
141		/* Set up important VPA fields */
142		for (i = 0; i < MAXCPU; i++) {
143			bzero(splpar_vpa[i], sizeof(splpar_vpa));
144			/* First two: VPA size */
145			splpar_vpa[i][4] =
146			    (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
147			splpar_vpa[i][5] =
148			    (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
149			splpar_vpa[i][0xba] = 1;	/* Maintain FPRs */
150			splpar_vpa[i][0xbb] = 1;	/* Maintain PMCs */
151			splpar_vpa[i][0xfc] = 0xff;	/* Maintain full SLB */
152			splpar_vpa[i][0xfd] = 0xff;
153			splpar_vpa[i][0xff] = 1;	/* Maintain Altivec */
154		}
155		mb();
156
157		/* Set up hypervisor CPU stuff */
158		chrp_smp_ap_init(plat);
159	}
160#endif
161
162	/* Some systems (e.g. QEMU) need Open Firmware to stand down */
163	ofw_quiesce();
164
165	return (0);
166}
167
168static int
169parse_drconf_memory(struct mem_region *ofmem, int *msz,
170		    struct mem_region *ofavail, int *asz)
171{
172	phandle_t phandle;
173	vm_offset_t base;
174	int i, idx, len, lasz, lmsz, res;
175	uint32_t flags, lmb_size[2];
176	uint32_t *dmem;
177
178	lmsz = *msz;
179	lasz = *asz;
180
181	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
182	if (phandle == -1)
183		/* No drconf node, return. */
184		return (0);
185
186	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
187	    sizeof(lmb_size));
188	if (res == -1)
189		return (0);
190	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
191
192	/* Parse the /ibm,dynamic-memory.
193	   The first position gives the # of entries. The next two words
194 	   reflect the address of the memory block. The next four words are
195	   the DRC index, reserved, list index and flags.
196	   (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
197
198	    #el  Addr   DRC-idx  res   list-idx  flags
199	   -------------------------------------------------
200	   | 4 |   8   |   4   |   4   |   4   |   4   |....
201	   -------------------------------------------------
202	*/
203
204	len = OF_getproplen(phandle, "ibm,dynamic-memory");
205	if (len > 0) {
206
207		/* We have to use a variable length array on the stack
208		   since we have very limited stack space.
209		*/
210		cell_t arr[len/sizeof(cell_t)];
211
212		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
213		    sizeof(arr));
214		if (res == -1)
215			return (0);
216
217		/* Number of elements */
218		idx = arr[0];
219
220		/* First address, in arr[1], arr[2]*/
221		dmem = &arr[1];
222
223		for (i = 0; i < idx; i++) {
224			base = ((uint64_t)dmem[0] << 32) + dmem[1];
225			dmem += 4;
226			flags = dmem[1];
227			/* Use region only if available and not reserved. */
228			if ((flags & 0x8) && !(flags & 0x80)) {
229				ofmem[lmsz].mr_start = base;
230				ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
231				ofavail[lasz].mr_start = base;
232				ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
233				lmsz++;
234				lasz++;
235			}
236			dmem += 2;
237		}
238	}
239
240	*msz = lmsz;
241	*asz = lasz;
242
243	return (1);
244}
245
246void
247chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
248    struct mem_region *avail, int *availsz)
249{
250	vm_offset_t maxphysaddr;
251	int i;
252
253	ofw_mem_regions(phys, physsz, avail, availsz);
254	parse_drconf_memory(phys, physsz, avail, availsz);
255
256	/*
257	 * On some firmwares (SLOF), some memory may be marked available that
258	 * doesn't actually exist. This manifests as an extension of the last
259	 * available segment past the end of physical memory, so truncate that
260	 * one.
261	 */
262	maxphysaddr = 0;
263	for (i = 0; i < *physsz; i++)
264		if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
265			maxphysaddr = phys[i].mr_start + phys[i].mr_size;
266
267	for (i = 0; i < *availsz; i++)
268		if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
269			avail[i].mr_size = maxphysaddr - avail[i].mr_start;
270}
271
272static vm_offset_t
273chrp_real_maxaddr(platform_t plat)
274{
275	return (realmaxaddr);
276}
277
278static u_long
279chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
280{
281	phandle_t phandle;
282	int32_t ticks = -1;
283
284	phandle = cpuref->cr_hwref;
285
286	OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
287
288	if (ticks <= 0)
289		panic("Unable to determine timebase frequency!");
290
291	return (ticks);
292}
293
294static int
295chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
296{
297	char buf[8];
298	phandle_t cpu, dev, root;
299	int res, cpuid;
300
301	root = OF_peer(0);
302
303	dev = OF_child(root);
304	while (dev != 0) {
305		res = OF_getprop(dev, "name", buf, sizeof(buf));
306		if (res > 0 && strcmp(buf, "cpus") == 0)
307			break;
308		dev = OF_peer(dev);
309	}
310	if (dev == 0) {
311		/*
312		 * psim doesn't have a name property on the /cpus node,
313		 * but it can be found directly
314		 */
315		dev = OF_finddevice("/cpus");
316		if (dev == 0)
317			return (ENOENT);
318	}
319
320	cpu = OF_child(dev);
321
322	while (cpu != 0) {
323		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
324		if (res > 0 && strcmp(buf, "cpu") == 0)
325			break;
326		cpu = OF_peer(cpu);
327	}
328	if (cpu == 0)
329		return (ENOENT);
330
331	cpuref->cr_hwref = cpu;
332	res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
333	    sizeof(cpuid));
334	if (res <= 0)
335		res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
336	if (res <= 0)
337		cpuid = 0;
338	cpuref->cr_cpuid = cpuid;
339
340	return (0);
341}
342
343static int
344chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
345{
346	char buf[8];
347	phandle_t cpu;
348	int i, res, cpuid;
349
350	/* Check for whether it should be the next thread */
351	res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
352	if (res > 0) {
353		cell_t interrupt_servers[res/sizeof(cell_t)];
354		OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
355		    interrupt_servers, res);
356		for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
357			if (interrupt_servers[i] == cpuref->cr_cpuid) {
358				cpuref->cr_cpuid = interrupt_servers[i+1];
359				return (0);
360			}
361		}
362	}
363
364	/* Next CPU core/package */
365	cpu = OF_peer(cpuref->cr_hwref);
366	while (cpu != 0) {
367		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
368		if (res > 0 && strcmp(buf, "cpu") == 0)
369			break;
370		cpu = OF_peer(cpu);
371	}
372	if (cpu == 0)
373		return (ENOENT);
374
375	cpuref->cr_hwref = cpu;
376	res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
377	    sizeof(cpuid));
378	if (res <= 0)
379		res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
380	if (res <= 0)
381		cpuid = 0;
382	cpuref->cr_cpuid = cpuid;
383
384	return (0);
385}
386
387static int
388chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
389{
390	ihandle_t inst;
391	phandle_t bsp, chosen;
392	int res, cpuid;
393
394	chosen = OF_finddevice("/chosen");
395	if (chosen == 0)
396		return (ENXIO);
397
398	res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst));
399	if (res < 0)
400		return (ENXIO);
401
402	bsp = OF_instance_to_package(inst);
403
404	/* Pick the primary thread. Can it be any other? */
405	cpuref->cr_hwref = bsp;
406	res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
407	    sizeof(cpuid));
408	if (res <= 0)
409		res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid));
410	if (res <= 0)
411		cpuid = 0;
412	cpuref->cr_cpuid = cpuid;
413
414	return (0);
415}
416
417#ifdef SMP
418static int
419chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
420{
421	cell_t start_cpu;
422	int result, err, timeout;
423
424	if (!rtas_exists()) {
425		printf("RTAS uninitialized: unable to start AP %d\n",
426		    pc->pc_cpuid);
427		return (ENXIO);
428	}
429
430	start_cpu = rtas_token_lookup("start-cpu");
431	if (start_cpu == -1) {
432		printf("RTAS unknown method: unable to start AP %d\n",
433		    pc->pc_cpuid);
434		return (ENXIO);
435	}
436
437	ap_pcpu = pc;
438	powerpc_sync();
439
440	result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
441	    &err);
442	if (result < 0 || err != 0) {
443		printf("RTAS error (%d/%d): unable to start AP %d\n",
444		    result, err, pc->pc_cpuid);
445		return (ENXIO);
446	}
447
448	timeout = 10000;
449	while (!pc->pc_awake && timeout--)
450		DELAY(100);
451
452	return ((pc->pc_awake) ? 0 : EBUSY);
453}
454
455static struct cpu_group *
456chrp_smp_topo(platform_t plat)
457{
458	struct pcpu *pc, *last_pc;
459	int i, ncores, ncpus;
460
461	ncores = ncpus = 0;
462	last_pc = NULL;
463	for (i = 0; i <= mp_maxid; i++) {
464		pc = pcpu_find(i);
465		if (pc == NULL)
466			continue;
467		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
468			ncores++;
469		last_pc = pc;
470		ncpus++;
471	}
472
473	if (ncpus % ncores != 0) {
474		printf("WARNING: Irregular SMP topology. Performance may be "
475		     "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
476		return (smp_topo_none());
477	}
478
479	/* Don't do anything fancier for non-threaded SMP */
480	if (ncpus == ncores)
481		return (smp_topo_none());
482
483	return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
484}
485#endif
486
487static void
488chrp_reset(platform_t platform)
489{
490	OF_reboot();
491}
492
493#ifdef __powerpc64__
494static void
495phyp_cpu_idle(sbintime_t sbt)
496{
497	phyp_hcall(H_CEDE);
498}
499
500static void
501chrp_smp_ap_init(platform_t platform)
502{
503	if (!(mfmsr() & PSL_HV)) {
504		/* Register VPA */
505		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid),
506		    splpar_vpa[PCPU_GET(cpuid)]);
507
508		/* Set interrupt priority */
509		phyp_hcall(H_CPPR, 0xff);
510	}
511}
512#else
513static void
514chrp_smp_ap_init(platform_t platform)
515{
516}
517#endif
518
519