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