예제 #1
0
/*
 * 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));
}
예제 #2
0
파일: trap.c 프로젝트: rootid/os161
/*
 * 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));
}