/* $NetBSD: m68k_trap.c,v 1.4 2024/01/20 00:15:31 thorpej Exp $ */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: trap.c 1.32 91/04/06$ * * @(#)trap.c 7.15 (Berkeley) 8/2/91 */ #include __KERNEL_RCSID(0, "$NetBSD: m68k_trap.c,v 1.4 2024/01/20 00:15:31 thorpej Exp $"); #include "opt_m68k_arch.h" #include #include #include #include #include #include #include #include #include #include extern int suline(void *, void *); /* locore.s */ volatile int astpending; #ifdef M68060 #define KDFAULT_060(c) (cputype == CPU_68060 && ((c) & FSLW_TM_SV)) #define WRFAULT_060(c) (cputype == CPU_68060 && ((c) & FSLW_RW_W)) #else #define KDFAULT_060(c) 0 #define WRFAULT_060(c) 0 #endif #ifdef M68040 #define KDFAULT_040(c) (cputype == CPU_68040 && \ ((c) & SSW4_TMMASK) == SSW4_TMKD) #define WRFAULT_040(c) (cputype == CPU_68040 && \ ((c) & (SSW4_LK|SSW4_RW)) != SSW4_RW) #else #define KDFAULT_040(c) 0 #define WRFAULT_040(c) 0 #endif #if defined(M68030) || defined(M68020) #define KDFAULT_OTH(c) (cputype <= CPU_68030 && \ ((c) & (SSW_DF|SSW_FCMASK)) == (SSW_DF|FC_SUPERD)) #define WRFAULT_OTH(c) (cputype <= CPU_68030 && \ (((c) & SSW_DF) != 0 && \ ((((c) & SSW_RW) == 0) || (((c) & SSW_RM) != 0)))) #else #define KDFAULT_OTH(c) 0 #define WRFAULT_OTH(c) 0 #endif #define KDFAULT(c) (KDFAULT_060(c) || KDFAULT_040(c) || KDFAULT_OTH(c)) #define WRFAULT(c) (WRFAULT_060(c) || WRFAULT_040(c) || WRFAULT_OTH(c)) #ifdef DEBUG extern int mmudebug, mmupid; #define MDB_FOLLOW 1 #define MDB_WBFOLLOW 2 #define MDB_WBFAILED 4 #define MDB_ISPID(pid) ((pid) == mmupid) #endif #ifdef M68040 #ifdef DEBUG struct writebackstats { int calls; int cpushes; int move16s; int wb1s, wb2s, wb3s; int wbsize[4]; } wbstats; static const char *f7sz[] = { "longword", "byte", "word", "line" }; static const char *f7tt[] = { "normal", "MOVE16", "AFC", "ACK" }; static const char *f7tm[] = { "d-push", "u-data", "u-code", "M-data", "M-code", "k-data", "k-code", "RES" }; static const char wberrstr[] = "WARNING: pid %d(%s) writeback [%s] failed, pc=%x fa=%x wba=%x wbd=%x\n"; static void dumpssw(u_short ssw) { printf(" SSW: %x: ", ssw); if (ssw & SSW4_CP) printf("CP,"); if (ssw & SSW4_CU) printf("CU,"); if (ssw & SSW4_CT) printf("CT,"); if (ssw & SSW4_CM) printf("CM,"); if (ssw & SSW4_MA) printf("MA,"); if (ssw & SSW4_ATC) printf("ATC,"); if (ssw & SSW4_LK) printf("LK,"); if (ssw & SSW4_RW) printf("RW,"); printf(" SZ=%s, TT=%s, TM=%s\n", f7sz[(ssw & SSW4_SZMASK) >> 5], f7tt[(ssw & SSW4_TTMASK) >> 3], f7tm[ssw & SSW4_TMMASK]); } static void dumpwb(int num, u_short s, u_int a, u_int d) { struct proc *p = curproc; paddr_t pa; printf(" writeback #%d: VA %x, data %x, SZ=%s, TT=%s, TM=%s\n", num, a, d, f7sz[(s & SSW4_SZMASK) >> 5], f7tt[(s & SSW4_TTMASK) >> 3], f7tm[s & SSW4_TMMASK]); printf(" PA "); if (pmap_extract(p->p_vmspace->vm_map.pmap, (vaddr_t)a, &pa) == false) printf(""); else { u_long val; if (ufetch_long((void *)a, &val) != 0) val = (u_long)-1; printf("%lx, current value %lx", pa, val); } printf("\n"); } #endif /* DEBUG */ /* Because calling memcpy() for 16 bytes is *way* too much overhead ... */ static inline void fastcopy16(u_int *dst, const u_int *src) { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst = *src; } int m68040_writeback(struct frame *fp, int docachepush) { struct fmt7 *f = &fp->f_fmt7; struct lwp *l = curlwp; struct proc *p = l->l_proc; struct pcb *pcb = lwp_getpcb(l); int err = 0; u_int fa = 0; void *oonfault = pcb->pcb_onfault; paddr_t pa; #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) { printf(" pid=%d, fa=%x,", p->p_pid, f->f_fa); dumpssw(f->f_ssw); } wbstats.calls++; #endif /* * Deal with special cases first. */ if ((f->f_ssw & SSW4_TMMASK) == SSW4_TMDCP) { /* * Dcache push fault. * Line-align the address and write out the push data to * the indicated physical address. */ #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) { printf(" pushing %s to PA %x, data %x", f7sz[(f->f_ssw & SSW4_SZMASK) >> 5], f->f_fa, f->f_pd0); if ((f->f_ssw & SSW4_SZMASK) == SSW4_SZLN) printf("/%x/%x/%x", f->f_pd1, f->f_pd2, f->f_pd3); printf("\n"); } if (f->f_wb1s & SSW4_WBSV) panic("writeback: cache push with WB1S valid"); wbstats.cpushes++; #endif /* * XXX there are security problems if we attempt to do a * cache push after a signal handler has been called. */ if (docachepush) { pmap_enter(pmap_kernel(), (vaddr_t)vmmap, trunc_page(f->f_fa), VM_PROT_WRITE, VM_PROT_WRITE|PMAP_WIRED); pmap_update(pmap_kernel()); fa = (u_int)&vmmap[m68k_page_offset(f->f_fa) & ~0xF]; fastcopy16((void *)fa, (void *)&f->f_pd0); (void) pmap_extract(pmap_kernel(), (vaddr_t)fa, &pa); DCFL(pa); pmap_remove(pmap_kernel(), (vaddr_t)vmmap, (vaddr_t)&vmmap[PAGE_SIZE]); pmap_update(pmap_kernel()); } else printf("WARNING: pid %d(%s) uid %d: CPUSH not done\n", p->p_pid, p->p_comm, kauth_cred_geteuid(l->l_cred)); } else if ((f->f_ssw & (SSW4_RW|SSW4_TTMASK)) == SSW4_TTM16) { /* * MOVE16 fault. * Line-align the address and write out the push data to * the indicated virtual address. */ #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) printf(" MOVE16 to VA %x(%x), data %x/%x/%x/%x\n", f->f_fa, f->f_fa & ~0xF, f->f_pd0, f->f_pd1, f->f_pd2, f->f_pd3); if (f->f_wb1s & SSW4_WBSV) panic("writeback: MOVE16 with WB1S valid"); wbstats.move16s++; #endif if (KDFAULT(f->f_wb1s)) fastcopy16((void *)(f->f_fa & ~0xF), (void *)&f->f_pd0); else err = suline((void *)(f->f_fa & ~0xF), (void *)&f->f_pd0); if (err) { fa = f->f_fa & ~0xF; #ifdef DEBUG if (mmudebug & MDB_WBFAILED) printf(wberrstr, p->p_pid, p->p_comm, "MOVE16", fp->f_pc, f->f_fa, f->f_fa & ~0xF, f->f_pd0); #endif } } else if (f->f_wb1s & SSW4_WBSV) { /* * Writeback #1. * Position the "memory-aligned" data and write it out. */ u_int wb1d = f->f_wb1d; int off; #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) dumpwb(1, f->f_wb1s, f->f_wb1a, f->f_wb1d); wbstats.wb1s++; wbstats.wbsize[(f->f_wb2s&SSW4_SZMASK)>>5]++; #endif off = (f->f_wb1a & 3) * 8; switch (f->f_wb1s & SSW4_SZMASK) { case SSW4_SZLW: if (off) wb1d = (wb1d >> (32 - off)) | (wb1d << off); if (KDFAULT(f->f_wb1s)) *(long *)f->f_wb1a = wb1d; else err = ustore_long((void *)f->f_wb1a, wb1d); break; case SSW4_SZB: off = 24 - off; if (off) wb1d >>= off; if (KDFAULT(f->f_wb1s)) *(char *)f->f_wb1a = wb1d; else err = ustore_char((void *)f->f_wb1a, wb1d); break; case SSW4_SZW: off = (off + 16) % 32; if (off) wb1d = (wb1d >> (32 - off)) | (wb1d << off); if (KDFAULT(f->f_wb1s)) *(short *)f->f_wb1a = wb1d; else err = ustore_short((void *)f->f_wb1a, wb1d); break; } if (err) { fa = f->f_wb1a; #ifdef DEBUG if (mmudebug & MDB_WBFAILED) printf(wberrstr, p->p_pid, p->p_comm, "#1", fp->f_pc, f->f_fa, f->f_wb1a, f->f_wb1d); #endif } } /* * Deal with the "normal" writebacks. * * XXX writeback2 is known to reflect a LINE size writeback after * a MOVE16 was already dealt with above. Ignore it. */ if (err == 0 && (f->f_wb2s & SSW4_WBSV) && (f->f_wb2s & SSW4_SZMASK) != SSW4_SZLN) { #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) dumpwb(2, f->f_wb2s, f->f_wb2a, f->f_wb2d); wbstats.wb2s++; wbstats.wbsize[(f->f_wb2s&SSW4_SZMASK)>>5]++; #endif switch (f->f_wb2s & SSW4_SZMASK) { case SSW4_SZLW: if (KDFAULT(f->f_wb2s)) *(long *)f->f_wb2a = f->f_wb2d; else err = ustore_long((void *)f->f_wb2a, f->f_wb2d); break; case SSW4_SZB: if (KDFAULT(f->f_wb2s)) *(char *)f->f_wb2a = f->f_wb2d; else err = ustore_char((void *)f->f_wb2a, f->f_wb2d); break; case SSW4_SZW: if (KDFAULT(f->f_wb2s)) *(short *)f->f_wb2a = f->f_wb2d; else err = ustore_short((void *)f->f_wb2a, f->f_wb2d); break; } if (err) { fa = f->f_wb2a; #ifdef DEBUG if (mmudebug & MDB_WBFAILED) { printf(wberrstr, p->p_pid, p->p_comm, "#2", fp->f_pc, f->f_fa, f->f_wb2a, f->f_wb2d); dumpssw(f->f_ssw); dumpwb(2, f->f_wb2s, f->f_wb2a, f->f_wb2d); } #endif } } if (err == 0 && (f->f_wb3s & SSW4_WBSV)) { #ifdef DEBUG if ((mmudebug & MDB_WBFOLLOW) || MDB_ISPID(p->p_pid)) dumpwb(3, f->f_wb3s, f->f_wb3a, f->f_wb3d); wbstats.wb3s++; wbstats.wbsize[(f->f_wb3s&SSW4_SZMASK)>>5]++; #endif switch (f->f_wb3s & SSW4_SZMASK) { case SSW4_SZLW: if (KDFAULT(f->f_wb3s)) *(long *)f->f_wb3a = f->f_wb3d; else err = ustore_long((void *)f->f_wb3a, f->f_wb3d); break; case SSW4_SZB: if (KDFAULT(f->f_wb3s)) *(char *)f->f_wb3a = f->f_wb3d; else err = ustore_char((void *)f->f_wb3a, f->f_wb3d); break; case SSW4_SZW: if (KDFAULT(f->f_wb3s)) *(short *)f->f_wb3a = f->f_wb3d; else err = ustore_short((void *)f->f_wb3a, f->f_wb3d); break; #ifdef DEBUG case SSW4_SZLN: panic("writeback: wb3s indicates LINE write"); #endif } if (err) { fa = f->f_wb3a; #ifdef DEBUG if (mmudebug & MDB_WBFAILED) printf(wberrstr, p->p_pid, p->p_comm, "#3", fp->f_pc, f->f_fa, f->f_wb3a, f->f_wb3d); #endif } } pcb->pcb_onfault = oonfault; if (err) err = SIGSEGV; return err; } #endif /* M68040 */