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