1 /* $OpenBSD: cpu.c,v 1.84 2024/08/17 13:35:01 mpi Exp $ */
2
3 /*
4 * Copyright (c) 1997-2004 Opsycon AB (www.opsycon.se)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/proc.h>
32 #include <sys/atomic.h>
33 #include <sys/device.h>
34 #include <sys/malloc.h>
35
36 #include <uvm/uvm_extern.h>
37
38 #include <machine/cpu.h>
39 #include <mips64/mips_cpu.h>
40 #include <machine/autoconf.h>
41
42 int cpumatch(struct device *, void *, void *);
43 void cpuattach(struct device *, struct device *, void *);
44
45 struct cpu_info cpu_info_primary;
46 struct cpu_info *cpu_info_list = &cpu_info_primary;
47 struct cpu_info *cpu_info_secondaries;
48
49 extern void cpu_idle_cycle_nop(void);
50 extern void cpu_idle_cycle_wait(void);
51 void (*cpu_idle_cycle_func)(void) = cpu_idle_cycle_nop;
52
53 vaddr_t cache_valias_mask;
54 int cpu_has_synced_cp0_count;
55 int cpu_has_userlocal;
56
57 const struct cfattach cpu_ca = {
58 sizeof(struct device), cpumatch, cpuattach
59 };
60 struct cfdriver cpu_cd = {
61 NULL, "cpu", DV_DULL,
62 };
63
64 int
cpumatch(struct device * parent,void * match,void * aux)65 cpumatch(struct device *parent, void *match, void *aux)
66 {
67 struct cpu_attach_args *caa = aux;
68
69 /* make sure that we're looking for a CPU. */
70 if (strcmp(caa->caa_maa.maa_name, cpu_cd.cd_name) != 0)
71 return 0;
72
73 return 20; /* Make CPU probe first */
74 }
75
76 void
cpuattach(struct device * parent,struct device * dev,void * aux)77 cpuattach(struct device *parent, struct device *dev, void *aux)
78 {
79 struct cpu_attach_args *caa = aux;
80 struct cpu_hwinfo *ch = caa->caa_hw;
81 struct cpu_info *ci;
82 int cpuno = dev->dv_unit;
83 int fptype, vers_maj, vers_min;
84 int displayver;
85
86 #ifdef MULTIPROCESSOR
87 if (cpuno == 0) {
88 ci = &cpu_info_primary;
89 ci->ci_flags |= CPUF_RUNNING | CPUF_PRESENT | CPUF_PRIMARY;
90 if (ncpusfound > 1) {
91 cpu_info_secondaries = (struct cpu_info *)
92 alloc_contiguous_pages(sizeof(struct cpu_info) *
93 (ncpusfound - 1));
94 if (cpu_info_secondaries == NULL)
95 panic("unable to allocate cpu_info");
96 }
97 } else {
98 ci = &cpu_info_secondaries[cpuno - 1];
99 ci->ci_next = cpu_info_list->ci_next;
100 cpu_info_list->ci_next = ci;
101 ci->ci_flags |= CPUF_PRESENT;
102 }
103 #else
104 ci = &cpu_info_primary;
105 #endif
106 ci->ci_self = ci;
107 ci->ci_cpuid = cpuno;
108 ci->ci_dev = dev;
109 bcopy(ch, &ci->ci_hw, sizeof(struct cpu_hwinfo));
110 #ifdef MULTIPROCESSOR
111 /*
112 * When attaching secondary processors, cache information is not
113 * available yet. Copy the cache information from the primary cpu
114 * instead.
115 * XXX The MP boot sequence needs to be reworked to avoid this.
116 */
117 if (!CPU_IS_PRIMARY(ci)) {
118 ci->ci_l1inst = cpu_info_primary.ci_l1inst;
119 ci->ci_l1data = cpu_info_primary.ci_l1data;
120 ci->ci_l2 = cpu_info_primary.ci_l2;
121 ci->ci_l3 = cpu_info_primary.ci_l3;
122 }
123 #endif
124
125 printf(": ");
126
127 displayver = 1;
128 fptype = (ch->c1prid >> 8) & 0xff;
129 vers_maj = (ch->c0prid >> 4) & 0x0f;
130 vers_min = ch->c0prid & 0x0f;
131 switch (ch->type) {
132 case MIPS_LOONGSON2:
133 switch (ch->c0prid & 0xff) {
134 case 0x00:
135 case 0x02:
136 case 0x03:
137 printf("STC Loongson2%c CPU", 'C' + vers_min);
138 break;
139 case 0x05:
140 printf("STC Loongson3%c CPU", 'A' + vers_min - 5);
141 break;
142 case 0x08:
143 printf("STC Loongson3A2000/3B2000 CPU");
144 break;
145 default:
146 printf("Unknown STC Loongson CPU type (%02x)",
147 ch->c0prid & 0xff);
148 break;
149 }
150 displayver = 0;
151 break;
152 case MIPS_CN50XX:
153 printf("CN50xx CPU");
154 fptype = MIPS_SOFT;
155 break;
156 case MIPS_CN61XX:
157 if (ci->ci_l2.size < 1024 * 1024)
158 printf("CN60xx CPU");
159 else
160 printf("CN61xx CPU");
161 fptype = MIPS_SOFT;
162 break;
163 case MIPS_CN63XX:
164 printf("CN62xx/CN63xx CPU");
165 fptype = MIPS_SOFT;
166 break;
167 case MIPS_CN66XX:
168 printf("CN66xx CPU");
169 fptype = MIPS_SOFT;
170 break;
171 case MIPS_CN68XX:
172 printf("CN68xx CPU");
173 fptype = MIPS_SOFT;
174 break;
175 case MIPS_CN71XX:
176 printf("CN70xx/CN71xx CPU");
177 break;
178 case MIPS_CN73XX:
179 printf("CN72xx/CN73xx CPU");
180 break;
181 case MIPS_CN78XX:
182 printf("CN76xx/CN77xx/CN78xx CPU");
183 break;
184 default:
185 printf("Unknown CPU type (0x%x)", ch->type);
186 break;
187 }
188 if (displayver != 0)
189 printf(" rev %d.%d", vers_maj, vers_min);
190 printf(" %d MHz, ", ch->clock / 1000000);
191
192 displayver = 1;
193 vers_maj = (ch->c1prid >> 4) & 0x0f;
194 vers_min = ch->c1prid & 0x0f;
195 switch (fptype) {
196 case MIPS_SOFT:
197 #ifdef FPUEMUL
198 printf("Software FP emulation");
199 #else
200 printf("no FPU");
201 #endif
202 displayver = 0;
203 break;
204 case MIPS_LOONGSON2:
205 switch (ch->c1prid & 0xff) {
206 case 0x00:
207 case 0x02:
208 case 0x03:
209 printf("STC Loongson2%c FPU", 'C' + vers_min);
210 break;
211 case 0x05:
212 printf("STC Loongson3%c FPU", 'A' + vers_min - 5);
213 break;
214 case 0x08:
215 printf("STC Loongson3A2000/3B2000 FPU");
216 break;
217 default:
218 printf("Unknown STC Loongson FPU type (%02x)",
219 ch->c1prid & 0xff);
220 break;
221 }
222 displayver = 0;
223 break;
224 case MIPS_CN71XX:
225 printf("CN70xx/CN71xx FPU");
226 break;
227 case MIPS_CN73XX:
228 printf("CN72xx/CN73xx FPU");
229 break;
230 case MIPS_CN78XX:
231 printf("CN76xx/CN77xx/CN78xx FPU");
232 break;
233 default:
234 printf("Unknown FPU type (0x%x)", fptype);
235 break;
236 }
237 if (displayver != 0)
238 printf(" rev %d.%d", vers_maj, vers_min);
239 printf("\n");
240
241 if (ci->ci_l1inst.sets == ci->ci_l1data.sets) {
242 printf("cpu%d: cache L1-I %dKB D %dKB ", cpuno,
243 ci->ci_l1inst.size / 1024, ci->ci_l1data.size / 1024);
244 if (ci->ci_l1inst.sets == 1)
245 printf("direct");
246 else
247 printf("%d way", ci->ci_l1inst.sets);
248 } else {
249 printf("cpu%d: cache L1-I %dKB ", cpuno,
250 ci->ci_l1inst.size / 1024);
251 if (ci->ci_l1inst.sets == 1)
252 printf("direct");
253 else
254 printf("%d way", ci->ci_l1inst.sets);
255 printf(" D %dKB ", ci->ci_l1data.size / 1024);
256 if (ci->ci_l1data.sets == 1)
257 printf("direct");
258 else
259 printf("%d way", ci->ci_l1data.sets);
260 }
261
262 if (ci->ci_l2.size != 0) {
263 printf(", L2 %dKB ", ci->ci_l2.size / 1024);
264 if (ci->ci_l2.sets == 1)
265 printf("direct");
266 else
267 printf("%d way", ci->ci_l2.sets);
268 }
269 if (ci->ci_l3.size != 0) {
270 printf(", L3 %dKB ", ci->ci_l3.size / 1024);
271 if (ci->ci_l3.sets == 1)
272 printf("direct");
273 else
274 printf("%d way", ci->ci_l3.sets);
275 }
276
277 if (cpuno == 0) {
278 switch (ch->type) {
279 case MIPS_CN50XX:
280 case MIPS_CN61XX:
281 case MIPS_CN71XX:
282 case MIPS_CN73XX:
283 cpu_idle_cycle_func = cpu_idle_cycle_wait;
284 break;
285 }
286 }
287
288 printf("\n");
289
290 #ifdef DEBUG
291 printf("cpu%d: L1 set size %d:%d\n", cpuno,
292 ci->ci_l1inst.setsize, ci->ci_l1data.setsize);
293 printf("cpu%d: L1 line size %d:%d\n", cpuno,
294 ci->ci_l1inst.linesize, ci->ci_l1data.linesize);
295 printf("cpu%d: L2 line size %d\n", cpuno, ci->ci_l2.linesize);
296 printf("cpu%d: cache configuration %x\n",
297 cpuno, ci->ci_cacheconfiguration);
298 printf("cpu%d: virtual alias mask 0x%lx\n", cpuno, cache_valias_mask);
299 printf("cpu%d: config register %016lx, status register %016lx\n",
300 cpuno, cp0_get_config(), getsr());
301 #endif
302 }
303
304 void
cpu_switchto(struct proc * oldproc,struct proc * newproc)305 cpu_switchto(struct proc *oldproc, struct proc *newproc)
306 {
307 #ifdef MULTIPROCESSOR
308 struct cpu_info *ci = curcpu();
309 if (ci->ci_fpuproc)
310 save_fpu();
311 #endif
312
313 cpu_switchto_asm(oldproc, newproc);
314 }
315
316 void
enable_fpu(struct proc * p)317 enable_fpu(struct proc *p)
318 {
319 struct cpu_info *ci = curcpu();
320
321 if (!CPU_HAS_FPU(ci))
322 return;
323
324 if (p->p_md.md_regs->sr & SR_FR_32)
325 MipsSwitchFPState(ci->ci_fpuproc, p->p_md.md_regs);
326 else
327 MipsSwitchFPState16(ci->ci_fpuproc, p->p_md.md_regs);
328 atomic_inc_int(&uvmexp.fpswtch);
329
330 ci->ci_fpuproc = p;
331 p->p_md.md_regs->sr |= SR_COP_1_BIT;
332 p->p_md.md_flags |= MDP_FPUSED;
333 }
334
335 void
save_fpu(void)336 save_fpu(void)
337 {
338 struct cpu_info *ci = curcpu();
339 struct proc *p;
340
341 if (!CPU_HAS_FPU(ci))
342 return;
343
344 KASSERT(ci->ci_fpuproc);
345 p = ci->ci_fpuproc;
346 if (p->p_md.md_regs->sr & SR_FR_32)
347 MipsSaveCurFPState(p);
348 else
349 MipsSaveCurFPState16(p);
350 }
351
352 void
need_resched(struct cpu_info * ci)353 need_resched(struct cpu_info *ci)
354 {
355 ci->ci_want_resched = 1;
356
357 if (ci->ci_curproc != NULL) {
358 /*
359 * Ensure that preceding stores are visible to other CPUs
360 * before setting the AST flag.
361 */
362 membar_producer();
363
364 aston(ci->ci_curproc);
365 cpu_unidle(ci);
366 }
367 }
368
369 #ifdef MULTIPROCESSOR
370 struct cpu_info *
get_cpu_info(int cpuno)371 get_cpu_info(int cpuno)
372 {
373 struct cpu_info *ci;
374 CPU_INFO_ITERATOR cii;
375
376 CPU_INFO_FOREACH(cii, ci) {
377 if (ci->ci_cpuid == cpuno)
378 return ci;
379 }
380 return NULL;
381 }
382
383 void
cpu_boot_secondary_processors(void)384 cpu_boot_secondary_processors(void)
385 {
386 struct cpu_info *ci;
387 CPU_INFO_ITERATOR cii;
388
389 mips64_ipi_init();
390
391 CPU_INFO_FOREACH(cii, ci) {
392 if ((ci->ci_flags & CPUF_PRESENT) == 0)
393 continue;
394 if (CPU_IS_PRIMARY(ci))
395 continue;
396
397 ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;
398 clockqueue_init(&ci->ci_queue);
399 sched_init_cpu(ci);
400 cpu_boot_secondary(ci);
401 }
402 }
403
404 void
cpu_unidle(struct cpu_info * ci)405 cpu_unidle(struct cpu_info *ci)
406 {
407 if (ci != curcpu())
408 mips64_send_ipi(ci->ci_cpuid, MIPS64_IPI_NOP);
409 }
410
411 vaddr_t
alloc_contiguous_pages(size_t sz)412 alloc_contiguous_pages(size_t sz)
413 {
414 const struct kmem_pa_mode kp_contig = {
415 .kp_constraint = &no_constraint,
416 .kp_maxseg = 1,
417 .kp_zero = 1
418 };
419 paddr_t pa;
420
421 pa = (paddr_t)km_alloc(round_page(sz), &kv_any, &kp_contig, &kd_nowait);
422 if (pa == 0)
423 return 0;
424
425 return PHYS_TO_XKPHYS(pa, CCA_CACHED);
426 }
427 #endif
428