xref: /plan9-contrib/sys/src/9/vt4/trap.c (revision d6dfd9ef91cf0fa8514a249d5f2a550978c19369)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 
7 #include	<tos.h>
8 #include	"ureg.h"
9 
10 #include	"io.h"
11 
12 enum {
13 	VECSIZE = 0x100,
14 	VECBASE = PHYSSRAM,
15 };
16 
17 static struct {
18 	ulong off;
19 	char *name;
20 } intcause[] = {
21 	{ INT_RESET,	"system reset" },
22 	{ INT_MCHECK,	"machine check" },
23 	{ INT_DSI,	"data access" },
24 	{ INT_ISI,	"instruction access" },
25 	{ INT_EI,	"external interrupt" },
26 	{ INT_ALIGN,	"alignment" },
27 	{ INT_PROG,	"program exception" },
28 	{ INT_FPU,	"floating-point unavailable" },
29 	{ INT_DEC,	"decrementer" },
30 	{ INT_SYSCALL,	"system call" },
31 	{ INT_TRACE,	"trace trap" },
32 	{ INT_FPA,	"floating point unavailable" },
33 	{ INT_APU,	"auxiliary processor unavailable" },
34 	{ INT_PIT,	"programmable interval timer interrrupt" },
35 	{ INT_FIT,	"fixed interval timer interrupt" },
36 	{ INT_WDT,	"watch dog timer interrupt" },
37 	{ INT_DMISS,	"data TLB miss" },
38 	{ INT_IMISS,	"instruction TLB miss" },
39 	{ INT_DEBUG,	"debug interrupt" },
40 	{ INT_DEBUG+VECSIZE, "system reset" },
41 	{ 0,		"unknown interrupt" }
42 };
43 
44 static char *excname(ulong, u32int);
45 
46 char *regname[]={
47 	"CAUSE",	"SRR1",
48 	"PC",		"GOK",
49 	"LR",		"CR",
50 	"XER",	"CTR",
51 	"R0",		"R1",
52 	"R2",		"R3",
53 	"R4",		"R5",
54 	"R6",		"R7",
55 	"R8",		"R9",
56 	"R10",	"R11",
57 	"R12",	"R13",
58 	"R14",	"R15",
59 	"R16",	"R17",
60 	"R18",	"R19",
61 	"R20",	"R21",
62 	"R22",	"R23",
63 	"R24",	"R25",
64 	"R26",	"R27",
65 	"R28",	"R29",
66 	"R30",	"R31",
67 };
68 
69 static int probing, trapped;
70 
71 /* populate a normal vector */
72 static void
sethvec(int v,void (* r)(void))73 sethvec(int v, void (*r)(void))
74 {
75 	ulong *vp, pa, o;
76 
77 	vp = (ulong*)KADDR(v);
78 	vp[0] = 0x7c1043a6;			/* MOVW R0, SPR(SPRG0) */
79 	vp[1] = 0x7c0802a6;			/* MOVW LR, R0 */
80 	vp[2] = 0x7c1243a6;			/* MOVW R0, SPR(SPRG2) */
81 	pa = PADDR(r);
82 	o = pa >> 25;
83 	if(o != 0 && o != 0x7F){
84 		/* a branch too far: running from ROM */
85 		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
86 		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
87 		vp[5] = 0x7c0803a6;		/* MOVW	R0, LR */
88 		vp[6] = 0x4e800021;		/* BL (LR) */
89 		if (v == VECBASE + INT_PIT || v == VECBASE + INT_FIT) {
90 			uartlputc('?');
91 			uartlputc('v');
92 			print("branch too far for vector %#x!\n", v);
93 		}
94 	}else
95 		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
96 	dcflush(PTR2UINT(vp), 8*BY2WD);
97 }
98 
99 /* populate a tlb-miss vector */
100 static void
sethvec2(int v,void (* r)(void))101 sethvec2(int v, void (*r)(void))
102 {
103 	ulong *vp;
104 	char msg[128];
105 
106 	if (((ulong)r & ~KSEGM) >= (1<<26)) {
107 		uartlputc('?');
108 		uartlputc('t');
109 		snprint(msg, sizeof msg,
110 			"tlb miss handler address %#p too high\n", r);
111 		uartlputs(msg);
112 	}
113 	vp = (ulong*)KADDR(v);
114 	vp[0] = (18<<26)|((ulong)r&~KSEGM)|2;	/* ba */
115 	dcflush(PTR2UINT(vp), sizeof *vp);
116 }
117 
118 static void
faultpower(Ureg * ureg,ulong addr,int read)119 faultpower(Ureg *ureg, ulong addr, int read)
120 {
121 	int user, insyscall, n;
122 	char buf[ERRMAX];
123 
124 	user = (ureg->srr1 & MSR_PR) != 0;
125 	if(!user){
126 //		if(vmapsync(addr))
127 //			return;
128 //		if(addr >= USTKTOP)
129 //			panic("kernel fault: bad address pc=%.8#lux addr=%.8#lux",
130 //				ureg->pc, addr);
131 		if(up == nil)
132 			panic("kernel fault: no user process pc=%.8#lux addr=%.8#lux",
133 				ureg->pc, addr);
134 	}
135 	if(up == nil)
136 		panic("user fault: up=0 pc=%.8#lux addr=%.8#lux", ureg->pc, addr);
137 	insyscall = 0;
138 	if (up) {
139 		insyscall = up->insyscall;
140 		up->insyscall = 1;
141 	}
142 	n = fault(addr, read);		/* repair user-mode fault */
143 	if(n < 0){
144 		if(!user){
145 			dumpregs(ureg);
146 			panic("fault: addr %#lux", addr);
147 		}else if(0)
148 			dumpregs(ureg);
149 		seprint(buf, buf + sizeof buf, "sys: trap: fault %s addr=%#lux",
150 			read? "read" : "write", addr);
151 		postnote(up, 1, buf, NDebug);
152 	}
153 	if (up)
154 		up->insyscall = insyscall;
155 }
156 
157 static void
setlights(int user)158 setlights(int user)
159 {
160 	if (up == nil)
161 		lightstate(Ledidle);
162 	else
163 		lightstate(user == 0? Ledkern: Leduser);
164 }
165 
166 void
kexit(Ureg *)167 kexit(Ureg*)
168 {
169 	uvlong t;
170 	Tos *tos;
171 
172 	/* precise time accounting, kernel exit */
173 	tos = (Tos*)(USTKTOP-sizeof(Tos));
174 	cycles(&t);
175 	tos->kcycles += t - up->kentry;
176 	tos->pcycles = up->pcycles;
177 	tos->pid = up->pid;
178 // surely only need to set tos->pid on rfork and exec?
179 }
180 
181 void
trap(Ureg * ur)182 trap(Ureg *ur)
183 {
184 	int ecode, user, v;
185 	u32int esr;
186 	char buf[ERRMAX];
187 
188 	if (ur == nil)
189 		panic("trap: nil ur");
190 	v = ur->cause;
191 	lightstate(Ledtrap);
192 	ur->cause &= 0xFFE0;
193 	ecode = ur->cause;
194 	esr = getesr();
195 	clrmchk();
196 
197 	user = (ur->srr1 & MSR_PR) != 0;
198 	if(user){
199 		cycles(&up->kentry);
200 		up->dbgreg = ur;
201 	}
202 	switch(ecode){
203 	case INT_SYSCALL:
204 		if(!user)
205 			panic("syscall in kernel: srr1 %#4.4luX pc %#p",
206 				ur->srr1, ur->pc);
207 		syscall(ur);
208 		setlights(user);
209 		return;		/* syscall() calls notify itself */
210 
211 	case INT_PIT:
212 		m->intr++;
213 		clockintr(ur);
214 		break;
215 
216 	case INT_WDT:
217 		puttsr(~0);
218 		panic("watchdog timer went off at pc %#lux", ur->pc);
219 		break;
220 
221 	case INT_MCHECK:
222 		if (probing && !user) {
223 			if (trapped++ > 0)
224 				panic("trap: recursive probe");
225 			break;		/* continue at next instruction */
226 		}
227 		if(esr & ESR_MCI){
228 			iprint("mcheck-mci %lux\n", ur->pc);
229 			faultpower(ur, ur->pc, 1);
230 			break;
231 		}
232 		iprint("mcheck %#lux esr=%#ux dear=%#ux\n", ur->pc, esr, getdear());
233 		ur->pc -= 4;	/* back up to faulting instruction */
234 		/* fall through */
235 	case INT_DSI:
236 	case INT_DMISS:
237 		faultpower(ur, getdear(), !(esr&ESR_DST));
238 		break;
239 
240 	case INT_ISI:
241 	case INT_IMISS:
242 		faultpower(ur, ur->pc, 1);
243 		break;
244 
245 	case INT_EI:
246 		m->intr++;
247 		intr(ur);
248 		break;
249 
250 	case 0:
251 		puttsr(~0);
252 		if (v == 0)
253 			panic("watchdog reset? probable jump via "
254 				"zeroed pointer; pc %#lux lr %#lux",
255 				ur->pc, ur->lr);
256 		else
257 			panic("watchdog reset? interrupt at vector zero; "
258 				"pc %#lux lr %#lux", ur->pc, ur->lr);
259 		break;
260 
261 	case INT_DEBUG + VECSIZE:
262 		panic("reset");
263 		break;
264 
265 	case INT_PROG:
266 		if(esr & ESR_PIL && user){
267 			if(fpuemu(ur))
268 				break;
269 			/* otherwise it's an illegal instruction */
270 		}
271 		/* fall through */
272 	default:
273 		if(user){
274 			spllo();
275 			sprint(buf, "sys: trap: %s", excname(ecode, esr));
276 			postnote(up, 1, buf, NDebug);
277 			break;
278 		}
279 		splhi();
280 		print("kernel %s; vector=%#ux pc=%#lux\n",
281 			excname(ecode, esr), ecode, ur->pc);
282 		if (ecode == 0)
283 			print("probable jump via zeroed pointer; pc %#lux lr %#lux\n",
284 				ur->pc, ur->lr);
285 		dumpregs(ur);
286 		dumpstack();
287 		if(m->machno == 0)
288 			spllo();
289 		exit(1);
290 	}
291 	splhi();
292 	setlights(user);
293 
294 	/* delaysched set because we held a lock or because our quantum ended */
295 	if(up && up->delaysched && ecode == INT_PIT){
296 		sched();
297 		splhi();
298 		setlights(user);
299 	}
300 
301 	if(user){
302 		if(up->procctl || up->nnote)
303 			notify(ur);
304 		kexit(ur);
305 	}
306 }
307 
308 void
trapinit(void)309 trapinit(void)
310 {
311 	int i;
312 
313 	clrmchk();
314 	intrinit();
315 
316 	/*
317 	 * set all exceptions to trap by default
318 	 */
319 	for(i = 0; i < INT_DEBUG + VECSIZE; i += VECSIZE)
320 		sethvec(VECBASE + i, trapvec);
321 
322 	/*
323 	 * set exception handlers
324 	 */
325 	sethvec(VECBASE + INT_RESET, trapcritvec);
326 	sethvec(VECBASE + INT_MCHECK, trapcritvec);
327 	sethvec(VECBASE + INT_DSI, trapvec);
328 	sethvec(VECBASE + INT_ISI, trapvec);
329 	sethvec(VECBASE + INT_EI, trapvec);
330 	sethvec(VECBASE + INT_ALIGN, trapvec);
331 	sethvec(VECBASE + INT_PROG, trapvec);
332 	sethvec(VECBASE + INT_FPU, trapvec);
333 	sethvec(VECBASE + INT_SYSCALL, trapvec);
334 	sethvec(VECBASE + INT_APU, trapvec);
335 	sethvec(VECBASE + INT_PIT, trapvec);
336 	sethvec(VECBASE + INT_FIT, trapvec);
337 	sethvec(VECBASE + INT_WDT, trapcritvec);
338 	sethvec2(VECBASE + INT_DMISS, dtlbmiss);
339 	sethvec2(VECBASE + INT_IMISS, itlbmiss);
340 	sethvec(VECBASE + INT_DEBUG, trapcritvec);
341 	/*
342 	 * the last word of sram (0xfffffffc) contains a branch
343 	 * to the start of sram beyond the vectors (0xfffe2100),
344 	 * which initially will be the start of our bootstrap loader.
345 	 * overwrite it so that we can get control if the machine should
346 	 * reset.
347 	 */
348 	sethvec(VECBASE + INT_DEBUG + VECSIZE, trapcritvec);
349 	sethvec(0, trapcritvec);
350 	sync();
351 
352 	putevpr(VECBASE);
353 	sync();
354 
355 	putmsr(getmsr() | MSR_ME);
356 	sync();
357 }
358 
359 static char*
excname(ulong ivoff,u32int esr)360 excname(ulong ivoff, u32int esr)
361 {
362 	int i;
363 
364 	if(ivoff == INT_PROG){
365 		if(esr & ESR_PIL)
366 			return "illegal instruction";
367 		if(esr & ESR_PPR)
368 			return "privileged";
369 		if(esr & ESR_PTR)
370 			return "trap with successful compare";
371 		if(esr & ESR_PEU)
372 			return "unimplemented APU/FPU";
373 		if(esr & ESR_PAP)
374 			return "APU exception";
375 		if(esr & ESR_U0F)
376 			return "data storage: u0 fault";
377 	}
378 	for(i=0; intcause[i].off != 0; i++)
379 		if(intcause[i].off == ivoff)
380 			break;
381 	return intcause[i].name;
382 }
383 
384 /*
385  * Fill in enough of Ureg to get a stack trace, and call a function.
386  * Used by debugging interface rdb.
387  */
388 void
callwithureg(void (* fn)(Ureg *))389 callwithureg(void (*fn)(Ureg*))
390 {
391 	Ureg ureg;
392 
393 	ureg.pc = getcallerpc(&fn);
394 	ureg.sp = PTR2UINT(&fn);
395 	fn(&ureg);
396 }
397 
398 void
dumpstack(void)399 dumpstack(void)
400 {
401 	ulong l, v;
402 	int i;
403 
404 	if(up == 0)
405 		return;
406 	i = 0;
407 	for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
408 		v = *(ulong*)l;
409 		if(KTZERO < v && v < (ulong)etext){
410 			iprint("%lux=%lux, ", l, v);
411 			if(i++ == 4){
412 				iprint("\n");
413 				i = 0;
414 			}
415 		}
416 	}
417 }
418 
419 void
dumpregs(Ureg * ureg)420 dumpregs(Ureg *ureg)
421 {
422 	int i;
423 	uintptr *l;
424 
425 	splhi();		/* prevent recursive dumps */
426 	if(up != nil)
427 		iprint("cpu%d: registers for %s %ld\n",
428 			m->machno, up->text, up->pid);
429 	else
430 		iprint("cpu%d: registers for kernel\n", m->machno);
431 
432 	if (ureg == nil) {
433 		iprint("nil ureg, no dump\n");
434 		return;
435 	}
436 	l = &ureg->cause;
437 	for(i = 0; i < nelem(regname); i += 2, l += 2)
438 		iprint("%s\t%.8p\t%s\t%.8p\n", regname[i], l[0], regname[i+1], l[1]);
439 }
440 
441 static void
linkproc(void)442 linkproc(void)
443 {
444 	spllo();
445 	up->kpfun(up->kparg);
446 	pexit("", 0);
447 }
448 
449 void
kprocchild(Proc * p,void (* func)(void *),void * arg)450 kprocchild(Proc* p, void (*func)(void*), void* arg)
451 {
452 	p->sched.pc = PTR2UINT(linkproc);
453 	p->sched.sp = PTR2UINT(p->kstack+KSTACK);
454 	p->sched.sp = STACKALIGN(p->sched.sp);
455 
456 	p->kpfun = func;
457 	p->kparg = arg;
458 }
459 
460 uintptr
userpc(void)461 userpc(void)
462 {
463 	Ureg *ureg = up->dbgreg;
464 	return ureg->pc;
465 }
466 
467 /*
468  * This routine must save the values of registers the user is not
469  * permitted to write from devproc and then restore the saved values
470  * before returning
471  */
472 void
setregisters(Ureg * ureg,char * pureg,char * uva,int n)473 setregisters(Ureg *ureg, char *pureg, char *uva, int n)
474 {
475 	u32int status;
476 
477 	status = ureg->status;
478 	memmove(pureg, uva, n);
479 	ureg->status = status;
480 }
481 
482 /*
483  * Give enough context in the ureg to produce a kernel stack for
484  * a sleeping process
485  */
486 void
setkernur(Ureg * ureg,Proc * p)487 setkernur(Ureg* ureg, Proc* p)
488 {
489 	ureg->pc = p->sched.pc;
490 	ureg->sp = p->sched.sp+BY2SE;
491 }
492 
493 uintptr
dbgpc(Proc * p)494 dbgpc(Proc* p)
495 {
496 	Ureg *ureg;
497 
498 	ureg = p->dbgreg;
499 	if(ureg == 0)
500 		return 0;
501 	return ureg->pc;
502 }
503 
504 vlong
probeaddr(uintptr addr)505 probeaddr(uintptr addr)
506 {
507 	vlong v;
508 	static Lock fltlck;
509 
510 	ilock(&fltlck);
511 	trapped = 0;
512 	probing = 1;
513 	barriers();
514 
515 	v = *(ulong *)addr;	/* this may cause a fault */
516 	USED(probing);
517 	barriers();
518 
519 	probing = 0;
520 	barriers();
521 	if (trapped)
522 		v = -1;
523 	iunlock(&fltlck);
524 	return v;
525 }
526