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