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/endian.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/bus.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/sched.h>
41 #include <sys/smp.h>
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44
45 #include <machine/bus.h>
46 #include <machine/cpu.h>
47 #include <machine/hid.h>
48 #include <machine/platformvar.h>
49 #include <machine/rtas.h>
50 #include <machine/smp.h>
51 #include <machine/spr.h>
52 #include <machine/trap.h>
53
54 #include <dev/ofw/openfirm.h>
55 #include <machine/ofw_machdep.h>
56
57 #include "platform_if.h"
58
59 #ifdef SMP
60 extern void *ap_pcpu;
61 #endif
62
63 #ifdef __powerpc64__
64 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
65 #endif
66
67 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
68
69 static int chrp_probe(platform_t);
70 static int chrp_attach(platform_t);
71 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
72 struct mem_region *avail, int *availsz);
73 static vm_offset_t chrp_real_maxaddr(platform_t);
74 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
75 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
76 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
77 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
78 static void chrp_smp_ap_init(platform_t);
79 static int chrp_cpuref_init(void);
80 #ifdef SMP
81 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
82 static void chrp_smp_probe_threads(platform_t plat);
83 static struct cpu_group *chrp_smp_topo(platform_t plat);
84 #endif
85 static void chrp_reset(platform_t);
86 #ifdef __powerpc64__
87 #include "phyp-hvcall.h"
88 static void phyp_cpu_idle(sbintime_t sbt);
89 #endif
90
91 static struct cpuref platform_cpuref[MAXCPU];
92 static int platform_cpuref_cnt;
93 static int platform_cpuref_valid;
94
95 static platform_method_t chrp_methods[] = {
96 PLATFORMMETHOD(platform_probe, chrp_probe),
97 PLATFORMMETHOD(platform_attach, chrp_attach),
98 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
99 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
100 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
101
102 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
103 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
104 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
105 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
106 #ifdef SMP
107 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
108 PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads),
109 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
110 #endif
111
112 PLATFORMMETHOD(platform_reset, chrp_reset),
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 int quiesce;
137 #ifdef __powerpc64__
138 int i;
139 #if BYTE_ORDER == LITTLE_ENDIAN
140 int result;
141 #endif
142
143 /* XXX: check for /rtas/ibm,hypertas-functions? */
144 if (!(mfmsr() & PSL_HV)) {
145 struct mem_region *phys, *avail;
146 int nphys, navail;
147 vm_offset_t off;
148
149 mem_regions(&phys, &nphys, &avail, &navail);
150
151 realmaxaddr = 0;
152 for (i = 0; i < nphys; i++) {
153 off = phys[i].mr_start + phys[i].mr_size;
154 realmaxaddr = MAX(off, realmaxaddr);
155 }
156
157 if (!radix_mmu)
158 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
159 cpu_idle_hook = phyp_cpu_idle;
160
161 /* Set up important VPA fields */
162 for (i = 0; i < MAXCPU; i++) {
163 /* First two: VPA size */
164 splpar_vpa[i][4] =
165 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
166 splpar_vpa[i][5] =
167 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
168 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
169 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
170 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
171 splpar_vpa[i][0xfd] = 0xff;
172 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
173 }
174 mb();
175
176 /* Set up hypervisor CPU stuff */
177 chrp_smp_ap_init(plat);
178
179 #if BYTE_ORDER == LITTLE_ENDIAN
180 /*
181 * Ask the hypervisor to update the LPAR ILE bit.
182 *
183 * This involves all processors reentering the hypervisor
184 * so the change appears simultaneously in all processors.
185 * This can take a long time.
186 */
187 for(;;) {
188 result = phyp_hcall(H_SET_MODE, 1UL,
189 H_SET_MODE_RSRC_ILE, 0, 0);
190 if (result == H_SUCCESS)
191 break;
192 DELAY(1000);
193 }
194 #endif
195
196 }
197 #endif
198 chrp_cpuref_init();
199
200 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
201 quiesce = 1;
202 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
203 if (quiesce)
204 ofw_quiesce();
205
206 return (0);
207 }
208
209 static int
210 parse_drconf_memory(struct mem_region *ofmem, int *msz,
211 struct mem_region *ofavail, int *asz)
212 {
213 phandle_t phandle;
214 vm_offset_t base;
215 int i, idx, len, lasz, lmsz, res;
216 uint32_t flags, lmb_size[2];
217 uint32_t *dmem;
218
219 lmsz = *msz;
220 lasz = *asz;
221
222 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
223 if (phandle == -1)
224 /* No drconf node, return. */
225 return (0);
226
227 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
228 sizeof(lmb_size));
229 if (res == -1)
230 return (0);
231 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
232
233 /* Parse the /ibm,dynamic-memory.
234 The first position gives the # of entries. The next two words
235 reflect the address of the memory block. The next four words are
236 the DRC index, reserved, list index and flags.
237 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
238
239 #el Addr DRC-idx res list-idx flags
240 -------------------------------------------------
241 | 4 | 8 | 4 | 4 | 4 | 4 |....
242 -------------------------------------------------
243 */
244
245 len = OF_getproplen(phandle, "ibm,dynamic-memory");
246 if (len > 0) {
247 /* We have to use a variable length array on the stack
248 since we have very limited stack space.
249 */
250 cell_t arr[len/sizeof(cell_t)];
251
252 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
253 sizeof(arr));
254 if (res == -1)
255 return (0);
256
257 /* Number of elements */
258 idx = arr[0];
259
260 /* First address, in arr[1], arr[2]*/
261 dmem = &arr[1];
262
263 for (i = 0; i < idx; i++) {
264 base = ((uint64_t)dmem[0] << 32) + dmem[1];
265 dmem += 4;
266 flags = dmem[1];
267 /* Use region only if available and not reserved. */
268 if ((flags & 0x8) && !(flags & 0x80)) {
269 ofmem[lmsz].mr_start = base;
270 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
271 ofavail[lasz].mr_start = base;
272 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
273 lmsz++;
274 lasz++;
275 }
276 dmem += 2;
277 }
278 }
279
280 *msz = lmsz;
281 *asz = lasz;
282
283 return (1);
284 }
285
286 void
287 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
288 struct mem_region *avail, int *availsz)
289 {
290 vm_offset_t maxphysaddr;
291 int i;
292
293 ofw_mem_regions(phys, physsz, avail, availsz);
294 parse_drconf_memory(phys, physsz, avail, availsz);
295
296 /*
297 * On some firmwares (SLOF), some memory may be marked available that
298 * doesn't actually exist. This manifests as an extension of the last
299 * available segment past the end of physical memory, so truncate that
300 * one.
301 */
302 maxphysaddr = 0;
303 for (i = 0; i < *physsz; i++)
304 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
305 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
306
307 for (i = 0; i < *availsz; i++)
308 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
309 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
310 }
311
312 static vm_offset_t
313 chrp_real_maxaddr(platform_t plat)
314 {
315 return (realmaxaddr);
316 }
317
318 static u_long
319 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
320 {
321 phandle_t cpus, cpunode;
322 int32_t ticks = -1;
323 int res;
324 char buf[8];
325
326 cpus = OF_finddevice("/cpus");
327 if (cpus == -1)
328 panic("CPU tree not found on Open Firmware\n");
329
330 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
331 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
332 if (res > 0 && strcmp(buf, "cpu") == 0)
333 break;
334 }
335 if (cpunode <= 0)
336 panic("CPU node not found on Open Firmware\n");
337
338 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
339
340 if (ticks <= 0)
341 panic("Unable to determine timebase frequency!");
342
343 return (ticks);
344 }
345
346 static int
347 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
348 {
349
350 if (platform_cpuref_valid == 0)
351 return (EINVAL);
352
353 cpuref->cr_cpuid = 0;
354 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
355
356 return (0);
357 }
358
359 static int
360 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
361 {
362 int id;
363
364 if (platform_cpuref_valid == 0)
365 return (EINVAL);
366
367 id = cpuref->cr_cpuid + 1;
368 if (id >= platform_cpuref_cnt)
369 return (ENOENT);
370
371 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
372 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
373
374 return (0);
375 }
376
377 static int
378 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
379 {
380
381 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
382 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
383 return (0);
384 }
385
386 static void
387 get_cpu_reg(phandle_t cpu, cell_t *reg)
388 {
389 int res;
390
391 res = OF_getproplen(cpu, "reg");
392 if (res != sizeof(cell_t))
393 panic("Unexpected length for CPU property reg on Open Firmware\n");
394 OF_getencprop(cpu, "reg", reg, res);
395 }
396
397 static int
398 chrp_cpuref_init(void)
399 {
400 phandle_t cpu, dev, chosen, pbsp;
401 ihandle_t ibsp;
402 char buf[32];
403 int a, bsp, res, res2, tmp_cpuref_cnt;
404 static struct cpuref tmp_cpuref[MAXCPU];
405 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
406
407 if (platform_cpuref_valid)
408 return (0);
409
410 dev = OF_peer(0);
411 dev = OF_child(dev);
412 while (dev != 0) {
413 res = OF_getprop(dev, "name", buf, sizeof(buf));
414 if (res > 0 && strcmp(buf, "cpus") == 0)
415 break;
416 dev = OF_peer(dev);
417 }
418
419 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
420 res = OF_getproplen(dev, "#address-cells");
421 res2 = OF_getproplen(dev, "#size-cells");
422 if (res != res2 || res != sizeof(cell_t))
423 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
424 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
425 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
426 if (addr_cells != 1 || size_cells != 0)
427 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
428
429 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
430
431 chosen = OF_finddevice("/chosen");
432 if (chosen == -1)
433 panic("Device /chosen not found on Open Firmware\n");
434
435 bsp_reg = -1;
436
437 /* /chosen/cpu */
438 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
439 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
440 pbsp = OF_instance_to_package(be32toh(ibsp));
441 if (pbsp != -1)
442 get_cpu_reg(pbsp, &bsp_reg);
443 }
444
445 /* /chosen/fdtbootcpu */
446 if (bsp_reg == -1) {
447 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
448 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
449 }
450
451 if (bsp_reg == -1)
452 panic("Boot CPU not found on Open Firmware\n");
453
454 bsp = -1;
455 tmp_cpuref_cnt = 0;
456 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
457 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
458 if (res > 0 && strcmp(buf, "cpu") == 0) {
459 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
460 if (res > 0) {
461 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
462 interrupt_servers, res);
463
464 get_cpu_reg(cpu, ®);
465 if (reg == bsp_reg)
466 bsp = tmp_cpuref_cnt;
467
468 for (a = 0; a < res/sizeof(cell_t); a++) {
469 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
470 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
471 tmp_cpuref_cnt++;
472 }
473 }
474 }
475 }
476
477 if (bsp == -1)
478 panic("Boot CPU not found\n");
479
480 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
481 for (a = bsp; a < tmp_cpuref_cnt; a++) {
482 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
483 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
484 platform_cpuref_cnt++;
485 }
486 for (a = 0; a < bsp; a++) {
487 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
488 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
489 platform_cpuref_cnt++;
490 }
491
492 platform_cpuref_valid = 1;
493
494 return (0);
495 }
496
497 #ifdef SMP
498 static int
499 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
500 {
501 cell_t start_cpu;
502 int result, err, timeout;
503
504 if (!rtas_exists()) {
505 printf("RTAS uninitialized: unable to start AP %d\n",
506 pc->pc_cpuid);
507 return (ENXIO);
508 }
509
510 start_cpu = rtas_token_lookup("start-cpu");
511 if (start_cpu == -1) {
512 printf("RTAS unknown method: unable to start AP %d\n",
513 pc->pc_cpuid);
514 return (ENXIO);
515 }
516
517 ap_pcpu = pc;
518 powerpc_sync();
519
520 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
521 &err);
522 if (result < 0 || err != 0) {
523 printf("RTAS error (%d/%d): unable to start AP %d\n",
524 result, err, pc->pc_cpuid);
525 return (ENXIO);
526 }
527
528 timeout = 10000;
529 while (!pc->pc_awake && timeout--)
530 DELAY(100);
531
532 return ((pc->pc_awake) ? 0 : EBUSY);
533 }
534
535 static void
536 chrp_smp_probe_threads(platform_t plat)
537 {
538 struct pcpu *pc, *last_pc;
539 int i, ncores;
540
541 ncores = 0;
542 last_pc = NULL;
543 for (i = 0; i <= mp_maxid; i++) {
544 pc = pcpu_find(i);
545 if (pc == NULL)
546 continue;
547 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
548 ncores++;
549 last_pc = pc;
550 }
551
552 mp_ncores = ncores;
553 if (mp_ncpus % ncores == 0)
554 smp_threads_per_core = mp_ncpus / ncores;
555 }
556
557 static struct cpu_group *
558 chrp_smp_topo(platform_t plat)
559 {
560
561 if (mp_ncpus % mp_ncores != 0) {
562 printf("WARNING: Irregular SMP topology. Performance may be "
563 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
564 return (smp_topo_none());
565 }
566
567 /* Don't do anything fancier for non-threaded SMP */
568 if (mp_ncpus == mp_ncores)
569 return (smp_topo_none());
570
571 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
572 CG_FLAG_SMT));
573 }
574 #endif
575
576 static void
577 chrp_reset(platform_t platform)
578 {
579 OF_reboot();
580 }
581
582 #ifdef __powerpc64__
583 static void
584 phyp_cpu_idle(sbintime_t sbt)
585 {
586 register_t msr;
587
588 msr = mfmsr();
589
590 mtmsr(msr & ~PSL_EE);
591 if (sched_runnable()) {
592 mtmsr(msr);
593 return;
594 }
595
596 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
597 mtmsr(msr);
598 }
599
600 static void
601 chrp_smp_ap_init(platform_t platform)
602 {
603 if (!(mfmsr() & PSL_HV)) {
604 /* Register VPA */
605 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
606 splpar_vpa[PCPU_GET(hwref)]);
607
608 /* Set interrupt priority */
609 phyp_hcall(H_CPPR, 0xff);
610 }
611 }
612 #else
613 static void
614 chrp_smp_ap_init(platform_t platform)
615 {
616 }
617 #endif
Cache object: 2b029d6c221b61db88cf047061a32fe2
|