xref: /openbsd/sys/arch/sparc64/sparc64/emul.c (revision 898184e3)
1 /*	$OpenBSD: emul.c,v 1.22 2011/07/11 15:40:47 guenther Exp $	*/
2 /*	$NetBSD: emul.c,v 1.8 2001/06/29 23:58:40 eeh Exp $	*/
3 
4 /*-
5  * Copyright (c) 1997, 2001 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Christos Zoulas.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/proc.h>
36 #include <sys/signalvar.h>
37 #include <sys/malloc.h>
38 #include <machine/reg.h>
39 #include <machine/instr.h>
40 #include <machine/cpu.h>
41 #include <machine/psl.h>
42 #include <uvm/uvm_extern.h>
43 
44 #ifdef DEBUG_EMUL
45 # define DPRINTF(a) printf a
46 #else
47 # define DPRINTF(a)
48 #endif
49 
50 #define GPR(tf, i)	((int32_t *)(u_long)&tf->tf_global)[i]
51 #define IPR(tf, i)	((int32_t *)(u_long)tf->tf_out[6])[i - 16]
52 #define FPR(p, i)	((int32_t) p->p_md.md_fpstate->fs_regs[i])
53 #define FPRSET(p, i, v)	p->p_md.md_fpstate->fs_regs[i] = (v)
54 
55 static __inline int readgpreg(struct trapframe64 *, int, void *);
56 static __inline int readfpreg(struct proc *, int, void *);
57 static __inline int writegpreg(struct trapframe64 *, int, const void *);
58 static __inline int writefpreg(struct proc *, int, const void *);
59 static __inline int decodeaddr(struct trapframe64 *, union instr *, void *);
60 static int muldiv(struct trapframe64 *, union instr *, int32_t *, int32_t *,
61     int32_t *);
62 void swap_quad(int64_t *);
63 
64 #define	REGNAME(i)	"goli"[i >> 3], i & 7
65 
66 
67 static __inline int
68 readgpreg(tf, i, val)
69 	struct trapframe64 *tf;
70 	int i;
71 	void *val;
72 {
73 	int error = 0;
74 	if (i == 0)
75 		*(int32_t *) val = 0;
76 	else if (i < 16)
77 		*(int32_t *) val = GPR(tf, i);
78 	else
79 		error = copyin(&IPR(tf, i), val, sizeof(int32_t));
80 
81 	return error;
82 }
83 
84 
85 static __inline int
86 writegpreg(tf, i, val)
87 	struct trapframe64 *tf;
88 	int i;
89 	const void *val;
90 {
91 	int error = 0;
92 
93 	if (i == 0)
94 		return error;
95 	else if (i < 16)
96 		GPR(tf, i) = *(int32_t *) val;
97 	else
98 		/* XXX: Fix copyout prototype */
99 		error = copyout((caddr_t) val, &IPR(tf, i), sizeof(int32_t));
100 
101 	return error;
102 }
103 
104 
105 static __inline int
106 readfpreg(p, i, val)
107 	struct proc *p;
108 	int i;
109 	void *val;
110 {
111 	*(int32_t *) val = FPR(p, i);
112 	return 0;
113 }
114 
115 
116 static __inline int
117 writefpreg(p, i, val)
118 	struct proc *p;
119 	int i;
120 	const void *val;
121 {
122 	FPRSET(p, i, *(const int32_t *) val);
123 	return 0;
124 }
125 
126 static __inline int
127 decodeaddr(tf, code, val)
128 	struct trapframe64 *tf;
129 	union instr *code;
130 	void *val;
131 {
132 	if (code->i_simm13.i_i)
133 		*((int32_t *) val) = code->i_simm13.i_simm13;
134 	else {
135 		int error;
136 
137 		if (code->i_asi.i_asi)
138 			return EINVAL;
139 		if ((error = readgpreg(tf, code->i_asi.i_rs2, val)) != 0)
140 			return error;
141 	}
142 	return 0;
143 }
144 
145 
146 static int
147 muldiv(tf, code, rd, rs1, rs2)
148 	struct trapframe64 *tf;
149 	union instr *code;
150 	int32_t *rd, *rs1, *rs2;
151 {
152 	/*
153 	 * We check for {S,U}{MUL,DIV}{,cc}
154 	 *
155 	 * [c = condition code, s = sign]
156 	 * Mul = 0c101s
157 	 * Div = 0c111s
158 	 */
159 	union {
160 		struct {
161 			unsigned unused:26;	/* padding */
162 			unsigned zero:1;	/* zero by opcode */
163 			unsigned cc:1;		/* one to send condition code */
164 			unsigned one1:1;	/* one by opcode */
165 			unsigned div:1;		/* one if divide */
166 			unsigned one2:1;	/* one by opcode */
167 			unsigned sgn:1;		/* sign bit */
168 		} bits;
169 		int num;
170 	} op;
171 
172 	op.num = code->i_op3.i_op3;
173 
174 #ifdef DEBUG_EMUL
175 	printf("muldiv 0x%x: %c%s%s %c%d, %c%d, ", code->i_int,
176 	    "us"[op.bits.sgn], op.bits.div ? "div" : "mul",
177 	    op.bits.cc ? "cc" : "", REGNAME(code->i_op3.i_rd),
178 	    REGNAME(code->i_op3.i_rs1));
179 	if (code->i_loadstore.i_i)
180 		printf("0x%x\n", *rs2);
181 	else
182 		printf("%c%d\n", REGNAME(code->i_asi.i_rs2));
183 #endif
184 
185 	if (op.bits.div) {
186 		if (*rs2 == 0) {
187 			/*
188 			 * XXX: to be 100% correct here, on sunos we need to
189 			 *	ignore the error and return *rd = *rs1.
190 			 *	It should be easy to fix by passing struct
191 			 *	proc in here.
192 			 */
193 			DPRINTF(("muldiv: avoid zerodivide\n"));
194 			return EINVAL;
195 		}
196 		*rd = *rs1 / *rs2;
197 		DPRINTF(("muldiv: %d / %d = %d\n", *rs1, *rs2, *rd));
198 	}
199 	else {
200 		*rd = *rs1 * *rs2;
201 		DPRINTF(("muldiv: %d * %d = %d\n", *rs1, *rs2, *rd));
202 	}
203 
204 	if (op.bits.cc) {
205 		/* Set condition codes */
206 		tf->tf_tstate &= ~(TSTATE_CCR);
207 
208 		if (*rd == 0)
209 			tf->tf_tstate |= (u_int64_t)(ICC_Z|XCC_Z) << TSTATE_CCR_SHIFT;
210 		else {
211 			if (op.bits.sgn && *rd < 0)
212 				tf->tf_tstate |= (u_int64_t)(ICC_N|XCC_N) << TSTATE_CCR_SHIFT;
213 			if (op.bits.div) {
214 				if (*rd * *rs2 != *rs1)
215 					tf->tf_tstate |= (u_int64_t)(ICC_V|XCC_V) << TSTATE_CCR_SHIFT;
216 			}
217 			else {
218 				if (*rd / *rs2 != *rs1)
219 					tf->tf_tstate |= (u_int64_t)(ICC_V|XCC_V) << TSTATE_CCR_SHIFT;
220 			}
221 		}
222 	}
223 
224 	return 0;
225 }
226 
227 /*
228  * Emulate unimplemented instructions on earlier sparc chips.
229  */
230 int
231 emulinstr(pc, tf)
232 	vaddr_t pc;
233 	struct trapframe64 *tf;
234 {
235 	union instr code;
236 	int32_t rs1, rs2, rd;
237 	int error;
238 
239 	/* fetch and check the instruction that caused the fault */
240 	error = copyin((caddr_t) pc, &code.i_int, sizeof(code.i_int));
241 	if (error != 0) {
242 		DPRINTF(("emulinstr: Bad instruction fetch\n"));
243 		return (SIGILL);
244 	}
245 
246 	/* Only support format 2 */
247 	if (code.i_any.i_op != 2) {
248 		DPRINTF(("emulinstr: Not a format 2 instruction\n"));
249 		return (SIGILL);
250 	}
251 
252 	write_user_windows();
253 
254 	if ((error = readgpreg(tf, code.i_op3.i_rs1, &rs1)) != 0) {
255 		DPRINTF(("emulinstr: read rs1 %d\n", error));
256 		return (SIGILL);
257 	}
258 
259 	if ((error = decodeaddr(tf, &code, &rs2)) != 0) {
260 		DPRINTF(("emulinstr: decode addr %d\n", error));
261 		return (SIGILL);
262 	}
263 
264 	switch (code.i_op3.i_op3) {
265 	case IOP3_FLUSH:
266 /*		cpuinfo.cache_flush((caddr_t)(rs1 + rs2), 4); XXX */
267 		return (0);
268 
269 	default:
270 		if ((code.i_op3.i_op3 & 0x2a) != 0xa) {
271 			DPRINTF(("emulinstr: Unsupported op3 0x%x\n",
272 			    code.i_op3.i_op3));
273 			return (SIGILL);
274 		}
275 		else if ((error = muldiv(tf, &code, &rd, &rs1, &rs2)) != 0)
276 			return (SIGFPE);
277 	}
278 
279 	if ((error = writegpreg(tf, code.i_op3.i_rd, &rd)) != 0) {
280 		DPRINTF(("muldiv: write rd %d\n", error));
281 		return (SIGILL);
282 	}
283 
284 	return (0);
285 }
286 
287 #define	SIGN_EXT13(v)	(((int64_t)(v) << 51) >> 51)
288 
289 void
290 swap_quad(int64_t *p)
291 {
292 	int64_t t;
293 
294 	t = htole64(p[0]);
295 	p[0] = htole64(p[1]);
296 	p[1] = t;
297 }
298 
299 /*
300  * emulate STQF, STQFA, LDQF, and LDQFA
301  */
302 int
303 emul_qf(int32_t insv, struct proc *p, union sigval sv, struct trapframe *tf)
304 {
305 	extern struct fpstate64 initfpstate;
306 	struct fpstate64 *fs = p->p_md.md_fpstate;
307 	int64_t addr, buf[2];
308 	union instr ins;
309 	int freg, isload, err;
310 	u_int8_t asi;
311 
312 	ins.i_int = insv;
313 	freg = ins.i_op3.i_rd & ~1;
314 	freg |= (ins.i_op3.i_rd & 1) << 5;
315 
316 	if (ins.i_op3.i_op3 == IOP3_LDQF || ins.i_op3.i_op3 == IOP3_LDQFA)
317 		isload = 1;
318 	else
319 		isload = 0;
320 
321 	if (ins.i_op3.i_op3 == IOP3_STQF || ins.i_op3.i_op3 == IOP3_LDQF)
322 		asi = ASI_PRIMARY;
323 	else if (ins.i_loadstore.i_i)
324 		asi = (tf->tf_tstate & TSTATE_ASI) >> TSTATE_ASI_SHIFT;
325 	else
326 		asi = ins.i_asi.i_asi;
327 
328 	addr = tf->tf_global[ins.i_asi.i_rs1];
329 	if (ins.i_loadstore.i_i)
330 		addr += SIGN_EXT13(ins.i_simm13.i_simm13);
331 	else
332 		addr += tf->tf_global[ins.i_asi.i_rs2];
333 
334 	if (asi < ASI_PRIMARY) {
335 		/* privileged asi */
336 		KERNEL_LOCK();
337 		trapsignal(p, SIGILL, 0, ILL_PRVOPC, sv);
338 		KERNEL_UNLOCK();
339 		return (0);
340 	}
341 	if (asi > ASI_SECONDARY_NOFAULT_LITTLE ||
342 	    (asi > ASI_SECONDARY_NOFAULT && asi < ASI_PRIMARY_LITTLE)) {
343 		/* architecturally undefined user ASI's */
344 		goto segv;
345 	}
346 
347 	if ((freg & 3) != 0) {
348 		/* only valid for %fN where N % 4 = 0 */
349 		KERNEL_LOCK();
350 		trapsignal(p, SIGILL, 0, ILL_ILLOPN, sv);
351 		KERNEL_UNLOCK();
352 		return (0);
353 	}
354 
355 	if ((addr & 3) != 0) {
356 		/* request is not aligned */
357 		KERNEL_LOCK();
358 		trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);
359 		KERNEL_UNLOCK();
360 		return (0);
361 	}
362 
363 	fs = p->p_md.md_fpstate;
364 	if (fs == NULL) {
365 		KERNEL_LOCK();
366 		/* don't currently have an fpu context, get one */
367 		fs = malloc(sizeof(*fs), M_SUBPROC, M_WAITOK);
368 		*fs = initfpstate;
369 		fs->fs_qsize = 0;
370 		p->p_md.md_fpstate = fs;
371 		KERNEL_UNLOCK();
372 	} else
373 		fpusave_proc(p, 1);
374 
375 	/* Ok, try to do the actual operation (finally) */
376 	if (isload) {
377 		err = copyin((caddr_t)addr, buf, sizeof(buf));
378 		if (err != 0 && (asi & 2) == 0)
379 			goto segv;
380 		if (err == 0) {
381 			if (asi & 8)
382 				swap_quad(buf);
383 			bcopy(buf, &fs->fs_regs[freg], sizeof(buf));
384 		}
385 	} else {
386 		bcopy(&fs->fs_regs[freg], buf, sizeof(buf));
387 		if (asi & 8)
388 			swap_quad(buf);
389 		if (copyout(buf, (caddr_t)addr, sizeof(buf)) && (asi & 2) == 0)
390 			goto segv;
391 	}
392 
393 	return (1);
394 
395 segv:
396 	KERNEL_LOCK();
397 	trapsignal(p, SIGSEGV, isload ? VM_PROT_READ : VM_PROT_WRITE,
398 	    SEGV_MAPERR, sv);
399 	KERNEL_UNLOCK();
400 	return (0);
401 }
402 
403 int
404 emul_popc(int32_t insv, struct proc *p, union sigval sv, struct trapframe *tf)
405 {
406 	u_int64_t val, ret = 0;
407 	union instr ins;
408 
409 	ins.i_int = insv;
410 	if (ins.i_simm13.i_i == 0)
411 		val = tf->tf_global[ins.i_asi.i_rs2];
412 	else
413 		val = SIGN_EXT13(ins.i_simm13.i_simm13);
414 
415 	for (; val != 0; val >>= 1)
416 		ret += val & 1;
417 
418 	tf->tf_global[ins.i_asi.i_rd] = ret;
419 	return (1);
420 }
421