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