trap.c revision 1.27
1/* $OpenBSD: trap.c,v 1.27 2001/04/01 06:13:40 mickey Exp $ */ 2 3/* 4 * Copyright (c) 1998-2000 Michael Shalayeff 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Michael Shalayeff. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* #define INTRDEBUG */ 34/* #define TRAPDEBUG */ 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/kernel.h> 39#include <sys/syscall.h> 40#include <sys/ktrace.h> 41#include <sys/proc.h> 42#include <sys/signalvar.h> 43#include <sys/user.h> 44#include <sys/acct.h> 45#include <sys/signal.h> 46#include <sys/device.h> 47 48#include <net/netisr.h> 49 50#include <vm/vm.h> 51#include <vm/vm_kern.h> 52#include <uvm/uvm.h> 53 54#include <machine/iomod.h> 55#include <machine/cpufunc.h> 56#include <machine/reg.h> 57#include <machine/autoconf.h> 58 59#ifdef DDB 60#include <machine/db_machdep.h> 61#endif 62 63#if defined(INTRDEBUG) || defined(TRAPDEBUG) 64#include <ddb/db_output.h> 65#endif 66 67 68const char *trap_type[] = { 69 "invalid", 70 "HPMC", 71 "power failure", 72 "recovery counter", 73 "external interrupt", 74 "LPMC", 75 "ITLB miss fault", 76 "instruction protection", 77 "Illegal instruction", 78 "break instruction", 79 "privileged operation", 80 "privileged register", 81 "overflow", 82 "conditional", 83 "assist exception", 84 "DTLB miss", 85 "ITLB non-access miss", 86 "DTLB non-access miss", 87 "data protection/rights/alignment", 88 "data break", 89 "TLB dirty", 90 "page reference", 91 "assist emulation", 92 "higher-priv transfer", 93 "lower-priv transfer", 94 "taken branch", 95 "data access rights", 96 "data protection", 97 "unaligned data ref", 98}; 99int trap_types = sizeof(trap_type)/sizeof(trap_type[0]); 100 101u_int32_t sir; 102int want_resched, astpending; 103 104void pmap_hptdump __P((void)); 105void cpu_intr __P((struct trapframe *frame)); 106void syscall __P((struct trapframe *frame, int *args)); 107 108static __inline void 109userret (struct proc *p, register_t pc, u_quad_t oticks) 110{ 111 int sig; 112 113 /* take pending signals */ 114 while ((sig = CURSIG(p)) != 0) 115 postsig(sig); 116 117 p->p_priority = p->p_usrpri; 118 if (want_resched) { 119 register int s; 120 /* 121 * Since we are curproc, a clock interrupt could 122 * change our priority without changing run queues 123 * (the running process is not kept on a run queue). 124 * If this happened after we setrunqueue ourselves but 125 * before we switch()'ed, we might not be on the queue 126 * indicated by our priority. 127 */ 128 s = splstatclock(); 129 setrunqueue(p); 130 p->p_stats->p_ru.ru_nivcsw++; 131 mi_switch(); 132 splx(s); 133 while ((sig = CURSIG(p)) != 0) 134 postsig(sig); 135 } 136 137 /* 138 * If profiling, charge recent system time to the trapped pc. 139 */ 140 if (p->p_flag & P_PROFIL) { 141 extern int psratio; 142 143 addupc_task(p, pc, (int)(p->p_sticks - oticks) * psratio); 144 } 145 146 curpriority = p->p_priority; 147} 148 149void 150trap(type, frame) 151 int type; 152 struct trapframe *frame; 153{ 154 struct proc *p = curproc; 155 struct pcb *pcbp; 156 register vaddr_t va; 157 register vm_map_t map; 158 struct vmspace *vm; 159 register vm_prot_t vftype; 160 register pa_space_t space; 161 u_int opcode; 162 int ret; 163 union sigval sv; 164 int s, si; 165 const char *tts; 166 167 opcode = frame->tf_iir; 168 if (type == T_ITLBMISS || type == T_ITLBMISSNA) { 169 va = frame->tf_iioq_head; 170 space = frame->tf_iisq_head; 171 vftype = VM_PROT_READ; /* XXX VM_PROT_EXECUTE ??? */ 172 } else { 173 va = frame->tf_ior; 174 space = frame->tf_isr; 175 vftype = inst_store(opcode) ? VM_PROT_WRITE : VM_PROT_READ; 176 } 177 178 if (frame->tf_flags & TFF_LAST) 179 p->p_md.md_regs = frame; 180 181#ifdef TRAPDEBUG 182 if ((type & ~T_USER) > trap_types) 183 tts = "reserved"; 184 else 185 tts = trap_type[type & ~T_USER]; 186 187 if (type != T_INTERRUPT && (type & ~T_USER) != T_IBREAK) 188 db_printf("trap: %d, %s for %x:%x at %x:%x, fl=%x, fp=%p\n", 189 type, tts, space, va, frame->tf_iisq_head, 190 frame->tf_iioq_head, frame->tf_flags, frame); 191 else if ((type & ~T_USER) == T_IBREAK) 192 db_printf("trap: break instruction %x:%x at %x:%x, fp=%p\n", 193 break5(opcode), break13(opcode), 194 frame->tf_iisq_head, frame->tf_iioq_head, frame); 195 196 { 197 extern int etext; 198 if (frame < (struct trapframe *)&etext) { 199 printf("trap: bogus frame ptr %p\n", frame); 200 goto dead_end; 201 } 202 } 203#endif 204 switch (type) { 205 case T_NONEXIST: 206 case T_NONEXIST|T_USER: 207#ifndef DDB 208 /* we've got screwed up by the central scrutinizer */ 209 panic ("trap: elvis has just left the building!"); 210 break; 211#else 212 goto dead_end; 213#endif 214 case T_RECOVERY: 215 case T_RECOVERY|T_USER: 216#ifndef DDB 217 /* XXX will implement later */ 218 printf ("trap: handicapped"); 219 break; 220#else 221 goto dead_end; 222#endif 223 224#ifdef DIAGNOSTIC 225 case T_EXCEPTION: 226 panic("FPU/SFU emulation botch"); 227 228 /* these just can't happen ever */ 229 case T_PRIV_OP: 230 case T_PRIV_REG: 231 /* these just can't make it to the trap() ever */ 232 case T_HPMC: case T_HPMC | T_USER: 233 case T_EMULATION: case T_EMULATION | T_USER: 234#endif 235 case T_IBREAK: 236 case T_DATALIGN: 237 case T_DBREAK: 238 dead_end: 239#ifdef DDB 240 if (kdb_trap (type, va, frame)) { 241 if (type == T_IBREAK) { 242 /* skip break instruction */ 243 frame->tf_iioq_head = frame->tf_iioq_tail; 244 frame->tf_iioq_tail += 4; 245 } 246 return; 247 } 248#else 249 if (type == T_DATALIGN) 250 panic ("trap: %s at 0x%x", tts, va); 251 else 252 panic ("trap: no debugger for \"%s\" (%d)", tts, type); 253#endif 254 break; 255 256 case T_IBREAK | T_USER: 257 case T_DBREAK | T_USER: 258 /* pass to user debugger */ 259 break; 260 261 case T_EXCEPTION | T_USER: /* co-proc assist trap */ 262 sv.sival_int = va; 263 trapsignal(p, SIGFPE, type &~ T_USER, FPE_FLTINV, sv); 264 break; 265 266 case T_OVERFLOW | T_USER: 267 sv.sival_int = va; 268 trapsignal(p, SIGFPE, type &~ T_USER, FPE_INTOVF, sv); 269 break; 270 271 case T_CONDITION | T_USER: 272 break; 273 274 case T_ILLEGAL | T_USER: 275 sv.sival_int = va; 276 trapsignal(p, SIGILL, type &~ T_USER, ILL_ILLOPC, sv); 277 break; 278 279 case T_PRIV_OP | T_USER: 280 sv.sival_int = va; 281 trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVOPC, sv); 282 break; 283 284 case T_PRIV_REG | T_USER: 285 sv.sival_int = va; 286 trapsignal(p, SIGILL, type &~ T_USER, ILL_PRVREG, sv); 287 break; 288 289 /* these should never got here */ 290 case T_HIGHERPL | T_USER: 291 case T_LOWERPL | T_USER: 292 sv.sival_int = va; 293 trapsignal(p, SIGSEGV, type &~ T_USER, SEGV_ACCERR, sv); 294 break; 295 296 case T_IPROT | T_USER: 297 case T_DPROT | T_USER: 298 sv.sival_int = va; 299 trapsignal(p, SIGSEGV, vftype, SEGV_ACCERR, sv); 300 break; 301 302 case T_DATACC: case T_USER | T_DATACC: 303 case T_ITLBMISS: case T_USER | T_ITLBMISS: 304 case T_DTLBMISS: case T_USER | T_DTLBMISS: 305 case T_ITLBMISSNA: case T_USER | T_ITLBMISSNA: 306 case T_DTLBMISSNA: case T_USER | T_DTLBMISSNA: 307 case T_TLB_DIRTY: case T_USER | T_TLB_DIRTY: 308 va = hppa_trunc_page(va); 309 vm = p->p_vmspace; 310 311 if (!vm) { 312#ifdef TRAPDEBUG 313 printf("trap: no vm, p=%p\n", p); 314#endif 315 goto dead_end; 316 } 317 318 /* 319 * it could be a kernel map for exec_map faults 320 */ 321 if (!(type & T_USER) && space == HPPA_SID_KERNEL) 322 map = kernel_map; 323 else 324 map = &vm->vm_map; 325 326 if (map->pmap->pmap_space != space) { 327#ifdef TRAPDEBUG 328 printf("trap: space missmatch %d != %d\n", 329 space, map->pmap->pmap_space); 330#endif 331 /* actually dump the user, crap the kernel */ 332 goto dead_end; 333 } 334 335 ret = uvm_fault(map, va, 0, vftype); 336 337#ifdef TRAPDEBUG 338 printf("uvm_fault(%p, %x, %d, %d)=%d\n", 339 map, va, 0, vftype, ret); 340#endif 341 342 /* 343 * If this was a stack access we keep track of the maximum 344 * accessed stack size. Also, if uvm_fault gets a protection 345 * failure it is due to accessing the stack region outside 346 * the current limit and we need to reflect that as an access 347 * error. 348 */ 349 if (va >= (vaddr_t)vm->vm_maxsaddr + vm->vm_ssize) { 350 if (ret == KERN_SUCCESS) { 351 vsize_t nss = clrnd(btoc(va - USRSTACK + NBPG)); 352 if (nss > vm->vm_ssize) 353 vm->vm_ssize = nss; 354 } else if (ret == KERN_PROTECTION_FAILURE) 355 ret = KERN_INVALID_ADDRESS; 356 } 357 358 if (ret != KERN_SUCCESS) { 359 if (type & T_USER) { 360printf("trapsignal: uvm_fault\n"); 361 sv.sival_int = frame->tf_ior; 362 trapsignal(p, SIGSEGV, vftype, SEGV_MAPERR, sv); 363 } else { 364 if (p && p->p_addr->u_pcb.pcb_onfault) { 365#ifdef PMAPDEBUG 366 printf("trap: copyin/out %d\n",ret); 367#endif 368 pcbp = &p->p_addr->u_pcb; 369 frame->tf_iioq_tail = 4 + 370 (frame->tf_iioq_head = 371 pcbp->pcb_onfault); 372 pcbp->pcb_onfault = 0; 373 break; 374 } 375#if 1 376if (kdb_trap (type, va, frame)) 377 return; 378#else 379 panic("trap: uvm_fault(%p, %x, %d, %d): %d", 380 map, va, 0, vftype, ret); 381#endif 382 } 383 } 384 break; 385 386 case T_DATALIGN | T_USER: 387 sv.sival_int = va; 388 trapsignal(p, SIGBUS, vftype, BUS_ADRALN, sv); 389 break; 390 391 case T_INTERRUPT: 392 case T_INTERRUPT|T_USER: 393 frame->tf_flags |= TFF_INTR; 394 cpu_intr(frame); 395#if 0 396if (kdb_trap (type, va, frame)) 397return; 398#endif 399 /* FALLTHROUGH */ 400 case T_LOWERPL: 401 __asm __volatile ("ldcws 0(%1), %0" 402 : "=r" (si) : "r" (&sir)); 403 s = spl0(); 404 if (si & SIR_CLOCK) { 405 splclock(); 406 softclock(); 407 spl0(); 408 } 409 410 if (si & SIR_NET) { 411 register int ni; 412 /* use atomic "load & clear" */ 413 __asm __volatile ("ldcws 0(%1), %0" 414 : "=r" (ni) : "r" (&netisr)); 415 splnet(); 416#define DONETISR(m,c) if (ni & (1 << (m))) c() 417#include <net/netisr_dispatch.h> 418 } 419 splx(s); 420 break; 421 422 case T_DPROT: 423 case T_IPROT: 424 case T_OVERFLOW: 425 case T_CONDITION: 426 case T_ILLEGAL: 427 case T_HIGHERPL: 428 case T_TAKENBR: 429 case T_POWERFAIL: 430 case T_LPMC: 431 case T_PAGEREF: 432 case T_DATAPID: case T_DATAPID | T_USER: 433 if (0 /* T-chip */) { 434 break; 435 } 436 /* FALLTHROUGH to unimplemented */ 437 default: 438#if 1 439if (kdb_trap (type, va, frame)) 440 return; 441#endif 442 panic ("trap: unimplemented \'%s\' (%d)", tts, type); 443 } 444 445 if (type & T_USER) 446 userret(p, p->p_md.md_regs->tf_iioq_head, 0); 447} 448 449void 450child_return(p) 451 struct proc *p; 452{ 453 userret(p, p->p_md.md_regs->tf_iioq_head, 0); 454#ifdef KTRACE 455 if (KTRPOINT(p, KTR_SYSRET)) 456 ktrsysret(p, SYS_fork, 0, 0); 457#endif 458} 459 460/* 461 * call actual syscall routine 462 * from the low-level syscall handler: 463 * - all HPPA_FRAME_NARGS syscall's arguments supposed to be copied onto 464 * our stack, this wins compared to copyin just needed amount anyway 465 * - register args are copied onto stack too 466 */ 467void 468syscall(frame, args) 469 struct trapframe *frame; 470 int *args; 471{ 472 register struct proc *p; 473 register const struct sysent *callp; 474 int nsys, code, argsize, error; 475 int rval[2]; 476 477 uvmexp.syscalls++; 478 479 if (!USERMODE(frame->tf_iioq_head)) 480 panic("syscall"); 481 482 p = curproc; 483 p->p_md.md_regs = frame; 484 nsys = p->p_emul->e_nsysent; 485 callp = p->p_emul->e_sysent; 486 code = frame->tf_t1; 487 switch (code) { 488 case SYS_syscall: 489 code = *args; 490 args += 1; 491 break; 492 case SYS___syscall: 493 if (callp != sysent) 494 break; 495 code = *args; 496 args += 2; 497 } 498 499 if (code < 0 || code >= nsys) 500 callp += p->p_emul->e_nosys; /* bad syscall # */ 501 else 502 callp += code; 503 argsize = callp->sy_argsize; 504 505#ifdef SYSCALL_DEBUG 506 scdebug_call(p, code, args); 507#endif 508#ifdef KTRACE 509 if (KTRPOINT(p, KTR_SYSCALL)) 510 ktrsyscall(p, code, argsize, args); 511#endif 512 513 rval[0] = 0; 514 rval[1] = 0; 515 switch (error = (*callp->sy_call)(p, args, rval)) { 516 case 0: 517 p = curproc; /* changes on exec() */ 518 frame = p->p_md.md_regs; 519 frame->tf_ret0 = rval[0]; 520 frame->tf_ret1 = rval[1]; 521 frame->tf_t1 = 0; 522 break; 523 case ERESTART: 524 frame->tf_iioq_head -= 4; /* right? XXX */ 525 frame->tf_iioq_tail -= 4; /* right? XXX */ 526 break; 527 case EJUSTRETURN: 528 p = curproc; 529 break; 530 default: 531 if (p->p_emul->e_errno) 532 error = p->p_emul->e_errno[error]; 533 frame->tf_t1 = error; 534 break; 535 } 536#ifdef SYSCALL_DEBUG 537 scdebug_ret(p, code, error, rval); 538#endif 539 userret(p, frame->tf_iioq_head, 0); 540#ifdef KTRACE 541 if (KTRPOINT(p, KTR_SYSRET)) 542 ktrsysret(p, code, error, rval[0]); 543#endif 544} 545 546/* all the interrupts, minus cpu clock, which is the last */ 547struct cpu_intr_vector { 548 struct evcnt evcnt; 549 int pri; 550 int (*handler) __P((void *)); 551 void *arg; 552} cpu_intr_vectors[CPU_NINTS]; 553 554void * 555cpu_intr_establish(pri, irq, handler, arg, dv) 556 int pri, irq; 557 int (*handler) __P((void *)); 558 void *arg; 559 struct device *dv; 560{ 561 register struct cpu_intr_vector *iv; 562 563 if (0 <= irq && irq < CPU_NINTS && cpu_intr_vectors[irq].handler) 564 return NULL; 565 566 iv = &cpu_intr_vectors[irq]; 567 iv->pri = pri; 568 iv->handler = handler; 569 iv->arg = arg; 570 evcnt_attach(dv, dv->dv_xname, &iv->evcnt); 571 572 return iv; 573} 574 575void 576cpu_intr(frame) 577 struct trapframe *frame; 578{ 579 u_int32_t eirr; 580 register struct cpu_intr_vector *iv; 581 register int bit; 582 583 do { 584 mfctl(CR_EIRR, eirr); 585 eirr &= frame->tf_eiem; 586 bit = ffs(eirr) - 1; 587 if (bit >= 0) { 588 mtctl(1 << bit, CR_EIRR); 589 eirr &= ~(1 << bit); 590 /* ((struct iomod *)cpu_gethpa(0))->io_eir = 0; */ 591#ifdef INTRDEBUG 592 if (bit != 31) 593 db_printf ("cpu_intr: 0x%08x\n", (1 << bit)); 594#endif 595 iv = &cpu_intr_vectors[bit]; 596 if (iv->handler) { 597 register int s, r; 598 599 iv->evcnt.ev_count++; 600 s = splx(iv->pri); 601 /* no arg means pass the frame */ 602 r = (iv->handler)(iv->arg? iv->arg:frame); 603 splx(s); 604#ifdef INTRDEBUG 605 if (!r) 606 db_printf ("%s: can't handle interrupt\n", 607 iv->evcnt.ev_name); 608#endif 609 } 610#ifdef INTRDEBUG 611 else 612 db_printf ("cpu_intr: stray interrupt %d\n", bit); 613#endif 614 } 615 } while (eirr); 616} 617