]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/teg2/syscall.c
devarch: restrict i/o port access to 64K, disallow msr 32-bit wrap arround (thanks...
[plan9front.git] / sys / src / 9 / teg2 / syscall.c
1 /* we use l1 and l2 cache ops to help stability. */
2
3 #include "u.h"
4 #include "../port/lib.h"
5 #include "mem.h"
6 #include "dat.h"
7 #include "fns.h"
8 #include "../port/error.h"
9 #include "../port/systab.h"
10
11 #include <tos.h>
12 #include "ureg.h"
13
14 #include "arm.h"
15
16 enum {
17         Psrsysbits = PsrMask | PsrDfiq | PsrDirq | PsrDasabt | PsrMbz,
18 };
19
20 typedef struct {
21         uintptr ip;
22         Ureg*   arg0;
23         char*   arg1;
24         char    msg[ERRMAX];
25         Ureg*   old;
26         Ureg    ureg;
27 } NFrame;
28
29 /*
30  *   Return user to state before notify()
31  */
32 static void
33 noted(Ureg* cur, uintptr arg0)
34 {
35         NFrame *nf;
36         Ureg *nur;
37
38         qlock(&up->debug);
39         if(arg0 != NRSTR && !up->notified){
40                 qunlock(&up->debug);
41                 pprint("call to noted() when not notified\n");
42                 pexit("Suicide", 0);
43         }
44         up->notified = 0;
45         fpunoted();
46
47         nf = up->ureg;
48
49         /* sanity clause */
50         if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){
51                 qunlock(&up->debug);
52                 pprint("bad ureg in noted %#p\n", nf);
53                 pexit("Suicide", 0);
54         }
55
56         /* don't let user change system flags */
57         nur = &nf->ureg;
58         nur->psr &= Psrsysbits;
59         nur->psr |= cur->psr & ~Psrsysbits;
60
61         memmove(cur, nur, sizeof(Ureg));
62
63         switch((int)arg0){
64         case NCONT:
65         case NRSTR:
66                 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
67                         qunlock(&up->debug);
68                         pprint("suicide: trap in noted\n");
69                         pexit("Suicide", 0);
70                 }
71                 up->ureg = nf->old;
72                 qunlock(&up->debug);
73                 break;
74         case NSAVE:
75                 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
76                         qunlock(&up->debug);
77                         pprint("suicide: trap in noted\n");
78                         pexit("Suicide", 0);
79                 }
80                 qunlock(&up->debug);
81
82                 splhi();
83                 nf->arg1 = nf->msg;
84                 nf->arg0 = &nf->ureg;
85                 nf->ip = 0;
86                 cur->sp = PTR2UINT(nf);
87                 cur->r0 = PTR2UINT(nf->arg0);
88                 break;
89         default:
90                 up->lastnote.flag = NDebug;
91                 /*FALLTHROUGH*/
92         case NDFLT:
93                 qunlock(&up->debug);
94                 if(up->lastnote.flag == NDebug)
95                         pprint("suicide: %s\n", up->lastnote.msg);
96                 pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
97         }
98 }
99
100 /*
101  *  Call user, if necessary, with note.
102  *  Pass user the Ureg struct and the note on his stack.
103  */
104 int
105 notify(Ureg* ureg)
106 {
107         int l;
108         Note *n;
109         u32int s;
110         uintptr sp;
111         NFrame *nf;
112
113         if(up->procctl)
114                 procctl();
115         if(up->nnote == 0)
116                 return 0;
117
118         fpunotify(ureg);
119
120         s = spllo();
121         qlock(&up->debug);
122
123         up->notepending = 0;
124         n = &up->note[0];
125         if(strncmp(n->msg, "sys:", 4) == 0){
126                 l = strlen(n->msg);
127                 if(l > ERRMAX-23)       /* " pc=0x0123456789abcdef\0" */
128                         l = ERRMAX-23;
129                 snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->pc);
130         }
131
132         if(n->flag != NUser && (up->notified || up->notify == 0)){
133                 qunlock(&up->debug);
134                 if(n->flag == NDebug)
135                         pprint("suicide: %s\n", n->msg);
136                 pexit(n->msg, n->flag != NDebug);
137         }
138
139         if(up->notified){
140                 qunlock(&up->debug);
141                 splhi();
142                 return 0;
143         }
144                 
145         if(up->notify == nil){
146                 qunlock(&up->debug);
147                 pexit(n->msg, n->flag != NDebug);
148         }
149         if(!okaddr(PTR2UINT(up->notify), 1, 0)){
150                 qunlock(&up->debug);
151                 pprint("suicide: notify function address %#p\n", up->notify);
152                 pexit("Suicide", 0);
153         }
154
155         sp = ureg->sp - sizeof(NFrame);
156         if(!okaddr(sp, sizeof(NFrame), 1)){
157                 qunlock(&up->debug);
158                 pprint("suicide: notify stack address %#p\n", sp);
159                 pexit("Suicide", 0);
160         }
161
162         nf = UINT2PTR(sp);
163         memmove(&nf->ureg, ureg, sizeof(Ureg));
164         nf->old = up->ureg;
165         up->ureg = nf;
166         memmove(nf->msg, up->note[0].msg, ERRMAX);
167         nf->arg1 = nf->msg;
168         nf->arg0 = &nf->ureg;
169         nf->ip = 0;
170
171         ureg->sp = sp;
172         ureg->pc = PTR2UINT(up->notify);
173         ureg->r0 = PTR2UINT(nf->arg0);
174
175         up->notified = 1;
176         up->nnote--;
177         memmove(&up->lastnote, &up->note[0], sizeof(Note));
178         memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
179
180         qunlock(&up->debug);
181         splx(s);
182
183         l1cache->wb();                          /* is this needed? */
184         return 1;
185 }
186
187 void
188 syscall(Ureg* ureg)
189 {
190         char *e;
191         u32int s;
192         ulong sp;
193         long ret;
194         int i, scallnr;
195         vlong startns, stopns;
196
197         if(!userureg(ureg))
198                 panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux",
199                         ureg->pc, ureg->r14, ureg->psr);
200
201         cycles(&up->kentry);
202
203         m->syscall++;
204         up->insyscall = 1;
205         up->pc = ureg->pc;
206         up->dbgreg = ureg;
207
208         scallnr = ureg->r0;
209         up->scallnr = scallnr;
210         spllo();
211         sp = ureg->sp;
212
213         if(up->procctl == Proc_tracesyscall){
214                 /*
215                  * Redundant validaddr.  Do we care?
216                  * Tracing syscalls is not exactly a fast path...
217                  * Beware, validaddr currently does a pexit rather
218                  * than an error if there's a problem; that might
219                  * change in the future.
220                  */
221                 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
222                         validaddr(sp, sizeof(Sargs)+BY2WD, 0);
223
224                 syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD));
225                 up->procctl = Proc_stopme;
226                 procctl();
227                 if (up->syscalltrace) 
228                         free(up->syscalltrace);
229                 up->syscalltrace = nil;
230         }
231
232         up->nerrlab = 0;
233         ret = -1;
234         startns = todget(nil);
235
236         l1cache->wb();                  /* system is more stable with this */
237         if(!waserror()){
238                 if(scallnr >= nsyscall){
239                         pprint("bad sys call number %d pc %#lux\n",
240                                 scallnr, ureg->pc);
241                         postnote(up, 1, "sys: bad sys call", NDebug);
242                         error(Ebadarg);
243                 }
244
245                 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
246                         validaddr(sp, sizeof(Sargs)+BY2WD, 0);
247
248                 up->s = *((Sargs*)(sp+BY2WD));
249                 up->psstate = sysctab[scallnr];
250
251         /*      iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */
252
253                 ret = systab[scallnr]((va_list)up->s.args);
254                 poperror();
255         }else{
256                 /* failure: save the error buffer for errstr */
257                 e = up->syserrstr;
258                 up->syserrstr = up->errstr;
259                 up->errstr = e;
260         }
261         if(up->nerrlab){
262                 print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab);
263                 for(i = 0; i < NERR; i++)
264                         print("sp=%#p pc=%#p\n",
265                                 up->errlab[i].sp, up->errlab[i].pc);
266                 panic("error stack");
267         }
268
269         /*
270          *  Put return value in frame.  On the x86 the syscall is
271          *  just another trap and the return value from syscall is
272          *  ignored.  On other machines the return value is put into
273          *  the results register by caller of syscall.
274          */
275         ureg->r0 = ret;
276
277         if(up->procctl == Proc_tracesyscall){
278                 stopns = todget(nil);
279                 sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns);
280                 s = splhi();
281                 up->procctl = Proc_stopme;
282                 procctl();
283                 splx(s);
284                 if(up->syscalltrace)
285                         free(up->syscalltrace);
286                 up->syscalltrace = nil;
287         }
288
289         up->insyscall = 0;
290         up->psstate = 0;
291
292         if(scallnr == NOTED)
293                 noted(ureg, *(ulong*)(sp+BY2WD));
294
295         splhi();
296         if(scallnr != RFORK && (up->procctl || up->nnote))
297                 notify(ureg);
298
299         l1cache->wb();                  /* system is more stable with this */
300
301         /* if we delayed sched because we held a lock, sched now */
302         if(up->delaysched){
303                 sched();
304                 splhi();
305         }
306         kexit(ureg);
307 }
308
309 uintptr
310 execregs(uintptr entry, ulong ssize, ulong nargs)
311 {
312         ulong *sp;
313         Ureg *ureg;
314
315         sp = (ulong*)(USTKTOP - ssize);
316         *--sp = nargs;
317
318         ureg = up->dbgreg;
319 //      memset(ureg, 0, 15*sizeof(ulong));
320         ureg->r13 = (ulong)sp;
321         ureg->pc = entry;
322 //print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs);
323         allcache->wbse(ureg, sizeof *ureg);             /* is this needed? */
324
325         /*
326          * return the address of kernel/user shared data
327          * (e.g. clock stuff)
328          */
329         return USTKTOP-sizeof(Tos);
330 }
331
332 void
333 sysprocsetup(Proc* p)
334 {
335         fpusysprocsetup(p);
336 }
337
338 /* 
339  *  Craft a return frame which will cause the child to pop out of
340  *  the scheduler in user mode with the return register zero.  Set
341  *  pc to point to a l.s return function.
342  */
343 void
344 forkchild(Proc *p, Ureg *ureg)
345 {
346         Ureg *cureg;
347
348         p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg);
349         p->sched.pc = (ulong)forkret;
350
351         cureg = (Ureg*)(p->sched.sp);
352         memmove(cureg, ureg, sizeof(Ureg));
353
354         /* syscall returns 0 for child */
355         cureg->r0 = 0;
356
357         /* Things from bottom of syscall which were never executed */
358         p->psstate = 0;
359         p->insyscall = 0;
360 }