]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sysproc.c
pc64: dont 4 byte align stack pointer for amd64 in sysexec()
[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])
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                 magic = l2be(exec.magic);
284                 text = l2be(exec.text);
285                 entry = l2be(exec.entry);
286                 if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){
287                         if(magic == S_MAGIC){
288                                 text += 8;
289                                 align = 0x200000ull;    /* 2MB segment alignment for amd64 */
290                         }
291                         if(text >= (USTKTOP-USTKSIZE)-(UTZERO+sizeof(Exec))
292                         || entry < UTZERO+sizeof(Exec)
293                         || entry >= UTZERO+sizeof(Exec)+text)
294                                 error(Ebadexec);
295                         break; /* for binary */
296                 }
297
298                 /*
299                  * Process #! /bin/sh args ...
300                  */
301                 memmove(line, &exec, sizeof(Exec));
302                 if(indir || line[0]!='#' || line[1]!='!')
303                         error(Ebadexec);
304                 n = shargs(line, n, progarg);
305                 if(n == 0)
306                         error(Ebadexec);
307                 indir = 1;
308                 /*
309                  * First arg becomes complete file name
310                  */
311                 progarg[n++] = file;
312                 progarg[n] = 0;
313                 argp0++;
314                 file = progarg[0];
315                 if(strlen(elem) >= sizeof progelem)
316                         error(Ebadexec);
317                 strcpy(progelem, elem);
318                 progarg[0] = progelem;
319                 poperror();
320                 cclose(tc);
321         }
322
323         data = l2be(exec.data);
324         bss = l2be(exec.bss);
325         align--;
326         t = (UTZERO+sizeof(Exec)+text+align) & ~align;
327         align = BY2PG-1;
328         d = (t + data + align) & ~align;
329         bssend = t + data + bss;
330         b = (bssend + align) & ~align;
331         if(t >= (USTKTOP-USTKSIZE) || d >= (USTKTOP-USTKSIZE) || b >= (USTKTOP-USTKSIZE))
332                 error(Ebadexec);
333
334         /*
335          * Args: pass 1: count
336          */
337         nbytes = sizeof(Tos);           /* hole for profiling clock at top of stack (and more) */
338         nargs = 0;
339         if(indir){
340                 argp = progarg;
341                 while(*argp){
342                         a = *argp++;
343                         nbytes += strlen(a) + 1;
344                         nargs++;
345                 }
346         }
347         argp = argp0;
348         evenaddr((uintptr)argp);
349         validaddr((uintptr)argp, BY2WD, 0);
350         while(*argp){
351                 a = *argp++;
352                 if(((uintptr)argp&(BY2PG-1)) < BY2WD)
353                         validaddr((uintptr)argp, BY2WD, 0);
354                 validaddr((uintptr)a, 1, 0);
355                 nbytes += ((char*)vmemchr(a, 0, 0x7FFFFFFF) - a) + 1;
356                 nargs++;
357         }
358         ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1));
359
360         /*
361          * 8-byte align SP for those (e.g. sparc) that need it.
362          * execregs() will subtract another 4 bytes for argc.
363          */
364         if(BY2WD == 4 && (ssize+4) & 7)
365                 ssize += 4;
366
367         if(PGROUND(ssize) >= USTKSIZE)
368                 error(Enovmem);
369
370         /*
371          * Build the stack segment, putting it in kernel virtual for the moment
372          */
373         qlock(&up->seglock);
374         if(waserror()){
375                 qunlock(&up->seglock);
376                 nexterror();
377         }
378
379         s = up->seg[SSEG];
380         do {
381                 tstk = s->base;
382                 if(tstk <= USTKSIZE)
383                         error(Enovmem);
384         } while((s = isoverlap(up, tstk-USTKSIZE, USTKSIZE)) != nil);
385         up->seg[ESEG] = newseg(SG_STACK, tstk-USTKSIZE, USTKSIZE/BY2PG);
386
387         /*
388          * Args: pass 2: assemble; the pages will be faulted in
389          */
390         tos = (Tos*)(tstk - sizeof(Tos));
391         tos->cyclefreq = m->cyclefreq;
392         tos->kcycles = 0;
393         tos->pcycles = 0;
394         tos->clock = 0;
395
396         argv = (char**)(tstk - ssize);
397         charp = (char*)(tstk - nbytes);
398         a = charp;
399         if(indir)
400                 argp = progarg;
401         else
402                 argp = argp0;
403
404         for(i=0; i<nargs; i++){
405                 if(indir && *argp==0) {
406                         indir = 0;
407                         argp = argp0;
408                 }
409                 *argv++ = charp + (USTKTOP-tstk);
410                 n = strlen(*argp) + 1;
411                 memmove(charp, *argp++, n);
412                 charp += n;
413         }
414
415         /* copy args; easiest from new process's stack */
416         n = charp - a;
417         if(n > 128)     /* don't waste too much space on huge arg lists */
418                 n = 128;
419         args = smalloc(n);
420         memmove(args, a, n);
421         if(n>0 && args[n-1]!='\0'){
422                 /* make sure last arg is NUL-terminated */
423                 /* put NUL at UTF-8 character boundary */
424                 for(i=n-1; i>0; --i)
425                         if(fullrune(args+i, n-i))
426                                 break;
427                 args[i] = 0;
428                 n = i+1;
429         }
430
431         /*
432          * Committed.
433          * Free old memory.
434          * Special segments are maintained across exec
435          */
436         for(i = SSEG; i <= BSEG; i++) {
437                 putseg(up->seg[i]);
438                 /* prevent a second free if we have an error */
439                 up->seg[i] = 0;
440         }
441         for(i = ESEG+1; i < NSEG; i++) {
442                 s = up->seg[i];
443                 if(s != 0 && (s->type&SG_CEXEC) != 0) {
444                         putseg(s);
445                         up->seg[i] = 0;
446                 }
447         }
448
449         /*
450          * Close on exec
451          */
452         if((f = up->fgrp) != nil){
453                 for(i=0; i<=f->maxfd; i++)
454                         fdclose(i, CCEXEC);
455         }
456
457         /* Text.  Shared. Attaches to cache image if possible */
458         /* attachimage returns a locked cache image */
459         img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT);
460         ts = img->s;
461         up->seg[TSEG] = ts;
462         ts->flushme = 1;
463         ts->fstart = 0;
464         ts->flen = sizeof(Exec)+text;
465         unlock(img);
466
467         /* Data. Shared. */
468         s = newseg(SG_DATA, t, (d-t)>>PGSHIFT);
469         up->seg[DSEG] = s;
470
471         /* Attached by hand */
472         incref(img);
473         s->image = img;
474         s->fstart = ts->fstart+ts->flen;
475         s->flen = data;
476
477         /* BSS. Zero fill on demand */
478         up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT);
479
480         /*
481          * Move the stack
482          */
483         s = up->seg[ESEG];
484         up->seg[ESEG] = 0;
485         s->base = USTKTOP-USTKSIZE;
486         s->top = USTKTOP;
487         relocateseg(s, USTKTOP-tstk);
488         up->seg[SSEG] = s;
489         qunlock(&up->seglock);
490         poperror();     /* seglock */
491
492         /*
493          *  '/' processes are higher priority (hack to make /ip more responsive).
494          */
495         if(devtab[tc->type]->dc == L'/')
496                 up->basepri = PriRoot;
497         up->priority = up->basepri;
498         poperror();     /* tc */
499         cclose(tc);
500         poperror();     /* file0 */
501         free(file0);
502
503         qlock(&up->debug);
504         free(up->text);
505         up->text = elem;
506         free(up->args);
507         up->args = args;
508         up->nargs = n;
509         up->setargs = 0;
510
511         up->nnote = 0;
512         up->notify = 0;
513         up->notified = 0;
514         up->privatemem = 0;
515         procsetup(up);
516         qunlock(&up->debug);
517
518         /*
519          *  At this point, the mmu contains info about the old address
520          *  space and needs to be flushed
521          */
522         flushmmu();
523
524         if(up->hang)
525                 up->procctl = Proc_stopme;
526         return execregs(entry, ssize, nargs);
527 }
528
529 int
530 shargs(char *s, int n, char **ap)
531 {
532         int i;
533
534         s += 2;
535         n -= 2;         /* skip #! */
536         for(i=0; s[i]!='\n'; i++)
537                 if(i == n-1)
538                         return 0;
539         s[i] = 0;
540         *ap = 0;
541         i = 0;
542         for(;;) {
543                 while(*s==' ' || *s=='\t')
544                         s++;
545                 if(*s == 0)
546                         break;
547                 i++;
548                 *ap++ = s;
549                 *ap = 0;
550                 while(*s && *s!=' ' && *s!='\t')
551                         s++;
552                 if(*s == 0)
553                         break;
554                 else
555                         *s++ = 0;
556         }
557         return i;
558 }
559
560 int
561 return0(void*)
562 {
563         return 0;
564 }
565
566 uintptr
567 syssleep(va_list list)
568 {
569         long ms;
570
571         ms = va_arg(list, long);
572         if(ms <= 0) {
573                 if (up->edf && (up->edf->flags & Admitted))
574                         edfyield();
575                 else
576                         yield();
577         } else {
578                 if(ms < TK2MS(1))
579                         ms = TK2MS(1);
580                 tsleep(&up->sleep, return0, 0, ms);
581         }
582         return 0;
583 }
584
585 uintptr
586 sysalarm(va_list list)
587 {
588         return procalarm(va_arg(list, ulong));
589 }
590
591
592 uintptr
593 sysexits(va_list list)
594 {
595         char *status;
596         char *inval = "invalid exit string";
597         char buf[ERRMAX];
598
599         status = va_arg(list, char*);
600         if(status){
601                 if(waserror())
602                         status = inval;
603                 else{
604                         validaddr((uintptr)status, 1, 0);
605                         if(vmemchr(status, 0, ERRMAX) == 0){
606                                 memmove(buf, status, ERRMAX);
607                                 buf[ERRMAX-1] = 0;
608                                 status = buf;
609                         }
610                         poperror();
611                 }
612
613         }
614         pexit(status, 1);
615         return 0;       /* not reached */
616 }
617
618 uintptr
619 sys_wait(va_list list)
620 {
621         ulong pid;
622         Waitmsg w;
623         OWaitmsg *ow;
624
625         ow = va_arg(list, OWaitmsg*);
626         if(ow == nil)
627                 pid = pwait(nil);
628         else {
629                 validaddr((uintptr)ow, sizeof(OWaitmsg), 1);
630                 evenaddr((uintptr)ow);
631                 pid = pwait(&w);
632         }
633         if(ow != nil){
634                 readnum(0, ow->pid, NUMSIZE, w.pid, NUMSIZE);
635                 readnum(0, ow->time+TUser*NUMSIZE, NUMSIZE, w.time[TUser], NUMSIZE);
636                 readnum(0, ow->time+TSys*NUMSIZE, NUMSIZE, w.time[TSys], NUMSIZE);
637                 readnum(0, ow->time+TReal*NUMSIZE, NUMSIZE, w.time[TReal], NUMSIZE);
638                 strncpy(ow->msg, w.msg, sizeof(ow->msg)-1);
639                 ow->msg[sizeof(ow->msg)-1] = '\0';
640         }
641         return pid;
642 }
643
644 uintptr
645 sysawait(va_list list)
646 {
647         char *p;
648         Waitmsg w;
649         uint n;
650
651         p = va_arg(list, char*);
652         n = va_arg(list, uint);
653         validaddr((uintptr)p, n, 1);
654         pwait(&w);
655         return (uintptr)snprint(p, n, "%d %lud %lud %lud %q",
656                 w.pid,
657                 w.time[TUser], w.time[TSys], w.time[TReal],
658                 w.msg);
659 }
660
661 void
662 werrstr(char *fmt, ...)
663 {
664         va_list va;
665
666         if(up == nil)
667                 return;
668
669         va_start(va, fmt);
670         vseprint(up->syserrstr, up->syserrstr+ERRMAX, fmt, va);
671         va_end(va);
672 }
673
674 static int
675 generrstr(char *buf, uint nbuf)
676 {
677         char tmp[ERRMAX];
678
679         if(nbuf == 0)
680                 error(Ebadarg);
681         validaddr((uintptr)buf, nbuf, 1);
682         if(nbuf > sizeof tmp)
683                 nbuf = sizeof tmp;
684         memmove(tmp, buf, nbuf);
685
686         /* make sure it's NUL-terminated */
687         tmp[nbuf-1] = '\0';
688         memmove(buf, up->syserrstr, nbuf);
689         buf[nbuf-1] = '\0';
690         memmove(up->syserrstr, tmp, nbuf);
691         return 0;
692 }
693
694 uintptr
695 syserrstr(va_list list)
696 {
697         char *buf;
698         uint len;
699
700         buf = va_arg(list, char*);
701         len = va_arg(list, uint);
702         return (uintptr)generrstr(buf, len);
703 }
704
705 /* compatibility for old binaries */
706 uintptr
707 sys_errstr(va_list list)
708 {
709         return (uintptr)generrstr(va_arg(list, char*), 64);
710 }
711
712 uintptr
713 sysnotify(va_list list)
714 {
715         int (*f)(void*, char*);
716         f = va_arg(list, void*);
717         if(f != 0)
718                 validaddr((uintptr)f, sizeof(void*), 0);
719         up->notify = f;
720         return 0;
721 }
722
723 uintptr
724 sysnoted(va_list list)
725 {
726         if(va_arg(list, int) !=NRSTR && !up->notified)
727                 error(Egreg);
728         return 0;
729 }
730
731 uintptr
732 syssegbrk(va_list list)
733 {
734         int i;
735         uintptr addr;
736         Segment *s;
737
738         addr = va_arg(list, uintptr);
739         for(i = 0; i < NSEG; i++) {
740                 s = up->seg[i];
741                 if(s == 0 || addr < s->base || addr >= s->top)
742                         continue;
743                 switch(s->type&SG_TYPE) {
744                 case SG_TEXT:
745                 case SG_DATA:
746                 case SG_STACK:
747                         error(Ebadarg);
748                 default:
749                         return (uintptr)ibrk(va_arg(list, uintptr), i);
750                 }
751         }
752         error(Ebadarg);
753         return 0;       /* not reached */
754 }
755
756 uintptr
757 syssegattach(va_list list)
758 {
759         ulong attr;
760         char *name;
761         uintptr va;
762         ulong len;
763
764         attr = va_arg(list, ulong);
765         name = va_arg(list, char*);
766         va = va_arg(list, uintptr);
767         len = va_arg(list, ulong);
768         return segattach(up, attr, name, va, len);
769 }
770
771 uintptr
772 syssegdetach(va_list list)
773 {
774         int i;
775         uintptr addr;
776         Segment *s;
777
778         addr = va_arg(list, uintptr);
779
780         qlock(&up->seglock);
781         if(waserror()){
782                 qunlock(&up->seglock);
783                 nexterror();
784         }
785
786         s = 0;
787         for(i = 0; i < NSEG; i++)
788                 if(s = up->seg[i]) {
789                         qlock(&s->lk);
790                         if((addr >= s->base && addr < s->top) ||
791                            (s->top == s->base && addr == s->base))
792                                 goto found;
793                         qunlock(&s->lk);
794                 }
795
796         error(Ebadarg);
797
798 found:
799         /*
800          * Check we are not detaching the initial stack segment.
801          */
802         if(s == up->seg[SSEG]){
803                 qunlock(&s->lk);
804                 error(Ebadarg);
805         }
806         up->seg[i] = 0;
807         qunlock(&s->lk);
808         putseg(s);
809         qunlock(&up->seglock);
810         poperror();
811
812         /* Ensure we flush any entries from the lost segment */
813         flushmmu();
814         return 0;
815 }
816
817 uintptr
818 syssegfree(va_list list)
819 {
820         Segment *s;
821         uintptr from, to;
822
823         from = va_arg(list, uintptr);
824         s = seg(up, from, 1);
825         if(s == nil)
826                 error(Ebadarg);
827         to = va_arg(list, ulong);
828         to += from;
829         to &= ~(BY2PG-1);
830         from = PGROUND(from);
831
832         if(to > s->top) {
833                 qunlock(&s->lk);
834                 error(Ebadarg);
835         }
836
837         mfreeseg(s, from, (to - from) / BY2PG);
838         qunlock(&s->lk);
839         flushmmu();
840         return 0;
841 }
842
843 /* For binary compatibility */
844 uintptr
845 sysbrk_(va_list list)
846 {
847         return (uintptr)ibrk(va_arg(list, uintptr), BSEG);
848 }
849
850 uintptr
851 sysrendezvous(va_list list)
852 {
853         uintptr tag, val, new;
854         Proc *p, **l;
855
856         tag = va_arg(list, uintptr);
857         new = va_arg(list, uintptr);
858         l = &REND(up->rgrp, tag);
859
860         lock(up->rgrp);
861         for(p = *l; p; p = p->rendhash) {
862                 if(p->rendtag == tag) {
863                         *l = p->rendhash;
864                         val = p->rendval;
865                         p->rendval = new;
866                         unlock(up->rgrp);
867
868                         ready(p);
869
870                         return val;
871                 }
872                 l = &p->rendhash;
873         }
874
875         /* Going to sleep here */
876         up->rendtag = tag;
877         up->rendval = new;
878         up->rendhash = *l;
879         *l = up;
880         up->state = Rendezvous;
881         unlock(up->rgrp);
882
883         sched();
884
885         return up->rendval;
886 }
887
888 /*
889  * The implementation of semaphores is complicated by needing
890  * to avoid rescheduling in syssemrelease, so that it is safe
891  * to call from real-time processes.  This means syssemrelease
892  * cannot acquire any qlocks, only spin locks.
893  * 
894  * Semacquire and semrelease must both manipulate the semaphore
895  * wait list.  Lock-free linked lists only exist in theory, not
896  * in practice, so the wait list is protected by a spin lock.
897  * 
898  * The semaphore value *addr is stored in user memory, so it
899  * cannot be read or written while holding spin locks.
900  * 
901  * Thus, we can access the list only when holding the lock, and
902  * we can access the semaphore only when not holding the lock.
903  * This makes things interesting.  Note that sleep's condition function
904  * is called while holding two locks - r and up->rlock - so it cannot
905  * access the semaphore value either.
906  * 
907  * An acquirer announces its intention to try for the semaphore
908  * by putting a Sema structure onto the wait list and then
909  * setting Sema.waiting.  After one last check of semaphore,
910  * the acquirer sleeps until Sema.waiting==0.  A releaser of n
911  * must wake up n acquirers who have Sema.waiting set.  It does
912  * this by clearing Sema.waiting and then calling wakeup.
913  * 
914  * There are three interesting races here.  
915  
916  * The first is that in this particular sleep/wakeup usage, a single
917  * wakeup can rouse a process from two consecutive sleeps!  
918  * The ordering is:
919  * 
920  *      (a) set Sema.waiting = 1
921  *      (a) call sleep
922  *      (b) set Sema.waiting = 0
923  *      (a) check Sema.waiting inside sleep, return w/o sleeping
924  *      (a) try for semaphore, fail
925  *      (a) set Sema.waiting = 1
926  *      (a) call sleep
927  *      (b) call wakeup(a)
928  *      (a) wake up again
929  * 
930  * This is okay - semacquire will just go around the loop
931  * again.  It does mean that at the top of the for(;;) loop in
932  * semacquire, phore.waiting might already be set to 1.
933  * 
934  * The second is that a releaser might wake an acquirer who is
935  * interrupted before he can acquire the lock.  Since
936  * release(n) issues only n wakeup calls -- only n can be used
937  * anyway -- if the interrupted process is not going to use his
938  * wakeup call he must pass it on to another acquirer.
939  * 
940  * The third race is similar to the second but more subtle.  An
941  * acquirer sets waiting=1 and then does a final canacquire()
942  * before going to sleep.  The opposite order would result in
943  * missing wakeups that happen between canacquire and
944  * waiting=1.  (In fact, the whole point of Sema.waiting is to
945  * avoid missing wakeups between canacquire() and sleep().) But
946  * there can be spurious wakeups between a successful
947  * canacquire() and the following semdequeue().  This wakeup is
948  * not useful to the acquirer, since he has already acquired
949  * the semaphore.  Like in the previous case, though, the
950  * acquirer must pass the wakeup call along.
951  * 
952  * This is all rather subtle.  The code below has been verified
953  * with the spin model /sys/src/9/port/semaphore.p.  The
954  * original code anticipated the second race but not the first
955  * or third, which were caught only with spin.  The first race
956  * is mentioned in /sys/doc/sleep.ps, but I'd forgotten about it.
957  * It was lucky that my abstract model of sleep/wakeup still managed
958  * to preserve that behavior.
959  *
960  * I remain slightly concerned about memory coherence
961  * outside of locks.  The spin model does not take 
962  * queued processor writes into account so we have to
963  * think hard.  The only variables accessed outside locks
964  * are the semaphore value itself and the boolean flag
965  * Sema.waiting.  The value is only accessed with cmpswap,
966  * whose job description includes doing the right thing as
967  * far as memory coherence across processors.  That leaves
968  * Sema.waiting.  To handle it, we call coherence() before each
969  * read and after each write.           - rsc
970  */
971
972 /* Add semaphore p with addr a to list in seg. */
973 static void
974 semqueue(Segment *s, long *a, Sema *p)
975 {
976         memset(p, 0, sizeof *p);
977         p->addr = a;
978         lock(&s->sema); /* uses s->sema.Rendez.Lock, but no one else is */
979         p->next = &s->sema;
980         p->prev = s->sema.prev;
981         p->next->prev = p;
982         p->prev->next = p;
983         unlock(&s->sema);
984 }
985
986 /* Remove semaphore p from list in seg. */
987 static void
988 semdequeue(Segment *s, Sema *p)
989 {
990         lock(&s->sema);
991         p->next->prev = p->prev;
992         p->prev->next = p->next;
993         unlock(&s->sema);
994 }
995
996 /* Wake up n waiters with addr a on list in seg. */
997 static void
998 semwakeup(Segment *s, long *a, long n)
999 {
1000         Sema *p;
1001         
1002         lock(&s->sema);
1003         for(p=s->sema.next; p!=&s->sema && n>0; p=p->next){
1004                 if(p->addr == a && p->waiting){
1005                         p->waiting = 0;
1006                         coherence();
1007                         wakeup(p);
1008                         n--;
1009                 }
1010         }
1011         unlock(&s->sema);
1012 }
1013
1014 /* Add delta to semaphore and wake up waiters as appropriate. */
1015 static long
1016 semrelease(Segment *s, long *addr, long delta)
1017 {
1018         long value;
1019
1020         do
1021                 value = *addr;
1022         while(!cmpswap(addr, value, value+delta));
1023         semwakeup(s, addr, delta);
1024         return value+delta;
1025 }
1026
1027 /* Try to acquire semaphore using compare-and-swap */
1028 static int
1029 canacquire(long *addr)
1030 {
1031         long value;
1032         
1033         while((value=*addr) > 0)
1034                 if(cmpswap(addr, value, value-1))
1035                         return 1;
1036         return 0;
1037 }               
1038
1039 /* Should we wake up? */
1040 static int
1041 semawoke(void *p)
1042 {
1043         coherence();
1044         return !((Sema*)p)->waiting;
1045 }
1046
1047 /* Acquire semaphore (subtract 1). */
1048 static int
1049 semacquire(Segment *s, long *addr, int block)
1050 {
1051         int acquired;
1052         Sema phore;
1053
1054         if(canacquire(addr))
1055                 return 1;
1056         if(!block)
1057                 return 0;
1058
1059         acquired = 0;
1060         semqueue(s, addr, &phore);
1061         for(;;){
1062                 phore.waiting = 1;
1063                 coherence();
1064                 if(canacquire(addr)){
1065                         acquired = 1;
1066                         break;
1067                 }
1068                 if(waserror())
1069                         break;
1070                 sleep(&phore, semawoke, &phore);
1071                 poperror();
1072         }
1073         semdequeue(s, &phore);
1074         coherence();    /* not strictly necessary due to lock in semdequeue */
1075         if(!phore.waiting)
1076                 semwakeup(s, addr, 1);
1077         if(!acquired)
1078                 nexterror();
1079         return 1;
1080 }
1081
1082 /* Acquire semaphore or time-out */
1083 static int
1084 tsemacquire(Segment *s, long *addr, ulong ms)
1085 {
1086         int acquired, timedout;
1087         ulong t, elms;
1088         Sema phore;
1089
1090         if(canacquire(addr))
1091                 return 1;
1092         if(ms == 0)
1093                 return 0;
1094         acquired = timedout = 0;
1095         semqueue(s, addr, &phore);
1096         for(;;){
1097                 phore.waiting = 1;
1098                 coherence();
1099                 if(canacquire(addr)){
1100                         acquired = 1;
1101                         break;
1102                 }
1103                 if(waserror())
1104                         break;
1105                 t = m->ticks;
1106                 tsleep(&phore, semawoke, &phore, ms);
1107                 elms = TK2MS(m->ticks - t);
1108                 poperror();
1109                 if(elms >= ms){
1110                         timedout = 1;
1111                         break;
1112                 }
1113                 ms -= elms;
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(timedout)
1120                 return 0;
1121         if(!acquired)
1122                 nexterror();
1123         return 1;
1124 }
1125
1126 uintptr
1127 syssemacquire(va_list list)
1128 {
1129         int block;
1130         long *addr;
1131         Segment *s;
1132
1133         addr = va_arg(list, long*);
1134         block = va_arg(list, int);
1135         evenaddr((uintptr)addr);
1136         s = seg(up, (uintptr)addr, 0);
1137         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1138                 validaddr((uintptr)addr, sizeof(long), 1);
1139                 error(Ebadarg);
1140         }
1141         if(*addr < 0)
1142                 error(Ebadarg);
1143         return (uintptr)semacquire(s, addr, block);
1144 }
1145
1146 uintptr
1147 systsemacquire(va_list list)
1148 {
1149         long *addr;
1150         ulong ms;
1151         Segment *s;
1152
1153         addr = va_arg(list, long*);
1154         ms = va_arg(list, ulong);
1155         evenaddr((uintptr)addr);
1156         s = seg(up, (uintptr)addr, 0);
1157         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1158                 validaddr((uintptr)addr, sizeof(long), 1);
1159                 error(Ebadarg);
1160         }
1161         if(*addr < 0)
1162                 error(Ebadarg);
1163         return (uintptr)tsemacquire(s, addr, ms);
1164 }
1165
1166 uintptr
1167 syssemrelease(va_list list)
1168 {
1169         long *addr, delta;
1170         Segment *s;
1171
1172         addr = va_arg(list, long*);
1173         delta = va_arg(list, long);
1174         evenaddr((uintptr)addr);
1175         s = seg(up, (uintptr)addr, 0);
1176         if(s == nil || (s->type&SG_RONLY) != 0 || (uintptr)addr+sizeof(long) > s->top){
1177                 validaddr((uintptr)addr, sizeof(long), 1);
1178                 error(Ebadarg);
1179         }
1180         /* delta == 0 is a no-op, not a release */
1181         if(delta < 0 || *addr < 0)
1182                 error(Ebadarg);
1183         return (uintptr)semrelease(s, addr, delta);
1184 }