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; }
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); }