1 /* $OpenBSD: trap.c,v 1.135 2024/11/07 16:02:29 miod Exp $ */ 2 /* $NetBSD: trap.c,v 1.3 1996/10/13 03:31:37 christos Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 6 * Copyright (C) 1995, 1996 TooLs GmbH. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 #include <sys/param.h> 35 #include <sys/proc.h> 36 #include <sys/signalvar.h> 37 #include <sys/reboot.h> 38 #include <sys/systm.h> 39 #include <sys/user.h> 40 #include <sys/pool.h> 41 #include <sys/syscall.h> 42 #include <sys/syscall_mi.h> 43 44 #include <dev/cons.h> 45 46 #include <uvm/uvm_extern.h> 47 48 #include <machine/cpu.h> 49 #include <machine/fpu.h> 50 #include <machine/frame.h> 51 #include <machine/pcb.h> 52 #include <machine/psl.h> 53 #include <machine/trap.h> 54 #include <machine/db_machdep.h> 55 56 #include <ddb/db_extern.h> 57 #include <ddb/db_sym.h> 58 #include <ddb/db_output.h> 59 60 static int fix_unaligned(struct proc *p, struct trapframe *frame); 61 int badaddr(char *addr, u_int32_t len); 62 void trap(struct trapframe *frame); 63 64 /* XXX This definition should probably be somewhere else */ 65 #define FIRSTARG 3 /* first syscall argument is in reg 3 */ 66 67 #ifdef ALTIVEC 68 static int altivec_assist(struct proc *p, vaddr_t); 69 70 /* 71 * Save state of the vector processor, This is done lazily in the hope 72 * that few processes in the system will be using the vector unit 73 * and that the exception time taken to switch them will be less than 74 * the necessary time to save the vector on every context switch. 75 * 76 * Also note that in this version, the VRSAVE register is saved with 77 * the state of the current process holding the vector processor, 78 * and the contents of that register are not used to optimize the save. 79 * 80 * This can lead to VRSAVE corruption, data passing between processes, 81 * because this register is accessible without the MSR[VEC] bit set. 82 * To store/restore this cleanly a processor identifier bit would need 83 * to be saved and this register saved on every context switch. 84 * Since we do not use the information, we may be able to get by 85 * with not saving it rigorously. 86 */ 87 void 88 save_vec(struct proc *p) 89 { 90 struct pcb *pcb = &p->p_addr->u_pcb; 91 struct vreg *pcb_vr = pcb->pcb_vr; 92 u_int32_t oldmsr, msr; 93 94 /* first we enable vector so that we dont throw an exception 95 * in kernel mode 96 */ 97 oldmsr = ppc_mfmsr(); 98 msr = (oldmsr & ~PSL_EE) | PSL_VEC; 99 ppc_mtmsr(msr); 100 __asm__ volatile ("sync;isync"); 101 102 pcb->pcb_vr->vrsave = ppc_mfvrsave(); 103 104 #define STR(x) #x 105 #define SAVE_VEC_REG(reg, addr) \ 106 __asm__ volatile ("stvxl %0, 0, %1" :: "n"(reg),"r" (addr)); 107 108 SAVE_VEC_REG(0,&pcb_vr->vreg[0]); 109 SAVE_VEC_REG(1,&pcb_vr->vreg[1]); 110 SAVE_VEC_REG(2,&pcb_vr->vreg[2]); 111 SAVE_VEC_REG(3,&pcb_vr->vreg[3]); 112 SAVE_VEC_REG(4,&pcb_vr->vreg[4]); 113 SAVE_VEC_REG(5,&pcb_vr->vreg[5]); 114 SAVE_VEC_REG(6,&pcb_vr->vreg[6]); 115 SAVE_VEC_REG(7,&pcb_vr->vreg[7]); 116 SAVE_VEC_REG(8,&pcb_vr->vreg[8]); 117 SAVE_VEC_REG(9,&pcb_vr->vreg[9]); 118 SAVE_VEC_REG(10,&pcb_vr->vreg[10]); 119 SAVE_VEC_REG(11,&pcb_vr->vreg[11]); 120 SAVE_VEC_REG(12,&pcb_vr->vreg[12]); 121 SAVE_VEC_REG(13,&pcb_vr->vreg[13]); 122 SAVE_VEC_REG(14,&pcb_vr->vreg[14]); 123 SAVE_VEC_REG(15,&pcb_vr->vreg[15]); 124 SAVE_VEC_REG(16,&pcb_vr->vreg[16]); 125 SAVE_VEC_REG(17,&pcb_vr->vreg[17]); 126 SAVE_VEC_REG(18,&pcb_vr->vreg[18]); 127 SAVE_VEC_REG(19,&pcb_vr->vreg[19]); 128 SAVE_VEC_REG(20,&pcb_vr->vreg[20]); 129 SAVE_VEC_REG(21,&pcb_vr->vreg[21]); 130 SAVE_VEC_REG(22,&pcb_vr->vreg[22]); 131 SAVE_VEC_REG(23,&pcb_vr->vreg[23]); 132 SAVE_VEC_REG(24,&pcb_vr->vreg[24]); 133 SAVE_VEC_REG(25,&pcb_vr->vreg[25]); 134 SAVE_VEC_REG(26,&pcb_vr->vreg[26]); 135 SAVE_VEC_REG(27,&pcb_vr->vreg[27]); 136 SAVE_VEC_REG(28,&pcb_vr->vreg[28]); 137 SAVE_VEC_REG(29,&pcb_vr->vreg[29]); 138 SAVE_VEC_REG(30,&pcb_vr->vreg[30]); 139 SAVE_VEC_REG(31,&pcb_vr->vreg[31]); 140 __asm__ volatile ("mfvscr 0"); 141 SAVE_VEC_REG(0,&pcb_vr->vscr); 142 143 curcpu()->ci_vecproc = NULL; 144 pcb->pcb_veccpu = NULL; 145 146 /* fix kernel msr back */ 147 ppc_mtmsr(oldmsr); 148 } 149 150 /* 151 * Copy the context of a given process into the vector registers. 152 */ 153 void 154 enable_vec(struct proc *p) 155 { 156 struct pcb *pcb = &p->p_addr->u_pcb; 157 struct vreg *pcb_vr; 158 struct cpu_info *ci = curcpu(); 159 u_int32_t oldmsr, msr; 160 161 /* If this is the very first altivec instruction executed 162 * by this process, create a context. 163 */ 164 if (pcb->pcb_vr == NULL) 165 pcb->pcb_vr = pool_get(&ppc_vecpl, PR_WAITOK | PR_ZERO); 166 pcb_vr = pcb->pcb_vr; 167 168 if (curcpu()->ci_vecproc != NULL || pcb->pcb_veccpu != NULL) 169 printf("attempting to restore vector in use vecproc %p" 170 " veccpu %p\n", curcpu()->ci_vecproc, pcb->pcb_veccpu); 171 172 /* first we enable vector so that we dont throw an exception 173 * in kernel mode 174 */ 175 oldmsr = ppc_mfmsr(); 176 msr = (oldmsr & ~PSL_EE) | PSL_VEC; 177 ppc_mtmsr(msr); 178 __asm__ volatile ("sync;isync"); 179 ci->ci_vecproc = p; 180 pcb->pcb_veccpu = ci; 181 182 #define LOAD_VEC_REG(reg, addr) \ 183 __asm__ volatile ("lvxl %0, 0, %1" :: "n"(reg), "r" (addr)); 184 185 LOAD_VEC_REG(0, &pcb_vr->vscr); 186 __asm__ volatile ("mtvscr 0"); 187 ppc_mtvrsave(pcb_vr->vrsave); 188 189 LOAD_VEC_REG(0, &pcb_vr->vreg[0]); 190 LOAD_VEC_REG(1, &pcb_vr->vreg[1]); 191 LOAD_VEC_REG(2, &pcb_vr->vreg[2]); 192 LOAD_VEC_REG(3, &pcb_vr->vreg[3]); 193 LOAD_VEC_REG(4, &pcb_vr->vreg[4]); 194 LOAD_VEC_REG(5, &pcb_vr->vreg[5]); 195 LOAD_VEC_REG(6, &pcb_vr->vreg[6]); 196 LOAD_VEC_REG(7, &pcb_vr->vreg[7]); 197 LOAD_VEC_REG(8, &pcb_vr->vreg[8]); 198 LOAD_VEC_REG(9, &pcb_vr->vreg[9]); 199 LOAD_VEC_REG(10, &pcb_vr->vreg[10]); 200 LOAD_VEC_REG(11, &pcb_vr->vreg[11]); 201 LOAD_VEC_REG(12, &pcb_vr->vreg[12]); 202 LOAD_VEC_REG(13, &pcb_vr->vreg[13]); 203 LOAD_VEC_REG(14, &pcb_vr->vreg[14]); 204 LOAD_VEC_REG(15, &pcb_vr->vreg[15]); 205 LOAD_VEC_REG(16, &pcb_vr->vreg[16]); 206 LOAD_VEC_REG(17, &pcb_vr->vreg[17]); 207 LOAD_VEC_REG(18, &pcb_vr->vreg[18]); 208 LOAD_VEC_REG(19, &pcb_vr->vreg[19]); 209 LOAD_VEC_REG(20, &pcb_vr->vreg[20]); 210 LOAD_VEC_REG(21, &pcb_vr->vreg[21]); 211 LOAD_VEC_REG(22, &pcb_vr->vreg[22]); 212 LOAD_VEC_REG(23, &pcb_vr->vreg[23]); 213 LOAD_VEC_REG(24, &pcb_vr->vreg[24]); 214 LOAD_VEC_REG(25, &pcb_vr->vreg[25]); 215 LOAD_VEC_REG(26, &pcb_vr->vreg[26]); 216 LOAD_VEC_REG(27, &pcb_vr->vreg[27]); 217 LOAD_VEC_REG(28, &pcb_vr->vreg[28]); 218 LOAD_VEC_REG(29, &pcb_vr->vreg[29]); 219 LOAD_VEC_REG(30, &pcb_vr->vreg[30]); 220 LOAD_VEC_REG(31, &pcb_vr->vreg[31]); 221 222 /* fix kernel msr back */ 223 ppc_mtmsr(oldmsr); 224 } 225 #endif /* ALTIVEC */ 226 227 void 228 trap(struct trapframe *frame) 229 { 230 struct cpu_info *ci = curcpu(); 231 struct proc *p = curproc; 232 int type = frame->exc; 233 union sigval sv; 234 const char *name; 235 db_expr_t offset; 236 faultbuf *fb; 237 struct vm_map *map; 238 vaddr_t va; 239 int access_type; 240 const struct sysent *callp = sysent; 241 register_t code, error; 242 register_t *params, rval[2]; 243 244 if (frame->srr1 & PSL_PR) { 245 type |= EXC_USER; 246 refreshcreds(p); 247 } 248 249 switch (type) { 250 case EXC_TRC|EXC_USER: 251 sv.sival_int = frame->srr0; 252 trapsignal(p, SIGTRAP, type, TRAP_TRACE, sv); 253 break; 254 case EXC_MCHK: 255 if ((fb = p->p_addr->u_pcb.pcb_onfault)) { 256 p->p_addr->u_pcb.pcb_onfault = 0; 257 frame->srr0 = fb->pc; /* PC */ 258 frame->fixreg[1] = fb->sp; /* SP */ 259 frame->fixreg[3] = 1; /* != 0 */ 260 frame->cr = fb->cr; 261 bcopy(&fb->regs[0], &frame->fixreg[13], 19*4); 262 return; 263 } 264 goto brain_damage; 265 266 case EXC_DSI: 267 map = kernel_map; 268 va = frame->dar; 269 if ((va >> ADDR_SR_SHIFT) == PPC_USER_SR) { 270 sr_t user_sr; 271 272 asm ("mfsr %0, %1" : "=r"(user_sr) : "K"(PPC_USER_SR)); 273 va &= ADDR_PIDX | ADDR_POFF; 274 va |= user_sr << ADDR_SR_SHIFT; 275 map = &p->p_vmspace->vm_map; 276 if (pte_spill_v(map->pmap, va, frame->dsisr, 0)) 277 return; 278 } 279 if (frame->dsisr & DSISR_STORE) 280 access_type = PROT_WRITE; 281 else 282 access_type = PROT_READ; 283 284 error = uvm_fault(map, trunc_page(va), 0, access_type); 285 if (error == 0) 286 return; 287 288 if ((fb = p->p_addr->u_pcb.pcb_onfault)) { 289 p->p_addr->u_pcb.pcb_onfault = 0; 290 frame->srr0 = fb->pc; /* PC */ 291 frame->fixreg[1] = fb->sp; /* SP */ 292 frame->fixreg[3] = 1; /* != 0 */ 293 frame->cr = fb->cr; 294 bcopy(&fb->regs[0], &frame->fixreg[13], 19*4); 295 return; 296 } 297 map = kernel_map; 298 goto brain_damage; 299 300 case EXC_DSI|EXC_USER: 301 /* Try spill handler first */ 302 if (pte_spill_v(p->p_vmspace->vm_map.pmap, 303 frame->dar, frame->dsisr, 0)) 304 break; 305 306 if (!uvm_map_inentry(p, &p->p_spinentry, PROC_STACK(p), 307 "[%s]%d/%d sp=%lx inside %lx-%lx: not MAP_STACK\n", 308 uvm_map_inentry_sp, p->p_vmspace->vm_map.sserial)) 309 goto out; 310 311 if (frame->dsisr & DSISR_STORE) 312 access_type = PROT_WRITE; 313 else 314 access_type = PROT_READ; 315 316 error = uvm_fault(&p->p_vmspace->vm_map, 317 trunc_page(frame->dar), 0, access_type); 318 if (error == 0) { 319 uvm_grow(p, frame->dar); 320 break; 321 } 322 323 /* 324 * keep this for later in case we want it later. 325 */ 326 sv.sival_int = frame->dar; 327 trapsignal(p, SIGSEGV, access_type, SEGV_MAPERR, sv); 328 break; 329 330 case EXC_ISI|EXC_USER: 331 /* Try spill handler */ 332 if (pte_spill_v(p->p_vmspace->vm_map.pmap, 333 frame->srr0, 0, 1)) 334 break; 335 336 access_type = PROT_EXEC; 337 338 error = uvm_fault(&p->p_vmspace->vm_map, 339 trunc_page(frame->srr0), 0, access_type); 340 341 if (error == 0) { 342 uvm_grow(p, frame->srr0); 343 break; 344 } 345 sv.sival_int = frame->srr0; 346 trapsignal(p, SIGSEGV, PROT_EXEC, SEGV_MAPERR, sv); 347 break; 348 349 case EXC_MCHK|EXC_USER: 350 /* XXX Likely that returning from this trap is bogus... */ 351 /* XXX Have to make sure that sigreturn does the right thing. */ 352 sv.sival_int = frame->srr0; 353 trapsignal(p, SIGSEGV, PROT_EXEC, SEGV_MAPERR, sv); 354 break; 355 356 case EXC_SC|EXC_USER: 357 uvmexp.syscalls++; 358 359 code = frame->fixreg[0]; 360 if (code <= 0 || code >= SYS_MAXSYSCALL) { 361 error = ENOSYS; 362 goto bad; 363 } 364 365 callp += code; 366 367 params = frame->fixreg + FIRSTARG; 368 369 rval[0] = 0; 370 rval[1] = frame->fixreg[FIRSTARG + 1]; 371 372 error = mi_syscall(p, code, callp, params, rval); 373 374 switch (error) { 375 case 0: 376 frame->fixreg[0] = error; 377 frame->fixreg[FIRSTARG] = rval[0]; 378 frame->fixreg[FIRSTARG + 1] = rval[1]; 379 frame->cr &= ~0x10000000; 380 break; 381 case ERESTART: 382 /* 383 * Set user's pc back to redo the system call. 384 */ 385 frame->srr0 -= 4; 386 break; 387 case EJUSTRETURN: 388 /* nothing to do */ 389 break; 390 default: 391 frame->fixreg[FIRSTARG + 1] = rval[1]; 392 bad: 393 frame->fixreg[0] = error; 394 frame->fixreg[FIRSTARG] = error; 395 frame->cr |= 0x10000000; 396 break; 397 } 398 399 mi_syscall_return(p, code, error, rval); 400 goto finish; 401 402 case EXC_FPU|EXC_USER: 403 if (ci->ci_fpuproc) 404 save_fpu(); 405 uvmexp.fpswtch++; 406 enable_fpu(p); 407 break; 408 409 case EXC_ALI|EXC_USER: 410 /* alignment exception 411 * we check to see if this can be fixed up 412 * by the code that fixes the typical gcc misaligned code 413 * then kill the process if not. 414 */ 415 if (fix_unaligned(p, frame) == 0) 416 frame->srr0 += 4; 417 else { 418 sv.sival_int = frame->srr0; 419 trapsignal(p, SIGBUS, PROT_EXEC, BUS_ADRALN, sv); 420 } 421 break; 422 423 default: 424 425 brain_damage: 426 #ifdef DDB 427 /* set up registers */ 428 db_save_regs(frame); 429 db_find_sym_and_offset(frame->srr0, &name, &offset); 430 if (!name) { 431 name = "0"; 432 offset = frame->srr0; 433 } 434 #else 435 name = "0"; 436 offset = frame->srr0; 437 #endif 438 panic("trap type %x srr1 %x at %x (%s+0x%lx) lr %lx", 439 type, frame->srr1, frame->srr0, name, offset, frame->lr); 440 441 case EXC_PGM|EXC_USER: 442 if (frame->srr1 & (1<<(31-14))) { 443 sv.sival_int = frame->srr0; 444 trapsignal(p, SIGTRAP, type, TRAP_BRKPT, sv); 445 break; 446 } 447 sv.sival_int = frame->srr0; 448 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv); 449 break; 450 451 case EXC_PGM: 452 /* should check for correct byte here or panic */ 453 #ifdef DDB 454 db_save_regs(frame); 455 db_active++; 456 cnpollc(TRUE); 457 db_trap(T_BREAKPOINT, 0); 458 cnpollc(FALSE); 459 db_active--; 460 #else 461 panic("trap EXC_PGM"); 462 #endif 463 break; 464 465 /* This is not really a perf exception, but is an ALTIVEC unavail 466 * if we do not handle it, kill the process with illegal instruction. 467 */ 468 case EXC_PERF|EXC_USER: 469 #ifdef ALTIVEC 470 case EXC_VEC|EXC_USER: 471 if (ci->ci_vecproc) 472 save_vec(ci->ci_vecproc); 473 474 enable_vec(p); 475 break; 476 #else /* ALTIVEC */ 477 sv.sival_int = frame->srr0; 478 trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv); 479 break; 480 #endif 481 482 case EXC_VECAST_G4|EXC_USER: 483 case EXC_VECAST_G5|EXC_USER: 484 #ifdef ALTIVEC 485 if (altivec_assist(p, (vaddr_t)frame->srr0) == 0) { 486 frame->srr0 += 4; 487 break; 488 } 489 #endif 490 sv.sival_int = frame->srr0; 491 trapsignal(p, SIGFPE, 0, FPE_FLTRES, sv); 492 break; 493 494 case EXC_AST|EXC_USER: 495 p->p_md.md_astpending = 0; /* we are about to do it */ 496 uvmexp.softs++; 497 mi_ast(p, curcpu()->ci_want_resched); 498 break; 499 } 500 501 out: 502 userret(p); 503 504 finish: 505 /* 506 * If someone stole the fpu while we were away, disable it 507 */ 508 if (p != ci->ci_fpuproc) 509 frame->srr1 &= ~PSL_FP; 510 else if (p->p_addr->u_pcb.pcb_flags & PCB_FPU) 511 frame->srr1 |= PSL_FP; 512 513 #ifdef ALTIVEC 514 /* 515 * If someone stole the vector unit while we were away, disable it 516 */ 517 if (p == ci->ci_vecproc) 518 frame->srr1 |= PSL_VEC; 519 else 520 frame->srr1 &= ~PSL_VEC; 521 #endif /* ALTIVEC */ 522 } 523 524 void 525 child_return(void *arg) 526 { 527 struct proc *p = (struct proc *)arg; 528 struct trapframe *tf = trapframe(p); 529 530 tf->fixreg[0] = 0; 531 tf->fixreg[FIRSTARG] = 0; 532 tf->cr &= ~0x10000000; 533 /* Disable FPU, VECT, as we can't be fpuproc */ 534 tf->srr1 &= ~(PSL_FP|PSL_VEC); 535 536 KERNEL_UNLOCK(); 537 538 mi_child_return(p); 539 } 540 541 int 542 badaddr(char *addr, u_int32_t len) 543 { 544 faultbuf env; 545 u_int32_t v; 546 void *oldh = curpcb->pcb_onfault; 547 548 if (setfault(&env)) { 549 curpcb->pcb_onfault = oldh; 550 return EFAULT; 551 } 552 switch(len) { 553 case 4: 554 v = *((volatile u_int32_t *)addr); 555 break; 556 case 2: 557 v = *((volatile u_int16_t *)addr); 558 break; 559 default: 560 v = *((volatile u_int8_t *)addr); 561 break; 562 } 563 /* Make sure all loads retire before turning off fault handling!! */ 564 __asm__ volatile ("sync"); 565 curpcb->pcb_onfault = oldh; 566 return(0); 567 } 568 569 570 /* 571 * For now, this only deals with the particular unaligned access case 572 * that gcc tends to generate. Eventually it should handle all of the 573 * possibilities that can happen on a 32-bit PowerPC in big-endian mode. 574 */ 575 576 static int 577 fix_unaligned(struct proc *p, struct trapframe *frame) 578 { 579 int indicator = EXC_ALI_OPCODE_INDICATOR(frame->dsisr); 580 struct cpu_info *ci = curcpu(); 581 int reg; 582 double *fpr; 583 584 switch (indicator) { 585 case EXC_ALI_LFD: 586 case EXC_ALI_STFD: 587 reg = EXC_ALI_RST(frame->dsisr); 588 fpr = &p->p_addr->u_pcb.pcb_fpu.fpr[reg]; 589 590 /* Juggle the FPU to ensure that we've initialized 591 * the FPRs, and that their current state is in 592 * the PCB. 593 */ 594 if (ci->ci_fpuproc != p) { 595 if (ci->ci_fpuproc) 596 save_fpu(); 597 enable_fpu(p); 598 } 599 save_fpu(); 600 601 if (indicator == EXC_ALI_LFD) { 602 if (copyin((void *)frame->dar, fpr, 603 sizeof(double)) != 0) 604 return -1; 605 } else { 606 if (copyout(fpr, (void *)frame->dar, 607 sizeof(double)) != 0) 608 return -1; 609 } 610 enable_fpu(p); 611 return 0; 612 } 613 return -1; 614 } 615 616 #ifdef ALTIVEC 617 static inline int 618 copyinsn(struct proc *p, vaddr_t uva, int *insn) 619 { 620 struct vm_map *map = &p->p_vmspace->vm_map; 621 int error = 0; 622 623 if (__predict_false((uva & 3) != 0)) 624 return EFAULT; 625 626 do { 627 if (pmap_copyinsn(map->pmap, uva, (uint32_t *)insn) == 0) 628 break; 629 error = uvm_fault(map, trunc_page(uva), 0, PROT_EXEC); 630 } while (error == 0); 631 632 return error; 633 } 634 635 static int 636 altivec_assist(struct proc *p, vaddr_t user_pc) 637 { 638 /* These labels are in vecast.S */ 639 void vecast_asm(uint32_t, void *); 640 void vecast_vaddfp(void); 641 void vecast_vsubfp(void); 642 void vecast_vmaddfp(void); 643 void vecast_vnmsubfp(void); 644 void vecast_vrefp(void); 645 void vecast_vrsqrtefp(void); 646 void vecast_vlogefp(void); 647 void vecast_vexptefp(void); 648 void vecast_vctuxs(void); 649 void vecast_vctsxs(void); 650 651 uint32_t insn, op, va, vc, lo; 652 int error; 653 void (*lab)(void); 654 655 error = copyinsn(p, user_pc, &insn); 656 if (error) 657 return error; 658 op = (insn & 0xfc000000) >> 26; /* primary opcode */ 659 va = (insn & 0x001f0000) >> 16; /* vector A */ 660 vc = (insn & 0x000007c0) >> 6; /* vector C or extended opcode */ 661 lo = insn & 0x0000003f; /* extended opcode */ 662 663 /* Stop if this isn't an altivec instruction. */ 664 if (op != 4) 665 return EINVAL; 666 667 /* Decide which instruction to emulate. */ 668 lab = NULL; 669 switch (lo) { 670 case 10: 671 switch (vc) { 672 case 0: 673 lab = vecast_vaddfp; 674 break; 675 case 1: 676 lab = vecast_vsubfp; 677 break; 678 case 4: 679 if (va == 0) 680 lab = vecast_vrefp; 681 break; 682 case 5: 683 if (va == 0) 684 lab = vecast_vrsqrtefp; 685 break; 686 case 6: 687 if (va == 0) 688 lab = vecast_vexptefp; 689 break; 690 case 7: 691 if (va == 0) 692 lab = vecast_vlogefp; 693 break; 694 case 14: 695 lab = vecast_vctuxs; 696 break; 697 case 15: 698 lab = vecast_vctsxs; 699 break; 700 } 701 break; 702 case 46: 703 lab = vecast_vmaddfp; 704 break; 705 case 47: 706 lab = vecast_vnmsubfp; 707 break; 708 } 709 710 if (lab) { 711 vecast_asm(insn, lab); /* Emulate it. */ 712 return 0; 713 } else 714 return EINVAL; 715 } 716 #endif 717