xref: /openbsd/sys/arch/m88k/m88k/m88k_machdep.c (revision f943bcf5)
1 /*	$OpenBSD: m88k_machdep.c,v 1.73 2024/05/28 09:27:54 claudio Exp $	*/
2 /*
3  * Copyright (c) 1998, 1999, 2000, 2001 Steve Murphree, Jr.
4  * Copyright (c) 1996 Nivas Madhur
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Nivas Madhur.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 /*
34  * Mach Operating System
35  * Copyright (c) 1993-1991 Carnegie Mellon University
36  * Copyright (c) 1991 OMRON Corporation
37  * All Rights Reserved.
38  *
39  * Permission to use, copy, modify and distribute this software and its
40  * documentation is hereby granted, provided that both the copyright
41  * notice and this permission notice appear in all copies of the
42  * software, derivative works or modified versions, and any portions
43  * thereof, and that both notices appear in supporting documentation.
44  *
45  */
46 
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/proc.h>
51 #include <sys/user.h>
52 #include <sys/msgbuf.h>
53 #include <sys/exec.h>
54 #include <sys/errno.h>
55 #include <sys/malloc.h>
56 #ifdef MULTIPROCESSOR
57 #include <sys/mplock.h>
58 #endif
59 
60 #include <machine/asm.h>
61 #include <machine/asm_macro.h>
62 #include <machine/atomic.h>
63 #include <machine/cmmu.h>
64 #include <machine/cpu.h>
65 #include <machine/reg.h>
66 #ifdef M88100
67 #include <machine/m88100.h>
68 #endif
69 
70 #include <uvm/uvm_extern.h>
71 
72 #ifdef DDB
73 #include <machine/db_machdep.h>
74 #include <ddb/db_extern.h>
75 #include <ddb/db_interface.h>
76 #endif /* DDB */
77 
78 typedef struct {
79 	u_int32_t word_one, word_two;
80 } m88k_exception_vector_area;
81 
82 void	dumpconf(void);
83 void	dumpsys(void);
84 void	regdump(struct trapframe *f);
85 void	*vector_init(m88k_exception_vector_area *, u_int32_t *, int);
86 void	atomic_init(void);
87 
88 /*
89  * CMMU and CPU variables
90  */
91 
92 #ifdef MULTIPROCESSOR
93 cpuid_t	master_cpu;
94 __cpu_simple_lock_t cmmu_cpu_lock = __SIMPLELOCK_UNLOCKED;
95 #endif
96 
97 struct cpu_info m88k_cpus[MAX_CPUS] = {
98 #ifndef MULTIPROCESSOR
99 	{ .ci_flags = CIF_ALIVE | CIF_PRIMARY }
100 #endif
101 };
102 const struct cmmu_p *cmmu;
103 
104 /*
105  * safepri is a safe priority for sleep to set for a spin-wait
106  * during autoconfiguration or after a panic.
107  */
108 int   safepri = IPL_NONE;
109 
110 /*
111  * Set registers on exec.
112  * Clear all except sp and pc.
113  */
114 void
setregs(struct proc * p,struct exec_package * pack,u_long stack,struct ps_strings * arginfo)115 setregs(struct proc *p, struct exec_package *pack, u_long stack,
116     struct ps_strings *arginfo)
117 {
118 	struct trapframe *tf = (struct trapframe *)USER_REGS(p);
119 
120 	/*
121 	 * Setup proper floating-point settings. This is necessary because
122 	 * we will return through the exception path, which only saves the
123 	 * integer registers, and not through cpu_switchto() (which saves
124 	 * fcr62 and fcr63 in the pcb).  This is safe to do here since the
125 	 * FPU is enabled and the kernel doesn't use it.
126 	 */
127 	__asm__ volatile ("fstcr %r0, %fcr0");
128 	__asm__ volatile ("fstcr %r0, %fcr62");
129 	__asm__ volatile ("fstcr %r0, %fcr63");
130 
131 	memset(tf, 0, sizeof *tf);
132 
133 #ifdef M88110
134 	if (CPU_IS88110) {
135 		/*
136 		 * user mode, interrupts enabled,
137 		 * graphics unit, fp enabled
138 		 */
139 		tf->tf_epsr = PSR_SFD;
140 	}
141 #endif
142 #ifdef M88100
143 	if (CPU_IS88100) {
144 		/*
145 		 * user mode, interrupts enabled,
146 		 * no graphics unit, fp enabled
147 		 */
148 		tf->tf_epsr = PSR_SFD | PSR_SFD2;
149 	}
150 #endif
151 
152 	/*
153 	 * We want to start executing at pack->ep_entry. The way to
154 	 * do this is force the processor to fetch from ep_entry.
155 	 *
156 	 * However, since we will return through m{88100,88110}_syscall(),
157 	 * we need to setup registers so that the success return, when
158 	 * ``incrementing'' the instruction pointers, will cause the
159 	 * binary to start at the expected address.
160 	 *
161 	 * This relies on the fact that binaries start with
162 	 *
163 	 *	br.n	1f
164 	 *	 or	r2, r0, r30
165 	 * 1:
166 	 *
167 	 * So the first two instructions can be skipped.
168 	 */
169 #ifdef M88110
170 	if (CPU_IS88110) {
171                 /*
172                  * The first instruction we want to run is at
173                  * ep_entry + 8. m88110_syscall() returning EJUSTRETURN
174                  * will resume at exip + 4, so make it point to
175                  * ep_entry + 4.
176                  */
177 		tf->tf_exip = (pack->ep_entry & XIP_ADDR) + 4;
178 	}
179 #endif
180 #ifdef M88100
181 	if (CPU_IS88100) {
182                 /*
183                  * The first instruction we want to run is at
184                  * ep_entry + 8. m88100_syscall() returning EJUSTRETURN
185                  * will resume at snip, so make it point to
186                  * ep_entry + 8.
187                  */
188 		tf->tf_snip = ((pack->ep_entry + 8) & FIP_ADDR) | FIP_V;
189 		tf->tf_sfip = tf->tf_snip + 4;
190 	}
191 #endif
192 	tf->tf_r[2] = stack;
193 	tf->tf_r[31] = stack;
194 }
195 
196 #ifdef DDB
197 int longformat = 1;
198 void
regdump(struct trapframe * f)199 regdump(struct trapframe *f)
200 {
201 #define R(i) f->tf_r[i]
202 	printf("R00-05: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
203 	       R(0),R(1),R(2),R(3),R(4),R(5));
204 	printf("R06-11: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
205 	       R(6),R(7),R(8),R(9),R(10),R(11));
206 	printf("R12-17: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
207 	       R(12),R(13),R(14),R(15),R(16),R(17));
208 	printf("R18-23: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
209 	       R(18),R(19),R(20),R(21),R(22),R(23));
210 	printf("R24-29: 0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx  0x%08lx\n",
211 	       R(24),R(25),R(26),R(27),R(28),R(29));
212 	printf("R30-31: 0x%08lx  0x%08lx\n",R(30),R(31));
213 #ifdef M88110
214 	if (CPU_IS88110) {
215 		printf("exip %lx enip %lx\n", f->tf_exip, f->tf_enip);
216 	}
217 #endif
218 #ifdef M88100
219 	if (CPU_IS88100) {
220 		printf("sxip %lx snip %lx sfip %lx\n",
221 		    f->tf_sxip, f->tf_snip, f->tf_sfip);
222 	}
223 	if (CPU_IS88100 && ISSET(f->tf_dmt0, DMT_VALID)) {
224 		/* print dmt stuff for data access fault */
225 		printf("fault type %ld\n", (f->tf_dpfsr >> 16) & 0x7);
226 		dae_print((u_int *)f);
227 	}
228 	if (CPU_IS88100 && longformat != 0) {
229 		printf("fpsr %lx fpcr %lx epsr %lx ssbr %lx\n",
230 		    f->tf_fpsr, f->tf_fpcr, f->tf_epsr, f->tf_ssbr);
231 		printf("fpecr %lx fphs1 %lx fpls1 %lx fphs2 %lx fpls2 %lx\n",
232 		    f->tf_fpecr, f->tf_fphs1, f->tf_fpls1,
233 		    f->tf_fphs2, f->tf_fpls2);
234 		printf("fppt %lx fprh %lx fprl %lx fpit %lx\n",
235 		    f->tf_fppt, f->tf_fprh, f->tf_fprl, f->tf_fpit);
236 		printf("vector %ld mask %lx flags %lx scratch1 %lx cpu %p\n",
237 		    f->tf_vector, f->tf_mask, f->tf_flags,
238 		    f->tf_scratch1, f->tf_cpu);
239 	}
240 #endif
241 #ifdef M88110
242 	if (CPU_IS88110 && longformat != 0) {
243 		printf("fpsr %lx fpcr %lx fpecr %lx epsr %lx\n",
244 		    f->tf_fpsr, f->tf_fpcr, f->tf_fpecr, f->tf_epsr);
245 		printf("dsap %lx duap %lx dsr %lx dlar %lx dpar %lx\n",
246 		    f->tf_dsap, f->tf_duap, f->tf_dsr, f->tf_dlar, f->tf_dpar);
247 		printf("isap %lx iuap %lx isr %lx ilar %lx ipar %lx\n",
248 		    f->tf_isap, f->tf_iuap, f->tf_isr, f->tf_ilar, f->tf_ipar);
249 		printf("vector %ld mask %lx flags %lx scratch1 %lx cpu %p\n",
250 		    f->tf_vector, f->tf_mask, f->tf_flags,
251 		    f->tf_scratch1, f->tf_cpu);
252 	}
253 #endif
254 }
255 #endif	/* DDB */
256 
257 /*
258  * Set up the cpu_info pointer and the cpu number for the current processor.
259  */
260 struct cpu_info *
set_cpu_number(cpuid_t number)261 set_cpu_number(cpuid_t number)
262 {
263 	struct cpu_info *ci;
264 
265 #ifdef MULTIPROCESSOR
266 	ci = &m88k_cpus[number];
267 #else
268 	ci = &m88k_cpus[0];
269 #endif
270 	ci->ci_cpuid = number;
271 
272 	__asm__ volatile ("stcr %0, %%cr17" :: "r" (ci));
273 	flush_pipeline();
274 	return ci;
275 }
276 
277 /*
278  * Notify the current process (p) that it has a signal pending,
279  * process as soon as possible.
280  */
281 void
signotify(struct proc * p)282 signotify(struct proc *p)
283 {
284 	aston(p);
285 	cpu_unidle(p->p_cpu);
286 }
287 
288 #ifdef MULTIPROCESSOR
289 void
cpu_unidle(struct cpu_info * ci)290 cpu_unidle(struct cpu_info *ci)
291 {
292 	if (ci != curcpu())
293 		m88k_send_ipi(CI_IPI_NOTIFY, ci->ci_cpuid);
294 }
295 #endif
296 
297 /*
298  * Preempt the current process if in interrupt from user mode,
299  * or after the current trap/syscall if in system mode.
300  */
301 void
need_resched(struct cpu_info * ci)302 need_resched(struct cpu_info *ci)
303 {
304 	ci->ci_want_resched = 1;
305 
306 	/* There's a risk we'll be called before the idle threads start */
307 	if (ci->ci_curproc != NULL) {
308 		aston(ci->ci_curproc);
309 		if (ci != curcpu())
310 			cpu_unidle(ci);
311 	}
312 }
313 
314 /*
315  * Generic soft interrupt interface
316  */
317 
318 void	dosoftint(int);
319 int	softpending;
320 
321 void
dosoftint(int sir)322 dosoftint(int sir)
323 {
324 	int q, mask;
325 
326 #ifdef MULTIPROCESSOR
327 	__mp_lock(&kernel_lock);
328 #endif
329 
330 	for (q = SI_NQUEUES - 1, mask = 1 << (SI_NQUEUES - 1); mask != 0;
331 	    q--, mask >>= 1)
332 		if (mask & sir)
333 			softintr_dispatch(q);
334 
335 #ifdef MULTIPROCESSOR
336 	__mp_unlock(&kernel_lock);
337 #endif
338 }
339 
340 int
spl0()341 spl0()
342 {
343 	int sir;
344 	int s;
345 
346 	/*
347 	 * Try to avoid potentially expensive setipl calls if nothing
348 	 * seems to be pending.
349 	 */
350 	if ((sir = atomic_clear_int(&softpending)) != 0) {
351 		s = setipl(IPL_SOFTINT);
352 		dosoftint(sir);
353 		setipl(IPL_NONE);
354 	} else
355 		s = setipl(IPL_NONE);
356 
357 	return (s);
358 }
359 
360 #define EMPTY_BR	0xc0000000	/* empty "br" instruction */
361 #define NO_OP 		0xf4005800	/* "or r0, r0, r0" */
362 
363 #define BRANCH(FROM, TO) \
364 	(EMPTY_BR | ((((vaddr_t)(TO) - (vaddr_t)(FROM)) >> 2) & 0x03ffffff))
365 
366 #define SET_VECTOR_88100(NUM, VALUE) \
367 	do { \
368 		vbr[NUM].word_one = NO_OP; \
369 		vbr[NUM].word_two = BRANCH(&vbr[NUM].word_two, VALUE); \
370 	} while (0)
371 
372 #define SET_VECTOR_88110(NUM, VALUE) \
373 	do { \
374 		vbr[NUM].word_one = BRANCH(&vbr[NUM].word_one, VALUE); \
375 		vbr[NUM].word_two = NO_OP; \
376 	} while (0)
377 
378 /*
379  * vector_init(vector, vector_init_list, bootstrap)
380  *
381  * This routine sets up the m88k vector table for the running processor,
382  * as well as the atomic operation routines for multiprocessor kernels.
383  * This is the first C code to run, before anything is initialized.
384  *
385  * I would add an extra four bytes to the exception vectors page pointed
386  * to by the vbr, since the 88100 may execute the first instruction of the
387  * next trap handler, as documented in its Errata. Processing trap #511
388  * would then fall into the next page, unless the address computation wraps,
389  * or software traps can not trigger the issue - the Errata does not provide
390  * more detail. And since the MVME BUG does not add an extra NOP after its
391  * VBR page, I'll assume this is safe for now -- miod
392  */
393 void *
vector_init(m88k_exception_vector_area * vbr,u_int32_t * vector_init_list,int bootstrap)394 vector_init(m88k_exception_vector_area *vbr, u_int32_t *vector_init_list,
395     int bootstrap)
396 {
397 	u_int num;
398 	u_int32_t vec;
399 
400 	switch (cputyp) {
401 	default:
402 #ifdef M88110
403 	case CPU_88110:
404 	    {
405 		extern void m88110_sigsys(void);
406 		extern void m88110_syscall_handler(void);
407 		extern void m88110_cache_flush_handler(void);
408 		extern void m88110_stepbpt(void);
409 		extern void m88110_userbpt(void);
410 
411 		for (num = 0; (vec = vector_init_list[num]) != 0; num++)
412 			SET_VECTOR_88110(num, vec);
413 
414 		if (bootstrap)
415 			SET_VECTOR_88110(0x03, vector_init_list[num + 1]);
416 
417 		for (; num < 512; num++)
418 			SET_VECTOR_88110(num, m88110_sigsys);
419 
420 		SET_VECTOR_88110(450, m88110_syscall_handler);
421 		SET_VECTOR_88110(451, m88110_cache_flush_handler);
422 		/*
423 		 * GCC will by default produce explicit trap 503
424 		 * for division by zero
425 		 */
426 		SET_VECTOR_88110(503, vector_init_list[8]);
427 		SET_VECTOR_88110(504, m88110_stepbpt);
428 		SET_VECTOR_88110(511, m88110_userbpt);
429 	    }
430 		break;
431 #endif
432 #ifdef M88100
433 	case CPU_88100:
434 	    {
435 		extern void sigsys(void);
436 		extern void syscall_handler(void);
437 		extern void cache_flush_handler(void);
438 		extern void stepbpt(void);
439 		extern void userbpt(void);
440 
441 		for (num = 0; (vec = vector_init_list[num]) != 0; num++)
442 			SET_VECTOR_88100(num, vec);
443 
444 		if (bootstrap)
445 			SET_VECTOR_88100(0x03, vector_init_list[num + 1]);
446 
447 		for (; num < 512; num++)
448 			SET_VECTOR_88100(num, sigsys);
449 
450 		SET_VECTOR_88100(450, syscall_handler);
451 		SET_VECTOR_88100(451, cache_flush_handler);
452 		/*
453 		 * GCC will by default produce explicit trap 503
454 		 * for division by zero
455 		 */
456 		SET_VECTOR_88100(503, vector_init_list[8]);
457 		SET_VECTOR_88100(504, stepbpt);
458 		SET_VECTOR_88100(511, userbpt);
459 	    }
460 		break;
461 #endif
462 	}
463 
464 	return vbr;
465 }
466 
467 #ifdef MULTIPROCESSOR
468 /*
469  * void atomic_init(void);
470  *
471  * This routine sets up proper atomic operation code for SMP kernels
472  * with both 88100 and 88110 support compiled-in. This is crucial enough
473  * to have to be done as early as possible.
474  * This is among the first C code to run, before anything is initialized.
475  */
476 void
atomic_init()477 atomic_init()
478 {
479 #if defined(M88100) && defined(M88110)
480 	if (cputyp == CPU_88100) {
481 		extern uint32_t __atomic_lock[];
482 		extern uint32_t __atomic_lock_88100[], __atomic_lock_88100_end[];
483 		extern uint32_t __atomic_unlock[];
484 		extern uint32_t __atomic_unlock_88100[], __atomic_unlock_88100_end[];
485 
486 		uint32_t *s, *e, *d;
487 
488 		d = __atomic_lock;
489 		s = __atomic_lock_88100;
490 		e = __atomic_lock_88100_end;
491 		while (s != e)
492 				*d++ = *s++;
493 
494 		d = __atomic_unlock;
495 		s = __atomic_unlock_88100;
496 		e = __atomic_unlock_88100_end;
497 		while (s != e)
498 				*d++ = *s++;
499 	}
500 #endif	/* M88100 && M88110 */
501 }
502 #endif	/* MULTIPROCESSOR */
503