octeon_cpunode.c revision 1.16 1 /*-
2 * Copyright (c) 2014 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Matt Thomas of 3am Software Foundry.
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 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29 #define __INTR_PRIVATE
30 #include <sys/cdefs.h>
31
32 __KERNEL_RCSID(0, "$NetBSD: octeon_cpunode.c,v 1.16 2020/07/21 06:01:10 simonb Exp $");
33
34 #include "locators.h"
35 #include "cpunode.h"
36 #include "opt_multiprocessor.h"
37 #include "opt_ddb.h"
38
39 #include <sys/param.h>
40 #include <sys/atomic.h>
41 #include <sys/cpu.h>
42 #include <sys/device.h>
43 #include <sys/lwp.h>
44 #include <sys/reboot.h>
45 #include <sys/wdog.h>
46
47 #include <uvm/uvm.h>
48
49 #include <dev/sysmon/sysmonvar.h>
50
51 #include <mips/cache.h>
52 #include <mips/mips_opcode.h>
53 #include <mips/mips3_clock.h>
54
55 #include <mips/cavium/octeonvar.h>
56 #include <mips/cavium/dev/octeon_ciureg.h>
57 #include <mips/cavium/dev/octeon_corereg.h>
58
59 extern struct cpu_softc octeon_cpu_softc[];
60
61 struct cpunode_attach_args {
62 const char *cnaa_name;
63 int cnaa_cpunum;
64 };
65
66 struct cpunode_softc {
67 device_t sc_dev;
68 device_t sc_wdog_dev;
69 };
70
71 static int cpunode_mainbus_match(device_t, cfdata_t, void *);
72 static void cpunode_mainbus_attach(device_t, device_t, void *);
73
74 static int cpu_cpunode_match(device_t, cfdata_t, void *);
75 static void cpu_cpunode_attach(device_t, device_t, void *);
76
77 CFATTACH_DECL_NEW(cpunode, sizeof(struct cpunode_softc),
78 cpunode_mainbus_match, cpunode_mainbus_attach, NULL, NULL);
79
80 CFATTACH_DECL_NEW(cpu_cpunode, 0,
81 cpu_cpunode_match, cpu_cpunode_attach, NULL, NULL);
82
83 kcpuset_t *cpus_booted;
84
85 static void wdog_cpunode_poke(void *arg);
86
87 static int
88 cpunode_mainbus_print(void *aux, const char *pnp)
89 {
90 struct cpunode_attach_args * const cnaa = aux;
91
92 if (pnp)
93 aprint_normal("%s", pnp);
94
95 if (cnaa->cnaa_cpunum != CPUNODECF_CORE_DEFAULT)
96 aprint_normal(" core %d", cnaa->cnaa_cpunum);
97
98 return UNCONF;
99 }
100
101 int
102 cpunode_mainbus_match(device_t parent, cfdata_t cf, void *aux)
103 {
104
105 return 1;
106 }
107
108 void
109 cpunode_mainbus_attach(device_t parent, device_t self, void *aux)
110 {
111 struct cpunode_softc * const sc = device_private(self);
112 const uint64_t fuse = octeon_xkphys_read_8(CIU_FUSE);
113 int cpunum = 0;
114
115 sc->sc_dev = self;
116
117 aprint_naive(": %u core%s\n", popcount64(fuse), fuse == 1 ? "" : "s");
118 aprint_normal(": %u core%s", popcount64(fuse), fuse == 1 ? "" : "s");
119
120 const uint64_t cvmctl = mips_cp0_cvmctl_read();
121 aprint_normal(", %scrypto", (cvmctl & CP0_CVMCTL_NOCRYPTO) ? "no " : "");
122 aprint_normal((cvmctl & CP0_CVMCTL_KASUMI) ? "+kasumi" : "");
123 aprint_normal(", %s64bit-mul", (cvmctl & CP0_CVMCTL_NOMUL) ? "no " : "");
124 if (cvmctl & CP0_CVMCTL_REPUN)
125 aprint_normal(", unaligned-access ok");
126 #ifdef MULTIPROCESSOR
127 uint32_t booted[1];
128 kcpuset_export_u32(cpus_booted, booted, sizeof(booted));
129 aprint_normal(", booted %#" PRIx32, booted[0]);
130 #endif
131 aprint_normal("\n");
132
133 for (uint64_t f = fuse; f != 0; f >>= 1, cpunum++) {
134 struct cpunode_attach_args cnaa = {
135 .cnaa_name = "cpu",
136 .cnaa_cpunum = cpunum,
137 };
138 config_found(self, &cnaa, cpunode_mainbus_print);
139 }
140 #if NWDOG > 0
141 struct cpunode_attach_args cnaa = {
142 .cnaa_name = "wdog",
143 .cnaa_cpunum = CPUNODECF_CORE_DEFAULT,
144 };
145 config_found(self, &cnaa, cpunode_mainbus_print);
146 #endif
147 }
148
149 int
150 cpu_cpunode_match(device_t parent, cfdata_t cf, void *aux)
151 {
152 struct cpunode_attach_args * const cnaa = aux;
153 const int cpunum = cf->cf_loc[CPUNODECF_CORE];
154
155 return strcmp(cnaa->cnaa_name, cf->cf_name) == 0
156 && (cpunum == CPUNODECF_CORE_DEFAULT || cpunum == cnaa->cnaa_cpunum);
157 }
158
159 #if defined(MULTIPROCESSOR)
160 static bool
161 octeon_fixup_cpu_info_references(int32_t load_addr, uint32_t new_insns[2],
162 void *arg)
163 {
164 struct cpu_info * const ci = arg;
165
166 atomic_or_ulong(&curcpu()->ci_flags, CPUF_PRESENT);
167
168 KASSERT(MIPS_KSEG0_P(load_addr));
169 #ifdef MULTIPROCESSOR
170 KASSERT(!CPU_IS_PRIMARY(curcpu()));
171 #endif
172 load_addr += (intptr_t)ci - (intptr_t)&cpu_info_store;
173
174 KASSERT((intptr_t)ci <= load_addr);
175 KASSERT(load_addr < (intptr_t)(ci + 1));
176
177 KASSERT(INSN_LUI_P(new_insns[0]));
178 KASSERT(INSN_LOAD_P(new_insns[1]) || INSN_STORE_P(new_insns[1]));
179
180 /*
181 * Use the lui and load/store instruction as a prototype and
182 * make it refer to cpu1_info_store instead of cpu_info_store.
183 */
184 new_insns[0] &= __BITS(31,16);
185 new_insns[1] &= __BITS(31,16);
186 new_insns[0] |= (uint16_t)((load_addr + 0x8000) >> 16);
187 new_insns[1] |= (uint16_t)load_addr;
188 #ifdef DEBUG_VERBOSE
189 printf("%s: %08x: insn#1 %08x: lui r%u, %d\n",
190 __func__, load_addr, new_insns[0],
191 (new_insns[0] >> 16) & 31,
192 (int16_t)new_insns[0]);
193 printf("%s: %08x: insn#2 %08x: %c%c r%u, %d(r%u)\n",
194 __func__, load_addr, new_insns[1],
195 INSN_LOAD_P(new_insns[1]) ? 'l' : 's',
196 INSN_LW_P(new_insns[1]) ? 'w' : 'd',
197 (new_insns[1] >> 16) & 31,
198 (int16_t)new_insns[1],
199 (new_insns[1] >> 21) & 31);
200 #endif
201 return true;
202 }
203
204 static void
205 octeon_cpu_init(struct cpu_info *ci)
206 {
207 bool ok __diagused;
208
209 // First thing is setup the execption vectors for this cpu.
210 mips64r2_vector_init(&mips_splsw);
211
212 // Next rewrite those exceptions to use this cpu's cpu_info.
213 ok = mips_fixup_exceptions(octeon_fixup_cpu_info_references, ci);
214 KASSERT(ok);
215
216 (void) splhigh(); // make sure interrupts are masked
217
218 KASSERT((mipsNN_cp0_ebase_read() & MIPS_EBASE_CPUNUM) == ci->ci_cpuid);
219 KASSERT(curcpu() == ci);
220 KASSERT(ci->ci_cpl == IPL_HIGH);
221 KASSERT((mips_cp0_status_read() & MIPS_INT_MASK) == 0);
222 }
223
224 static void
225 octeon_cpu_run(struct cpu_info *ci)
226 {
227
228 octeon_intr_init(ci);
229
230 mips3_initclocks();
231 KASSERTMSG(ci->ci_cpl == IPL_NONE, "cpl %d", ci->ci_cpl);
232 KASSERT(mips_cp0_status_read() & MIPS_SR_INT_IE);
233
234 aprint_normal("%s: ", device_xname(ci->ci_dev));
235 cpu_identify(ci->ci_dev);
236 }
237 #endif /* MULTIPROCESSOR */
238
239 static void
240 cpu_cpunode_attach_common(device_t self, struct cpu_info *ci)
241 {
242 struct cpu_softc * const cpu __diagused = ci->ci_softc;
243
244 ci->ci_dev = self;
245 self->dv_private = ci;
246
247 KASSERTMSG(cpu != NULL, "ci %p index %d", ci, cpu_index(ci));
248
249 #if NWDOG > 0 || defined(DDB)
250 /* XXXXXX __mips_n32 and MIPS_PHYS_TO_XKPHYS_CACHED needed here?????? */
251 void **nmi_vector = (void *)MIPS_PHYS_TO_KSEG0(0x800 + 32*ci->ci_cpuid);
252 *nmi_vector = octeon_reset_vector;
253
254 struct vm_page * const pg = PMAP_ALLOC_POOLPAGE(UVM_PGA_ZERO);
255 KASSERT(pg != NULL);
256 const vaddr_t kva = PMAP_MAP_POOLPAGE(VM_PAGE_TO_PHYS(pg));
257 KASSERT(kva != 0);
258 ci->ci_nmi_stack = (void *)(kva + PAGE_SIZE - sizeof(struct kernframe));
259 #endif
260
261 #if NWDOG > 0
262 cpu->cpu_wdog_sih = softint_establish(SOFTINT_CLOCK|SOFTINT_MPSAFE,
263 wdog_cpunode_poke, cpu);
264 KASSERT(cpu->cpu_wdog_sih != NULL);
265 #endif
266
267 aprint_normal(": %lu.%02luMHz\n",
268 (ci->ci_cpu_freq + 5000) / 1000000,
269 ((ci->ci_cpu_freq + 5000) % 1000000) / 10000);
270 aprint_debug_dev(self, "hz cycles = %lu, delay divisor = %lu\n",
271 ci->ci_cycles_per_hz, ci->ci_divisor_delay);
272
273 if (CPU_IS_PRIMARY(ci)) {
274 aprint_normal("%s: ", device_xname(self));
275 cpu_identify(self);
276 }
277 cpu_attach_common(self, ci);
278 #ifdef MULTIPROCESSOR
279 KASSERT(cpuid_infos[ci->ci_cpuid] == ci);
280 #endif
281 }
282
283 void
284 cpu_cpunode_attach(device_t parent, device_t self, void *aux)
285 {
286 struct cpunode_attach_args * const cnaa = aux;
287 const int cpunum = cnaa->cnaa_cpunum;
288
289 if (cpunum == 0) {
290 cpu_cpunode_attach_common(self, curcpu());
291 #ifdef MULTIPROCESSOR
292 mips_locoresw.lsw_cpu_init = octeon_cpu_init;
293 mips_locoresw.lsw_cpu_run = octeon_cpu_run;
294 #endif
295 return;
296 }
297 #ifdef MULTIPROCESSOR
298 if ((boothowto & RB_MD1) != 0) {
299 aprint_naive("\n");
300 aprint_normal(": multiprocessor boot disabled\n");
301 return;
302 }
303
304 if (!kcpuset_isset(cpus_booted, cpunum)) {
305 aprint_naive(" disabled\n");
306 aprint_normal(" disabled (unresponsive)\n");
307 return;
308 }
309 struct cpu_info * const ci = cpu_info_alloc(NULL, cpunum, 0, cpunum, 0);
310
311 ci->ci_softc = &octeon_cpu_softc[cpunum];
312 ci->ci_softc->cpu_ci = ci;
313
314 cpu_cpunode_attach_common(self, ci);
315
316 KASSERT(ci->ci_data.cpu_idlelwp != NULL);
317 for (int i = 0; i < 100 && !kcpuset_isset(cpus_hatched, cpunum); i++) {
318 delay(10000);
319 }
320 if (!kcpuset_isset(cpus_hatched, cpunum)) {
321 #ifdef DDB
322 aprint_verbose_dev(self, "hatch failed ci=%p flags=%#lx\n", ci, ci->ci_flags);
323 cpu_Debugger();
324 #endif
325 panic("%s failed to hatch: ci=%p flags=%#lx",
326 cpu_name(ci), ci, ci->ci_flags);
327 }
328 #else
329 aprint_naive(": disabled\n");
330 aprint_normal(": disabled (uniprocessor kernel)\n");
331 #endif
332 }
333
334 #if NWDOG > 0
335 struct wdog_softc {
336 struct sysmon_wdog sc_smw;
337 device_t sc_dev;
338 u_int sc_wdog_period;
339 bool sc_wdog_armed;
340 };
341
342 #ifndef OCTEON_WDOG_PERIOD_DEFAULT
343 #define OCTEON_WDOG_PERIOD_DEFAULT 4
344 #endif
345
346 static int wdog_cpunode_match(device_t, cfdata_t, void *);
347 static void wdog_cpunode_attach(device_t, device_t, void *);
348
349 CFATTACH_DECL_NEW(wdog_cpunode, sizeof(struct wdog_softc),
350 wdog_cpunode_match, wdog_cpunode_attach, NULL, NULL);
351
352 static int
353 wdog_cpunode_setmode(struct sysmon_wdog *smw)
354 {
355 struct wdog_softc * const sc = smw->smw_cookie;
356
357 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
358 if (sc->sc_wdog_armed) {
359 CPU_INFO_ITERATOR cii;
360 struct cpu_info *ci;
361 for (CPU_INFO_FOREACH(cii, ci)) {
362 struct cpu_softc * const cpu = ci->ci_softc;
363 uint64_t wdog = mips3_ld(cpu->cpu_wdog);
364 wdog &= ~CIU_WDOGX_MODE;
365 mips3_sd(cpu->cpu_pp_poke, wdog);
366 aprint_verbose_dev(sc->sc_dev,
367 "%s: disable wdog=%#"PRIx64"\n",
368 cpu_name(ci), wdog);
369 mips3_sd(cpu->cpu_wdog, wdog);
370 mips3_sd(cpu->cpu_pp_poke, wdog);
371 }
372 sc->sc_wdog_armed = false;
373 }
374 } else if (!sc->sc_wdog_armed) {
375 kpreempt_disable();
376 struct cpu_info *ci = curcpu();
377 if (smw->smw_period == WDOG_PERIOD_DEFAULT) {
378 smw->smw_period = OCTEON_WDOG_PERIOD_DEFAULT;
379 }
380 uint64_t wdog_len = smw->smw_period * ci->ci_cpu_freq;
381 //
382 // This wdog is a 24-bit counter that decrements every 256
383 // cycles. This is then a 32-bit counter so as long wdog_len
384 // doesn't overflow a 32-bit value, we are fine. We write the
385 // 16-bits of the 32-bit period.
386 if ((wdog_len >> 32) != 0) {
387 kpreempt_enable();
388 return EINVAL;
389 }
390 sc->sc_wdog_period = smw->smw_period;
391 CPU_INFO_ITERATOR cii;
392 for (CPU_INFO_FOREACH(cii, ci)) {
393 struct cpu_softc * const cpu = ci->ci_softc;
394 uint64_t wdog = mips3_ld(cpu->cpu_wdog);
395 wdog &= ~(CIU_WDOGX_MODE|CIU_WDOGX_LEN);
396 wdog |= __SHIFTIN(3, CIU_WDOGX_MODE);
397 wdog |= __SHIFTIN(wdog_len >> 16, CIU_WDOGX_LEN);
398 aprint_verbose_dev(sc->sc_dev,
399 "%s: enable wdog=%#"PRIx64" (%#"PRIx64")\n",
400 cpu_name(ci), wdog, wdog_len);
401 mips3_sd(cpu->cpu_wdog, wdog);
402 }
403 sc->sc_wdog_armed = true;
404 kpreempt_enable();
405 }
406 return 0;
407 }
408
409 static void
410 wdog_cpunode_poke(void *arg)
411 {
412 struct cpu_softc *cpu = arg;
413
414 mips3_sd(cpu->cpu_pp_poke, 0);
415 }
416
417 static int
418 wdog_cpunode_tickle(struct sysmon_wdog *smw)
419 {
420
421 wdog_cpunode_poke(curcpu()->ci_softc);
422 #ifdef MULTIPROCESSOR
423 // We need to send IPIs to the other CPUs to poke their wdog.
424 cpu_send_ipi(NULL, IPI_WDOG);
425 #endif
426 return 0;
427 }
428
429 int
430 wdog_cpunode_match(device_t parent, cfdata_t cf, void *aux)
431 {
432 struct cpunode_softc * const sc = device_private(parent);
433 struct cpunode_attach_args * const cnaa = aux;
434 const int cpunum = cf->cf_loc[CPUNODECF_CORE];
435
436 return sc->sc_wdog_dev == NULL
437 && strcmp(cnaa->cnaa_name, cf->cf_name) == 0
438 && cpunum == CPUNODECF_CORE_DEFAULT;
439 }
440
441 void
442 wdog_cpunode_attach(device_t parent, device_t self, void *aux)
443 {
444 struct cpunode_softc * const psc = device_private(parent);
445 struct wdog_softc * const sc = device_private(self);
446 cfdata_t const cf = device_cfdata(self);
447
448 psc->sc_wdog_dev = self;
449
450 sc->sc_dev = self;
451 sc->sc_smw.smw_name = device_xname(self);
452 sc->sc_smw.smw_cookie = sc;
453 sc->sc_smw.smw_setmode = wdog_cpunode_setmode;
454 sc->sc_smw.smw_tickle = wdog_cpunode_tickle;
455 sc->sc_smw.smw_period = OCTEON_WDOG_PERIOD_DEFAULT;
456 sc->sc_wdog_period = sc->sc_smw.smw_period;
457
458 /*
459 * We need one softint per cpu. It's to tickle the softints on
460 * other CPUs.
461 */
462 #if 0 /* XXX unused? */
463 CPU_INFO_ITERATOR cii;
464 struct cpu_info *ci;
465 for (CPU_INFO_FOREACH(cii, ci)) {
466 }
467 #endif
468
469 aprint_normal(": default period is %u second%s\n",
470 sc->sc_wdog_period, sc->sc_wdog_period == 1 ? "" : "s");
471
472 if (sysmon_wdog_register(&sc->sc_smw) != 0) {
473 aprint_error_dev(self, "unable to register with sysmon\n");
474 return;
475 }
476
477 if (cf->cf_flags & 1) {
478 int error = sysmon_wdog_setmode(&sc->sc_smw, WDOG_MODE_KTICKLE,
479 sc->sc_wdog_period);
480 if (error)
481 aprint_error_dev(self,
482 "failed to start kernel tickler: %d\n", error);
483 }
484 }
485 #endif /* NWDOG > 0 */
486