#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "../port/error.h" #define FORMAT(ur) ((((ur)->vo)>>12)&0xF) #define OFFSET(ur) (((ur)->vo)&0xFFF) struct FFrame { ulong ea; /* effective address */ ushort ssw; /* special status word */ ushort wb3s; /* write back 3 status */ ushort wb2s; /* write back 2 status */ ushort wb1s; /* write back 1 status */ ulong fa; /* fault address */ ulong wb3a; /* write back 3 address */ ulong wb3d; /* write back 3 data */ ulong wb2a; /* write back 2 address */ ulong wb2d; /* write back 2 data */ ulong wb1a; /* write back 1 address */ ulong wb1d; /* write back 3 data */ ulong pd1; /* push data lw1 */ ulong pd2; /* push data lw2 */ ulong pd3; /* push data lw3 */ }; static uchar tsz[4] = { 4, 1, 2, 0 }; /* * SSW bits */ #define READ 0x0100 #define TM 0x0007 /* * Writeback bits */ #define WBVALID (1<<7) #define WBSIZE (3<<5) #define WBSZSH 5 #define WBTT (3<<3) #define WBTM (7<<0) int wbfault(ulong, int, ushort, int, ulong); ulong normalize(ulong, ulong, ulong); extern struct{ ulong addr; ulong baddr; Lock; }kmapalloc; void fault68040(Ureg *ur, FFrame *f) { ulong addr, badvaddr; int user, read, insyscall; char buf[ERRLEN]; flushatc(); if(u == 0){ dumpregs(ur); print("ssw=%ux ea=%lux fa=%lux\n", f->ssw, f->ea, f->fa); panic("fault u==0 pc=%lux fa=%lux ssw=%lux", ur->pc, f->fa, f->ssw); } insyscall = u->p->insyscall; u->p->insyscall = 1; addr = f->fa; addr &= VAMASK; badvaddr = addr; addr &= ~(BY2PG-1); read = 0; if(f->ssw & READ) read = 1; user = !(ur->sr&SUPER); if(user) u->dbgreg = ur; if((f->ssw&TM) == 0) panic("cache push pc %lux addr %lux ssw %lux\n", ur->pc, f->fa, f->ssw); /* print("fault pc=%lux addr=%lux read %d\n", ur->pc, badvaddr, read); /**/ /* * original fault plus 3 writeback buffers */ if(fault(addr, read) < 0 || wbfault(f->wb1a, user, f->wb1s, 1, normalize(f->wb1s, f->wb1a, f->wb1d)) < 0 || wbfault(f->wb2a, user, f->wb2s, 2, f->wb2d) < 0 || wbfault(f->wb3a, user, f->wb3s, 3, f->wb3d) < 0){ if(user){ sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", badvaddr); postnote(u->p, 1, buf, NDebug); notify(ur); return; } dumpregs(ur); print("wb1a %lux wb1s %lux wb1d %lux\n", f->wb1a, f->wb1s, f->wb1d); print("wb2a %lux wb2s %lux wb2d %lux\n", f->wb2a, f->wb2s, f->wb2d); print("wb3a %lux wb3s %lux wb3d %lux\n", f->wb3a, f->wb3s, f->wb3d); panic("fault: 0x%lux 0x%lux", badvaddr, addr); } u->p->insyscall = insyscall; if(user) notify(ur); } /* * get relevant data into the right hand side of the long */ ulong normalize(ulong stat, ulong addr, ulong val) { int thecase, size; size = (stat&WBSIZE) >> WBSZSH; thecase = (size<<2) | (addr&3); switch(thecase){ /* long write */ case 0: return val; case 1: return (val<<8) | ((val>>24)&0xff); case 2: return (val<<16) | ((val>>16)&0xffff); case 3: return (val<<24) | ((val>>8)&0xffffff); /* byte write */ case 4: return val>>24; case 5: return val>>16; case 6: return val>>8; case 7: return val; /* short write */ case 8: return val>>16; case 9: return val>>8; case 10: return val; case 11: return (val<<8) | ((val>>24)&0xff); } } /* * Check fault status before doing writeback */ int wbfault(ulong fa, int user, ushort wbs, int wbn, ulong d) { int s; ulong addr; if(!(wbs & WBVALID)) return 0; if(wbs & WBTT) panic("writeback%d status %ux", wbn, wbs); addr = fa & ~(BY2PG-1); s = tsz[(wbs&WBSIZE) >> WBSZSH]; if(user || !kernaddr(addr)){ if(fault(fa, 0) < 0){ print("wbfault: %lux, 0\n", fa); return -1; } if(addr != ((fa+s-1)&~(BY2PG-1))) if(fault(fa+s-1, 0) < 0){ print("wbfault: %lux, %lux\n", fa, fa+s-1); return -1; } } switch(s){ case 4: *(ulong*)fa = d; break; case 1: *(uchar*)fa = d; break; case 2: *(ushort*)fa = d; break; default: panic("writeback%d %d\n", wbn, s); } return 0; }