]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sysproc.c
devmnt: deal with partial response for Tversion request in mntversion()
[plan9front.git] / sys / src / 9 / port / sysproc.c
1 #include        "u.h"
2 #include        "tos.h"
3 #include        "../port/lib.h"
4 #include        "mem.h"
5 #include        "dat.h"
6 #include        "fns.h"
7 #include        "../port/error.h"
8 #include        "edf.h"
9
10 #include        <a.out.h>
11
12 extern void checkpages(void);
13 extern void checkpagerefs(void);
14
15 uintptr
16 sysr1(va_list)
17 {
18         if(!iseve())
19                 error(Eperm);
20         checkpagerefs();
21         return 0;
22 }
23
24 static void
25 abortion(void*)
26 {
27         pexit("fork aborted", 1);
28 }
29
30 uintptr
31 sysrfork(va_list list)
32 {
33         Proc *p;
34         int n, i;
35         Fgrp *ofg;
36         Pgrp *opg;
37         Rgrp *org;
38         Egrp *oeg;
39         ulong pid, flag;
40         Mach *wm;
41
42         flag = va_arg(list, ulong);
43         /* Check flags before we commit */
44         if((flag & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG))
45                 error(Ebadarg);
46         if((flag & (RFNAMEG|RFCNAMEG)) == (RFNAMEG|RFCNAMEG))
47                 error(Ebadarg);
48         if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG))
49                 error(Ebadarg);
50
51         if((flag&RFPROC) == 0) {
52                 if(flag & (RFMEM|RFNOWAIT))
53                         error(Ebadarg);
54                 if(flag & (RFFDG|RFCFDG)) {
55                         ofg = up->fgrp;
56                         if(flag & RFFDG)
57                                 up->fgrp = dupfgrp(ofg);
58                         else
59                                 up->fgrp = dupfgrp(nil);
60                         closefgrp(ofg);
61                 }
62                 if(flag & (RFNAMEG|RFCNAMEG)) {
63                         opg = up->pgrp;
64                         up->pgrp = newpgrp();
65                         if(flag & RFNAMEG)
66                                 pgrpcpy(up->pgrp, opg);
67                         /* inherit noattach */
68                         up->pgrp->noattach = opg->noattach;
69                         closepgrp(opg);
70                 }
71                 if(flag & RFNOMNT)
72                         up->pgrp->noattach = 1;
73                 if(flag & RFREND) {
74                         org = up->rgrp;
75                         up->rgrp = newrgrp();
76                         closergrp(org);
77                 }
78                 if(flag & (RFENVG|RFCENVG)) {
79                         oeg = up->egrp;
80                         up->egrp = smalloc(sizeof(Egrp));
81                         up->egrp->ref = 1;
82                         if(flag & RFENVG)
83                                 envcpy(up->egrp, oeg);
84                         closeegrp(oeg);
85                 }
86                 if(flag & RFNOTEG)
87                         up->noteid = pidalloc(0);
88                 return 0;
89         }
90
91         p = newproc();
92
93         p->scallnr = up->scallnr;
94         p->s = up->s;
95         p->nerrlab = 0;
96         p->slash = up->slash;
97         p->dot = up->dot;
98         incref(p->dot);
99
100         memmove(p->note, up->note, sizeof(p->note));
101         p->privatemem = up->privatemem;
102         p->noswap = up->noswap;
103         p->nnote = up->nnote;
104         p->notified = 0;
105         p->lastnote = up->lastnote;
106         p->notify = up->notify;
107         p->ureg = up->ureg;
108         p->dbgreg = 0;
109
110         /* Abort the child process on error */
111         if(waserror()){
112                 p->kp = 1;
113                 kprocchild(p, abortion, 0);
114                 ready(p);
115                 nexterror();
116         }
117
118         /* Make a new set of memory segments */
119         n = flag & RFMEM;
120         qlock(&p->seglock);
121         if(waserror()){
122                 qunlock(&p->seglock);
123                 nexterror();
124         }
125         for(i = 0; i < NSEG; i++)
126                 if(up->seg[i] != nil)
127                         p->seg[i] = dupseg(up->seg, i, n);
128         qunlock(&p->seglock);
129         poperror();
130
131         /* File descriptors */
132         if(flag & (RFFDG|RFCFDG)) {
133                 if(flag & RFFDG)
134                         p->fgrp = dupfgrp(up->fgrp);
135                 else
136                         p->fgrp = dupfgrp(nil);
137         }
138         else {
139                 p->fgrp = up->fgrp;
140                 incref(p->fgrp);
141         }
142
143         /* Process groups */
144         if(flag & (RFNAMEG|RFCNAMEG)) {
145                 p->pgrp = newpgrp();
146                 if(flag & RFNAMEG)
147                         pgrpcpy(p->pgrp, up->pgrp);
148                 /* inherit noattach */
149                 p->pgrp->noattach = up->pgrp->noattach;
150         }
151         else {
152                 p->pgrp = up->pgrp;
153                 incref(p->pgrp);
154         }
155         if(flag & RFNOMNT)
156                 p->pgrp->noattach = 1;
157
158         if(flag & RFREND)
159                 p->rgrp = newrgrp();
160         else {
161                 incref(up->rgrp);
162                 p->rgrp = up->rgrp;
163         }
164
165         /* Environment group */
166         if(flag & (RFENVG|RFCENVG)) {
167                 p->egrp = smalloc(sizeof(Egrp));
168                 p->egrp->ref = 1;
169                 if(flag & RFENVG)
170                         envcpy(p->egrp, up->egrp);
171         }
172         else {
173                 p->egrp = up->egrp;
174                 incref(p->egrp);
175         }
176         p->hang = up->hang;
177         p->procmode = up->procmode;
178         if(up->procctl == Proc_tracesyscall)
179                 p->procctl = Proc_tracesyscall;
180
181         poperror();     /* abortion */
182
183         /* Craft a return frame which will cause the child to pop out of
184          * the scheduler in user mode with the return register zero
185          */
186         forkchild(p, up->dbgreg);
187
188         p->parent = up;
189         if((flag&RFNOWAIT) == 0){
190                 p->parentpid = up->pid;
191                 lock(&up->exl);
192                 up->nchild++;
193                 unlock(&up->exl);
194         }
195         if((flag&RFNOTEG) == 0)
196                 p->noteid = up->noteid;
197
198         pid = p->pid;
199         memset(p->time, 0, sizeof(p->time));
200         p->time[TReal] = MACHP(0)->ticks;
201
202         kstrdup(&p->text, up->text);
203         kstrdup(&p->user, up->user);
204
205         procfork(p);
206
207         /*
208          *  since the bss/data segments are now shareable,
209          *  any mmu info about this process is now stale
210          *  (i.e. has bad properties) and has to be discarded.
211          */
212         flushmmu();
213         p->basepri = up->basepri;
214         p->priority = up->basepri;
215         p->fixedpri = up->fixedpri;
216         p->mp = up->mp;
217         wm = up->wired;
218         if(wm)
219                 procwired(p, wm->machno);
220         ready(p);
221         sched();
222         return pid;
223 }
224
225 static int
226 shargs(char *s, int n, char **ap)
227 {
228         int i;
229
230         s += 2;
231         n -= 2;         /* skip #! */
232         for(i=0;; i++){
233                 if(i >= n)
234                         return 0;
235                 if(s[i]=='\n')
236                         break;
237         }
238         s[i] = 0;
239
240         i = 0;
241         for(;;) {
242                 while(*s==' ' || *s=='\t')
243                         s++;
244                 if(*s == 0)
245                         break;
246                 ap[i++] = s++;
247                 while(*s && *s!=' ' && *s!='\t')
248                         s++;
249                 if(*s == 0)
250                         break;
251                 *s++ = 0;
252         }
253         ap[i] = nil;
254         return i;
255 }
256
257 static ulong
258 l2be(long l)
259 {
260         uchar *cp;
261
262         cp = (uchar*)&l;
263         return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3];
264 }
265
266 uintptr
267 sysexec(va_list list)
268 {
269         Segment *s, *ts;
270         int i;
271         Chan *tc;
272         char **argv, **argp, **argp0;
273         char *a, *e, *charp, *args, *file, *file0;
274         char *progarg[sizeof(Exec)/2+1], *elem, progelem[64];
275         ulong magic, ssize, nargs, nbytes, n;
276         uintptr t, d, b, entry, bssend, text, data, bss, tstk, align;
277         int indir;
278         Exec exec;
279         char line[sizeof(Exec)];
280         Fgrp *f;
281         Image *img;
282         Tos *tos;
283
284         args = elem = nil;
285         file0 = va_arg(list, char*);
286         validaddr((uintptr)file0, 1, 0);
287         argp0 = va_arg(list, char**);
288         evenaddr((uintptr)argp0);
289         validaddr((uintptr)argp0, 2*BY2WD, 0);
290         if(*argp0 == nil)
291                 error(Ebadarg);
292         file0 = validnamedup(file0, 1);
293         if(waserror()){
294                 free(file0);
295                 free(elem);
296                 free(args);
297                 /* Disaster after commit */
298                 if(up->seg[SSEG] == nil)
299                         pexit(up->errstr, 1);
300                 s = up->seg[ESEG];
301                 if(s != nil){
302                         putseg(s);
303                         up->seg[ESEG] = nil;
304                 }
305                 nexterror();
306         }
307         align = BY2PG;
308         indir = 0;
309         file = file0;
310         for(;;){
311                 tc = namec(file, Aopen, OEXEC, 0);
312                 if(waserror()){
313                         cclose(tc);
314                         nexterror();
315                 }
316                 if(!indir)
317                         kstrdup(&elem, up->genbuf);
318
319                 n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0);
320                 if(n <= 2)
321                         error(Ebadexec);
322                 magic = l2be(exec.magic);
323                 if(n == sizeof(Exec) && (magic == AOUT_MAGIC)){
324                         entry = l2be(exec.entry);
325                         text = l2be(exec.text);
326                         if(magic & HDR_MAGIC)
327                                 text += 8;
328                         switch(magic){
329                         case S_MAGIC:   /* 2MB segment alignment for amd64 */
330                                 align = 0x200000;
331                                 break;
332                         case V_MAGIC:   /* 16K segment alignment for mips */
333                                 align = 0x4000;
334                                 break;
335                         }
336                         if(text >= (USTKTOP-USTKSIZE)-(UTZERO+sizeof(Exec))
337                         || entry < UTZERO+sizeof(Exec)
338                         || entry >= UTZERO+sizeof(Exec)+text)
339                                 error(Ebadexec);
340                         break; /* for binary */
341                 }
342
343                 /*
344                  * Process #! /bin/sh args ...
345                  */
346                 memmove(line, &exec, n);
347                 if(indir || line[0]!='#' || line[1]!='!')
348                         error(Ebadexec);
349                 n = shargs(line, n, progarg);
350                 if(n < 1)
351                         error(Ebadexec);
352                 indir = 1;
353                 /*
354                  * First arg becomes complete file name
355                  */
356                 progarg[n++] = file;
357                 progarg[n] = nil;
358                 argp0++;
359                 file = progarg[0];
360                 if(strlen(elem) >= sizeof progelem)
361                         error(Ebadexec);
362                 strcpy(progelem, elem);
363                 progarg[0] = progelem;
364                 poperror();
365                 cclose(tc);
366         }
367
368         data = l2be(exec.data);
369         bss = l2be(exec.bss);
370         align--;
371         t = (UTZERO+sizeof(Exec)+text+align) & ~align;
372         align = BY2PG-1;
373         d = (t + data + align) & ~align;
374         bssend = t + data + bss;
375         b = (bssend + align) & ~align;
376         if(t >= (USTKTOP-USTKSIZE) || d >= (USTKTOP-USTKSIZE) || b >= (USTKTOP-USTKSIZE))
377                 error(Ebadexec);
378
379         /*
380          * Args: pass 1: count
381          */
382         nbytes = sizeof(Tos);           /* hole for profiling clock at top of stack (and more) */
383         nargs = 0;
384         if(indir){
385                 argp = progarg;
386                 while(*argp != nil){
387                         a = *argp++;
388                         nbytes += strlen(a) + 1;
389                         nargs++;
390                 }
391         }
392         argp = argp0;
393         while(*argp != nil){
394                 a = *argp++;
395                 if(((uintptr)argp&(BY2PG-1)) < BY2WD)
396                         validaddr((uintptr)argp, BY2WD, 0);
397                 validaddr((uintptr)a, 1, 0);
398                 e = vmemchr(a, 0, USTKSIZE);
399                 if(e == nil)
400                         error(Ebadarg);
401                 nbytes += (e - a) + 1;
402                 if(nbytes >= USTKSIZE)
403                         error(Enovmem);
404                 nargs++;
405         }
406         ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1));
407
408         /*
409          * 8-byte align SP for those (e.g. sparc) that need it.
410          * execregs() will subtract another 4 bytes for argc.
411          */
412         if(BY2WD == 4 && (ssize+4) & 7)
413                 ssize += 4;
414
415         if(PGROUND(ssize) >= USTKSIZE)
416                 error(Enovmem);
417
418         /*
419          * Build the stack segment, putting it in kernel virtual for the moment
420          */
421         qlock(&up->seglock);
422         if(waserror()){
423                 qunlock(&up->seglock);
424                 nexterror();
425         }
426
427         s = up->seg[SSEG];
428         do {
429                 tstk = s->base;
430                 if(tstk <= USTKSIZE)
431                         error(Enovmem);
432         } while((s = isoverlap(up, tstk-USTKSIZE, USTKSIZE)) != nil);
433         up->seg[ESEG] = newseg(SG_STACK, tstk-USTKSIZE, USTKSIZE/BY2PG);
434
435         /*
436          * Args: pass 2: assemble; the pages will be faulted in
437          */
438         tos = (Tos*)(tstk - sizeof(Tos));
439         tos->cyclefreq = m->cyclefreq;
440         tos->kcycles = 0;
441         tos->pcycles = 0;
442         tos->clock = 0;
443
444         argv = (char**)(tstk - ssize);
445         charp = (char*)(tstk - nbytes);
446         if(indir)
447                 argp = progarg;
448         else
449                 argp = argp0;
450
451         for(i=0; i<nargs; i++){
452                 if(indir && *argp==nil) {
453                         indir = 0;
454                         argp = argp0;
455                 }
456                 *argv++ = charp + (USTKTOP-tstk);
457                 a = *argp++;
458                 if(indir)
459                         e = strchr(a, 0);
460                 else {
461                         validaddr((uintptr)a, 1, 0);
462                         e = vmemchr(a, 0, (char*)tstk - charp);
463                         if(e == nil)
464                                 error(Ebadarg);
465                 }
466                 n = (e - a) + 1;
467                 memmove(charp, a, n);
468                 charp += n;
469         }
470
471         /* copy args; easiest from new process's stack */
472         a = (char*)(tstk - nbytes);
473         n = charp - a;
474         if(n > 128)     /* don't waste too much space on huge arg lists */
475                 n = 128;
476         args = smalloc(n);
477         memmove(args, a, n);
478         if(n>0 && args[n-1]!='\0'){
479                 /* make sure last arg is NUL-terminated */
480                 /* put NUL at UTF-8 character boundary */
481                 for(i=n-1; i>0; --i)
482                         if(fullrune(args+i, n-i))
483                                 break;
484                 args[i] = 0;
485                 n = i+1;
486         }
487
488         /*
489          * Committed.
490          * Free old memory.
491          * Special segments are maintained across exec
492          */
493         for(i = SSEG; i <= BSEG; i++) {
494                 putseg(up->seg[i]);
495                 /* prevent a second free if we have an error */
496                 up->seg[i] = nil;
497         }
498         for(i = ESEG+1; i < NSEG; i++) {
499                 s = up->seg[i];
500                 if(s != nil && (s->type&SG_CEXEC) != 0) {
501                         putseg(s);
502                         up->seg[i] = nil;
503                 }
504         }
505
506         /*
507          * Close on exec
508          */
509         if((f = up->fgrp) != nil) {
510                 for(i=0; i<=f->maxfd; i++)
511                         fdclose(i, CCEXEC);
512         }
513
514         /* Text.  Shared. Attaches to cache image if possible */
515         /* attachimage returns a locked cache image */
516         img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT);
517         ts = img->s;
518         up->seg[TSEG] = ts;
519         ts->flushme = 1;
520         ts->fstart = 0;
521         ts->flen = sizeof(Exec)+text;
522         unlock(img);
523
524         /* Data. Shared. */
525         s = newseg(SG_DATA, t, (d-t)>>PGSHIFT);
526         up->seg[DSEG] = s;
527
528         /* Attached by hand */
529         incref(img);
530         s->image = img;
531         s->fstart = ts->fstart+ts->flen;
532         s->flen = data;
533
534         /* BSS. Zero fill on demand */
535         up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT);
536
537         /*
538          * Move the stack
539          */
540         s = up->seg[ESEG];
541         up->seg[ESEG] = nil;
542         s->base = USTKTOP-USTKSIZE;
543         s->top = USTKTOP;
544         relocateseg(s, USTKTOP-tstk);
545         up->seg[SSEG] = s;
546         qunlock(&up->seglock);
547         poperror();     /* seglock */
548
549         /*
550          *  '/' processes are higher priority (hack to make /ip more responsive).
551          */
552         if(devtab[tc->type]->dc == L'/')
553                 up->basepri = PriRoot;
554         up->priority = up->basepri;
555         poperror();     /* tc */
556         cclose(tc);
557         poperror();     /* file0 */
558         free(file0);
559
560         qlock(&up->debug);
561         free(up->text);
562         up->text = elem;
563         free(up->args);
564         up->args = args;
565         up->nargs = n;
566         up->setargs = 0;
567
568         up->nnote = 0;
569         up->notify = 0;
570         up->notified = 0;
571         up->privatemem = 0;
572         up->noswap = 0;
573         procsetup(up);
574         qunlock(&up->debug);
575
576         /*
577          *  At this point, the mmu contains info about the old address
578          *  space and needs to be flushed
579          */
580         flushmmu();
581
582         if(up->hang)
583                 up->procctl = Proc_stopme;
584         return execregs(entry, ssize, nargs);
585 }
586
587 int
588 return0(void*)
589 {
590         return 0;
591 }
592
593 uintptr
594 syssleep(va_list list)
595 {
596         long ms;
597
598         ms = va_arg(list, long);
599         if(ms <= 0) {
600                 if (up->edf != nil && (up->edf->flags & Admitted))
601                         edfyield();
602                 else
603                         yield();
604         } else {
605                 if(ms < TK2MS(1))
606                         ms = TK2MS(1);
607                 tsleep(&up->sleep, return0, 0, ms);
608         }
609         return 0;
610 }
611
612 uintptr
613 sysalarm(va_list list)
614 {
615         return procalarm(va_arg(list, ulong));
616 }
617
618
619 uintptr
620 sysexits(va_list list)
621 {
622         char *status;
623         char *inval = "invalid exit string";
624         char buf[ERRMAX];
625
626         status = va_arg(list, char*);
627         if(status != nil){
628                 if(waserror())
629                         status = inval;
630                 else{
631                         validaddr((uintptr)status, 1, 0);
632                         if(vmemchr(status, 0, ERRMAX) == nil){
633                                 memmove(buf, status, ERRMAX);
634                                 buf[ERRMAX-1] = 0;
635                                 status = buf;
636                         }
637                         poperror();
638                 }
639
640         }
641         pexit(status, 1);
642         return 0;       /* not reached */
643 }
644
645 uintptr
646 sys_wait(va_list list)
647 {
648         ulong pid;
649         Waitmsg w;
650         OWaitmsg *ow;
651
652         ow = va_arg(list, OWaitmsg*);
653         if(ow == nil)
654                 pid = pwait(nil);
655         else {
656                 validaddr((uintptr)ow, sizeof(OWaitmsg), 1);
657                 evenaddr((uintptr)ow);
658                 pid = pwait(&w);
659         }
660         if(ow != nil){
661                 readnum(0, ow->pid, NUMSIZE, w.pid, NUMSIZE);
662                 readnum(0, ow->time+TUser*NUMSIZE, NUMSIZE, w.time[TUser], NUMSIZE);
663                 readnum(0, ow->time+TSys*NUMSIZE, NUMSIZE, w.time[TSys], NUMSIZE);
664                 readnum(0, ow->time+TReal*NUMSIZE, NUMSIZE, w.time[TReal], NUMSIZE);
665                 strncpy(ow->msg, w.msg, sizeof(ow->msg)-1);
666                 ow->msg[sizeof(ow->msg)-1] = '\0';
667         }
668         return pid;
669 }
670
671 uintptr
672 sysawait(va_list list)
673 {
674         char *p;
675         Waitmsg w;
676         uint n;
677
678         p = va_arg(list, char*);
679         n = va_arg(list, uint);
680         validaddr((uintptr)p, n, 1);
681         pwait(&w);
682         return (uintptr)snprint(p, n, "%d %lud %lud %lud %q",
683                 w.pid,
684                 w.time[TUser], w.time[TSys], w.time[TReal],
685                 w.msg);
686 }
687
688 void
689 werrstr(char *fmt, ...)
690 {
691         va_list va;
692
693         if(up == nil)
694                 return;
695
696         va_start(va, fmt);
697         vseprint(up->syserrstr, up->syserrstr+ERRMAX, fmt, va);
698         va_end(va);
699 }
700
701 static int
702 generrstr(char *buf, uint nbuf)
703 {
704         char tmp[ERRMAX];
705
706         if(nbuf == 0)
707                 error(Ebadarg);
708         validaddr((uintptr)buf, nbuf, 1);
709         if(nbuf > sizeof tmp)
710                 nbuf = sizeof tmp;
711         memmove(tmp, buf, nbuf);
712
713         /* make sure it's NUL-terminated */
714         tmp[nbuf-1] = '\0';
715         memmove(buf, up->syserrstr, nbuf);
716         buf[nbuf-1] = '\0';
717         memmove(up->syserrstr, tmp, nbuf);
718         return 0;
719 }
720
721 uintptr
722 syserrstr(va_list list)
723 {
724         char *buf;
725         uint len;
726
727         buf = va_arg(list, char*);
728         len = va_arg(list, uint);
729         return (uintptr)generrstr(buf, len);
730 }
731
732 /* compatibility for old binaries */
733 uintptr
734 sys_errstr(va_list list)
735 {
736         return (uintptr)generrstr(va_arg(list, char*), 64);
737 }
738
739 uintptr
740 sysnotify(va_list list)
741 {
742         int (*f)(void*, char*);
743         f = va_arg(list, void*);
744         if(f != nil)
745                 validaddr((uintptr)f, sizeof(void*), 0);
746         up->notify = f;
747         return 0;
748 }
749
750 uintptr
751 sysnoted(va_list list)
752 {
753         if(va_arg(list, int) != NRSTR && !up->notified)
754                 error(Egreg);
755         return 0;
756 }
757
758 uintptr
759 syssegbrk(va_list list)
760 {
761         int i;
762         uintptr addr;
763         Segment *s;
764
765         addr = va_arg(list, uintptr);
766         for(i = 0; i < NSEG; i++) {
767                 s = up->seg[i];
768                 if(s == nil || addr < s->base || addr >= s->top)
769                         continue;
770                 switch(s->type&SG_TYPE) {
771                 case SG_TEXT:
772                 case SG_DATA:
773                 case SG_STACK:
774                 case SG_PHYSICAL:
775                 case SG_FIXED:
776                         error(Ebadarg);
777                 default:
778                         return ibrk(va_arg(list, uintptr), i);
779                 }
780         }
781         error(Ebadarg);
782         return 0;       /* not reached */
783 }
784
785 uintptr
786 syssegattach(va_list list)
787 {
788         ulong attr;
789         char *name;
790         uintptr va;
791         ulong len;
792
793         attr = va_arg(list, ulong);
794         name = va_arg(list, char*);
795         va = va_arg(list, uintptr);
796         len = va_arg(list, ulong);
797         validaddr((uintptr)name, 1, 0);
798         name = validnamedup(name, 1);
799         if(waserror()){
800                 free(name);
801                 nexterror();
802         }
803         va = segattach(up, attr, name, va, len);
804         free(name);
805         poperror();
806         return va;
807 }
808
809 uintptr
810 syssegdetach(va_list list)
811 {
812         int i;
813         uintptr addr;
814         Segment *s;
815
816         addr = va_arg(list, uintptr);
817
818         qlock(&up->seglock);
819         if(waserror()){
820                 qunlock(&up->seglock);
821                 nexterror();
822         }
823
824         s = nil;
825         for(i = 0; i < NSEG; i++)
826                 if((s = up->seg[i]) != nil) {
827                         qlock(s);
828                         if((addr >= s->base && addr < s->top) ||
829                            (s->top == s->base && addr == s->base))
830                                 goto found;
831                         qunlock(s);
832                 }
833
834         error(Ebadarg);
835
836 found:
837         /*
838          * Check we are not detaching the initial stack segment.
839          */
840         if(s == up->seg[SSEG]){
841                 qunlock(s);
842                 error(Ebadarg);
843         }
844         up->seg[i] = nil;
845         qunlock(s);
846         putseg(s);
847         qunlock(&up->seglock);
848         poperror();
849
850         /* Ensure we flush any entries from the lost segment */
851         flushmmu();
852         return 0;
853 }
854
855 uintptr
856 syssegfree(va_list list)
857 {
858         Segment *s;
859         uintptr from, to;
860
861         from = va_arg(list, uintptr);
862         to = va_arg(list, ulong);
863         to += from;
864         if(to < from)
865                 error(Ebadarg);
866         s = seg(up, from, 1);
867         if(s == nil)
868                 error(Ebadarg);
869         to &= ~(BY2PG-1);
870         from = PGROUND(from);
871         if(from >= to) {
872                 qunlock(s);
873                 return 0;
874         }
875         if(to > s->top) {
876                 qunlock(s);
877                 error(Ebadarg);
878         }
879         mfreeseg(s, from, (to - from) / BY2PG);
880         qunlock(s);
881         flushmmu();
882         return 0;
883 }
884
885 /* For binary compatibility */
886 uintptr
887 sysbrk_(va_list list)
888 {
889         return ibrk(va_arg(list, uintptr), BSEG);
890 }
891
892 uintptr
893 sysrendezvous(va_list list)
894 {
895         uintptr tag, val, new;
896         Proc *p, **l;
897
898         tag = va_arg(list, uintptr);
899         new = va_arg(list, uintptr);
900         l = &REND(up->rgrp, tag);
901
902         lock(up->rgrp);
903         for(p = *l; p != nil; p = p->rendhash) {
904                 if(p->rendtag == tag) {
905                         *l = p->rendhash;
906                         val = p->rendval;
907                         p->rendval = new;
908                         unlock(up->rgrp);
909
910                         ready(p);
911
912                         return val;
913                 }
914                 l = &p->rendhash;
915         }
916
917         /* Going to sleep here */
918         up->rendtag = tag;
919         up->rendval = new;
920         up->rendhash = *l;
921         *l = up;
922         up->state = Rendezvous;
923         unlock(up->rgrp);
924
925         sched();
926
927         return up->rendval;
928 }
929
930 /*
931  * The implementation of semaphores is complicated by needing
932  * to avoid rescheduling in syssemrelease, so that it is safe
933  * to call from real-time processes.  This means syssemrelease
934  * cannot acquire any qlocks, only spin locks.
935  * 
936  * Semacquire and semrelease must both manipulate the semaphore
937  * wait list.  Lock-free linked lists only exist in theory, not
938  * in practice, so the wait list is protected by a spin lock.
939  * 
940  * The semaphore value *addr is stored in user memory, so it
941  * cannot be read or written while holding spin locks.
942  * 
943  * Thus, we can access the list only when holding the lock, and
944  * we can access the semaphore only when not holding the lock.
945  * This makes things interesting.  Note that sleep's condition function
946  * is called while holding two locks - r and up->rlock - so it cannot
947  * access the semaphore value either.
948  * 
949  * An acquirer announces its intention to try for the semaphore
950  * by putting a Sema structure onto the wait list and then
951  * setting Sema.waiting.  After one last check of semaphore,
952  * the acquirer sleeps until Sema.waiting==0.  A releaser of n
953  * must wake up n acquirers who have Sema.waiting set.  It does
954  * this by clearing Sema.waiting and then calling wakeup.
955  * 
956  * There are three interesting races here.  
957  
958  * The first is that in this particular sleep/wakeup usage, a single
959  * wakeup can rouse a process from two consecutive sleeps!  
960  * The ordering is:
961  * 
962  *      (a) set Sema.waiting = 1
963  *      (a) call sleep
964  *      (b) set Sema.waiting = 0
965  *      (a) check Sema.waiting inside sleep, return w/o sleeping
966  *      (a) try for semaphore, fail
967  *      (a) set Sema.waiting = 1
968  *      (a) call sleep
969  *      (b) call wakeup(a)
970  *      (a) wake up again
971  * 
972  * This is okay - semacquire will just go around the loop
973  * again.  It does mean that at the top of the for(;;) loop in
974  * semacquire, phore.waiting might already be set to 1.
975  * 
976  * The second is that a releaser might wake an acquirer who is
977  * interrupted before he can acquire the lock.  Since
978  * release(n) issues only n wakeup calls -- only n can be used
979  * anyway -- if the interrupted process is not going to use his
980  * wakeup call he must pass it on to another acquirer.
981  * 
982  * The third race is similar to the second but more subtle.  An
983  * acquirer sets waiting=1 and then does a final canacquire()
984  * before going to sleep.  The opposite order would result in
985  * missing wakeups that happen between canacquire and
986  * waiting=1.  (In fact, the whole point of Sema.waiting is to
987  * avoid missing wakeups between canacquire() and sleep().) But
988  * there can be spurious wakeups between a successful
989  * canacquire() and the following semdequeue().  This wakeup is
990  * not useful to the acquirer, since he has already acquired
991  * the semaphore.  Like in the previous case, though, the
992  * acquirer must pass the wakeup call along.
993  * 
994  * This is all rather subtle.  The code below has been verified
995  * with the spin model /sys/src/9/port/semaphore.p.  The
996  * original code anticipated the second race but not the first
997  * or third, which were caught only with spin.  The first race
998  * is mentioned in /sys/doc/sleep.ps, but I'd forgotten about it.
999  * It was lucky that my abstract model of sleep/wakeup still managed
1000  * to preserve that behavior.
1001  *
1002  * I remain slightly concerned about memory coherence
1003  * outside of locks.  The spin model does not take 
1004  * queued processor writes into account so we have to
1005  * think hard.  The only variables accessed outside locks
1006  * are the semaphore value itself and the boolean flag
1007  * Sema.waiting.  The value is only accessed with cmpswap,
1008  * whose job description includes doing the right thing as
1009  * far as memory coherence across processors.  That leaves
1010  * Sema.waiting.  To handle it, we call coherence() before each
1011  * read and after each write.           - rsc
1012  */
1013
1014 /* Add semaphore p with addr a to list in seg. */
1015 static void
1016 semqueue(Segment *s, long *a, Sema *p)
1017 {
1018         memset(p, 0, sizeof *p);
1019         p->addr = a;
1020         lock(&s->sema); /* uses s->sema.Rendez.Lock, but no one else is */
1021         p->next = &s->sema;
1022         p->prev = s->sema.prev;
1023         p->next->prev = p;
1024         p->prev->next = p;
1025         unlock(&s->sema);
1026 }
1027
1028 /* Remove semaphore p from list in seg. */
1029 static void
1030 semdequeue(Segment *s, Sema *p)
1031 {
1032         lock(&s->sema);
1033         p->next->prev = p->prev;
1034         p->prev->next = p->next;
1035         unlock(&s->sema);
1036 }
1037
1038 /* Wake up n waiters with addr a on list in seg. */
1039 static void
1040 semwakeup(Segment *s, long *a, long n)
1041 {
1042         Sema *p;
1043         
1044         lock(&s->sema);
1045         for(p=s->sema.next; p!=&s->sema && n>0; p=p->next){
1046                 if(p->addr == a && p->waiting){
1047                         p->waiting = 0;
1048                         coherence();
1049                         wakeup(p);
1050                         n--;
1051                 }
1052         }
1053         unlock(&s->sema);
1054 }
1055
1056 /* Add delta to semaphore and wake up waiters as appropriate. */
1057 static long
1058 semrelease(Segment *s, long *addr, long delta)
1059 {
1060         long value;
1061
1062         do
1063                 value = *addr;
1064         while(!cmpswap(addr, value, value+delta));
1065         semwakeup(s, addr, delta);
1066         return value+delta;
1067 }
1068
1069 /* Try to acquire semaphore using compare-and-swap */
1070 static int
1071 canacquire(long *addr)
1072 {
1073         long value;
1074         
1075         while((value=*addr) > 0)
1076                 if(cmpswap(addr, value, value-1))
1077                         return 1;
1078         return 0;
1079 }               
1080
1081 /* Should we wake up? */
1082 static int
1083 semawoke(void *p)
1084 {
1085         coherence();
1086         return !((Sema*)p)->waiting;
1087 }
1088
1089 /* Acquire semaphore (subtract 1). */
1090 static int
1091 semacquire(Segment *s, long *addr, int block)
1092 {
1093         int acquired;
1094         Sema phore;
1095
1096         if(canacquire(addr))
1097                 return 1;
1098         if(!block)
1099                 return 0;
1100
1101         acquired = 0;
1102         semqueue(s, addr, &phore);
1103         for(;;){
1104                 phore.waiting = 1;
1105                 coherence();
1106                 if(canacquire(addr)){
1107                         acquired = 1;
1108                         break;
1109                 }
1110                 if(waserror())
1111                         break;
1112                 sleep(&phore, semawoke, &phore);
1113                 poperror();
1114         }
1115         semdequeue(s, &phore);
1116         coherence();    /* not strictly necessary due to lock in semdequeue */
1117         if(!phore.waiting)
1118                 semwakeup(s, addr, 1);
1119         if(!acquired)
1120                 nexterror();
1121         return 1;
1122 }
1123
1124 /* Acquire semaphore or time-out */
1125 static int
1126 tsemacquire(Segment *s, long *addr, ulong ms)
1127 {
1128         int acquired, timedout;
1129         ulong t, elms;
1130         Sema phore;
1131
1132         if(canacquire(addr))
1133                 return 1;
1134         if(ms == 0)
1135                 return 0;
1136         acquired = timedout = 0;
1137         semqueue(s, addr, &phore);
1138         for(;;){
1139                 phore.waiting = 1;
1140                 coherence();
1141                 if(canacquire(addr)){
1142                         acquired = 1;
1143                         break;
1144                 }
1145                 if(waserror())
1146                         break;
1147                 t = m->ticks;
1148                 tsleep(&phore, semawoke, &phore, ms);
1149                 elms = TK2MS(m->ticks - t);
1150                 poperror();
1151                 if(elms >= ms){
1152                         timedout = 1;
1153                         break;
1154                 }
1155                 ms -= elms;
1156         }
1157         semdequeue(s, &phore);
1158         coherence();    /* not strictly necessary due to lock in semdequeue */
1159         if(!phore.waiting)
1160                 semwakeup(s, addr, 1);
1161         if(timedout)
1162                 return 0;
1163         if(!acquired)
1164                 nexterror();
1165         return 1;
1166 }
1167
1168 uintptr
1169 syssemacquire(va_list list)
1170 {
1171         int block;
1172         long *addr;
1173         Segment *s;
1174
1175         addr = va_arg(list, long*);
1176         block = va_arg(list, int);
1177         evenaddr((uintptr)addr);
1178         s = seg(up, (uintptr)addr, 0);
1179         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1180                 validaddr((uintptr)addr, sizeof(long), 1);
1181                 error(Ebadarg);
1182         }
1183         if(*addr < 0)
1184                 error(Ebadarg);
1185         return (uintptr)semacquire(s, addr, block);
1186 }
1187
1188 uintptr
1189 systsemacquire(va_list list)
1190 {
1191         long *addr;
1192         ulong ms;
1193         Segment *s;
1194
1195         addr = va_arg(list, long*);
1196         ms = va_arg(list, ulong);
1197         evenaddr((uintptr)addr);
1198         s = seg(up, (uintptr)addr, 0);
1199         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1200                 validaddr((uintptr)addr, sizeof(long), 1);
1201                 error(Ebadarg);
1202         }
1203         if(*addr < 0)
1204                 error(Ebadarg);
1205         return (uintptr)tsemacquire(s, addr, ms);
1206 }
1207
1208 uintptr
1209 syssemrelease(va_list list)
1210 {
1211         long *addr, delta;
1212         Segment *s;
1213
1214         addr = va_arg(list, long*);
1215         delta = va_arg(list, long);
1216         evenaddr((uintptr)addr);
1217         s = seg(up, (uintptr)addr, 0);
1218         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1219                 validaddr((uintptr)addr, sizeof(long), 1);
1220                 error(Ebadarg);
1221         }
1222         /* delta == 0 is a no-op, not a release */
1223         if(delta < 0 || *addr < 0)
1224                 error(Ebadarg);
1225         return (uintptr)semrelease(s, addr, delta);
1226 }
1227
1228 /* For binary compatibility */
1229 uintptr
1230 sys_nsec(va_list list)
1231 {
1232         vlong *v;
1233
1234         /* return in register on 64bit machine */
1235         if(sizeof(uintptr) == sizeof(vlong)){
1236                 USED(list);
1237                 return (uintptr)todget(nil);
1238         }
1239
1240         v = va_arg(list, vlong*);
1241         evenaddr((uintptr)v);
1242         validaddr((uintptr)v, sizeof(vlong), 1);
1243         *v = todget(nil);
1244         return 0;
1245 }