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