コード例 #1
0
ファイル: m197_machdep.c プロジェクト: enukane/openbsd-work
int
m197_ipi_handler(struct trapframe *eframe)
{
	struct cpu_info *ci = curcpu();
	int ipi;
	u_int32_t arg1, arg2;

	if ((ipi = atomic_clear_int(&ci->ci_ipi)) == 0)
		return 1;

	/*
	 * Synchronous IPIs. There can only be one pending at the same time,
	 * sending processor will wait for us to have processed the current
	 * one before sending a new one.
	 * We process them ASAP, ignoring any other pending ipi - sender will
	 * take care of resending an ipi if necessary.
	 */
	if (ipi & CI_IPI_SYNCHRONOUS) {
		/* no need to use atomic ops, the other cpu waits */
		/* leave asynchronous ipi pending */
		ci->ci_ipi = ipi & ~CI_IPI_SYNCHRONOUS;

		arg1 = ci->ci_ipi_arg1;
		arg2 = ci->ci_ipi_arg2;

		if (ipi & CI_IPI_TLB_FLUSH_KERNEL) {
			cmmu_tlbis(ci->ci_cpuid, arg1, arg2);
		}
		else if (ipi & CI_IPI_TLB_FLUSH_USER) {
			cmmu_tlbiu(ci->ci_cpuid, arg1, arg2);
		}
		else if (ipi & CI_IPI_CACHE_FLUSH) {
			cmmu_cache_wbinv(ci->ci_cpuid, arg1, arg2);
		}
		else if (ipi & CI_IPI_ICACHE_FLUSH) {
			cmmu_icache_inv(ci->ci_cpuid, arg1, arg2);
		}
		else if (ipi & CI_IPI_DMA_CACHECTL) {
			dma_cachectl_local(arg1, arg2, DMA_CACHE_INV);
		}

		return 0;
	}

	/*
	 * Asynchronous IPIs. We can have as many bits set as possible.
	 */

	if (ipi & CI_IPI_CLOCK) {
		/*
		 * Even if the current spl level would allow it, we can
		 * not run the clock handlers from there because we would
		 * need to grab the kernel lock, which might already
		 * held by the other processor.
		 *
		 * Instead, schedule a soft interrupt. But remember the
		 * important fields from the exception frame first, so
		 * that a valid clockframe can be reconstructed from the
		 * soft interrupt handler (which can not get an exception
		 * frame).
		 */
		if (ipi & CI_IPI_HARDCLOCK) {
			ci->ci_h_sxip = eframe->tf_sxip;
			ci->ci_h_epsr = eframe->tf_epsr;
		}
		if (ipi & CI_IPI_STATCLOCK) {
			ci->ci_s_sxip = eframe->tf_sxip;
			ci->ci_s_epsr = eframe->tf_epsr;
		}

		/* inflict ourselves a soft ipi */
		ci->ci_softipi_cb = m197_soft_ipi;
	}

	if (ipi & CI_IPI_DDB) {
#ifdef DDB
		/*
		 * Another processor has entered DDB. Spin on the ddb lock
		 * until it is done.
		 */
		extern struct __mp_lock ddb_mp_lock;

		ci->ci_ddb_state = CI_DDB_PAUSE;

		__mp_lock(&ddb_mp_lock);
		__mp_unlock(&ddb_mp_lock);

		ci->ci_ddb_state = CI_DDB_RUNNING;

		/*
		 * If ddb is hoping to us, it's our turn to enter ddb now.
		 */
		if (ci->ci_cpuid == ddb_mp_nextcpu)
			Debugger();
#endif
	}
	if (ipi & CI_IPI_NOTIFY) {
		/* nothing to do! */
	}

	return 1;
}
コード例 #2
0
void
m88110_trap(u_int type, struct trapframe *frame)
{
	struct proc *p;
	struct vm_map *map;
	vaddr_t va, pcb_onfault;
	vm_prot_t ftype;
	int fault_type;
	u_long fault_code;
	vaddr_t fault_addr;
	struct vmspace *vm;
	union sigval sv;
	int result;
#ifdef DDB
        int s;
	u_int psr;
#endif
	int sig = 0;

	uvmexp.traps++;
	if ((p = curproc) == NULL)
		p = &proc0;

	fault_type = SI_NOINFO;
	fault_code = 0;
	fault_addr = frame->tf_exip & XIP_ADDR;

	/*
	 * 88110 errata #16 (4.2) or #3 (5.1.1):
	 * ``bsr, br, bcnd, jsr and jmp instructions with the .n extension
	 *   can cause the enip value to be incremented by 4 incorrectly
	 *   if the instruction in the delay slot is the first word of a
	 *   page which misses in the mmu and results in a hardware
	 *   tablewalk which encounters an exception or an invalid
	 *   descriptor.  The exip value in this case will point to the
	 *   first word of the page, and the D bit will be set.
	 *
	 *   Note: if the instruction is a jsr.n r1, r1 will be overwritten
	 *   with erroneous data.  Therefore, no recovery is possible. Do
	 *   not allow this instruction to occupy the last word of a page.
	 *
	 *   Suggested fix: recover in general by backing up the exip by 4
	 *   and clearing the delay bit before an rte when the lower 3 hex
	 *   digits of the exip are 001.''
	 */
	if ((frame->tf_exip & PAGE_MASK) == 0x00000001 && type == T_INSTFLT) {
		u_int instr;

		/*
		 * Note that we have initialized fault_addr above, so that
		 * signals provide the correct address if necessary.
		 */
		frame->tf_exip = (frame->tf_exip & ~1) - 4;

		/*
		 * Check the instruction at the (backed up) exip.
		 * If it is a jsr.n, abort.
		 */
		if (!USERMODE(frame->tf_epsr)) {
			instr = *(u_int *)fault_addr;
			if (instr == 0xf400cc01)
				panic("mc88110 errata #16, exip %p enip %p",
				    (frame->tf_exip + 4) | 1, frame->tf_enip);
		} else {
			/* copyin here should not fail */
			if (copyin((const void *)frame->tf_exip, &instr,
			    sizeof instr) == 0 &&
			    instr == 0xf400cc01) {
				uprintf("mc88110 errata #16, exip %p enip %p",
				    (frame->tf_exip + 4) | 1, frame->tf_enip);
				sig = SIGILL;
			}
		}
	}

	if (USERMODE(frame->tf_epsr)) {
		type += T_USER;
		p->p_md.md_tf = frame;	/* for ptrace/signals */
	}

	if (sig != 0)
		goto deliver;

	switch (type) {
	default:
lose:
		panictrap(frame->tf_vector, frame);
		break;
		/*NOTREACHED*/

#ifdef DEBUG
	case T_110_DRM+T_USER:
	case T_110_DRM:
		printf("DMMU read miss: Hardware Table Searches should be enabled!\n");
		goto lose;
	case T_110_DWM+T_USER:
	case T_110_DWM:
		printf("DMMU write miss: Hardware Table Searches should be enabled!\n");
		goto lose;
	case T_110_IAM+T_USER:
	case T_110_IAM:
		printf("IMMU miss: Hardware Table Searches should be enabled!\n");
		goto lose;
#endif

#ifdef DDB
	case T_KDB_TRACE:
		s = splhigh();
		set_psr((psr = get_psr()) & ~PSR_IND);
		ddb_break_trap(T_KDB_TRACE, (db_regs_t*)frame);
		set_psr(psr);
		splx(s);
		return;
	case T_KDB_BREAK:
		s = splhigh();
		set_psr((psr = get_psr()) & ~PSR_IND);
		ddb_break_trap(T_KDB_BREAK, (db_regs_t*)frame);
		set_psr(psr);
		splx(s);
		return;
	case T_KDB_ENTRY:
		s = splhigh();
		set_psr((psr = get_psr()) & ~PSR_IND);
		ddb_entry_trap(T_KDB_ENTRY, (db_regs_t*)frame);
		set_psr(psr);
		/* skip trap instruction */
		m88110_skip_insn(frame);
		splx(s);
		return;
#endif /* DDB */
	case T_ILLFLT:
		/*
		 * The 88110 seems to trigger an instruction fault in
		 * supervisor mode when running the following sequence:
		 *
		 *	bcnd.n cond, reg, 1f
		 *	arithmetic insn
		 *	...
		 *  	the same exact arithmetic insn
		 *  1:	another arithmetic insn stalled by the previous one
		 *	...
		 *
		 * The exception is reported with exip pointing to the
		 * branch address. I don't know, at this point, if there
		 * is any better workaround than the aggressive one
		 * implemented below; I don't see how this could relate to
		 * any of the 88110 errata (although it might be related to
		 * branch prediction).
		 *
		 * For the record, the exact sequence triggering the
		 * spurious exception is:
		 *
		 *	bcnd.n	eq0, r2,  1f
		 *	 or	r25, r0,  r22
		 *	bsr	somewhere
		 *	or	r25, r0,  r22
		 *  1:	cmp	r13, r25, r20
		 *
		 * within the same cache line.
		 *
		 * Simply ignoring the exception and returning does not
		 * cause the exception to disappear. Clearing the
		 * instruction cache works, but on 88110+88410 systems,
		 * the 88410 needs to be invalidated as well. (note that
		 * the size passed to the flush routines does not matter
		 * since there is no way to flush a subset of the 88110
		 * I$ anyway)
		 */
	    {
		extern void *kernel_text, *etext;

		if (fault_addr >= (vaddr_t)&kernel_text &&
		    fault_addr < (vaddr_t)&etext) {
			cmmu_icache_inv(curcpu()->ci_cpuid,
			    trunc_page(fault_addr), PAGE_SIZE);
			cmmu_cache_wbinv(curcpu()->ci_cpuid,
			    trunc_page(fault_addr), PAGE_SIZE);
			return;
		}
	    }
		goto lose;
	case T_MISALGNFLT:
		printf("kernel misaligned access exception @%p\n",
		    frame->tf_exip);
		goto lose;
	case T_INSTFLT:
		/* kernel mode instruction access fault.
		 * Should never, never happen for a non-paged kernel.
		 */
#ifdef TRAPDEBUG
		printf("Kernel Instruction fault exip %x isr %x ilar %x\n",
		    frame->tf_exip, frame->tf_isr, frame->tf_ilar);
#endif
		goto lose;

	case T_DATAFLT:
		/* kernel mode data fault */

		/* data fault on the user address? */
		if ((frame->tf_dsr & CMMU_DSR_SU) == 0) {
			KERNEL_LOCK();
			goto m88110_user_fault;
		}

#ifdef TRAPDEBUG
		printf("Kernel Data access fault exip %x dsr %x dlar %x\n",
		    frame->tf_exip, frame->tf_dsr, frame->tf_dlar);
#endif

		fault_addr = frame->tf_dlar;
		if (frame->tf_dsr & CMMU_DSR_RW) {
			ftype = VM_PROT_READ;
			fault_code = VM_PROT_READ;
		} else {
			ftype = VM_PROT_READ|VM_PROT_WRITE;
			fault_code = VM_PROT_WRITE;
		}

		va = trunc_page((vaddr_t)fault_addr);

		KERNEL_LOCK();
		vm = p->p_vmspace;
		map = kernel_map;

		if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) {
			/*
			 * On a segment or a page fault, call uvm_fault() to
			 * resolve the fault.
			 */
			if ((pcb_onfault = p->p_addr->u_pcb.pcb_onfault) != 0)
				p->p_addr->u_pcb.pcb_onfault = 0;
			result = uvm_fault(map, va, VM_FAULT_INVALID, ftype);
			p->p_addr->u_pcb.pcb_onfault = pcb_onfault;
			/*
			 * This could be a fault caused in copyout*()
			 * while accessing kernel space.
			 */
			if (result != 0 && pcb_onfault != 0) {
				frame->tf_exip = pcb_onfault;
				/*
				 * Continue as if the fault had been resolved.
				 */
				result = 0;
			}
			if (result == 0) {
				KERNEL_UNLOCK();
				return;
			}
		}
		KERNEL_UNLOCK();
		goto lose;
	case T_INSTFLT+T_USER:
		/* User mode instruction access fault */
		/* FALLTHROUGH */
	case T_DATAFLT+T_USER:
		KERNEL_LOCK();
m88110_user_fault:
		if (type == T_INSTFLT+T_USER) {
			ftype = VM_PROT_READ;
			fault_code = VM_PROT_READ;
#ifdef TRAPDEBUG
			printf("User Instruction fault exip %x isr %x ilar %x\n",
			    frame->tf_exip, frame->tf_isr, frame->tf_ilar);
#endif
		} else {
			fault_addr = frame->tf_dlar;
			if (frame->tf_dsr & CMMU_DSR_RW) {
				ftype = VM_PROT_READ;
				fault_code = VM_PROT_READ;
			} else {
				ftype = VM_PROT_READ|VM_PROT_WRITE;
				fault_code = VM_PROT_WRITE;
			}
#ifdef TRAPDEBUG
			printf("User Data access fault exip %x dsr %x dlar %x\n",
			    frame->tf_exip, frame->tf_dsr, frame->tf_dlar);
#endif
		}

		va = trunc_page((vaddr_t)fault_addr);

		vm = p->p_vmspace;
		map = &vm->vm_map;
		if ((pcb_onfault = p->p_addr->u_pcb.pcb_onfault) != 0)
			p->p_addr->u_pcb.pcb_onfault = 0;

		/*
		 * Call uvm_fault() to resolve non-bus error faults
		 * whenever possible.
		 */
		if (type == T_INSTFLT+T_USER) {
			/* instruction faults */
			if (frame->tf_isr &
			    (CMMU_ISR_BE | CMMU_ISR_SP | CMMU_ISR_TBE)) {
				/* bus error, supervisor protection */
				result = EACCES;
			} else
			if (frame->tf_isr & (CMMU_ISR_SI | CMMU_ISR_PI)) {
				/* segment or page fault */
				result = uvm_fault(map, va, VM_FAULT_INVALID, ftype);
			} else {
#ifdef TRAPDEBUG
				printf("Unexpected Instruction fault isr %x\n",
				    frame->tf_isr);
#endif
				KERNEL_UNLOCK();
				goto lose;
			}
		} else {
			/* data faults */
			if (frame->tf_dsr & CMMU_DSR_BE) {
				/* bus error */
				result = EACCES;
			} else
			if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) {
				/* segment or page fault */
				result = uvm_fault(map, va, VM_FAULT_INVALID, ftype);
			} else
			if (frame->tf_dsr & (CMMU_DSR_CP | CMMU_DSR_WA)) {
				/* copyback or write allocate error */
				result = EACCES;
			} else
			if (frame->tf_dsr & CMMU_DSR_WE) {
				/* write fault  */
				/* This could be a write protection fault or an
				 * exception to set the used and modified bits
				 * in the pte. Basically, if we got a write
				 * error, then we already have a pte entry that
				 * faulted in from a previous seg fault or page
				 * fault.
				 * Get the pte and check the status of the
				 * modified and valid bits to determine if this
				 * indeed a real write fault.  XXX smurph
				 */
				if (pmap_set_modify(map->pmap, va)) {
#ifdef TRAPDEBUG
					printf("Corrected userland write fault, pmap %p va %p\n",
					    map->pmap, va);
#endif
					result = 0;
				} else {
					/* must be a real wp fault */
#ifdef TRAPDEBUG
					printf("Uncorrected userland write fault, pmap %p va %p\n",
					    map->pmap, va);
#endif
					result = uvm_fault(map, va, VM_FAULT_INVALID, ftype);
				}
			} else {
#ifdef TRAPDEBUG
				printf("Unexpected Data access fault dsr %x\n",
				    frame->tf_dsr);
#endif
				KERNEL_UNLOCK();
				goto lose;
			}
		}
		p->p_addr->u_pcb.pcb_onfault = pcb_onfault;

		if ((caddr_t)va >= vm->vm_maxsaddr) {
			if (result == 0)
				uvm_grow(p, va);
			else if (result == EACCES)
				result = EFAULT;
		}
		KERNEL_UNLOCK();

		/*
		 * This could be a fault caused in copyin*()
		 * while accessing user space.
		 */
		if (result != 0 && pcb_onfault != 0) {
			frame->tf_exip = pcb_onfault;
			/*
			 * Continue as if the fault had been resolved.
			 */
			result = 0;
		}

		if (result != 0) {
			sig = result == EACCES ? SIGBUS : SIGSEGV;
			fault_type = result == EACCES ?
			    BUS_ADRERR : SEGV_MAPERR;
		}
		break;
	case T_MISALGNFLT+T_USER:
		/* Fix any misaligned ld.d or st.d instructions */
		sig = double_reg_fixup(frame);
		fault_type = BUS_ADRALN;
		if (sig == 0) {
			/* skip recovered instruction */
			m88110_skip_insn(frame);
			goto userexit;
		}
		break;
	case T_PRIVINFLT+T_USER:
		fault_type = ILL_PRVREG;
		/* FALLTHROUGH */
	case T_ILLFLT+T_USER:
