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
noted(Ureg * cur,uintptr arg0)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; /* arg 1 is string */
84 nf->arg0 = &nf->ureg; /* arg 0 XX(FP) is ureg* */
85 nf->ip = 0;
86 cur->sp = PTR2UINT(nf);
87 cur->r0 = PTR2UINT(nf->arg0); /* arg 0 in reg is ureg* */
88 break;
89 default:
90 pprint("unknown noted arg %#p\n", arg0);
91 up->lastnote.flag = NDebug;
92 /*FALLTHROUGH*/
93 case NDFLT:
94 if(up->lastnote.flag == NDebug){
95 qunlock(&up->debug);
96 pprint("suicide: %s\n", up->lastnote.msg);
97 }
98 else
99 qunlock(&up->debug);
100 pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
101 }
102 }
103
104 /*
105 * Call user, if necessary, with note.
106 * Pass user the Ureg struct and the note on his stack.
107 */
108 int
notify(Ureg * ureg)109 notify(Ureg* ureg)
110 {
111 int l;
112 Note *n;
113 u32int s;
114 uintptr sp;
115 NFrame *nf;
116
117 if(up->procctl)
118 procctl(up);
119 if(up->nnote == 0)
120 return 0;
121
122 fpunotify(ureg);
123
124 s = spllo();
125 qlock(&up->debug);
126
127 up->notepending = 0;
128 n = &up->note[0];
129 if(strncmp(n->msg, "sys:", 4) == 0){
130 l = strlen(n->msg);
131 if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */
132 l = ERRMAX-23;
133 snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->pc);
134 }
135
136 if(n->flag != NUser && (up->notified || up->notify == 0)){
137 if(n->flag == NDebug)
138 pprint("suicide: %s\n", n->msg);
139 qunlock(&up->debug);
140 pexit(n->msg, n->flag != NDebug);
141 }
142
143 if(up->notified){
144 qunlock(&up->debug);
145 splhi();
146 return 0;
147 }
148
149 if(up->notify == nil){
150 qunlock(&up->debug);
151 pexit(n->msg, n->flag != NDebug);
152 }
153 if(!okaddr(PTR2UINT(up->notify), 1, 0)){
154 pprint("suicide: notify function address %#p\n", up->notify);
155 qunlock(&up->debug);
156 pexit("Suicide", 0);
157 }
158
159 sp = ureg->sp - sizeof(NFrame);
160 if(!okaddr(sp, sizeof(NFrame), 1)){
161 qunlock(&up->debug);
162 pprint("suicide: notify stack address %#p\n", sp);
163 pexit("Suicide", 0);
164 }
165
166 nf = UINT2PTR(sp);
167 memmove(&nf->ureg, ureg, sizeof(Ureg));
168 nf->old = up->ureg;
169 up->ureg = nf;
170 memmove(nf->msg, up->note[0].msg, ERRMAX);
171 nf->arg1 = nf->msg; /* arg 1 is string */
172 nf->arg0 = &nf->ureg; /* arg 0 XX(FP) is ureg* */
173 ureg->r0 = PTR2UINT(nf->arg0); /* arg 0 in r0 is ureg* */
174 nf->ip = 0;
175
176 ureg->sp = sp;
177 ureg->pc = PTR2UINT(up->notify);
178
179 up->notified = 1;
180 up->nnote--;
181 memmove(&up->lastnote, &up->note[0], sizeof(Note));
182 memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
183
184 qunlock(&up->debug);
185 splx(s);
186
187 l1cache->wb(); /* is this needed? */
188 return 1;
189 }
190
191 void
syscall(Ureg * ureg)192 syscall(Ureg* ureg)
193 {
194 char *e;
195 u32int s;
196 ulong sp;
197 long ret;
198 int i, scallnr;
199 vlong startns, stopns;
200
201 if(!userureg(ureg))
202 panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux",
203 ureg->pc, ureg->r14, ureg->psr);
204
205 cycles(&up->kentry);
206
207 m->syscall++;
208 up->insyscall = 1;
209 up->pc = ureg->pc;
210 up->dbgreg = ureg;
211
212 scallnr = ureg->r0;
213 up->scallnr = scallnr;
214 if(scallnr == RFORK)
215 fpusysrfork(ureg);
216 spllo();
217 sp = ureg->sp;
218
219 if(up->procctl == Proc_tracesyscall){
220 /*
221 * Redundant validaddr. Do we care?
222 * Tracing syscalls is not exactly a fast path...
223 * Beware, validaddr currently does a pexit rather
224 * than an error if there's a problem; that might
225 * change in the future.
226 */
227 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
228 validaddr(sp, sizeof(Sargs)+BY2WD, 0);
229
230 syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD));
231 up->procctl = Proc_stopme;
232 procctl(up);
233 if (up->syscalltrace)
234 free(up->syscalltrace);
235 up->syscalltrace = nil;
236 }
237
238 up->nerrlab = 0;
239 ret = -1;
240 startns = todget(nil);
241
242 l1cache->wb(); /* system is more stable with this */
243 if(!waserror()){
244 if(scallnr >= nsyscall){
245 pprint("bad sys call number %d pc %#lux\n",
246 scallnr, ureg->pc);
247 postnote(up, 1, "sys: bad sys call", NDebug);
248 error(Ebadarg);
249 }
250
251 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
252 validaddr(sp, sizeof(Sargs)+BY2WD, 0);
253
254 up->s = *((Sargs*)(sp+BY2WD));
255 up->psstate = sysctab[scallnr];
256
257 /* iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */
258
259 ret = systab[scallnr](up->s.args);
260 poperror();
261 }else{
262 /* failure: save the error buffer for errstr */
263 e = up->syserrstr;
264 up->syserrstr = up->errstr;
265 up->errstr = e;
266 }
267 if(up->nerrlab){
268 print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab);
269 for(i = 0; i < NERR; i++)
270 print("sp=%#p pc=%#p\n",
271 up->errlab[i].sp, up->errlab[i].pc);
272 panic("error stack");
273 }
274
275 /*
276 * Put return value in frame. On the x86 the syscall is
277 * just another trap and the return value from syscall is
278 * ignored. On other machines the return value is put into
279 * the results register by caller of syscall.
280 */
281 ureg->r0 = ret;
282
283 if(up->procctl == Proc_tracesyscall){
284 stopns = todget(nil);
285 up->procctl = Proc_stopme;
286 sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns);
287 s = splhi();
288 procctl(up);
289 splx(s);
290 if(up->syscalltrace)
291 free(up->syscalltrace);
292 up->syscalltrace = nil;
293 }
294
295 up->insyscall = 0;
296 up->psstate = 0;
297
298 if(scallnr == NOTED)
299 noted(ureg, *(ulong*)(sp+BY2WD));
300
301 splhi();
302 if(scallnr != RFORK && (up->procctl || up->nnote))
303 notify(ureg);
304
305 l1cache->wb(); /* system is more stable with this */
306
307 /* if we delayed sched because we held a lock, sched now */
308 if(up->delaysched){
309 sched();
310 splhi();
311 }
312 kexit(ureg);
313 }
314
315 long
execregs(ulong entry,ulong ssize,ulong nargs)316 execregs(ulong entry, ulong ssize, ulong nargs)
317 {
318 ulong *sp;
319 Ureg *ureg;
320
321 sp = (ulong*)(USTKTOP - ssize);
322 *--sp = nargs;
323
324 ureg = up->dbgreg;
325 // memset(ureg, 0, 15*sizeof(ulong));
326 ureg->r13 = (ulong)sp;
327 ureg->pc = entry;
328 //print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs);
329 allcache->wbse(ureg, sizeof *ureg); /* is this needed? */
330
331 /*
332 * return the address of kernel/user shared data
333 * (e.g. clock stuff)
334 */
335 return USTKTOP-sizeof(Tos);
336 }
337
338 void
sysprocsetup(Proc * p)339 sysprocsetup(Proc* p)
340 {
341 fpusysprocsetup(p);
342 }
343
344 /*
345 * Craft a return frame which will cause the child to pop out of
346 * the scheduler in user mode with the return register zero. Set
347 * pc to point to a l.s return function.
348 */
349 void
forkchild(Proc * p,Ureg * ureg)350 forkchild(Proc *p, Ureg *ureg)
351 {
352 Ureg *cureg;
353
354 p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg);
355 p->sched.pc = (ulong)forkret;
356
357 cureg = (Ureg*)(p->sched.sp);
358 memmove(cureg, ureg, sizeof(Ureg));
359
360 /* syscall returns 0 for child */
361 cureg->r0 = 0;
362
363 /* Things from bottom of syscall which were never executed */
364 p->psstate = 0;
365 p->insyscall = 0;
366
367 fpusysrforkchild(p, cureg, up);
368 }
369