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