xref: /plan9-contrib/sys/src/9k/k10/syscall.c (revision d2e450be46c1f486e7113435d56267330fd9a676)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 #include "../port/error.h"
8 
9 #include "/sys/src/libc/9syscall/sys.h"
10 
11 #include <tos.h>
12 #include <ptrace.h>
13 
14 #include "amd64.h"
15 #include "ureg.h"
16 
17 typedef struct {
18 	uintptr	ip;
19 	Ureg*	arg0;
20 	char*	arg1;
21 	char	msg[ERRMAX];
22 	Ureg*	old;
23 	Ureg	ureg;
24 } NFrame;
25 
26 /*
27  *   Return user to state before notify()
28  */
29 static void
noted(Ureg * cur,uintptr arg0)30 noted(Ureg* cur, uintptr arg0)
31 {
32 	NFrame *nf;
33 	Note note;
34 	Ureg *nur;
35 
36 	qlock(&up->debug);
37 	if(arg0 != NRSTR && !up->notified){
38 		qunlock(&up->debug);
39 		pprint("suicide: call to noted when not notified\n");
40 		pexit("Suicide", 0);
41 	}
42 	up->notified = 0;
43 	fpunoted();
44 
45 	nf = up->ureg;
46 
47 	/* sanity clause */
48 	if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){
49 		qunlock(&up->debug);
50 		pprint("suicide: bad ureg %#p in noted\n", nf);
51 		pexit("Suicide", 0);
52 	}
53 
54 	/*
55 	 * Check the segment selectors are all valid.
56 	 */
57 	nur = &nf->ureg;
58 #ifdef notdef
59 	if(nur->cs != SSEL(SiUCS, SsRPL3) || nur->ss != SSEL(SiUDS, SsRPL3)
60 	|| nur->ds != SSEL(SiUDS, SsRPL3) || nur->es != SSEL(SiUDS, SsRPL3)
61 	|| nur->fs != SSEL(SiUDS, SsRPL3) || nur->gs != SSEL(SiUDS, SsRPL3)){
62 		qunlock(&up->debug);
63 		pprint("suicide: bad segment selector in noted\n");
64 		pexit("Suicide", 0);
65 	}
66 #endif /* notdef */
67 
68 	/* don't let user change system flags */
69 	nur->flags &= (Of|Df|Sf|Zf|Af|Pf|Cf);
70 	nur->flags |= cur->flags & ~(Of|Df|Sf|Zf|Af|Pf|Cf);
71 
72 	memmove(cur, nur, sizeof(Ureg));
73 
74 	switch((int)arg0){
75 	case NCONT:
76 	case NRSTR:
77 		if(!okaddr(nur->ip, BY2SE, 0) || !okaddr(nur->sp, BY2SE, 0)){
78 			qunlock(&up->debug);
79 			pprint("suicide: trap in noted pc=%#p sp=%#p\n",
80 				nur->ip, nur->sp);
81 			pexit("Suicide", 0);
82 		}
83 		up->ureg = nf->old;
84 		qunlock(&up->debug);
85 		break;
86 	case NSAVE:
87 		if(!okaddr(nur->ip, BY2SE, 0) || !okaddr(nur->sp, BY2SE, 0)){
88 			qunlock(&up->debug);
89 			pprint("suicide: trap in noted pc=%#p sp=%#p\n",
90 				nur->ip, nur->sp);
91 			pexit("Suicide", 0);
92 		}
93 		qunlock(&up->debug);
94 
95 		splhi();
96 		nf->arg1 = nf->msg;
97 		nf->arg0 = &nf->ureg;
98 		cur->bp = PTR2UINT(nf->arg0);
99 		nf->ip = 0;
100 		cur->sp = PTR2UINT(nf);
101 		break;
102 	default:
103 		memmove(&note, &up->lastnote, sizeof(Note));
104 		qunlock(&up->debug);
105 		pprint("suicide: bad arg %#p in noted: %s\n", arg0, note.msg);
106 		pexit(note.msg, 0);
107 		break;
108 	case NDFLT:
109 		memmove(&note, &up->lastnote, sizeof(Note));
110 		qunlock(&up->debug);
111 		if(note.flag == NDebug)
112 			pprint("suicide: %s\n", note.msg);
113 		pexit(note.msg, note.flag != NDebug);
114 		break;
115 	}
116 }
117 
118 /*
119  *  Call user, if necessary, with note.
120  *  Pass user the Ureg struct and the note on his stack.
121  */
122 int
notify(Ureg * ureg)123 notify(Ureg* ureg)
124 {
125 	int l;
126 	Mreg s;
127 	Note note;
128 	uintptr sp;
129 	NFrame *nf;
130 
131 	if(up->procctl)
132 		procctl(up);
133 	if(up->nnote == 0)
134 		return 0;
135 
136 	fpunotify(ureg);
137 
138 	s = spllo();
139 	qlock(&up->debug);
140 
141 	up->notepending = 0;
142 	memmove(&note, &up->note[0], sizeof(Note));
143 	if(strncmp(note.msg, "sys:", 4) == 0){
144 		l = strlen(note.msg);
145 		if(l > ERRMAX-sizeof(" pc=0x0123456789abcdef"))
146 			l = ERRMAX-sizeof(" pc=0x0123456789abcdef");
147 		sprint(note.msg+l, " pc=%#p", ureg->ip);
148 	}
149 
150 	if(note.flag != NUser && (up->notified || up->notify == nil)){
151 		qunlock(&up->debug);
152 		if(note.flag == NDebug)
153 			pprint("suicide: %s\n", note.msg);
154 		pexit(note.msg, note.flag != NDebug);
155 	}
156 
157 	if(up->notified){
158 		qunlock(&up->debug);
159 		splhi();
160 		return 0;
161 	}
162 
163 	if(up->notify == nil){
164 		qunlock(&up->debug);
165 		pexit(note.msg, note.flag != NDebug);
166 	}
167 	if(!okaddr(PTR2UINT(up->notify), sizeof(ureg->ip), 0)){
168 		qunlock(&up->debug);
169 		pprint("suicide: bad function address %#p in notify\n",
170 			up->notify);
171 		pexit("Suicide", 0);
172 	}
173 
174 	sp = ureg->sp - sizeof(NFrame);
175 	if(!okaddr(sp, sizeof(NFrame), 1)){
176 		qunlock(&up->debug);
177 		pprint("suicide: bad stack address %#p in notify\n", sp);
178 		pexit("Suicide", 0);
179 	}
180 
181 	nf = UINT2PTR(sp);
182 	memmove(&nf->ureg, ureg, sizeof(Ureg));
183 	nf->old = up->ureg;
184 	up->ureg = nf;
185 	memmove(nf->msg, note.msg, ERRMAX);
186 	nf->arg1 = nf->msg;
187 	nf->arg0 = &nf->ureg;
188 	ureg->bp = PTR2UINT(nf->arg0);
189 	nf->ip = 0;
190 
191 	ureg->sp = sp;
192 	ureg->ip = PTR2UINT(up->notify);
193 	up->notified = 1;
194 	up->nnote--;
195 	memmove(&up->lastnote, &note, sizeof(Note));
196 	memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
197 
198 	qunlock(&up->debug);
199 	splx(s);
200 
201 	return 1;
202 }
203 
204 void
syscall(int scallnr,Ureg * ureg)205 syscall(int scallnr, Ureg* ureg)
206 {
207 	char *e;
208 	uintptr	sp;
209 	int i, s;
210 	vlong startns, stopns;
211 	Ar0 ar0;
212 	static Ar0 zar0;
213 	void (*pt)(Proc*, int, vlong, vlong);
214 
215 	if(!userureg(ureg))
216 		panic("syscall: cs %#llux\n", ureg->cs);
217 
218 	cycles(&up->kentry);
219 
220 	m->syscall++;
221 	up->insyscall = 1;
222 	up->pc = ureg->ip;
223 	sp = ureg->sp;
224 	up->scallnr = scallnr;
225 	up->dbgreg = ureg;
226 	startns = 0;
227 
228 	if(up->trace && (pt = proctrace) != nil)
229 		pt(up, STrap, todget(nil), STrapSC|scallnr);
230 
231 	if(up->procctl == Proc_tracesyscall){
232 		/*
233 		 * Redundant validaddr.  Do we care?
234 		 * Tracing syscalls is not exactly a fast path...
235 		 * Beware, validaddr currently does a pexit rather
236 		 * than an error if there's a problem; that might
237 		 * change in the future.
238 		 */
239 		if(sp < (USTKTOP-PGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2V))
240 			validaddr((void *)sp, sizeof(up->arg)+BY2V, 0);
241 
242 		syscallfmt(scallnr, (va_list)(sp+BY2V));
243 		up->procctl = Proc_stopme;
244 		procctl(up);
245 		if(up->syscalltrace)
246 			free(up->syscalltrace);
247 		up->syscalltrace = nil;
248 		startns = todget(nil);
249 	}
250 
251 	if(scallnr == RFORK)
252 		fpusysrfork(ureg);
253 	spllo();
254 
255 	up->nerrlab = 0;
256 	ar0 = zar0;
257 	if(!waserror()){
258 		if(scallnr >= nsyscall || systab[scallnr].f == nil){
259 			pprint("bad sys call number %d pc %#llux\n",
260 				scallnr, ureg->ip);
261 			postnote(up, 1, "sys: bad sys call", NDebug);
262 			error(Ebadarg);
263 		}
264 
265 		if(sp < (USTKTOP-PGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2SE))
266 			validaddr(UINT2PTR(sp), sizeof(up->arg)+BY2SE, 0);
267 
268 		memmove(up->arg, UINT2PTR(sp+BY2SE), sizeof(up->arg));
269 		up->psstate = systab[scallnr].n;
270 
271 		systab[scallnr].f(&ar0, (va_list)up->arg);
272 		poperror();
273 	}
274 	else{
275 		/* failure: save the error buffer for errstr */
276 		e = up->syserrstr;
277 		up->syserrstr = up->errstr;
278 		up->errstr = e;
279 		if(DBGFLG && up->pid == 1)
280 			iprint("%s: syscall %s error %s\n",
281 				up->text, systab[scallnr].n, up->syserrstr);
282 		ar0 = systab[scallnr].r;
283 	}
284 	if(up->nerrlab){
285 		print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab);
286 		for(i = 0; i < NERR; i++)
287 			print("sp=%#p pc=%#p\n",
288 				up->errlab[i].sp, up->errlab[i].pc);
289 		panic("error stack");
290 	}
291 
292 	/*
293 	 * Put return value in frame.
294 	 * Which element of Ar0 to use is based on specific
295 	 * knowledge of the architecture.
296 	 */
297 	ureg->ax = ar0.p;
298 
299 	if(up->procctl == Proc_tracesyscall){
300 		stopns = todget(nil);
301 		up->procctl = Proc_stopme;
302 		sysretfmt(scallnr, (va_list)(sp+BY2V), &ar0, startns, stopns);
303 		s = splhi();
304 		procctl(up);
305 		splx(s);
306 		if(up->syscalltrace)
307 			free(up->syscalltrace);
308 		up->syscalltrace = nil;
309 	}
310 
311 	up->insyscall = 0;
312 	up->psstate = 0;
313 
314 	if(scallnr == NOTED)
315 		noted(ureg, *(uintptr*)(sp+BY2SE));
316 
317 	splhi();
318 	if(scallnr != RFORK && scallnr != NSEC && (up->procctl || up->nnote))
319 		notify(ureg);
320 
321 	/* if we delayed sched because we held a lock, sched now */
322 	if(up->delaysched){
323 		sched();
324 		splhi();
325 	}
326 	kexit(ureg);
327 }
328 
329 uintptr
sysexecstack(uintptr stack,int argc)330 sysexecstack(uintptr stack, int argc)
331 {
332 	/*
333 	 * Given a current bottom-of-stack and a count
334 	 * of pointer arguments to be pushed onto it followed
335 	 * by an integer argument count, return a suitably
336 	 * aligned new bottom-of-stack which will satisfy any
337 	 * hardware stack-alignment contraints.
338 	 * Rounding the stack down to be aligned with the
339 	 * natural size of a pointer variable usually suffices,
340 	 * but some architectures impose further restrictions,
341 	 * e.g. 32-bit SPARC, where the stack must be 8-byte
342 	 * aligned although pointers and integers are 32-bits.
343 	 */
344 	USED(argc);
345 
346 	return STACKALIGN(stack);
347 }
348 
349 void*
sysexecregs(uintptr entry,ulong ssize,ulong nargs)350 sysexecregs(uintptr entry, ulong ssize, ulong nargs)
351 {
352 	uintptr *sp;
353 	Ureg *ureg;
354 
355 	sp = (uintptr*)(USTKTOP - ssize);
356 	*--sp = nargs;
357 
358 	ureg = up->dbgreg;
359 	ureg->sp = PTR2UINT(sp);
360 	ureg->ip = entry;
361 	ureg->type = 64;			/* fiction for acid */
362 
363 	/*
364 	 * return the address of kernel/user shared data
365 	 * (e.g. clock stuff)
366 	 */
367 	return UINT2PTR(USTKTOP-sizeof(Tos));
368 }
369 
370 void
sysprocsetup(Proc * p)371 sysprocsetup(Proc* p)
372 {
373 	fpusysprocsetup(p);
374 }
375 
376 void
sysrforkchild(Proc * child,Proc * parent)377 sysrforkchild(Proc* child, Proc* parent)
378 {
379 	Ureg *cureg;
380 
381 	/*
382 	 * Add 3*BY2SE to the stack to account for
383 	 *  - the return PC
384 	 *  - trap's arguments (syscallnr, ureg)
385 	 */
386 	child->sched.sp = PTR2UINT(child->kstack+KSTACK-(sizeof(Ureg)+3*BY2SE));
387 	child->sched.pc = PTR2UINT(sysrforkret);
388 
389 	cureg = (Ureg*)(child->sched.sp+3*BY2SE);
390 	memmove(cureg, parent->dbgreg, sizeof(Ureg));
391 
392 	/* Things from bottom of syscall which were never executed */
393 	child->psstate = 0;
394 	child->insyscall = 0;
395 
396 	fpusysrforkchild(child, parent);
397 }
398