Home | History | Annotate | Line # | Download | only in sparc
      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
     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
     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
     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
    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
    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
    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
    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
    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