xref: /openbsd-src/sys/arch/mips64/mips64/mips64_machdep.c (revision 11d1f9b2143325c151ff5de652177687515961c9)
1 /*	$OpenBSD: mips64_machdep.c,v 1.43 2023/08/23 01:55:47 cheloha Exp $ */
2 
3 /*
4  * Copyright (c) 2009, 2010, 2012 Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * Copyright (c) 2001-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
24  * 1. Redistributions of source code must retain the above copyright
25  *    notice, this list of conditions and the following disclaimer.
26  * 2. Redistributions in binary form must reproduce the above copyright
27  *    notice, this list of conditions and the following disclaimer in the
28  *    documentation and/or other materials provided with the distribution.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
31  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
34  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  */
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/clockintr.h>
48 #include <sys/proc.h>
49 #include <sys/exec.h>
50 #include <sys/sysctl.h>
51 #include <sys/timetc.h>
52 
53 #include <machine/autoconf.h>
54 #include <machine/cpu.h>
55 #include <mips64/cache.h>
56 #include <mips64/mips_cpu.h>
57 #include <mips64/mips_opcode.h>
58 
59 #include <uvm/uvm_extern.h>
60 
61 #include <dev/clock_subr.h>
62 
63 /*
64  * Build a tlb trampoline
65  */
66 void
build_trampoline(vaddr_t addr,vaddr_t dest)67 build_trampoline(vaddr_t addr, vaddr_t dest)
68 {
69 	const uint32_t insns[] = {
70 		0x3c1a0000,	/* lui k0, imm16 */
71 		0x675a0000,	/* daddiu k0, k0, imm16 */
72 		0x001ad438,	/* dsll k0, k0, 0x10 */
73 		0x675a0000,	/* daddiu k0, k0, imm16 */
74 		0x001ad438,	/* dsll k0, k0, 0x10 */
75 		0x675a0000,	/* daddiu k0, k0, imm16 */
76 		0x03400008,	/* jr k0 */
77 		0x00000000	/* nop */
78 	};
79 	uint32_t *dst = (uint32_t *)addr;
80 	const uint32_t *src = insns;
81 	uint32_t a, b, c, d;
82 
83 	/*
84 	 * Decompose the handler address in the four components which,
85 	 * added with sign extension, will produce the correct address.
86 	 */
87 	d = dest & 0xffff;
88 	dest >>= 16;
89 	if (d & 0x8000)
90 		dest++;
91 	c = dest & 0xffff;
92 	dest >>= 16;
93 	if (c & 0x8000)
94 		dest++;
95 	b = dest & 0xffff;
96 	dest >>= 16;
97 	if (b & 0x8000)
98 		dest++;
99 	a = dest & 0xffff;
100 
101 	/*
102 	 * Build the trampoline, skipping noop computations.
103 	 */
104 	*dst++ = *src++ | a;
105 	if (b != 0)
106 		*dst++ = *src++ | b;
107 	else
108 		src++;
109 	*dst++ = *src++;
110 	if (c != 0)
111 		*dst++ = *src++ | c;
112 	else
113 		src++;
114 	*dst++ = *src++;
115 	if (d != 0)
116 		*dst++ = *src++ | d;
117 	else
118 		src++;
119 	*dst++ = *src++;
120 	*dst++ = *src++;
121 
122 	/*
123 	 * Note that we keep the delay slot instruction a nop, instead
124 	 * of branching to the second instruction of the handler and
125 	 * having its first instruction in the delay slot, so that the
126 	 * tlb handler is free to use k0 immediately.
127 	 */
128 }
129 
130 /*
131  * Prototype status registers value for userland processes.
132  */
133 register_t protosr = SR_FR_32 | SR_XX | SR_UX | SR_KSU_USER | SR_EXL |
134     SR_KX | SR_INT_ENAB;
135 
136 /*
137  * Set registers on exec for native exec format. For o64/64.
138  */
139 void
setregs(struct proc * p,struct exec_package * pack,u_long stack,struct ps_strings * arginfo)140 setregs(struct proc *p, struct exec_package *pack, u_long stack,
141     struct ps_strings *arginfo)
142 {
143 	struct cpu_info *ci = curcpu();
144 	struct trapframe *tf = p->p_md.md_regs;
145 
146 	memset(tf, 0, sizeof *tf);
147 	tf->sp = stack;
148 	tf->pc = pack->ep_entry & ~3;
149 	tf->t9 = pack->ep_entry & ~3; /* abicall req */
150 	tf->sr = protosr | (idle_mask & SR_INT_MASK);
151 
152 	if (CPU_HAS_FPU(ci))
153 		p->p_md.md_flags &= ~MDP_FPUSED;
154 	if (ci->ci_fpuproc == p)
155 		ci->ci_fpuproc = NULL;
156 }
157 
158 int
exec_md_map(struct proc * p,struct exec_package * pack)159 exec_md_map(struct proc *p, struct exec_package *pack)
160 {
161 #ifdef FPUEMUL
162 	struct cpu_info *ci = curcpu();
163 	vaddr_t va;
164 	int rc;
165 
166 	if (CPU_HAS_FPU(ci))
167 		return 0;
168 
169 	/*
170 	 * If we are running with FPU instruction emulation, we need
171 	 * to allocate a special page in the process' address space,
172 	 * in order to be able to emulate delay slot instructions of
173 	 * successful conditional branches.
174 	 */
175 
176 	va = 0;
177 	rc = uvm_map(&p->p_vmspace->vm_map, &va, PAGE_SIZE, NULL,
178 	    UVM_UNKNOWN_OFFSET, 0,
179 	    UVM_MAPFLAG(PROT_NONE, PROT_MASK, MAP_INHERIT_COPY,
180 	      MADV_NORMAL, UVM_FLAG_COPYONW));
181 	if (rc != 0)
182 		return rc;
183 #ifdef DEBUG
184 	printf("%s: p %p fppgva %p\n", __func__, p, (void *)va);
185 #endif
186 	p->p_md.md_fppgva = va;
187 #endif
188 
189 	return 0;
190 }
191 
192 /*
193  * Initial TLB setup for the current processor.
194  */
195 void
tlb_init(unsigned int tlbsize)196 tlb_init(unsigned int tlbsize)
197 {
198 	tlb_set_page_mask(TLB_PAGE_MASK);
199 	tlb_set_wired(0);
200 	tlb_flush(tlbsize);
201 #if UPAGES > 1
202 	tlb_set_wired(UPAGES / 2);
203 #endif
204 }
205 
206 /*
207  * Handle an ASID wrap.
208  */
209 void
tlb_asid_wrap(struct cpu_info * ci)210 tlb_asid_wrap(struct cpu_info *ci)
211 {
212 	tlb_flush(ci->ci_hw.tlbsize);
213 #if defined(CPU_OCTEON)
214 	Mips_InvalidateICache(ci, 0, ci->ci_l1inst.size);
215 #endif
216 }
217 
218 /*
219  *	Mips machine independent clock routines.
220  */
221 
222 void (*md_initclock)(void);
223 void (*md_startclock)(struct cpu_info *);
224 void (*md_triggerclock)(void);
225 
226 extern todr_chip_handle_t todr_handle;
227 
228 /*
229  * Wait "n" microseconds.
230  */
231 void
delay(int n)232 delay(int n)
233 {
234 	int dly;
235 	int p, c;
236 	struct cpu_info *ci = curcpu();
237 	uint32_t delayconst;
238 
239 	delayconst = ci->ci_delayconst;
240 	if (delayconst == 0)
241 		delayconst = bootcpu_hwinfo.clock / CP0_CYCLE_DIVIDER;
242 	p = cp0_get_count();
243 	dly = (delayconst / 1000000) * n;
244 	while (dly > 0) {
245 		c = cp0_get_count();
246 		dly -= c - p;
247 		p = c;
248 	}
249 }
250 
251 u_int cp0_get_timecount(struct timecounter *);
252 
253 struct timecounter cp0_timecounter = {
254 	.tc_get_timecount = cp0_get_timecount,
255 	.tc_counter_mask = 0xffffffff,
256 	.tc_frequency = 0,
257 	.tc_name = "CP0",
258 	.tc_quality = 0,
259 	.tc_priv = NULL,
260 	.tc_user = 0,
261 };
262 
263 u_int
cp0_get_timecount(struct timecounter * tc)264 cp0_get_timecount(struct timecounter *tc)
265 {
266 	return (cp0_get_count());
267 }
268 
269 /*
270  * Calibrate cpu internal counter against the TOD clock if available.
271  */
272 void
cp0_calibrate(struct cpu_info * ci)273 cp0_calibrate(struct cpu_info *ci)
274 {
275 	struct timeval rtctime;
276 	u_int first_cp0, second_cp0, cycles_per_sec;
277 	int first_sec;
278 
279 	if (todr_handle == NULL)
280 		return;
281 
282 	if (todr_gettime(todr_handle, &rtctime) != 0)
283 		return;
284 	first_sec = rtctime.tv_sec;
285 
286 	/* Let the clock tick one second. */
287 	do {
288 		first_cp0 = cp0_get_count();
289 		if (todr_gettime(todr_handle, &rtctime) != 0)
290 			return;
291 	} while (rtctime.tv_sec == first_sec);
292 	first_sec = rtctime.tv_sec;
293 	/* Let the clock tick one more second. */
294 	do {
295 		second_cp0 = cp0_get_count();
296 		if (todr_gettime(todr_handle, &rtctime) != 0)
297 			return;
298 	} while (rtctime.tv_sec == first_sec);
299 
300 	cycles_per_sec = second_cp0 - first_cp0;
301 	ci->ci_hw.clock = cycles_per_sec * CP0_CYCLE_DIVIDER;
302 	ci->ci_delayconst = cycles_per_sec;
303 }
304 
305 /*
306  * Prepare to start the clock interrupt dispatch cycle.
307  */
308 void
cpu_initclocks(void)309 cpu_initclocks(void)
310 {
311 	struct cpu_info *ci = curcpu();
312 
313 	tick = 1000000 / hz;	/* number of micro-seconds between interrupts */
314 	tick_nsec = 1000000000 / hz;
315 
316 	cp0_calibrate(ci);
317 
318 #ifndef MULTIPROCESSOR
319 	cpu_has_synced_cp0_count = 1;
320 #endif
321 	if (cpu_setperf == NULL && cpu_has_synced_cp0_count) {
322 		cp0_timecounter.tc_frequency =
323 		    (uint64_t)ci->ci_hw.clock / CP0_CYCLE_DIVIDER;
324 		tc_init(&cp0_timecounter);
325 	}
326 
327 	if (md_initclock != NULL)
328 		(*md_initclock)();
329 }
330 
331 void
cpu_startclock(void)332 cpu_startclock(void)
333 {
334 #ifdef DIAGNOSTIC
335 	if (md_startclock == NULL)
336 		panic("no clock");
337 #endif
338 	(*md_startclock)(curcpu());
339 }
340 
341 void
setstatclockrate(int newhz)342 setstatclockrate(int newhz)
343 {
344 }
345 
346 /*
347  * Decode instruction and figure out type.
348  */
349 int
classify_insn(uint32_t insn)350 classify_insn(uint32_t insn)
351 {
352 	InstFmt	inst;
353 
354 	inst.word = insn;
355 	switch (inst.JType.op) {
356 	case OP_SPECIAL:
357 		switch (inst.RType.func) {
358 		case OP_JR:
359 			return INSNCLASS_BRANCH;
360 		case OP_JALR:
361 			return INSNCLASS_CALL;
362 		}
363 		break;
364 
365 	case OP_BCOND:
366 		switch (inst.IType.rt) {
367 		case OP_BLTZ:
368 		case OP_BLTZL:
369 		case OP_BGEZ:
370 		case OP_BGEZL:
371 			return INSNCLASS_BRANCH;
372 		case OP_BLTZAL:
373 		case OP_BLTZALL:
374 		case OP_BGEZAL:
375 		case OP_BGEZALL:
376 			return INSNCLASS_CALL;
377 		}
378 		break;
379 
380 	case OP_JAL:
381 		return INSNCLASS_CALL;
382 
383 	case OP_J:
384 	case OP_BEQ:
385 	case OP_BEQL:
386 	case OP_BNE:
387 	case OP_BNEL:
388 	case OP_BLEZ:
389 	case OP_BLEZL:
390 	case OP_BGTZ:
391 	case OP_BGTZL:
392 		return INSNCLASS_BRANCH;
393 
394 	case OP_COP1:
395 		switch (inst.RType.rs) {
396 		case OP_BC:
397 			return INSNCLASS_BRANCH;
398 		}
399 		break;
400 	}
401 
402 	return INSNCLASS_NEUTRAL;
403 }
404 
405 /*
406  * Smash the startup code. There is no way to really unmap it
407  * because the kernel runs in the kseg0 or xkphys space.
408  */
409 void
unmap_startup(void)410 unmap_startup(void)
411 {
412 	extern uint32_t kernel_text[], endboot[];
413 	uint32_t *word = kernel_text;
414 
415 	while (word < endboot)
416 		*word++ = 0x00000034u;	/* TEQ zero, zero */
417 }
418