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