xref: /netbsd/sys/arch/sparc64/sparc64/emul.c (revision c4a72b64)
1 /*	$NetBSD: emul.c,v 1.10 2002/09/29 04:12:03 chs Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 2001 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 <sparc64/sparc64/cache.h>
47 
48 #define DEBUG_EMUL
49 #ifdef DEBUG_EMUL
50 # define DPRINTF(a) uprintf a
51 #else
52 # define DPRINTF(a)
53 #endif
54 
55 #define GPR(tf, i)	((int32_t *)(u_long)&tf->tf_global)[i]
56 #define IPR(tf, i)	((int32_t *)(u_long)tf->tf_out[6])[i - 16]
57 #define FPR(p, i)	((int32_t) p->p_md.md_fpstate->fs_regs[i])
58 
59 static __inline int readgpreg __P((struct trapframe64 *, int, void *));
60 static __inline int readfpreg __P((struct proc *, int, void *));
61 static __inline int writegpreg __P((struct trapframe64 *, int, const void *));
62 static __inline int writefpreg __P((struct proc *, int, const void *));
63 static __inline int decodeaddr __P((struct trapframe64 *, union instr *, void *));
64 static int muldiv __P((struct trapframe64 *, union instr *, int32_t *, int32_t *,
65     int32_t *));
66 
67 #define	REGNAME(i)	"goli"[i >> 3], i & 7
68 
69 
70 static __inline int
71 readgpreg(tf, i, val)
72 	struct trapframe64 *tf;
73 	int i;
74 	void *val;
75 {
76 	int error = 0;
77 	if (i == 0)
78 		*(int32_t *) val = 0;
79 	else if (i < 16)
80 		*(int32_t *) val = GPR(tf, i);
81 	else
82 		error = copyin(&IPR(tf, i), val, sizeof(int32_t));
83 
84 	return error;
85 }
86 
87 
88 static __inline int
89 writegpreg(tf, i, val)
90 	struct trapframe64 *tf;
91 	int i;
92 	const void *val;
93 {
94 	int error = 0;
95 
96 	if (i == 0)
97 		return error;
98 	else if (i < 16)
99 		GPR(tf, i) = *(int32_t *) val;
100 	else
101 		/* XXX: Fix copyout prototype */
102 		error = copyout((caddr_t) val, &IPR(tf, i), sizeof(int32_t));
103 
104 	return error;
105 }
106 
107 
108 static __inline int
109 readfpreg(p, i, val)
110 	struct proc *p;
111 	int i;
112 	void *val;
113 {
114 	*(int32_t *) val = FPR(p, i);
115 	return 0;
116 }
117 
118 
119 static __inline int
120 writefpreg(p, i, val)
121 	struct proc *p;
122 	int i;
123 	const void *val;
124 {
125 	FPR(p, i) = *(const int32_t *) val;
126 	return 0;
127 }
128 
129 static __inline int
130 decodeaddr(tf, code, val)
131 	struct trapframe64 *tf;
132 	union instr *code;
133 	void *val;
134 {
135 	if (code->i_simm13.i_i)
136 		*((int32_t *) val) = code->i_simm13.i_simm13;
137 	else {
138 		int error;
139 
140 		if (code->i_asi.i_asi)
141 			return EINVAL;
142 		if ((error = readgpreg(tf, code->i_asi.i_rs2, val)) != 0)
143 			return error;
144 	}
145 	return 0;
146 }
147 
148 
149 static int
150 muldiv(tf, code, rd, rs1, rs2)
151 	struct trapframe64 *tf;
152 	union instr *code;
153 	int32_t *rd, *rs1, *rs2;
154 {
155 	/*
156 	 * We check for {S,U}{MUL,DIV}{,cc}
157 	 *
158 	 * [c = condition code, s = sign]
159 	 * Mul = 0c101s
160 	 * Div = 0c111s
161 	 */
162 	union {
163 		struct {
164 			unsigned unused:26;	/* padding */
165 			unsigned zero:1;	/* zero by opcode */
166 			unsigned cc:1;		/* one to send condition code */
167 			unsigned one1:1;	/* one by opcode */
168 			unsigned div:1;		/* one if divide */
169 			unsigned one2:1;	/* one by opcode */
170 			unsigned sgn:1;		/* sign bit */
171 		} bits;
172 		int num;
173 	} op;
174 
175 	op.num = code->i_op3.i_op3;
176 
177 #ifdef DEBUG_EMUL
178 	uprintf("muldiv 0x%x: %c%s%s %c%d, %c%d, ", code->i_int,
179 	    "us"[op.bits.sgn], op.bits.div ? "div" : "mul",
180 	    op.bits.cc ? "cc" : "", REGNAME(code->i_op3.i_rd),
181 	    REGNAME(code->i_op3.i_rs1));
182 	if (code->i_loadstore.i_i)
183 		uprintf("0x%x\n", *rs2);
184 	else
185 		uprintf("%c%d\n", REGNAME(code->i_asi.i_rs2));
186 #endif
187 
188 	if (op.bits.div) {
189 		if (*rs2 == 0) {
190 			/*
191 			 * XXX: to be 100% correct here, on sunos we need to
192 			 *	ignore the error and return *rd = *rs1.
193 			 *	It should be easy to fix by passing struct
194 			 *	proc in here.
195 			 */
196 			DPRINTF(("emulinstr: avoid zerodivide\n"));
197 			return EINVAL;
198 		}
199 		*rd = *rs1 / *rs2;
200 		DPRINTF(("muldiv: %d / %d = %d\n", *rs1, *rs2, *rd));
201 	}
202 	else {
203 		*rd = *rs1 * *rs2;
204 		DPRINTF(("muldiv: %d * %d = %d\n", *rs1, *rs2, *rd));
205 	}
206 
207 	if (op.bits.cc) {
208 		/* Set condition codes */
209 		tf->tf_tstate &= ~(TSTATE_CCR);
210 
211 		if (*rd == 0)
212 			tf->tf_tstate |= (u_int64_t)(ICC_Z|XCC_Z) << TSTATE_CCR_SHIFT;
213 		else {
214 			if (op.bits.sgn && *rd < 0)
215 				tf->tf_tstate |= (u_int64_t)(ICC_N|XCC_N) << TSTATE_CCR_SHIFT;
216 			if (op.bits.div) {
217 				if (*rd * *rs2 != *rs1)
218 					tf->tf_tstate |= (u_int64_t)(ICC_V|XCC_V) << TSTATE_CCR_SHIFT;
219 			}
220 			else {
221 				if (*rd / *rs2 != *rs1)
222 					tf->tf_tstate |= (u_int64_t)(ICC_V|XCC_V) << TSTATE_CCR_SHIFT;
223 			}
224 		}
225 	}
226 
227 	return 0;
228 }
229 
230 /*
231  * Code to handle alignment faults on the sparc. This is enabled by sending
232  * a fixalign trap. Such code is generated by compiling with cc -misalign
233  * on SunOS, but we don't have such a feature yet on our gcc.
234  */
235 
236 int
237 fixalign(p, tf)
238 	struct proc *p;
239 	struct trapframe64 *tf;
240 {
241 	static u_char sizedef[] = { 0x4, 0xff, 0x2, 0x8 };
242 
243 	/*
244 	 * This is particular to load and store instructions
245 	 */
246 	union {
247 		struct {
248 			unsigned unused:26;	/* 26 padding */
249 			unsigned fl:1;		/* 1 bit float flag */
250 			unsigned op:1;		/* 1 bit opcode */
251 			unsigned sgn:1;		/* 1 bit sign */
252 			unsigned st:1;		/* 1 bit load/store */
253 			unsigned sz:2;		/* 2 bit size register */
254 		} bits;
255 		int num;
256 	} op;
257 
258 	union {
259 		double	d;
260 		int32_t i[2];
261 		int16_t s[4];
262 		int8_t  c[8];
263 	} data;
264 
265 	union instr code;
266 	size_t size;
267 	int64_t rs1, rs2;
268 	int error;
269 
270 	/* fetch and check the instruction that caused the fault */
271 	error = copyin((caddr_t)(u_long)tf->tf_pc, &code.i_int, sizeof(code.i_int));
272 	if (error != 0) {
273 		DPRINTF(("fixalign: Bad instruction fetch\n"));
274 		return EINVAL;
275 	}
276 
277 	/* Only support format 3 */
278 	if (code.i_any.i_op != 3) {
279 		DPRINTF(("fixalign: Not a load or store\n"));
280 		return EINVAL;
281 	}
282 
283 	op.num = code.i_loadstore.i_op3;
284 
285 	/* Check operand size */
286 	if ((size = sizedef[op.bits.sz]) == 0xff) {
287 		DPRINTF(("fixalign: Bad operand size\n"));
288 		return EINVAL;
289 	}
290 
291 	write_user_windows();
292 
293 	if ((error = readgpreg(tf, code.i_op3.i_rs1, &rs1)) != 0) {
294 		DPRINTF(("emulinstr: read rs1 %d\n", error));
295 		return error;
296 	}
297 
298 	if ((error = decodeaddr(tf, &code, &rs2)) != 0) {
299 		DPRINTF(("emulinstr: decode addr %d\n", error));
300 		return error;
301 	}
302 
303 
304 	rs1 += rs2;
305 
306 #ifdef DEBUG_EMUL
307 	uprintf("memalign 0x%x: %s%c%c %c%d, %c%d, ", code.i_int,
308 	    op.bits.st ? "st" : "ld", "us"[op.bits.sgn],
309 	    "w*hd"[op.bits.sz], op.bits.fl ? 'f' : REGNAME(code.i_op3.i_rd),
310 	    REGNAME(code.i_op3.i_rs1));
311 	if (code.i_loadstore.i_i)
312 		uprintf("0x%llx\n", (unsigned long long)rs2);
313 	else
314 		uprintf("%c%d\n", REGNAME(code.i_asi.i_rs2));
315 #endif
316 #ifdef DIAGNOSTIC
317 	if (op.bits.fl && p != fpproc)
318 		panic("fp align without being the FP owning process");
319 #endif
320 
321 	if (op.bits.st) {
322 		if (op.bits.fl) {
323 			if (p == fpproc) {
324 				savefpstate(p->p_md.md_fpstate);
325 				fpproc = NULL;
326 			}
327 
328 			error = readfpreg(p, code.i_op3.i_rd, &data.i[0]);
329 			if (error)
330 				return error;
331 			if (size == 8) {
332 				error = readfpreg(p, code.i_op3.i_rd + 1,
333 				    &data.i[1]);
334 				if (error)
335 					return error;
336 			}
337 		}
338 		else {
339 			error = readgpreg(tf, code.i_op3.i_rd, &data.i[0]);
340 			if (error)
341 				return error;
342 			if (size == 8) {
343 				error = readgpreg(tf, code.i_op3.i_rd + 1,
344 				    &data.i[1]);
345 				if (error)
346 					return error;
347 			}
348 		}
349 
350 		if (size == 2)
351 			return copyout(&data.s[1], (caddr_t)(u_long)rs1, size);
352 		else
353 			return copyout(&data.d, (caddr_t)(u_long)rs1, size);
354 	}
355 	else { /* load */
356 		if (size == 2) {
357 			error = copyin((caddr_t)(u_long)rs1, &data.s[1], size);
358 			if (error)
359 				return error;
360 
361 			/* Sign extend if necessary */
362 			if (op.bits.sgn && (data.s[1] & 0x8000) != 0)
363 				data.s[0] = ~0;
364 			else
365 				data.s[0] = 0;
366 		}
367 		else
368 			error = copyin((caddr_t)(u_long)rs1, &data.d, size);
369 
370 		if (error)
371 			return error;
372 
373 		if (op.bits.fl) {
374 			error = writefpreg(p, code.i_op3.i_rd, &data.i[0]);
375 			if (error)
376 				return error;
377 			if (size == 8) {
378 				error = writefpreg(p, code.i_op3.i_rd + 1,
379 				    &data.i[1]);
380 				if (error)
381 					return error;
382 			}
383 			loadfpstate(p->p_md.md_fpstate);
384 			fpproc = p;
385 		}
386 		else {
387 			error = writegpreg(tf, code.i_op3.i_rd, &data.i[0]);
388 			if (error)
389 				return error;
390 			if (size == 8)
391 				error = writegpreg(tf, code.i_op3.i_rd + 1,
392 				    &data.i[1]);
393 		}
394 	}
395 	return error;
396 }
397 
398 /*
399  * Emulate unimplemented instructions on earlier sparc chips.
400  */
401 int
402 emulinstr(pc, tf)
403 	vaddr_t pc;
404 	struct trapframe64 *tf;
405 {
406 	union instr code;
407 	int32_t rs1, rs2, rd;
408 	int error;
409 
410 	/* fetch and check the instruction that caused the fault */
411 	error = copyin((caddr_t) pc, &code.i_int, sizeof(code.i_int));
412 	if (error != 0) {
413 		DPRINTF(("emulinstr: Bad instruction fetch\n"));
414 		return SIGILL;
415 	}
416 
417 	/* Only support format 2 */
418 	if (code.i_any.i_op != 2) {
419 		DPRINTF(("emulinstr: Not a format 2 instruction\n"));
420 		return SIGILL;
421 	}
422 
423 	write_user_windows();
424 
425 	if ((error = readgpreg(tf, code.i_op3.i_rs1, &rs1)) != 0) {
426 		DPRINTF(("emulinstr: read rs1 %d\n", error));
427 		return SIGILL;
428 	}
429 
430 	if ((error = decodeaddr(tf, &code, &rs2)) != 0) {
431 		DPRINTF(("emulinstr: decode addr %d\n", error));
432 		return SIGILL;
433 	}
434 
435 	switch (code.i_op3.i_op3) {
436 	case IOP3_FLUSH:
437 		blast_icache();		/* XXX overkill */
438 		return 0;
439 
440 	default:
441 		if ((code.i_op3.i_op3 & 0x2a) != 0xa) {
442 			DPRINTF(("emulinstr: Unsupported op3 0x%x\n",
443 			    code.i_op3.i_op3));
444 			return SIGILL;
445 		}
446 		else if ((error = muldiv(tf, &code, &rd, &rs1, &rs2)) != 0)
447 			return SIGFPE;
448 	}
449 
450 	if ((error = writegpreg(tf, code.i_op3.i_rd, &rd)) != 0) {
451 		DPRINTF(("muldiv: write rd %d\n", error));
452 		return SIGILL;
453 	}
454 
455 	return 0;
456 }
457