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