/* * General trap (exception) handling function for mips. * This is called by the assembly-language exception handler once * the trapframe has been set up. */ void mips_trap(struct trapframe *tf) { u_int32_t code, isutlb, iskern; int savespl; /* The trap frame is supposed to be 37 registers long. */ assert(sizeof(struct trapframe)==(37*4)); /* Save the value of curspl, which belongs to the old context. */ savespl = curspl; /* Right now, interrupts should be off. */ curspl = SPL_HIGH; /* * Extract the exception code info from the register fields. */ code = (tf->tf_cause & CCA_CODE) >> CCA_CODESHIFT; isutlb = (tf->tf_cause & CCA_UTLB); iskern = (tf->tf_status & CST_KUp)==0; assert(code<NTRAPCODES); /* Make sure we haven't run off our stack */ if (curthread != NULL && curthread->t_stack != NULL) { assert((vaddr_t)tf > (vaddr_t)curthread->t_stack); assert((vaddr_t)tf < (vaddr_t)(curthread->t_stack+STACK_SIZE)); } /* Interrupt? Call the interrupt handler and return. */ if (code == EX_IRQ) { mips_interrupt(tf->tf_cause); goto done; } /* * While we're in the kernel, and not actually handling an * interrupt, leave spl where it was in the previous context, * which is probably low (interrupts on). */ splx(savespl); /* Syscall? Call the syscall handler and return. */ if (code == EX_SYS) { /* Interrupts should have been on while in user mode. */ assert(curspl==0); DEBUG(DB_SYSCALL, "syscall: #%d, args %x %x %x %x\n", tf->tf_v0, tf->tf_a0, tf->tf_a1, tf->tf_a2, tf->tf_a3); mips_syscall(tf); goto done; } /* * Ok, it wasn't any of the really easy cases. * Call vm_fault on the TLB exceptions. * Panic on the bus error exceptions. */ switch (code) { case EX_MOD: if (vm_fault(VM_FAULT_READONLY, tf->tf_vaddr)==0) { goto done; } break; case EX_TLBL: if (vm_fault(VM_FAULT_READ, tf->tf_vaddr)==0) { goto done; } break; case EX_TLBS: if (vm_fault(VM_FAULT_WRITE, tf->tf_vaddr)==0) { goto done; } break; case EX_IBE: case EX_DBE: /* * This means you loaded invalid TLB entries, or * touched invalid parts of the direct-mapped * segments. These are serious kernel errors, so * panic. * * The MIPS won't even tell you what invalid address * caused the bus error. */ panic("Bus error exception, PC=0x%x\n", tf->tf_epc); break; } /* * If we get to this point, it's a fatal fault - either it's * one of the other exceptions, like illegal instruction, or * it was a page fault we couldn't handle. */ if (!iskern) { /* * Fatal fault in user mode. * Kill the current user process. */ kill_curthread(tf->tf_epc, code, tf->tf_vaddr); goto done; } /* * Fatal fault in kernel mode. * * If pcb_badfaultfunc is set, we do not panic; badfaultfunc is * set by copyin/copyout and related functions to signify that * the addresses they're accessing are userlevel-supplied and * not trustable. What we actually want to do is resume * execution at the function pointed to by badfaultfunc. That's * going to be "copyfail" (see copyinout.c), which longjmps * back to copyin/copyout or wherever and returns EFAULT. * * Note that we do not just *call* this function, because that * won't necessarily do anything. We want the control flow * that is currently executing in copyin (or whichever), and * is stopped while we process the exception, to *teleport* to * copyerr. * * This is accomplished by changing tf->tf_epc and returning * from the exception handler. */ if (curthread != NULL && curthread->t_pcb.pcb_badfaultfunc != NULL) { tf->tf_epc = (vaddr_t) curthread->t_pcb.pcb_badfaultfunc; goto done; } /* * Really fatal kernel-mode fault. */ kprintf("panic: Fatal exception %u (%s) in kernel mode\n", code, trapcodenames[code]); kprintf("panic: EPC 0x%x, exception vaddr 0x%x\n", tf->tf_epc, tf->tf_vaddr); panic("I can't handle this... I think I'll just die now...\n"); done: /* Make sure interrupts are off */ splhigh(); /* * Restore previous context's curspl value. * * The previous context's actual interrupt status flag will * be restored by the RFE instruction at the end of trap return. */ curspl = savespl; /* * This assertion will fail if either * (1) curkstack is corrupted, or * (2) the trap frame is somehow on the wrong kernel stack. * * If curkstack is corrupted, the next trap back to the kernel * will (most likely) hang the system, so it's better to find * out now. */ assert(SAME_STACK(curkstack-1, (vaddr_t)tf)); }
/* * General trap (exception) handling function for mips. * This is called by the assembly-language exception handler once * the trapframe has been set up. */ void mips_trap(struct trapframe *tf) { uint32_t code; bool isutlb, iskern; int spl; /* The trap frame is supposed to be 37 registers long. */ KASSERT(sizeof(struct trapframe)==(37*4)); /* * Extract the exception code info from the register fields. */ code = (tf->tf_cause & CCA_CODE) >> CCA_CODESHIFT; isutlb = (tf->tf_cause & CCA_UTLB) != 0; iskern = (tf->tf_status & CST_KUp) == 0; KASSERT(code < NTRAPCODES); /* Make sure we haven't run off our stack */ if (curthread != NULL && curthread->t_stack != NULL) { KASSERT((vaddr_t)tf > (vaddr_t)curthread->t_stack); KASSERT((vaddr_t)tf < (vaddr_t)(curthread->t_stack + STACK_SIZE)); } /* Interrupt? Call the interrupt handler and return. */ if (code == EX_IRQ) { int old_in; bool doadjust; old_in = curthread->t_in_interrupt; curthread->t_in_interrupt = 1; /* * The processor has turned interrupts off; if the * currently recorded interrupt state is interrupts on * (spl of 0), adjust the recorded state to match, and * restore after processing the interrupt. * * How can we get an interrupt if the recorded state * is interrupts off? Well, as things currently stand * when the CPU finishes idling it flips interrupts on * and off to allow things to happen, but leaves * curspl high while doing so. * * While we're here, assert that the interrupt * handling code hasn't leaked a spinlock or an * splhigh(). */ if (curthread->t_curspl == 0) { KASSERT(curthread->t_curspl == 0); KASSERT(curthread->t_iplhigh_count == 0); curthread->t_curspl = IPL_HIGH; curthread->t_iplhigh_count++; doadjust = true; } else { doadjust = false; } mainbus_interrupt(tf); if (doadjust) { KASSERT(curthread->t_curspl == IPL_HIGH); KASSERT(curthread->t_iplhigh_count == 1); curthread->t_iplhigh_count--; curthread->t_curspl = 0; } curthread->t_in_interrupt = old_in; goto done2; } /* * The processor turned interrupts off when it took the trap. * * While we're in the kernel, and not actually handling an * interrupt, restore the interrupt state to where it was in * the previous context, which may be low (interrupts on). * * Do this by forcing splhigh(), which may do a redundant * cpu_irqoff() but forces the stored MI interrupt state into * sync, then restoring the previous state. */ spl = splhigh(); splx(spl); /* Syscall? Call the syscall handler and return. */ if (code == EX_SYS) { /* Interrupts should have been on while in user mode. */ KASSERT(curthread->t_curspl == 0); KASSERT(curthread->t_iplhigh_count == 0); DEBUG(DB_SYSCALL, "syscall: #%d, args %x %x %x %x\n", tf->tf_v0, tf->tf_a0, tf->tf_a1, tf->tf_a2, tf->tf_a3); syscall(tf); goto done; } /* * Ok, it wasn't any of the really easy cases. * Call vm_fault on the TLB exceptions. * Panic on the bus error exceptions. */ switch (code) { case EX_MOD: if (vm_fault(VM_FAULT_READONLY, tf->tf_vaddr)==0) { goto done; } break; case EX_TLBL: if (vm_fault(VM_FAULT_READ, tf->tf_vaddr)==0) { goto done; } break; case EX_TLBS: if (vm_fault(VM_FAULT_WRITE, tf->tf_vaddr)==0) { goto done; } break; case EX_IBE: case EX_DBE: /* * This means you loaded invalid TLB entries, or * touched invalid parts of the direct-mapped * segments. These are serious kernel errors, so * panic. * * The MIPS won't even tell you what invalid address * caused the bus error. */ panic("Bus error exception, PC=0x%x\n", tf->tf_epc); break; } /* * If we get to this point, it's a fatal fault - either it's * one of the other exceptions, like illegal instruction, or * it was a page fault we couldn't handle. */ if (!iskern) { /* * Fatal fault in user mode. * Kill the current user process. */ kill_curthread(tf->tf_epc, code, tf->tf_vaddr); goto done; } /* * Fatal fault in kernel mode. * * If pcb_badfaultfunc is set, we do not panic; badfaultfunc is * set by copyin/copyout and related functions to signify that * the addresses they're accessing are userlevel-supplied and * not trustable. What we actually want to do is resume * execution at the function pointed to by badfaultfunc. That's * going to be "copyfail" (see copyinout.c), which longjmps * back to copyin/copyout or wherever and returns EFAULT. * * Note that we do not just *call* this function, because that * won't necessarily do anything. We want the control flow * that is currently executing in copyin (or whichever), and * is stopped while we process the exception, to *teleport* to * copyfail. * * This is accomplished by changing tf->tf_epc and returning * from the exception handler. */ if (curthread != NULL && curthread->t_machdep.tm_badfaultfunc != NULL) { tf->tf_epc = (vaddr_t) curthread->t_machdep.tm_badfaultfunc; goto done; } /* * Really fatal kernel-mode fault. */ kprintf("panic: Fatal exception %u (%s) in kernel mode\n", code, trapcodenames[code]); kprintf("panic: EPC 0x%x, exception vaddr 0x%x\n", tf->tf_epc, tf->tf_vaddr); panic("I can't handle this... I think I'll just die now...\n"); done: /* * Turn interrupts off on the processor, without affecting the * stored interrupt state. */ cpu_irqoff(); done2: /* * The boot thread can get here (e.g. on interrupt return) but * since it doesn't go to userlevel, it can't be returning to * userlevel, so there's no need to set cputhreads[] and * cpustacks[]. Just return. */ if (curthread->t_stack == NULL) { return; } cputhreads[curcpu->c_number] = (vaddr_t)curthread; cpustacks[curcpu->c_number] = (vaddr_t)curthread->t_stack + STACK_SIZE; /* * This assertion will fail if either * (1) curthread->t_stack is corrupted, or * (2) the trap frame is somehow on the wrong kernel stack. * * If cpustacks[] is corrupted, the next trap back to the * kernel will (most likely) hang the system, so it's better * to find out now. */ KASSERT(SAME_STACK(cpustacks[curcpu->c_number]-1, (vaddr_t)tf)); }