xref: /openbsd/sys/arch/mips64/mips64/cpu.c (revision a5db6291)
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