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