xref: /netbsd/sys/arch/sparc/sparc/emul.c (revision bf9ec67e)
1 /*	$NetBSD: emul.c,v 1.5 1998/10/01 21:26:55 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/proc.h>
42 #include <machine/reg.h>
43 #include <machine/instr.h>
44 #include <machine/cpu.h>
45 #include <machine/psl.h>
46 #include <sparc/sparc/cpuvar.h>
47 
48 #ifdef DEBUG_EMUL
49 # define DPRINTF(a) uprintf a
50 #else
51 # define DPRINTF(a)
52 #endif
53 
54 #define GPR(tf, i)	((int32_t *) &tf->tf_global)[i]
55 #define IPR(tf, i)	((int32_t *) tf->tf_out[6])[i - 16]
56 #define FPR(p, i)	((int32_t) p->p_md.md_fpstate->fs_regs[i])
57 
58 static __inline int readgpreg __P((struct trapframe *, int, void *));
59 static __inline int readfpreg __P((struct proc *, int, void *));
60 static __inline int writegpreg __P((struct trapframe *, int, const void *));
61 static __inline int writefpreg __P((struct proc *, int, const void *));
62 static __inline int decodeaddr __P((struct trapframe *, union instr *, void *));
63 static int muldiv __P((struct trapframe *, union instr *, int32_t *, int32_t *,
64     int32_t *));
65 
66 #define	REGNAME(i)	"goli"[i >> 3], i & 7
67 
68 
69 static __inline int
70 readgpreg(tf, i, val)
71 	struct trapframe *tf;
72 	int i;
73 	void *val;
74 {
75 	int error = 0;
76 	if (i == 0)
77 		*(int32_t *) val = 0;
78 	else if (i < 16)
79 		*(int32_t *) val = GPR(tf, i);
80 	else
81 		error = copyin(&IPR(tf, i), val, sizeof(int32_t));
82 
83 	return error;
84 }
85 
86 
87 static __inline int
88 writegpreg(tf, i, val)
89 	struct trapframe *tf;
90 	int i;
91 	const void *val;
92 {
93 	int error = 0;
94 
95 	if (i == 0)
96 		return error;
97 	else if (i < 16)
98 		GPR(tf, i) = *(int32_t *) val;
99 	else
100 		/* XXX: Fix copyout prototype */
101 		error = copyout((caddr_t) val, &IPR(tf, i), sizeof(int32_t));
102 
103 	return error;
104 }
105 
106 
107 static __inline int
108 readfpreg(p, i, val)
109 	struct proc *p;
110 	int i;
111 	void *val;
112 {
113 	*(int32_t *) val = FPR(p, i);
114 	return 0;
115 }
116 
117 
118 static __inline int
119 writefpreg(p, i, val)
120 	struct proc *p;
121 	int i;
122 	const void *val;
123 {
124 	FPR(p, i) = *(const int32_t *) val;
125 	return 0;
126 }
127 
128 static __inline int
129 decodeaddr(tf, code, val)
130 	struct trapframe *tf;
131 	union instr *code;
132 	void *val;
133 {
134 	if (code->i_simm13.i_i)
135 		*((int32_t *) val) = code->i_simm13.i_simm13;
136 	else {
137 		int error;
138 
139 		if (code->i_asi.i_asi)
140 			return EINVAL;
141 		if ((error = readgpreg(tf, code->i_asi.i_rs2, val)) != 0)
142 			return error;
143 	}
144 	return 0;
145 }
146 
147 
148 static int
149 muldiv(tf, code, rd, rs1, rs2)
150 	struct trapframe *tf;
151 	union instr *code;
152 	int32_t *rd, *rs1, *rs2;
153 {
154 	/*
155 	 * We check for {S,U}{MUL,DIV}{,cc}
156 	 *
157 	 * [c = condition code, s = sign]
158 	 * Mul = 0c101s
159 	 * Div = 0c111s
160 	 */
161 	union {
162 		struct {
163 			unsigned unused:26;	/* padding */
164 			unsigned zero:1;	/* zero by opcode */
165 			unsigned cc:1;		/* one to send condition code */
166 			unsigned one1:1;	/* one by opcode */
167 			unsigned div:1;		/* one if divide */
168 			unsigned one2:1;	/* one by opcode */
169 			unsigned sgn:1;		/* sign bit */
170 		} bits;
171 		int num;
172 	} op;
173 
174 	op.num = code->i_op3.i_op3;
175 
176 #ifdef DEBUG_EMUL
177 	uprintf("muldiv 0x%x: %c%s%s %c%d, %c%d, ", code->i_int,
178 	    "us"[op.bits.sgn], op.bits.div ? "div" : "mul",
179 	    op.bits.cc ? "cc" : "", REGNAME(code->i_op3.i_rd),
180 	    REGNAME(code->i_op3.i_rs1));
181 	if (code->i_loadstore.i_i)
182 		uprintf("0x%x\n", *rs2);
183 	else
184 		uprintf("%c%d\n", REGNAME(code->i_asi.i_rs2));
185 #endif
186 
187 	if (op.bits.div) {
188 		if (*rs2 == 0) {
189 			/*
190 			 * XXX: to be 100% correct here, on sunos we need to
191 			 *	ignore the error and return *rd = *rs1.
192 			 *	It should be easy to fix by passing struct
193 			 *	proc in here.
194 			 */
195 			DPRINTF(("emulinstr: avoid zerodivide\n"));
196 			return EINVAL;
197 		}
198 		*rd = *rs1 / *rs2;
199 		DPRINTF(("muldiv: %d / %d = %d\n", *rs1, *rs2, *rd));
200 	}
201 	else {
202 		*rd = *rs1 * *rs2;
203 		DPRINTF(("muldiv: %d * %d = %d\n", *rs1, *rs2, *rd));
204 	}
205 
206 	if (op.bits.cc) {
207 		/* Set condition codes */
208 		tf->tf_psr &= ~PSR_ICC;
209 
210 		if (*rd == 0)
211 			tf->tf_psr |= PSR_Z << 20;
212 		else {
213 			if (op.bits.sgn && *rd < 0)
214 				tf->tf_psr |= PSR_N << 20;
215 			if (op.bits.div) {
216 				if (*rd * *rs2 != *rs1)
217 					tf->tf_psr |= PSR_O << 20;
218 			}
219 			else {
220 				if (*rd / *rs2 != *rs1)
221 					tf->tf_psr |= PSR_O << 20;
222 			}
223 		}
224 	}
225 
226 	return 0;
227 }
228 
229 /*
230  * Code to handle alignment faults on the sparc. This is enabled by sending
231  * a fixalign trap. Such code is generated by compiling with cc -misalign
232  * on SunOS, but we don't have such a feature yet on our gcc.
233  */
234 
235 int
236 fixalign(p, tf)
237 	struct proc *p;
238 	struct trapframe *tf;
239 {
240 	static u_char sizedef[] = { 0x4, 0xff, 0x2, 0x8 };
241 
242 	/*
243 	 * This is particular to load and store instructions
244 	 */
245 	union {
246 		struct {
247 			unsigned unused:26;	/* 26 padding */
248 			unsigned fl:1;		/* 1 bit float flag */
249 			unsigned op:1;		/* 1 bit opcode */
250 			unsigned sgn:1;		/* 1 bit sign */
251 			unsigned st:1;		/* 1 bit load/store */
252 			unsigned sz:2;		/* 2 bit size register */
253 		} bits;
254 		int num;
255 	} op;
256 
257 	union {
258 		double	d;
259 		int32_t i[2];
260 		int16_t s[4];
261 		int8_t  c[8];
262 	} data;
263 
264 	union instr code;
265 	size_t size;
266 	int32_t rs1, rs2;
267 	int error;
268 
269 	/* fetch and check the instruction that caused the fault */
270 	error = copyin((caddr_t) tf->tf_pc, &code.i_int, sizeof(code.i_int));
271 	if (error != 0) {
272 		DPRINTF(("fixalign: Bad instruction fetch\n"));
273 		return EINVAL;
274 	}
275 
276 	/* Only support format 3 */
277 	if (code.i_any.i_op != 3) {
278 		DPRINTF(("fixalign: Not a load or store\n"));
279 		return EINVAL;
280 	}
281 
282 	op.num = code.i_loadstore.i_op3;
283 
284 	/* Check operand size */
285 	if ((size = sizedef[op.bits.sz]) == 0xff) {
286 		DPRINTF(("fixalign: Bad operand size\n"));
287 		return EINVAL;
288 	}
289 
290 	write_user_windows();
291 
292 	if ((error = readgpreg(tf, code.i_op3.i_rs1, &rs1)) != 0) {
293 		DPRINTF(("emulinstr: read rs1 %d\n", error));
294 		return error;
295 	}
296 
297 	if ((error = decodeaddr(tf, &code, &rs2)) != 0) {
298 		DPRINTF(("emulinstr: decode addr %d\n", error));
299 		return error;
300 	}
301 
302 
303 	rs1 += rs2;
304 
305 #ifdef DEBUG_EMUL
306 	uprintf("memalign 0x%x: %s%c%c %c%d, %c%d, ", code.i_int,
307 	    op.bits.st ? "st" : "ld", "us"[op.bits.sgn],
308 	    "w*hd"[op.bits.sz], op.bits.fl ? 'f' : REGNAME(code.i_op3.i_rd),
309 	    REGNAME(code.i_op3.i_rs1));
310 	if (code.i_loadstore.i_i)
311 		uprintf("0x%x\n", rs2);
312 	else
313 		uprintf("%c%d\n", REGNAME(code.i_asi.i_rs2));
314 #endif
315 #ifdef DIAGNOSTIC
316 	if (op.bits.fl && p != cpuinfo.fpproc)
317 		panic("fp align without being the FP owning process");
318 #endif
319 
320 	if (op.bits.st) {
321 		if (op.bits.fl) {
322 			savefpstate(p->p_md.md_fpstate);
323 
324 			error = readfpreg(p, code.i_op3.i_rd, &data.i[0]);
325 			if (error)
326 				return error;
327 			if (size == 8) {
328 				error = readfpreg(p, code.i_op3.i_rd + 1,
329 				    &data.i[1]);
330 				if (error)
331 					return error;
332 			}
333 		}
334 		else {
335 			error = readgpreg(tf, code.i_op3.i_rd, &data.i[0]);
336 			if (error)
337 				return error;
338 			if (size == 8) {
339 				error = readgpreg(tf, code.i_op3.i_rd + 1,
340 				    &data.i[1]);
341 				if (error)
342 					return error;
343 			}
344 		}
345 
346 		if (size == 2)
347 			return copyout(&data.s[1], (caddr_t) rs1, size);
348 		else
349 			return copyout(&data.d, (caddr_t) rs1, size);
350 	}
351 	else { /* load */
352 		if (size == 2) {
353 			error = copyin((caddr_t) rs1, &data.s[1], size);
354 			if (error)
355 				return error;
356 
357 			/* Sign extend if necessary */
358 			if (op.bits.sgn && (data.s[1] & 0x8000) != 0)
359 				data.s[0] = ~0;
360 			else
361 				data.s[0] = 0;
362 		}
363 		else
364 			error = copyin((caddr_t) rs1, &data.d, size);
365 
366 		if (error)
367 			return error;
368 
369 		if (op.bits.fl) {
370 			error = writefpreg(p, code.i_op3.i_rd, &data.i[0]);
371 			if (error)
372 				return error;
373 			if (size == 8) {
374 				error = writefpreg(p, code.i_op3.i_rd + 1,
375 				    &data.i[1]);
376 				if (error)
377 					return error;
378 			}
379 			loadfpstate(p->p_md.md_fpstate);
380 		}
381 		else {
382 			error = writegpreg(tf, code.i_op3.i_rd, &data.i[0]);
383 			if (error)
384 				return error;
385 			if (size == 8)
386 				error = writegpreg(tf, code.i_op3.i_rd + 1,
387 				    &data.i[1]);
388 		}
389 	}
390 	return error;
391 }
392 
393 /*
394  * Emulate unimplemented instructions on earlier sparc chips.
395  */
396 int
397 emulinstr(pc, tf)
398 	int pc;
399 	struct trapframe *tf;
400 {
401 	union instr code;
402 	int32_t rs1, rs2, rd;
403 	int error;
404 
405 	/* fetch and check the instruction that caused the fault */
406 	error = copyin((caddr_t) pc, &code.i_int, sizeof(code.i_int));
407 	if (error != 0) {
408 		DPRINTF(("emulinstr: Bad instruction fetch\n"));
409 		return SIGILL;
410 	}
411 
412 	/* Only support format 2 */
413 	if (code.i_any.i_op != 2) {
414 		DPRINTF(("emulinstr: Not a format 2 instruction\n"));
415 		return SIGILL;
416 	}
417 
418 	write_user_windows();
419 
420 	if ((error = readgpreg(tf, code.i_op3.i_rs1, &rs1)) != 0) {
421 		DPRINTF(("emulinstr: read rs1 %d\n", error));
422 		return SIGILL;
423 	}
424 
425 	if ((error = decodeaddr(tf, &code, &rs2)) != 0) {
426 		DPRINTF(("emulinstr: decode addr %d\n", error));
427 		return SIGILL;
428 	}
429 
430 	switch (code.i_op3.i_op3) {
431 	case IOP3_FLUSH:
432 		cpuinfo.cache_flush((caddr_t)(rs1 + rs2), 4); /*XXX*/
433 		return 0;
434 
435 	default:
436 		if ((code.i_op3.i_op3 & 0x2a) != 0xa) {
437 			DPRINTF(("emulinstr: Unsupported op3 0x%x\n",
438 			    code.i_op3.i_op3));
439 			return SIGILL;
440 		}
441 		else if ((error = muldiv(tf, &code, &rd, &rs1, &rs2)) != 0)
442 			return SIGFPE;
443 	}
444 
445 	if ((error = writegpreg(tf, code.i_op3.i_rd, &rd)) != 0) {
446 		DPRINTF(("muldiv: write rd %d\n", error));
447 		return SIGILL;
448 	}
449 
450 	return 0;
451 }
452