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