xref: /plan9/sys/src/9/teg2/syscall.c (revision 391203424d8344b1a27dac59e524823ba616961b)
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