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