xref: /plan9-contrib/sys/src/9/loongson64/faultmips.c (revision 0c0b2b49cfb685ea1f1b8483a5cf30f72b8eb1f2)
1*0c0b2b49SDavid du Colombier #include	"u.h"
2*0c0b2b49SDavid du Colombier #include	"../port/lib.h"
3*0c0b2b49SDavid du Colombier #include	"mem.h"
4*0c0b2b49SDavid du Colombier #include	"dat.h"
5*0c0b2b49SDavid du Colombier #include	"fns.h"
6*0c0b2b49SDavid du Colombier #include	"ureg.h"
7*0c0b2b49SDavid du Colombier #include	"../port/error.h"
8*0c0b2b49SDavid du Colombier #include	"io.h"
9*0c0b2b49SDavid du Colombier 
10*0c0b2b49SDavid du Colombier enum {
11*0c0b2b49SDavid du Colombier 	Debug = 0,
12*0c0b2b49SDavid du Colombier };
13*0c0b2b49SDavid du Colombier 
14*0c0b2b49SDavid du Colombier typedef struct Fault Fault;
15*0c0b2b49SDavid du Colombier struct Fault {
16*0c0b2b49SDavid du Colombier 	uintptr	va;
17*0c0b2b49SDavid du Colombier 	ulong	pid;
18*0c0b2b49SDavid du Colombier 	uintptr	pc;
19*0c0b2b49SDavid du Colombier 	int	cnt;
20*0c0b2b49SDavid du Colombier 	char	*prog;
21*0c0b2b49SDavid du Colombier 	int	code;
22*0c0b2b49SDavid du Colombier };
23*0c0b2b49SDavid du Colombier 
24*0c0b2b49SDavid du Colombier extern char *excname[];
25*0c0b2b49SDavid du Colombier 
26*0c0b2b49SDavid du Colombier static Fault lflt, maxflt;
27*0c0b2b49SDavid du Colombier 
28*0c0b2b49SDavid du Colombier #define	OFR(r)		(uintptr)offsetof(Ureg, r)	/* offset into Ureg of r */
29*0c0b2b49SDavid du Colombier #define	REG(ur, r)	*acpureg(ur, r)			/* cpu reg in Ureg */
30*0c0b2b49SDavid du Colombier #define REGMASK		MASK(5)
31*0c0b2b49SDavid du Colombier 
32*0c0b2b49SDavid du Colombier static ulong dummyr0;
33*0c0b2b49SDavid du Colombier 
34*0c0b2b49SDavid du Colombier static	int	roff[32] = {
35*0c0b2b49SDavid du Colombier 	0,       OFR(r1), OFR(r2), OFR(r3),
36*0c0b2b49SDavid du Colombier 	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
37*0c0b2b49SDavid du Colombier 	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
38*0c0b2b49SDavid du Colombier 	OFR(r12), OFR(r13), OFR(r14), OFR(r15),
39*0c0b2b49SDavid du Colombier 	OFR(r16), OFR(r17), OFR(r18), OFR(r19),
40*0c0b2b49SDavid du Colombier 	OFR(r20), OFR(r21), OFR(r22), OFR(r23),
41*0c0b2b49SDavid du Colombier 	OFR(r24), OFR(r25), OFR(r26), OFR(r27),
42*0c0b2b49SDavid du Colombier 	OFR(r28), OFR(sp),  OFR(r30), OFR(r31),
43*0c0b2b49SDavid du Colombier };
44*0c0b2b49SDavid du Colombier 
45*0c0b2b49SDavid du Colombier static ulong *
acpureg(Ureg * ur,int r)46*0c0b2b49SDavid du Colombier acpureg(Ureg *ur, int r)
47*0c0b2b49SDavid du Colombier {
48*0c0b2b49SDavid du Colombier 	r &= REGMASK;
49*0c0b2b49SDavid du Colombier 	if (r == 0 || roff[r] == 0) {
50*0c0b2b49SDavid du Colombier 		dummyr0 = 0;
51*0c0b2b49SDavid du Colombier 		return &dummyr0;
52*0c0b2b49SDavid du Colombier 	}
53*0c0b2b49SDavid du Colombier 	return (ulong *)((char*)ur + roff[r]);
54*0c0b2b49SDavid du Colombier }
55*0c0b2b49SDavid du Colombier 
56*0c0b2b49SDavid du Colombier ulong *
reg(Ureg * ur,int r)57*0c0b2b49SDavid du Colombier reg(Ureg *ur, int r)
58*0c0b2b49SDavid du Colombier {
59*0c0b2b49SDavid du Colombier 	return &REG(ur, r);
60*0c0b2b49SDavid du Colombier }
61*0c0b2b49SDavid du Colombier 
62*0c0b2b49SDavid du Colombier /*
63*0c0b2b49SDavid du Colombier  * Ask if the instruction at EPC could have cause this badvaddr
64*0c0b2b49SDavid du Colombier  */
65*0c0b2b49SDavid du Colombier int
tstbadvaddr(Ureg * ur)66*0c0b2b49SDavid du Colombier tstbadvaddr(Ureg *ur)
67*0c0b2b49SDavid du Colombier {
68*0c0b2b49SDavid du Colombier 	int rn;
69*0c0b2b49SDavid du Colombier 	uintptr iw, off, ea;
70*0c0b2b49SDavid du Colombier 
71*0c0b2b49SDavid du Colombier 	iw = ur->pc;
72*0c0b2b49SDavid du Colombier 	if(ur->cause & BD)
73*0c0b2b49SDavid du Colombier 		iw += 4;
74*0c0b2b49SDavid du Colombier 
75*0c0b2b49SDavid du Colombier 	if(seg(up, iw, 0) == 0)
76*0c0b2b49SDavid du Colombier 		return 0;
77*0c0b2b49SDavid du Colombier 
78*0c0b2b49SDavid du Colombier 	iw = *(ulong*)iw;
79*0c0b2b49SDavid du Colombier 
80*0c0b2b49SDavid du Colombier /*	print("iw: %#lux\n", iw);	/**/
81*0c0b2b49SDavid du Colombier 
82*0c0b2b49SDavid du Colombier 	switch((iw>>26) & 0x3f) {
83*0c0b2b49SDavid du Colombier 	default:
84*0c0b2b49SDavid du Colombier 		return 1;
85*0c0b2b49SDavid du Colombier 	case 0x20:	/* LB */
86*0c0b2b49SDavid du Colombier 	case 0x24:	/* LBU */
87*0c0b2b49SDavid du Colombier 			/* LD */
88*0c0b2b49SDavid du Colombier 	case 0x35:
89*0c0b2b49SDavid du Colombier 	case 0x36:
90*0c0b2b49SDavid du Colombier 	case 0x37:	/* LDCz */
91*0c0b2b49SDavid du Colombier 	case 0x1A:	/* LDL */
92*0c0b2b49SDavid du Colombier 	case 0x1B:	/* LDR */
93*0c0b2b49SDavid du Colombier 	case 0x21:	/* LH */
94*0c0b2b49SDavid du Colombier 	case 0x25:	/* LHU */
95*0c0b2b49SDavid du Colombier 	case 0x30:	/* LL */
96*0c0b2b49SDavid du Colombier 	case 0x34:	/* LLD */
97*0c0b2b49SDavid du Colombier 	case 0x23:	/* LW */
98*0c0b2b49SDavid du Colombier 	case 0x31:
99*0c0b2b49SDavid du Colombier 	case 0x32:	/* LWCz possible 0x33 */
100*0c0b2b49SDavid du Colombier 	case 0x27:	/* LWU */
101*0c0b2b49SDavid du Colombier 	case 0x22:	/* LWL */
102*0c0b2b49SDavid du Colombier 	case 0x26:	/* LWR */
103*0c0b2b49SDavid du Colombier 		break;
104*0c0b2b49SDavid du Colombier 
105*0c0b2b49SDavid du Colombier 	case 0x28:	/* SB */
106*0c0b2b49SDavid du Colombier 	case 0x38:	/* SC */
107*0c0b2b49SDavid du Colombier 	case 0x3C:	/* SCD */
108*0c0b2b49SDavid du Colombier 	case 0x3D:
109*0c0b2b49SDavid du Colombier 	case 0x3E:
110*0c0b2b49SDavid du Colombier 	case 0x3F:	/* SDCz */
111*0c0b2b49SDavid du Colombier 	case 0x2C:	/* SDL */
112*0c0b2b49SDavid du Colombier 	case 0x2D:	/* SDR */
113*0c0b2b49SDavid du Colombier 	case 0x29:	/* SH */
114*0c0b2b49SDavid du Colombier 	case 0x2B:	/* SW */
115*0c0b2b49SDavid du Colombier 	case 0x39:
116*0c0b2b49SDavid du Colombier 	case 0x3A:	/* SWCz */
117*0c0b2b49SDavid du Colombier 	case 0x2A:	/* SWL */
118*0c0b2b49SDavid du Colombier 	case 0x2E:	/* SWR */
119*0c0b2b49SDavid du Colombier 		break;
120*0c0b2b49SDavid du Colombier 	}
121*0c0b2b49SDavid du Colombier 
122*0c0b2b49SDavid du Colombier 	off = iw & 0xffff;
123*0c0b2b49SDavid du Colombier 	if(off & 0x8000)
124*0c0b2b49SDavid du Colombier 		off |= ~0xffff;
125*0c0b2b49SDavid du Colombier 
126*0c0b2b49SDavid du Colombier 	rn = (iw>>21) & 0x1f;
127*0c0b2b49SDavid du Colombier 	ea = *reg(ur, rn);
128*0c0b2b49SDavid du Colombier 	if(rn == 0)
129*0c0b2b49SDavid du Colombier 		ea = 0;
130*0c0b2b49SDavid du Colombier 	ea += off;
131*0c0b2b49SDavid du Colombier 
132*0c0b2b49SDavid du Colombier 	/* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/
133*0c0b2b49SDavid du Colombier 
134*0c0b2b49SDavid du Colombier 	if(ur->badvaddr == ea)
135*0c0b2b49SDavid du Colombier 		return 0;
136*0c0b2b49SDavid du Colombier 
137*0c0b2b49SDavid du Colombier 	return 1;
138*0c0b2b49SDavid du Colombier }
139*0c0b2b49SDavid du Colombier 
140*0c0b2b49SDavid du Colombier /*
141*0c0b2b49SDavid du Colombier  * we think we get consecutive page faults from unlucky combinations of
142*0c0b2b49SDavid du Colombier  * scheduling and stlb hashes, and they only happen with 16K pages.
143*0c0b2b49SDavid du Colombier  * however, we also get page faults while servicing the exact same fault.
144*0c0b2b49SDavid du Colombier  * more than 5 consecutive faults is unusual, now that we have a better
145*0c0b2b49SDavid du Colombier  * hash function.
146*0c0b2b49SDavid du Colombier  *
147*0c0b2b49SDavid du Colombier  * this can be helpful during mmu and cache debugging.
148*0c0b2b49SDavid du Colombier  */
149*0c0b2b49SDavid du Colombier static int
ckfaultstuck(Ureg * ur,int read,int code)150*0c0b2b49SDavid du Colombier ckfaultstuck(Ureg *ur, int read, int code)
151*0c0b2b49SDavid du Colombier {
152*0c0b2b49SDavid du Colombier 	uintptr pc, va;
153*0c0b2b49SDavid du Colombier 
154*0c0b2b49SDavid du Colombier 	va = ur->badvaddr;
155*0c0b2b49SDavid du Colombier 	pc = ur->pc;
156*0c0b2b49SDavid du Colombier 	if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc ||
157*0c0b2b49SDavid du Colombier 	    code != lflt.code) {
158*0c0b2b49SDavid du Colombier 		/* at least one address or cause is different from last time */
159*0c0b2b49SDavid du Colombier 		lflt.cnt = 1;
160*0c0b2b49SDavid du Colombier 		lflt.va = va;
161*0c0b2b49SDavid du Colombier 		lflt.pid = up->pid;
162*0c0b2b49SDavid du Colombier 		lflt.pc = pc;
163*0c0b2b49SDavid du Colombier 		lflt.code = code;
164*0c0b2b49SDavid du Colombier 		return 0;
165*0c0b2b49SDavid du Colombier 	}
166*0c0b2b49SDavid du Colombier 	++lflt.cnt;
167*0c0b2b49SDavid du Colombier 	if (lflt.cnt >= 1000)	/* fixfault() isn't fixing underlying cause? */
168*0c0b2b49SDavid du Colombier 		panic("fault: %d consecutive faults for va %#p", lflt.cnt, va);
169*0c0b2b49SDavid du Colombier 	if (lflt.cnt > maxflt.cnt) {
170*0c0b2b49SDavid du Colombier 		maxflt.cnt = lflt.cnt;
171*0c0b2b49SDavid du Colombier 		maxflt.va = va;
172*0c0b2b49SDavid du Colombier 		maxflt.pid = up->pid;
173*0c0b2b49SDavid du Colombier 		maxflt.pc = pc;
174*0c0b2b49SDavid du Colombier 		kstrdup(&maxflt.prog, up->text);
175*0c0b2b49SDavid du Colombier 	}
176*0c0b2b49SDavid du Colombier 
177*0c0b2b49SDavid du Colombier 	/* we're servicing that fault now! */
178*0c0b2b49SDavid du Colombier 	/* adjust the threshold and program name to suit */
179*0c0b2b49SDavid du Colombier 	if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0)
180*0c0b2b49SDavid du Colombier 		return 0;
181*0c0b2b49SDavid du Colombier 	iprint("%d consecutive faults for va %#p at pc %#p in %s "
182*0c0b2b49SDavid du Colombier 		"pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid);
183*0c0b2b49SDavid du Colombier 	iprint("\t%s: %s%s r31 %#p tlbvirt %#p\n",
184*0c0b2b49SDavid du Colombier 		excname[code], va == pc? "[instruction] ": "",
185*0c0b2b49SDavid du Colombier 		(read? "read": "write"), ur->r31, tlbvirt());
186*0c0b2b49SDavid du Colombier 	return 0;
187*0c0b2b49SDavid du Colombier }
188*0c0b2b49SDavid du Colombier 
189*0c0b2b49SDavid du Colombier char *
faultsprint(char * p,char * ep)190*0c0b2b49SDavid du Colombier faultsprint(char *p, char *ep)
191*0c0b2b49SDavid du Colombier {
192*0c0b2b49SDavid du Colombier 	if (Debug)
193*0c0b2b49SDavid du Colombier 		p = seprint(p, ep,
194*0c0b2b49SDavid du Colombier 			"max consecutive faults %d for va %#p in %s\n",
195*0c0b2b49SDavid du Colombier 			maxflt.cnt, maxflt.va, maxflt.prog);
196*0c0b2b49SDavid du Colombier 	return p;
197*0c0b2b49SDavid du Colombier }
198*0c0b2b49SDavid du Colombier 
199*0c0b2b49SDavid du Colombier /*
200*0c0b2b49SDavid du Colombier  *  find out fault address and type of access.
201*0c0b2b49SDavid du Colombier  *  Call common fault handler.
202*0c0b2b49SDavid du Colombier  */
203*0c0b2b49SDavid du Colombier void
faultmips(Ureg * ur,int user,int code)204*0c0b2b49SDavid du Colombier faultmips(Ureg *ur, int user, int code)
205*0c0b2b49SDavid du Colombier {
206*0c0b2b49SDavid du Colombier 	int read;
207*0c0b2b49SDavid du Colombier 	uintptr addr;
208*0c0b2b49SDavid du Colombier 	char *p, buf[ERRMAX];
209*0c0b2b49SDavid du Colombier 	static int infault, printed;
210*0c0b2b49SDavid du Colombier 
211*0c0b2b49SDavid du Colombier 	if (0 && infault && !printed) {
212*0c0b2b49SDavid du Colombier 		printed = 1;
213*0c0b2b49SDavid du Colombier 		print("fault: recursive fault (%d deep) pc %#p va %#p\n",
214*0c0b2b49SDavid du Colombier 			infault+1, ur->pc, ur->badvaddr);
215*0c0b2b49SDavid du Colombier 	}
216*0c0b2b49SDavid du Colombier 	infault++;
217*0c0b2b49SDavid du Colombier 	if(waserror()){
218*0c0b2b49SDavid du Colombier 		infault--;
219*0c0b2b49SDavid du Colombier 		nexterror();
220*0c0b2b49SDavid du Colombier 	}
221*0c0b2b49SDavid du Colombier 
222*0c0b2b49SDavid du Colombier 	addr = ur->badvaddr;
223*0c0b2b49SDavid du Colombier 	addr &= ~(BY2PG-1);
224*0c0b2b49SDavid du Colombier 
225*0c0b2b49SDavid du Colombier 	read = !(code==CTLBM || code==CTLBS);
226*0c0b2b49SDavid du Colombier 
227*0c0b2b49SDavid du Colombier /*	print("fault: %s code %d va %#p pc %#p r31 %#p tlbvirt %#p\n",
228*0c0b2b49SDavid du Colombier 		up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/
229*0c0b2b49SDavid du Colombier 
230*0c0b2b49SDavid du Colombier 	if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0){
231*0c0b2b49SDavid du Colombier 		infault--;
232*0c0b2b49SDavid du Colombier 		poperror();
233*0c0b2b49SDavid du Colombier 		return;
234*0c0b2b49SDavid du Colombier 	}
235*0c0b2b49SDavid du Colombier 
236*0c0b2b49SDavid du Colombier 	infault--;
237*0c0b2b49SDavid du Colombier 	poperror();
238*0c0b2b49SDavid du Colombier 
239*0c0b2b49SDavid du Colombier 	if(tstbadvaddr(ur)) {
240*0c0b2b49SDavid du Colombier 		print("fault: spurious badvaddr %#p in %s at pc %#p\n",
241*0c0b2b49SDavid du Colombier 			ur->badvaddr, up->text, ur->pc);/**/
242*0c0b2b49SDavid du Colombier 		return;
243*0c0b2b49SDavid du Colombier 	}
244*0c0b2b49SDavid du Colombier 
245*0c0b2b49SDavid du Colombier 	if(user) {
246*0c0b2b49SDavid du Colombier 		p = "store";
247*0c0b2b49SDavid du Colombier 		if(read)
248*0c0b2b49SDavid du Colombier 			p = "load";
249*0c0b2b49SDavid du Colombier 		snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#p r31=%#p",
250*0c0b2b49SDavid du Colombier 			p, ur->badvaddr, ur->r31);
251*0c0b2b49SDavid du Colombier 		postnote(up, 1, buf, NDebug);
252*0c0b2b49SDavid du Colombier 		return;
253*0c0b2b49SDavid du Colombier 	}
254*0c0b2b49SDavid du Colombier 
255*0c0b2b49SDavid du Colombier 	print("kernel %s vaddr=%#p\n", excname[code], ur->badvaddr);
256*0c0b2b49SDavid du Colombier 	print("st=%#lux pc=%#p r31=%#p sp=%#p\n",
257*0c0b2b49SDavid du Colombier 		(ulong)ur->status, ur->pc, ur->r31, ur->sp);
258*0c0b2b49SDavid du Colombier 	dumpregs(ur);
259*0c0b2b49SDavid du Colombier 	panic("fault");
260*0c0b2b49SDavid du Colombier }
261*0c0b2b49SDavid du Colombier 
262*0c0b2b49SDavid du Colombier /*
263*0c0b2b49SDavid du Colombier  * called in syscallfmt.c, sysfile.c, sysproc.c
264*0c0b2b49SDavid du Colombier  */
265*0c0b2b49SDavid du Colombier void
validalign(uintptr addr,unsigned align)266*0c0b2b49SDavid du Colombier validalign(uintptr addr, unsigned align)
267*0c0b2b49SDavid du Colombier {
268*0c0b2b49SDavid du Colombier 	/*
269*0c0b2b49SDavid du Colombier 	 * Plan 9 is a 32-bit O/S, and the hardware it runs on
270*0c0b2b49SDavid du Colombier 	 * does not usually have instructions which move 64-bit
271*0c0b2b49SDavid du Colombier 	 * quantities directly, synthesizing the operations
272*0c0b2b49SDavid du Colombier 	 * with 32-bit move instructions. Therefore, the compiler
273*0c0b2b49SDavid du Colombier 	 * (and hardware) usually only enforce 32-bit alignment,
274*0c0b2b49SDavid du Colombier 	 * if at all.
275*0c0b2b49SDavid du Colombier 	 *
276*0c0b2b49SDavid du Colombier 	 * Take this out if the architecture warrants it.
277*0c0b2b49SDavid du Colombier 	 */
278*0c0b2b49SDavid du Colombier //	if(align == sizeof(vlong))
279*0c0b2b49SDavid du Colombier //		align = sizeof(long);
280*0c0b2b49SDavid du Colombier 
281*0c0b2b49SDavid du Colombier 	/*
282*0c0b2b49SDavid du Colombier 	 * Check align is a power of 2, then addr alignment.
283*0c0b2b49SDavid du Colombier 	 */
284*0c0b2b49SDavid du Colombier 	if((align != 0 && !(align & (align-1))) && !(addr & (align-1)))
285*0c0b2b49SDavid du Colombier 		return;
286*0c0b2b49SDavid du Colombier 	postnote(up, 1, "sys: odd address", NDebug);
287*0c0b2b49SDavid du Colombier 	error(Ebadarg);
288*0c0b2b49SDavid du Colombier 	/*NOTREACHED*/
289*0c0b2b49SDavid du Colombier }
290