xref: /plan9-contrib/sys/src/9/vt5/trap.c (revision 1c9d674cbfa2c1924558af05e99c43e5c5cd4845)
1 /* ppc440 traps */
2 #include	"u.h"
3 #include	"../port/lib.h"
4 #include	"mem.h"
5 #include	"dat.h"
6 #include	"fns.h"
7 
8 #include	<tos.h>
9 #include	"ureg.h"
10 
11 #include	"io.h"
12 
13 enum {
14 	VECSIZE = 0x100,
15 };
16 
17 extern int notify(Ureg*);
18 extern void syscall(Ureg*);
19 
20 static struct {
21 	ulong off;
22 	char *name;
23 } intcause[] = {
24 	{ INT_CI,	"critical input" },
25 	{ INT_MCHECK,	"machine check" },
26 	{ INT_DSI,	"data access" },
27 	{ INT_ISI,	"instruction access" },
28 	{ INT_EI,	"external interrupt" },
29 	{ INT_ALIGN,	"alignment" },
30 	{ INT_PROG,	"program exception" },
31 	{ INT_FPU,	"floating-point unavailable" },
32 	{ INT_DEC,	"decrementer" },
33 	{ INT_SYSCALL,	"system call" },
34 	{ INT_TRACE,	"trace trap" },
35 	{ INT_FPA,	"floating point unavailable" },
36 	{ INT_APU,	"auxiliary processor unavailable" },
37 	{ INT_PIT,	"programmable interval timer interrrupt" },
38 	{ INT_FIT,	"fixed interval timer interrupt" },
39 	{ INT_WDT,	"watch dog timer interrupt" },
40 	{ INT_DMISS,	"data TLB miss" },
41 	{ INT_IMISS,	"instruction TLB miss" },
42 	{ INT_DEBUG,	"debug interrupt" },
43 	{ INT_DEBUG+VECSIZE, "system reset" },
44 	{ 0,		"unknown interrupt" }
45 };
46 
47 static char *excname(ulong, u32int);
48 
49 char *regname[]={
50 	"CAUSE","SRR1",
51 	"PC",	"GOK",
52 	"LR",	"CR",
53 	"XER",	"CTR",
54 	"R0",	"R1",
55 	"R2",	"R3",
56 	"R4",	"R5",
57 	"R6",	"R7",
58 	"R8",	"R9",
59 	"R10",	"R11",
60 	"R12",	"R13",
61 	"R14",	"R15",
62 	"R16",	"R17",
63 	"R18",	"R19",
64 	"R20",	"R21",
65 	"R22",	"R23",
66 	"R24",	"R25",
67 	"R26",	"R27",
68 	"R28",	"R29",
69 	"R30",	"R31",
70 };
71 
72 static int probing, trapped;
73 
74 void	init0(void);
75 
76 #define	SPR(v)	(((v&0x1f)<<16) | (((v>>5)&0x1f)<<11))
77 
78 #define SPR_SPRG0	0x110		/* SPR General 0 */
79 #define SPR_SPRG2	0x112		/* SPR General 2 */
80 #define SPR_SPRG6W	0x116		/* SPR General 6; supervisor W  */
81 
82 static void
vecgen(int v,void (* r)(void),int r0spr,int maskce)83 vecgen(int v, void (*r)(void), int r0spr, int maskce)
84 {
85 	u32int *vp, o, ra;
86 	int i, d;
87 
88 	vp = (u32int*)KADDR(v);
89 	vp[0] = 0x7c0003a6 | SPR(r0spr);	/* MOVW R0, SPR(r0spr) */
90 	i = 1;
91 	if(maskce){			/* clear CE?  this stops crit. intrs. */
92 		vp[i++] = 0x7c0000a6;	/* MOVW   MSR, R0 */
93 		vp[i++] = 0x540003da;	/* RLWNM $0, R0, ~MSR_CE, R0 */
94 		vp[i++] = 0x7c000124;	/* MOVW	 R0, MSR */
95 	}
96 	vp[i++] = 0x7c0802a6;			/* MOVW LR, R0 */
97 	vp[i++] = 0x7c0003a6 | SPR(SPR_SPRG2);	/* MOVW R0, SPR(SPRG2) */
98 	d = (uchar*)r - (uchar*)&vp[i];
99 	o = (u32int)d >> 25;
100 	if(o != 0 && o != 0x7F){
101 		/* a branch too far: running from ROM */
102 		ra = (u32int)r;
103 		vp[i++] = (15<<26)|(ra>>16);	/* MOVW $r&~0xFFFF, R0 */
104 		vp[i++] = (24<<26)|(ra&0xFFFF);	/* OR $r&0xFFFF, R0 */
105 		vp[i++] = 0x7c0803a6;		/* MOVW	R0, LR */
106 		vp[i++] = 0x4e800021;		/* BL (LR) */
107 	}else
108 		vp[i++] = (18<<26)|(d&0x3FFFFFC)|1;	/* bl (relative) */
109 	dcflush(PTR2UINT(vp), i*sizeof(u32int));
110 }
111 
112 /* populate a normal vector */
113 static void
sethvec(int v,void (* r)(void))114 sethvec(int v, void (*r)(void))
115 {
116 	vecgen(v, r, SPR_SPRG0, 1);
117 }
118 
119 /* populate a tlb-miss vector */
120 static void
sethvec2(int v,void (* r)(void))121 sethvec2(int v, void (*r)(void))
122 {
123 	ulong *vp;
124 	long d;
125 
126 	vp = (ulong*)KADDR(v);
127 	d = (uchar*)r - (uchar*)&vp[0];
128 	if (d >= (1<<26)) {
129 		uartlputc('?');
130 iprint("sethvec2: v %#x vp %#p r %#p d %ld\n", v, vp, r, d);
131 		iprint("tlb miss handler address too high\n");
132 	}
133 	vp[0] = (18<<26)|(d & 0x3FFFFFC);	/* b (relative) */
134 	dcflush(PTR2UINT(vp), sizeof *vp);
135 }
136 
137 static void
faultpower(Ureg * ureg,ulong addr,int read)138 faultpower(Ureg *ureg, ulong addr, int read)
139 {
140 	int user, insyscall;
141 	char buf[ERRMAX];
142 
143 	/*
144 	 * There must be a user context.
145 	 * If not, the usual problem is causing a fault during
146 	 * initialisation before the system is fully up.
147 	 */
148 	user = (ureg->srr1 & MSR_PR) != 0;
149 	if(!user){
150 //		if(vmapsync(addr))
151 //			return;
152 //		if(addr >= USTKTOP)
153 //			panic("kernel fault: bad address pc=%.8#lux addr=%.8#lux",
154 //				ureg->pc, addr);
155 		if(up == nil)
156 			panic("kernel fault: no user process pc=%.8#lux addr=%.8#lux",
157 				ureg->pc, addr);
158 	}
159 	if(up == nil)
160 		panic("user fault: up=0 pc=%.8#lux addr=%.8#lux", ureg->pc, addr);
161 	insyscall = up->insyscall;
162 	up->insyscall = 1;
163 	if(fault(addr, read) < 0){
164 		/*
165 		 * It is possible to get here with !user if, for example,
166 		 * a process was in a system call accessing a shared
167 		 * segment but was preempted by another process which shrunk
168 		 * or deallocated the shared segment; when the original
169 		 * process resumes it may fault while in kernel mode.
170 		 * No need to panic this case, post a note to the process
171 		 * and unwind the error stack. There must be an error stack
172 		 * (up->nerrlab != 0) if this is a system call, if not then
173 		 * the game's a bogey.
174 		 */
175 		if(!user && (!insyscall || up->nerrlab == 0)){
176 			dumpregs(ureg);
177 			panic("fault: %#lux", addr);
178 		}
179 		sprint(buf, "sys: trap: fault %s addr=%#lux",
180 			read? "read": "write", addr);
181 		postnote(up, 1, buf, NDebug);
182 		if(insyscall)
183 			error(buf);
184 	}
185 	up->insyscall = insyscall;
186 }
187 
188 static void
setlights(int user)189 setlights(int user)
190 {
191 	if (up == nil)
192 		lightstate(Ledidle);
193 	else
194 		lightstate(user == 0? Ledkern: Leduser);
195 }
196 
197 void
syncall(void)198 syncall(void)
199 {
200 	sync();
201 	isync();
202 }
203 
204 void
kexit(Ureg *)205 kexit(Ureg*)
206 {
207 	uvlong t;
208 	Tos *tos;
209 
210 	/* precise time accounting, kernel exit */
211 	tos = (Tos*)(USTKTOP-sizeof(Tos));
212 	cycles(&t);
213 	tos->kcycles += t - up->kentry;
214 	tos->pcycles = up->pcycles;
215 	tos->pid = up->pid;
216 // surely only need to set tos->pid on rfork and exec?
217 }
218 
219 void
trap(Ureg * ur)220 trap(Ureg *ur)
221 {
222 	int ecode, user, v;
223 	u32int esr, mcsr;
224 	char buf[ERRMAX];
225 
226 	if (ur == nil)
227 		panic("trap: nil ur");
228 	v = ur->cause;
229 	ur->cause &= 0xFFE0;
230 	ecode = ur->cause;
231 
232 	esr = getesr();
233 	mcsr = getmcsr();
234 	clrmchk();
235 
236 	lightstate(Ledtrap);
237 	user = (ur->srr1 & MSR_PR) != 0;
238 	if(user){
239 		cycles(&up->kentry);
240 		up->dbgreg = ur;
241 	}
242 	switch(ecode){
243 	case INT_SYSCALL:
244 		if(!user)
245 			panic("syscall in kernel: srr1 %#4.4luX pc %#p",
246 				ur->srr1, ur->pc);
247 		syscall(ur);
248 		setlights(user);
249 		return;		/* syscall() calls notify itself */
250 
251 	case INT_PIT:
252 		m->intr++;
253 		clockintr(ur);
254 		break;
255 
256 	case INT_WDT:
257 		puttsr(~0);
258 		panic("watchdog timer went off at pc %#lux", ur->pc);
259 		break;
260 
261 	case INT_MCHECK:
262 		if (probing && !user) {
263 			if (trapped++ > 0)
264 				panic("trap: recursive probe on mcheck");
265 			break;		/* continue at next instruction */
266 		}
267 		if(esr & ESR_MCI){
268 			iprint("mcheck-mci %lux\n", ur->pc);
269 			faultpower(ur, ur->pc, 1);
270 			break;
271 		}
272 		iprint("mcheck %#lux esr=%#ux mcsr=%#ux dear=%#ux\n",
273 			ur->pc, esr, mcsr, getdear());
274 		ur->pc -= 4;	/* back up to faulting instruction */
275 		/* fall through */
276 	case INT_DSI:
277 	case INT_DMISS:
278 		faultpower(ur, getdear(), !(esr&ESR_DST));
279 		break;
280 
281 	case INT_ISI:
282 	case INT_IMISS:
283 		faultpower(ur, ur->pc, 1);
284 		break;
285 
286 	case INT_CI:
287 	case INT_EI:
288 		m->intr++;
289 		intr(ur);
290 		break;
291 
292 	case 0:
293 		puttsr(~0);
294 		if (v == 0)
295 			panic("watchdog reset? probable jump via "
296 				"zeroed pointer; pc %#lux lr %#lux",
297 				ur->pc, ur->lr);
298 		else
299 			panic("watchdog reset? interrupt at vector zero; "
300 				"pc %#lux lr %#lux", ur->pc, ur->lr);
301 		break;
302 
303 	case INT_DEBUG:
304 		putdbsr(~0);		/* extinguish source */
305 		print("debug interrupt at pc %#lux\n", ur->pc);
306 		break;
307 
308 	case INT_DEBUG + VECSIZE:
309 		panic("reset");
310 		break;
311 
312 	case INT_FPA:
313 	case INT_FPU:
314 		if(fpuavail(ur))
315 			break;
316 		esr |= ESR_PFP;
317 		/* fall through */
318 	case INT_PROG:
319 		if(esr & ESR_PFP){	/* floating-point enabled exception */
320 			fputrap(ur, user);
321 			break;
322 		}
323 		if(esr & ESR_PIL && user){
324 			if(fpuemu(ur))
325 				break;
326 			/* otherwise it's an illegal instruction */
327 		}
328 		/* fall through */
329 	default:
330 		if(user){
331 			spllo();
332 			sprint(buf, "sys: trap: %s", excname(ecode, esr));
333 			if(ecode == INT_ALIGN)
334 				sprint(buf+strlen(buf), " ea=%#ux", getdear());
335 			postnote(up, 1, buf, NDebug);
336 			break;
337 		}
338 		splhi();
339 		print("kernel %s; vector=%#ux pc=%#lux\n",
340 			excname(ecode, esr), ecode, ur->pc);
341 		if (ecode == 0)
342 			print("probable jump via zeroed pointer; pc %#lux lr %#lux\n",
343 				ur->pc, ur->lr);
344 		dumpregs(ur);
345 		dumpstack();
346 		if(m->machno == 0)
347 			spllo();
348 		exit(1);
349 	}
350 	splhi();
351 	setlights(user);
352 
353 	/* delaysched set because we held a lock or because our quantum ended */
354 	if(up && up->delaysched && ecode == INT_PIT){
355 		sched();
356 		splhi();
357 		setlights(user);
358 	}
359 
360 	if(user){
361 		if(up->procctl || up->nnote)
362 			notify(ur);
363 		kexit(ur);
364 	}
365 }
366 
367 void
trapinit(void)368 trapinit(void)
369 {
370 	int i;
371 
372 	clrmchk();
373 	intrinit();
374 
375 	/*
376 	 * set all exceptions to trap by default
377 	 */
378 	for(i = 0; i < INT_DEBUG + VECSIZE; i += VECSIZE)
379 		sethvec(VECBASE + i, trapvec);
380 
381 	/*
382 	 * set exception handlers
383 	 */
384 	vecgen(VECBASE + INT_CI, critintrvec, SPR_SPRG6W, 0);
385 	sethvec(VECBASE + INT_MCHECK, trapmvec);
386 	sethvec(VECBASE + INT_DSI, trapvec);
387 	sethvec(VECBASE + INT_ISI, trapvec);
388 	sethvec(VECBASE + INT_EI, trapvec);
389 	sethvec(VECBASE + INT_ALIGN, trapvec);
390 	sethvec(VECBASE + INT_PROG, trapvec);
391 	sethvec(VECBASE + INT_FPU, trapvec);
392 	sethvec(VECBASE + INT_DEC, trapvec);
393 	sethvec(VECBASE + INT_SYSCALL, trapvec);
394 	sethvec(VECBASE + INT_TRACE, trapvec);
395 	sethvec(VECBASE + INT_FPA, trapvec);
396 	sethvec(VECBASE + INT_APU, trapvec);
397 	sethvec(VECBASE + INT_PIT, trapvec);
398 //	sethvec(VECBASE + INT_FIT, trapvec);
399 	vecgen(VECBASE + INT_WDT, critintrvec, SPR_SPRG6W, 0);
400 	sethvec2(VECBASE + INT_DMISS, dtlbmiss);
401 	sethvec2(VECBASE + INT_IMISS, itlbmiss);
402 	vecgen(VECBASE + INT_DEBUG, critintrvec, SPR_SPRG6W, 0);
403 	sync();
404 
405 	putevpr(KZERO | VECBASE);
406 	sync();
407 
408 	putmsr(getmsr() | MSR_ME | MSR_DE);
409 	sync();
410 }
411 
412 static char*
excname(ulong ivoff,u32int esr)413 excname(ulong ivoff, u32int esr)
414 {
415 	int i;
416 
417 	if(ivoff == INT_PROG){
418 		if(esr & ESR_PIL)
419 			return "illegal instruction";
420 		if(esr & ESR_PPR)
421 			return "privileged";
422 		if(esr & ESR_PTR)
423 			return "trap with successful compare";
424 		if(esr & ESR_PEU)
425 			return "unimplemented APU/FPU";
426 		if(esr & ESR_PAP)
427 			return "APU exception";
428 		if(esr & ESR_U0F)
429 			return "data storage: u0 fault";
430 	}
431 	for(i=0; intcause[i].off != 0; i++)
432 		if(intcause[i].off == ivoff)
433 			break;
434 	return intcause[i].name;
435 }
436 
437 /*
438  * Fill in enough of Ureg to get a stack trace, and call a function.
439  * Used by debugging interface rdb.
440  */
441 void
callwithureg(void (* fn)(Ureg *))442 callwithureg(void (*fn)(Ureg*))
443 {
444 	Ureg ureg;
445 
446 	ureg.pc = getcallerpc(&fn);
447 	ureg.sp = PTR2UINT(&fn);
448 	fn(&ureg);
449 }
450 
451 void
dumpstack(void)452 dumpstack(void)
453 {
454 	ulong l, v;
455 	int i;
456 
457 	if(up == 0)
458 		return;
459 	i = 0;
460 	for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){
461 		v = *(ulong*)l;
462 		if(KTZERO < v && v < (ulong)etext){
463 			iprint("%lux=%lux, ", l, v);
464 			if(i++ == 4){
465 				iprint("\n");
466 				i = 0;
467 			}
468 		}
469 	}
470 }
471 
472 void
dumpregs(Ureg * ureg)473 dumpregs(Ureg *ureg)
474 {
475 	int i;
476 	uintptr *l;
477 
478 	splhi();		/* prevent recursive dumps */
479 	if(up != nil)
480 		iprint("cpu%d: registers for %s %ld\n",
481 			m->machno, up->text, up->pid);
482 	else
483 		iprint("cpu%d: registers for kernel\n", m->machno);
484 
485 	if (ureg == nil) {
486 		iprint("nil ureg, no dump\n");
487 		return;
488 	}
489 	l = &ureg->cause;
490 	for(i = 0; i < nelem(regname); i += 2, l += 2)
491 		iprint("%s\t%.8p\t%s\t%.8p\n", regname[i], l[0], regname[i+1], l[1]);
492 }
493 
494 static void
linkproc(void)495 linkproc(void)
496 {
497 	spllo();
498 	up->kpfun(up->kparg);
499 	pexit("", 0);
500 }
501 
502 void
kprocchild(Proc * p,void (* func)(void *),void * arg)503 kprocchild(Proc* p, void (*func)(void*), void* arg)
504 {
505 	p->sched.pc = PTR2UINT(linkproc);
506 	p->sched.sp = PTR2UINT(p->kstack+KSTACK);
507 	p->sched.sp = STACKALIGN(p->sched.sp);
508 
509 	p->kpfun = func;
510 	p->kparg = arg;
511 }
512 
513 uintptr
userpc(void)514 userpc(void)
515 {
516 	Ureg *ureg = up->dbgreg;
517 	return ureg->pc;
518 }
519 
520 /*
521  * This routine must save the values of registers the user is not
522  * permitted to write from devproc and then restore the saved values
523  * before returning
524  */
525 void
setregisters(Ureg * ureg,char * pureg,char * uva,int n)526 setregisters(Ureg *ureg, char *pureg, char *uva, int n)
527 {
528 	u32int status;
529 
530 	status = ureg->status;
531 	memmove(pureg, uva, n);
532 	ureg->status = status;
533 }
534 
535 /*
536  * Give enough context in the ureg to produce a kernel stack for
537  * a sleeping process
538  */
539 void
setkernur(Ureg * ureg,Proc * p)540 setkernur(Ureg* ureg, Proc* p)
541 {
542 	ureg->pc = p->sched.pc;
543 	ureg->sp = p->sched.sp+BY2SE;
544 }
545 
546 uintptr
dbgpc(Proc * p)547 dbgpc(Proc* p)
548 {
549 	Ureg *ureg;
550 
551 	ureg = p->dbgreg;
552 	if(ureg == 0)
553 		return 0;
554 
555 	return ureg->pc;
556 }
557 
558 static Lock mchklck;	/* only allow one probe or poke at a time */
559 
560 vlong
probeaddr(uintptr addr)561 probeaddr(uintptr addr)
562 {
563 	vlong v;
564 
565 	ilock(&mchklck);
566 	trapped = 0;
567 	probing = 1;
568 
569 	syncall();
570 	putmsr(getmsr() & ~MSR_ME);	/* disable machine check traps */
571 	syncall();
572 	clrmchk();
573 	syncall();
574 
575 	if (getesr() & ESR_MCI)		/* machine check happened? */
576 		iprint("probeaddr: mcheck before probe\n");
577 	v = *(ulong *)addr;		/* this may cause a machine check */
578 	syncall();
579 
580 	if (getesr() & ESR_MCI)		/* machine check happened? */
581 		trapped = 1;
582 	syncall();
583 	clrmchk();
584 	syncall();
585 	putmsr(getmsr() | MSR_ME);	/* enable machine check traps */
586 	syncall();
587 
588 	probing = 0;
589 	if (trapped)
590 		v = -1;
591 //iprint("probeaddr: trapped=%d for addr %lux\n", trapped, addr);
592 	iunlock(&mchklck);
593 	return v;
594 }
595 
596 vlong
pokeaddr(uintptr addr,uint v1,uint v2)597 pokeaddr(uintptr addr, uint v1, uint v2)
598 {
599 	vlong v;
600 	ulong *p;
601 
602 //iprint("probing %#lux...", addr);
603 	ilock(&mchklck);
604 	trapped = 0;
605 	probing = 1;
606 
607 	syncall();
608 	putmsr(getmsr() & ~MSR_ME);	/* disable machine check traps */
609 	syncall();
610 	clrmchk();
611 	syncall();
612 
613 	if (getesr() & ESR_MCI)		/* machine check happened? */
614 		iprint("pokeaddr: mcheck before probe\n");
615 
616 	p = (ulong *)addr;
617 	*p = v1;
618 	syncall();
619 	v = *p;				/* this may cause a machine check */
620 	syncall();
621 	if (getesr() & ESR_MCI || v != v1)	/* machine check happened? */
622 		trapped = 1;
623 
624 	*p = v2;
625 	syncall();
626 	v = *p;				/* this may cause a machine check */
627 	syncall();
628 	if (getesr() & ESR_MCI || v != v2)	/* machine check happened? */
629 		trapped = 1;
630 
631 	syncall();
632 	clrmchk();
633 	syncall();
634 	putmsr(getmsr() | MSR_ME);	/* enable machine check traps */
635 	syncall();
636 
637 	probing = 0;
638 	if (trapped)
639 		v = -1;
640 //iprint("pokeaddr: trapped=%d for addr %lux\n", trapped, addr);
641 	iunlock(&mchklck);
642 	return v;
643 }
644