#ifndef DDB
	case T_KDB_BREAK:
	case T_KDB_ENTRY:
	case T_KDB_TRACE:
#endif
	case T_KDB_BREAK+T_USER:
	case T_KDB_ENTRY+T_USER:
	case T_KDB_TRACE+T_USER:
		sig = SIGILL;
		break;
	case T_BNDFLT+T_USER:
		sig = SIGFPE;
		/* skip trap instruction */
		m88110_skip_insn(frame);
		break;
	case T_ZERODIV+T_USER:
		sig = SIGFPE;
		fault_type = FPE_INTDIV;
		/* skip trap instruction */
		m88110_skip_insn(frame);
		break;
	case T_OVFFLT+T_USER:
		sig = SIGFPE;
		fault_type = FPE_INTOVF;
		/* skip trap instruction */
		m88110_skip_insn(frame);
		break;
	case T_FPEPFLT+T_USER:
		m88110_fpu_exception(frame);
		goto userexit;
	case T_SIGSYS+T_USER:
		sig = SIGSYS;
		break;
	case T_STEPBPT+T_USER:
#ifdef PTRACE
		/*
		 * This trap is used by the kernel to support single-step
		 * debugging (although any user could generate this trap
		 * which should probably be handled differently). When a
		 * process is continued by a debugger with the PT_STEP
		 * function of ptrace (single step), the kernel inserts
		 * one or two breakpoints in the user process so that only
		 * one instruction (or two in the case of a delayed branch)
		 * is executed.  When this breakpoint is hit, we get the
		 * T_STEPBPT trap.
		 */
		{
			u_int instr;
			vaddr_t pc = PC_REGS(&frame->tf_regs);

			/* read break instruction */
			copyin((caddr_t)pc, &instr, sizeof(u_int));

			/* check and see if we got here by accident */
			if ((p->p_md.md_bp0va != pc &&
			     p->p_md.md_bp1va != pc) ||
			    instr != SSBREAKPOINT) {
				sig = SIGTRAP;
				fault_type = TRAP_TRACE;
				break;
			}

			/* restore original instruction and clear breakpoint */
			if (p->p_md.md_bp0va == pc) {
				ss_put_value(p, pc, p->p_md.md_bp0save);
				p->p_md.md_bp0va = 0;
			}
			if (p->p_md.md_bp1va == pc) {
				ss_put_value(p, pc, p->p_md.md_bp1save);
				p->p_md.md_bp1va = 0;
			}

			sig = SIGTRAP;
			fault_type = TRAP_BRKPT;
		}
#else
		sig = SIGTRAP;
		fault_type = TRAP_TRACE;
#endif
		break;
	case T_USERBPT+T_USER:
		/*
		 * This trap is meant to be used by debuggers to implement
		 * breakpoint debugging.  When we get this trap, we just
		 * return a signal which gets caught by the debugger.
		 */
		sig = SIGTRAP;
		fault_type = TRAP_BRKPT;
		break;
	}

	/*
	 * If trap from supervisor mode, just return
	 */
	if (type < T_USER)
		return;

	if (sig) {
deliver:
		sv.sival_ptr = (void *)fault_addr;
		KERNEL_LOCK();
		trapsignal(p, sig, fault_code, fault_type, sv);
		KERNEL_UNLOCK();
	}

userexit:
	userret(p);
}