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