]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/sgi/trap.c
sgi: work in progress kernel for sgi mips machines (only tested with r5k indy)
[plan9front.git] / sys / src / 9 / sgi / trap.c
1 /*
2  * traps, exceptions, faults and interrupts on ar7161
3  */
4 #include        "u.h"
5 #include        "../port/lib.h"
6 #include        "mem.h"
7 #include        "dat.h"
8 #include        "fns.h"
9 #include        "ureg.h"
10 #include        "io.h"
11 #include        <tos.h>
12 #include        "../port/error.h"
13
14 typedef struct Handler Handler;
15
16 struct Handler {
17         void    (*handler)(Ureg*, void *);
18         void    *arg;
19         Handler *next;                  /* at this interrupt level */
20 };
21
22 int     intr(Ureg*);
23 void    kernfault(Ureg*, int);
24 void    noted(Ureg*, ulong);
25 void    rfnote(Ureg**);
26
27 char *excname[] =
28 {
29         "trap: external interrupt",
30         "trap: TLB modification (store to unwritable)",
31         "trap: TLB miss (load or fetch)",
32         "trap: TLB miss (store)",
33         "trap: address error (load or fetch)",
34         "trap: address error (store)",
35         "trap: bus error (fetch)",
36         "trap: bus error (data load or store)",
37         "trap: system call",
38         "breakpoint",
39         "trap: reserved instruction",
40         "trap: coprocessor unusable",
41         "trap: arithmetic overflow",
42         "trap: TRAP exception",
43         "trap: VCE (instruction)",
44         "trap: floating-point exception",
45         "trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */
46         "trap: corextend unusable",
47         "trap: precise coprocessor 2 exception",
48         "trap: TLB read-inhibit",
49         "trap: TLB execute-inhibit",
50         "trap: undefined 21",
51         "trap: undefined 22",
52         "trap: WATCH exception",
53         "trap: machine checkcore",
54         "trap: undefined 25",
55         "trap: undefined 26",
56         "trap: undefined 27",
57         "trap: undefined 28",
58         "trap: undefined 29",
59         "trap: cache error",
60         "trap: VCE (data)",
61 };
62
63 char *fpcause[] =
64 {
65         "inexact operation",
66         "underflow",
67         "overflow",
68         "division by zero",
69         "invalid operation",
70 };
71 char    *fpexcname(Ureg*, ulong, char*, uint);
72 #define FPEXPMASK       (0x3f<<12)      /* Floating exception bits in fcr31 */
73
74 struct {
75         char    *name;
76         uint    off;
77 } regname[] = {
78         "STATUS", offsetof(Ureg, status),
79         "PC",   offsetof(Ureg, pc),
80         "SP",   offsetof(Ureg, sp),
81         "CAUSE",offsetof(Ureg, cause),
82         "BADADDR", offsetof(Ureg, badvaddr),
83         "TLBVIRT", offsetof(Ureg, tlbvirt),
84         "HI",   offsetof(Ureg, hi),
85         "LO",   offsetof(Ureg, lo),
86         "R31",  offsetof(Ureg, r31),
87         "R30",  offsetof(Ureg, r30),
88         "R28",  offsetof(Ureg, r28),
89         "R27",  offsetof(Ureg, r27),
90         "R26",  offsetof(Ureg, r26),
91         "R25",  offsetof(Ureg, r25),
92         "R24",  offsetof(Ureg, r24),
93         "R23",  offsetof(Ureg, r23),
94         "R22",  offsetof(Ureg, r22),
95         "R21",  offsetof(Ureg, r21),
96         "R20",  offsetof(Ureg, r20),
97         "R19",  offsetof(Ureg, r19),
98         "R18",  offsetof(Ureg, r18),
99         "R17",  offsetof(Ureg, r17),
100         "R16",  offsetof(Ureg, r16),
101         "R15",  offsetof(Ureg, r15),
102         "R14",  offsetof(Ureg, r14),
103         "R13",  offsetof(Ureg, r13),
104         "R12",  offsetof(Ureg, r12),
105         "R11",  offsetof(Ureg, r11),
106         "R10",  offsetof(Ureg, r10),
107         "R9",   offsetof(Ureg, r9),
108         "R8",   offsetof(Ureg, r8),
109         "R7",   offsetof(Ureg, r7),
110         "R6",   offsetof(Ureg, r6),
111         "R5",   offsetof(Ureg, r5),
112         "R4",   offsetof(Ureg, r4),
113         "R3",   offsetof(Ureg, r3),
114         "R2",   offsetof(Ureg, r2),
115         "R1",   offsetof(Ureg, r1),
116 };
117
118 static Handler handlers[8];
119
120 void
121 kvce(Ureg *ur, int ecode)
122 {
123         char c;
124         Pte **p;
125         Page **pg;
126         Segment *s;
127         ulong addr, soff;
128
129         c = 'D';
130         if(ecode == CVCEI)
131                 c = 'I';
132         print("Trap: VCE%c: addr=%#lux\n", c, ur->badvaddr);
133         if(up && !(ur->badvaddr & KSEGM)) {
134                 addr = ur->badvaddr;
135                 s = seg(up, addr, 0);
136                 if(s == nil){
137                         print("kvce: no seg for %#lux\n", addr);
138                         for(;;);
139                 }
140                 addr &= ~(BY2PG-1);
141                 soff = addr - s->base;
142                 p = &s->map[soff/PTEMAPMEM];
143                 if(*p){
144                         pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG];
145                         if(*pg)
146                                 print("kvce: pa=%#lux, va=%#lux\n",
147                                         (*pg)->pa, (*pg)->va);
148                         else
149                                 print("kvce: no *pg\n");
150                 }else
151                         print("kvce: no *p\n");
152         }
153 }
154
155 /* prepare to go to user space */
156 void
157 kexit(Ureg *ur)
158 {
159         Tos *tos;
160
161         /* replicate fpstate to ureg status */
162         if(up->fpstate != FPactive)
163                 ur->status &= ~CU1;
164
165         /* precise time accounting, kernel exit */
166         tos = (Tos*)(USTKTOP-sizeof(Tos));
167         tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry;
168         tos->pcycles = up->pcycles;
169         tos->pid = up->pid;
170 }
171
172 void
173 trap(Ureg *ur)
174 {
175         int ecode, clockintr, user, cop, x, fpchk;
176         ulong fpfcr31;
177         char buf[2*ERRMAX], buf1[ERRMAX], *fpexcep;
178         static int dumps;
179
180         if (up && (char *)(ur) - up->kstack < 1024 && dumps++ == 0) {
181                 iprint("trap: proc %ld kernel stack getting full\n", up->pid);
182                 dumpregs(ur);
183                 dumpstack();
184                 for(;;);
185         }
186         if (up == nil &&
187             (char *)(ur) - (char *)m->stack < 1024 && dumps++ == 0) {
188                 iprint("trap: cpu%d kernel stack getting full\n", m->machno);
189                 dumpregs(ur);
190                 dumpstack();
191                 for(;;);
192         }
193
194         ecode = (ur->cause>>2)&EXCMASK;
195         user = userureg(ur);
196         if (ur->cause & TS)
197                 panic("trap: tlb shutdown");
198
199         fpchk = 0;
200         if(user){
201                 up->dbgreg = ur;
202                 cycles(&up->kentry);
203         }
204
205         clockintr = 0;
206         switch(ecode){
207         case CINT:
208                 clockintr = intr(ur);
209                 break;
210
211         case CFPE:
212                 if(!user)
213                         goto Default;
214                 if(up->fpstate == FPactive){
215                         savefpregs(&up->fpsave);
216                         up->fpstate = FPinactive;
217                 }
218                 clrfpintr();
219                 fptrap(ur);
220                 fpchk = 1;
221                 break;
222
223         case CTLBM:
224         case CTLBL:
225         case CTLBS:
226                 if(up == nil || !user && (ur->badvaddr & KSEGM) == KSEG3) {
227                         kfault(ur);
228                         break;
229                 }
230                 x = up->insyscall;
231                 up->insyscall = 1;
232                 spllo();
233                 faultmips(ur, user, ecode);
234                 up->insyscall = x;
235                 break;
236
237         case CVCEI:
238         case CVCED:
239                 kvce(ur, ecode);
240                 goto Default;
241
242         case CWATCH:
243                 if(!user)
244                         panic("watchpoint trap from kernel mode pc=%#p",
245                                 ur->pc);
246                 // fpwatch(ur);
247                 break;
248
249         case CCPU:
250                 cop = (ur->cause>>28)&3;
251                 if(user && up && cop == 1) {
252                         if(up->fpstate & FPillegal) {
253                                 /* someone used floating point in a note handler */
254                                 postnote(up, 1,
255                                         "sys: floating point in note handler",
256                                         NDebug);
257                                 break;
258                         }
259                         if(up->fpstate == FPinit || up->fpstate == FPinactive){
260                                 restfpregs(&up->fpsave, up->fpsave.fpstatus&~FPEXPMASK);
261                                 up->fpstate = FPactive;
262                                 ur->status |= CU1;
263                                 break;
264                         }
265                         fpchk = 1;
266                         break;
267                 }
268                 /* Fallthrough */
269
270         Default:
271         default:
272                 if(user) {
273                         spllo();
274                         snprint(buf, sizeof buf, "sys: %s", excname[ecode]);
275                         postnote(up, 1, buf, NDebug);
276                         break;
277                 }
278                 if (ecode == CADREL || ecode == CADRES)
279                         iprint("kernel addr exception for va %#p pid %#ld %s\n",
280                                 ur->badvaddr, (up? up->pid: 0),
281                                 (up? up->text: ""));
282                 print("cpu%d: kernel %s pc=%#lux\n",
283                         m->machno, excname[ecode], ur->pc);
284                 dumpregs(ur);
285                 dumpstack();
286                 if(m->machno == 0)
287                         spllo();
288                 exit(1);
289         }
290
291         if(fpchk) {
292                 fpfcr31 = up->fpsave.fpstatus;
293                 if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) {
294                         spllo();
295                         fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1);
296                         snprint(buf, sizeof buf, "sys: fp: %s", fpexcep);
297                         postnote(up, 1, buf, NDebug);
298                 }
299         }
300
301         splhi();
302
303         /* delaysched set because we held a lock or because our quantum ended */
304         if(up && up->delaysched && clockintr){
305                 sched();
306                 splhi();
307         }
308
309         if(user){
310                 notify(ur);
311                 kexit(ur);
312         }
313 }
314
315 /* map HPC3 irq to INTR2 */
316 int
317 hpc3irqlevel(int irq)
318 {
319         *IO(uchar, LIO_0_MASK) |= 1 << (irq & 7);
320         return 2 + irq/8;
321 }
322
323 /*
324  *  set handlers
325  */
326 void
327 intrenable(int level, void (*h)(Ureg*, void *), void *arg)
328 {
329         Handler *hp;
330
331         hp = &handlers[level];
332         if (hp->handler != nil) {               /* occupied? */
333                 /* add a new one at the end of the chain */
334                 for (; hp->next != nil; hp = hp->next)
335                         ;
336                 if((hp->next = xalloc(sizeof *hp)) == nil)
337                         panic("intrenable: out of memory");
338                 hp = hp->next;
339         }
340         hp->arg = arg;
341         hp->handler = h;
342
343         intron(INTR0 << level);
344 }
345
346 int
347 intr(Ureg *ur)
348 {
349         ulong cause, mask;
350         int clockintr;
351         Handler *hh, *hp;
352
353         m->intr++;
354         clockintr = 0;
355         /*
356          * ignore interrupts that we have disabled, even if their cause bits
357          * are set.
358          */
359         cause = ur->cause & ur->status & INTMASK;
360         cause &= ~(INTR1|INTR0);                /* ignore sw interrupts */
361         if(cause & INTR7){
362                 clock(ur);
363                 cause &= ~INTR7;
364                 clockintr = 1;
365         }
366         hh = &handlers[2];
367         for(mask = INTR2; cause != 0 && mask < INTR7; mask <<= 1){
368                 if(cause & mask){
369                         for(hp = hh; hp != nil; hp = hp->next){
370                                 if(hp->handler != nil){
371                                         (*hp->handler)(ur, hp->arg);
372                                         cause &= ~mask;
373                                 }
374                         }
375                 }
376                 hh++;
377         }
378         if(cause != 0)
379                 iprint("unhandled interrupts %lux\n", cause);
380
381         /* preemptive scheduling */
382         if(up != nil && !clockintr)
383                 preempted();
384         /* if it was a clockintr, sched will be called at end of trap() */
385         return clockintr;
386 }
387
388 char*
389 fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size)
390 {
391         int i;
392         char *s;
393         ulong fppc;
394
395         fppc = ur->pc;
396         if(ur->cause & BD)      /* branch delay */
397                 fppc += 4;
398         s = 0;
399         if(fcr31 & (1<<17))
400                 s = "unimplemented operation";
401         else{
402                 fcr31 >>= 7;            /* trap enable bits */
403                 fcr31 &= (fcr31>>5);    /* anded with exceptions */
404                 for(i=0; i<5; i++)
405                         if(fcr31 & (1<<i))
406                                 s = fpcause[i];
407         }
408
409         if(s == 0)
410                 return "no floating point exception";
411
412         snprint(buf, size, "%s fppc=%#lux", s, fppc);
413         return buf;
414 }
415
416 static void
417 getpcsp(ulong *pc, ulong *sp)
418 {
419         *pc = getcallerpc(&pc);
420         *sp = (ulong)&pc-4;
421 }
422
423 void
424 callwithureg(void (*fn)(Ureg*))
425 {
426         Ureg ureg;
427
428         memset(&ureg, 0, sizeof ureg);
429         getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
430         ureg.r31 = getcallerpc(&fn);
431         fn(&ureg);
432 }
433
434 static void
435 _dumpstack(Ureg *ureg)
436 {
437         ulong l, v, top, i;
438         extern ulong etext;
439
440         print("ktrace /kernel/path %.8lux %.8lux %.8lux\n",
441                 ureg->pc, ureg->sp, ureg->r31);
442         if(up == nil)
443                 top = (ulong)MACHADDR + MACHSIZE;
444         else
445                 top = (ulong)up->kstack + KSTACK;
446         i = 0;
447         for(l=ureg->sp; l < top; l += BY2WD) {
448                 v = *(ulong*)l;
449                 if(KTZERO < v && v < (ulong)&etext) {
450                         print("%.8lux=%.8lux ", l, v);
451                         if((++i%4) == 0){
452                                 print("\n");
453                                 delay(200);
454                         }
455                 }
456         }
457         print("\n");
458 }
459
460 void
461 dumpstack(void)
462 {
463         callwithureg(_dumpstack);
464 }
465
466 static ulong
467 R(Ureg *ur, int i)
468 {
469         uchar *s;
470
471         s = (uchar*)ur;
472         return *(ulong*)(s + regname[i].off);
473 }
474
475 void
476 dumpregs(Ureg *ur)
477 {
478         int i;
479
480         if(up)
481                 print("registers for %s %lud\n", up->text, up->pid);
482         else
483                 print("registers for kernel\n");
484
485         for(i = 0; i < nelem(regname); i += 2)
486                 print("%s\t%#.8lux\t%s\t%#.8lux\n",
487                         regname[i].name,   R(ur, i),
488                         regname[i+1].name, R(ur, i+1));
489 }
490
491 int
492 notify(Ureg *ur)
493 {
494         int l, s;
495         ulong sp;
496         Note *n;
497
498         if(up->procctl)
499                 procctl();
500         if(up->nnote == 0)
501                 return 0;
502
503         if(up->fpstate == FPactive){
504                 savefpregs(&up->fpsave);
505                 up->fpstate = FPinactive;
506         }
507         up->fpstate |= FPillegal;
508
509         s = spllo();
510         qlock(&up->debug);
511         up->notepending = 0;
512         n = &up->note[0];
513         if(strncmp(n->msg, "sys:", 4) == 0) {
514                 l = strlen(n->msg);
515                 if(l > ERRMAX-15)       /* " pc=0x12345678\0" */
516                         l = ERRMAX-15;
517
518                 seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#lux", ur->pc);
519         }
520
521         if(n->flag != NUser && (up->notified || up->notify==0)) {
522                 if(n->flag == NDebug)
523                         pprint("suicide: %s\n", n->msg);
524
525                 qunlock(&up->debug);
526                 pexit(n->msg, n->flag!=NDebug);
527         }
528
529         if(up->notified) {
530                 qunlock(&up->debug);
531                 splx(s);
532                 return 0;
533         }
534
535         if(!up->notify) {
536                 qunlock(&up->debug);
537                 pexit(n->msg, n->flag!=NDebug);
538         }
539         sp = ur->usp & ~(BY2V-1);
540         sp -= sizeof(Ureg);
541
542         if(!okaddr((ulong)up->notify, BY2WD, 0) ||
543            !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) {
544                 pprint("suicide: bad address or sp in notify\n");
545                 qunlock(&up->debug);
546                 pexit("Suicide", 0);
547         }
548
549         memmove((Ureg*)sp, ur, sizeof(Ureg));   /* push user regs */
550         *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
551         up->ureg = (void*)sp;
552
553         sp -= BY2WD+ERRMAX;
554         memmove((char*)sp, up->note[0].msg, ERRMAX);    /* push err string */
555
556         sp -= 3*BY2WD;
557         *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD;     /* arg 2 is string */
558         ur->r1 = (long)up->ureg;                /* arg 1 is ureg* */
559         ((ulong*)sp)[1] = (ulong)up->ureg;      /* arg 1 0(FP) is ureg* */
560         ((ulong*)sp)[0] = 0;                    /* arg 0 is pc */
561         ur->usp = sp;
562         /*
563          * arrange to resume at user's handler as if handler(ureg, errstr)
564          * were being called.
565          */
566         ur->pc = (ulong)up->notify;
567
568         up->notified = 1;
569         up->nnote--;
570         memmove(&up->lastnote, &up->note[0], sizeof(Note));
571         memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
572
573         qunlock(&up->debug);
574         splx(s);
575         return 1;
576 }
577
578 /*
579  * Check that status is OK to return from note.
580  */
581 int
582 validstatus(ulong kstatus, ulong ustatus)
583 {
584 //      if((kstatus & (INTMASK|KX|SX|UX)) != (ustatus & (INTMASK|KX|SX|UX)))
585         if((kstatus & INTMASK) != (ustatus & INTMASK))
586                 return 0;
587         if((ustatus&(KSU|ERL|EXL|IE)) != (KUSER|EXL|IE))
588                 return 0;
589         if(ustatus & (0xFFFF0000&~CU1)) /* no CU3, CU2, CU0, RP, FR, RE, DS */
590                 return 0;
591         return 1;
592 }
593
594 /*
595  * Return user to state before notify(); called from user's handler.
596  */
597 void
598 noted(Ureg *kur, ulong arg0)
599 {
600         Ureg *nur;
601         ulong oureg, sp;
602
603         qlock(&up->debug);
604         if(arg0!=NRSTR && !up->notified) {
605                 qunlock(&up->debug);
606                 pprint("call to noted() when not notified\n");
607                 pexit("Suicide", 0);
608         }
609         up->notified = 0;
610
611         up->fpstate &= ~FPillegal;
612
613         nur = up->ureg;
614
615         oureg = (ulong)nur;
616         if((oureg & (BY2WD-1))
617         || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
618                 pprint("bad up->ureg in noted or call to noted() when not notified\n");
619                 qunlock(&up->debug);
620                 pexit("Suicide", 0);
621         }
622
623         if(0 && !validstatus(kur->status, nur->status)) {
624                 qunlock(&up->debug);
625                 pprint("bad noted ureg status %#lux\n", nur->status);
626                 pexit("Suicide", 0);
627         }
628
629         memmove(kur, up->ureg, sizeof(Ureg));
630         switch(arg0) {
631         case NCONT:
632         case NRSTR:                             /* only used by APE */
633                 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
634                         pprint("suicide: trap in noted\n");
635                         qunlock(&up->debug);
636                         pexit("Suicide", 0);
637                 }
638                 up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
639                 qunlock(&up->debug);
640                 splhi();
641                 break;
642
643         case NSAVE:                             /* only used by APE */
644                 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){
645                         pprint("suicide: trap in noted\n");
646                         qunlock(&up->debug);
647                         pexit("Suicide", 0);
648                 }
649                 qunlock(&up->debug);
650                 sp = oureg-4*BY2WD-ERRMAX;
651                 splhi();
652                 kur->sp = sp;
653                 kur->r1 = oureg;                /* arg 1 is ureg* */
654                 ((ulong*)sp)[1] = oureg;        /* arg 1 0(FP) is ureg* */
655                 ((ulong*)sp)[0] = 0;            /* arg 0 is pc */
656                 break;
657
658         default:
659                 pprint("unknown noted arg %#lux\n", arg0);
660                 up->lastnote.flag = NDebug;
661                 /* fall through */
662
663         case NDFLT:
664                 if(up->lastnote.flag == NDebug)
665                         pprint("suicide: %s\n", up->lastnote.msg);
666                 qunlock(&up->debug);
667                 pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
668         }
669 }
670
671 #include "../port/systab.h"
672
673 static void
674 sctracesetup(ulong scallnr, ulong sp, uintptr pc, vlong *startnsp)
675 {
676         if(up->procctl == Proc_tracesyscall){
677                 /*
678                  * Redundant validaddr.  Do we care?
679                  * Tracing syscalls is not exactly a fast path...
680                  * Beware, validaddr currently does a pexit rather
681                  * than an error if there's a problem; that might
682                  * change in the future.
683                  */
684                 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
685                         validaddr(sp, sizeof(Sargs)+BY2WD, 0);
686
687                 syscallfmt(scallnr, pc, (va_list)(sp+BY2WD));
688                 up->procctl = Proc_stopme;
689                 procctl();
690                 if(up->syscalltrace)
691                         free(up->syscalltrace);
692                 up->syscalltrace = nil;
693                 *startnsp = todget(nil);
694         }
695 }
696
697 static void
698 sctracefinish(ulong scallnr, ulong sp, int ret, vlong startns)
699 {
700         int s;
701
702         if(up->procctl == Proc_tracesyscall){
703                 up->procctl = Proc_stopme;
704                 sysretfmt(scallnr, (va_list)(sp+BY2WD), ret,
705                         startns, todget(nil));
706                 s = splhi();
707                 procctl();
708                 splx(s);
709                 if(up->syscalltrace)
710                         free(up->syscalltrace);
711                 up->syscalltrace = nil;
712         }
713 }
714
715 /*
716  * called directly from assembler, not via trap()
717  */
718 void
719 syscall(Ureg *ur)
720 {
721         int i;
722         volatile long ret;
723         ulong sp, scallnr;
724         vlong startns;
725         char *e;
726
727         cycles(&up->kentry);
728
729         m->syscall++;
730         up->insyscall = 1;
731         up->pc = ur->pc;
732         up->dbgreg = ur;
733         ur->cause = 16<<2;      /* for debugging: system call is undef 16 */
734
735         scallnr = ur->r1;
736         up->scallnr = ur->r1;
737         sp = ur->sp;
738         sctracesetup(scallnr, sp, ur->pc, &startns);
739
740         /* no fpu, so no fp state to save */
741         spllo();
742
743         up->nerrlab = 0;
744         ret = -1;
745         if(!waserror()) {
746                 if(scallnr >= nsyscall || systab[scallnr] == 0){
747                         pprint("bad sys call number %ld pc %#lux\n",
748                                 scallnr, ur->pc);
749                         postnote(up, 1, "sys: bad sys call", NDebug);
750                         error(Ebadarg);
751                 }
752
753                 if(sp & (BY2WD-1)){
754                         pprint("odd sp in sys call pc %#lux sp %#lux\n",
755                                 ur->pc, ur->sp);
756                         postnote(up, 1, "sys: odd stack", NDebug);
757                         error(Ebadarg);
758                 }
759
760                 if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
761                         validaddr(sp, sizeof(Sargs)+BY2WD, 0);
762
763                 up->s = *((Sargs*)(sp+BY2WD));
764                 up->psstate = sysctab[scallnr];
765
766                 ret = systab[scallnr]((va_list)up->s.args);
767                 poperror();
768         }else{
769                 /* failure: save the error buffer for errstr */
770                 e = up->syserrstr;
771                 up->syserrstr = up->errstr;
772                 up->errstr = e;
773                 if(0 && up->pid == 1)
774                         print("[%lud %s] syscall %lud: %s\n",
775                                 up->pid, up->text, scallnr, up->errstr);
776         }
777         if(up->nerrlab){
778                 print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
779                 for(i = 0; i < NERR; i++)
780                         print("sp=%#lux pc=%#lux\n",
781                                 up->errlab[i].sp, up->errlab[i].pc);
782                 panic("error stack");
783         }
784         sctracefinish(scallnr, sp, ret, startns);
785
786         ur->pc += 4;
787         ur->r1 = ret;
788
789         up->psstate = 0;
790         up->insyscall = 0;
791
792         if(scallnr == NOTED)                            /* ugly hack */
793                 noted(ur, *(ulong*)(sp+BY2WD)); /* may return */
794         splhi();
795         if(scallnr!=RFORK && (up->procctl || up->nnote))
796                 notify(ur);
797         /* if we delayed sched because we held a lock, sched now */
798         if(up->delaysched)
799                 sched();
800         kexit(ur);
801 }
802
803 void
804 forkchild(Proc *p, Ureg *ur)
805 {
806         Ureg *cur;
807
808         p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE;
809         p->sched.pc = (ulong)forkret;
810
811         cur = (Ureg*)(p->sched.sp+2*BY2WD);
812         memmove(cur, ur, sizeof(Ureg));
813
814         cur->status &= ~CU1;    /* FPU off when returning */
815
816         cur->r1 = 0;
817         cur->pc += 4;
818
819         /* Things from bottom of syscall we never got to execute */
820         p->psstate = 0;
821         p->insyscall = 0;
822 }
823
824 static
825 void
826 linkproc(void)
827 {
828         spllo();
829         up->kpfun(up->kparg);
830         pexit("kproc exiting", 0);
831 }
832
833 void
834 kprocchild(Proc *p, void (*func)(void*), void *arg)
835 {
836         p->sched.pc = (ulong)linkproc;
837         p->sched.sp = (ulong)p->kstack+KSTACK;
838
839         p->kpfun = func;
840         p->kparg = arg;
841 }
842
843 /* set up user registers before return from exec() */
844 uintptr
845 execregs(ulong entry, ulong ssize, ulong nargs)
846 {
847         Ureg *ur;
848         ulong *sp;
849
850         sp = (ulong*)(USTKTOP - ssize);
851         *--sp = nargs;
852
853         ur = (Ureg*)up->dbgreg;
854         ur->usp = (ulong)sp;
855         ur->pc = entry - 4;             /* syscall advances it */
856         return USTKTOP-sizeof(Tos);     /* address of kernel/user shared data */
857 }
858
859 ulong
860 userpc(void)
861 {
862         Ureg *ur;
863
864         ur = (Ureg*)up->dbgreg;
865         return ur->pc;
866 }
867
868 /*
869  * This routine must save the values of registers the user is not
870  * permitted to write from devproc and then restore the saved values
871  * before returning
872  */
873 void
874 setregisters(Ureg *xp, char *pureg, char *uva, int n)
875 {
876         ulong status;
877
878         status = xp->status;
879         memmove(pureg, uva, n);
880         xp->status = status;
881 }
882
883 /*
884  * Give enough context in the ureg to produce a kernel stack for
885  * a sleeping process
886  */
887 void
888 setkernur(Ureg *xp, Proc *p)
889 {
890         xp->pc = p->sched.pc;
891         xp->sp = p->sched.sp;
892         xp->r24 = (ulong)p;             /* up */
893         xp->r31 = (ulong)sched;
894 }
895
896 ulong
897 dbgpc(Proc *p)
898 {
899         Ureg *ur;
900
901         ur = p->dbgreg;
902         if(ur == 0)
903                 return 0;
904
905         return ur->pc;
906 }