1/*	$OpenBSD: cpu.c,v 1.83 2023/06/15 22:18:07 cheloha 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
42int	cpumatch(struct device *, void *, void *);
43void	cpuattach(struct device *, struct device *, void *);
44
45struct cpu_info cpu_info_primary;
46struct cpu_info *cpu_info_list = &cpu_info_primary;
47struct cpu_info *cpu_info_secondaries;
48
49extern void cpu_idle_cycle_nop(void);
50extern void cpu_idle_cycle_wait(void);
51void (*cpu_idle_cycle_func)(void) = cpu_idle_cycle_nop;
52
53vaddr_t	cache_valias_mask;
54int	cpu_has_synced_cp0_count;
55int	cpu_has_userlocal;
56
57const struct cfattach cpu_ca = {
58	sizeof(struct device), cpumatch, cpuattach
59};
60struct cfdriver cpu_cd = {
61	NULL, "cpu", DV_DULL,
62};
63
64int
65cpumatch(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
76void
77cpuattach(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
304void
305cpu_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
316void
317enable_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
335void
336save_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
352void
353need_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
370struct cpu_info *
371get_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
383void
384cpu_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
404void
405cpu_unidle(struct cpu_info *ci)
406{
407	if (ci != curcpu())
408		mips64_send_ipi(ci->ci_cpuid, MIPS64_IPI_NOP);
409}
410
411vaddr_t
412alloc_contiguous_pages(size_t size)
413{
414	struct pglist mlist;
415	struct vm_page *m;
416	int error;
417	paddr_t pa;
418
419	TAILQ_INIT(&mlist);
420	error = uvm_pglistalloc(round_page(size), 0, (paddr_t)-1, 0, 0,
421		&mlist, 1, UVM_PLA_NOWAIT | UVM_PLA_ZERO);
422	if (error)
423		return 0;
424	m = TAILQ_FIRST(&mlist);
425	pa = VM_PAGE_TO_PHYS(m);
426
427	return PHYS_TO_XKPHYS(pa, CCA_CACHED);
428}
429#endif
430