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