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