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