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