]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devcons.c
kernel: use 64-bit virtual entry point for expanded header, document behaviour in...
[plan9front.git] / sys / src / 9 / port / devcons.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 #include        <authsrv.h>
9
10 void    (*consdebug)(void) = nil;
11 void    (*screenputs)(char*, int) = nil;
12
13 Queue*  serialoq;               /* serial console output */
14 Queue*  kprintoq;               /* console output, for /dev/kprint */
15 ulong   kprintinuse;            /* test and set whether /dev/kprint is open */
16 int     iprintscreenputs = 1;
17
18 int     panicking;
19
20 char    *sysname;
21 vlong   fasthz;
22
23 static int      readtime(ulong, char*, int);
24 static int      readbintime(char*, int);
25 static int      writetime(char*, int);
26 static int      writebintime(char*, int);
27
28 enum
29 {
30         CMreboot,
31         CMpanic,
32         CMrdb,
33 };
34
35 Cmdtab rebootmsg[] =
36 {
37         CMreboot,       "reboot",       0,
38         CMpanic,        "panic",        0,
39         CMrdb,          "rdb",          0,
40 };
41
42 void
43 printinit(void)
44 {
45 }
46
47 int
48 consactive(void)
49 {
50         if(serialoq)
51                 return qlen(serialoq) > 0;
52         return 0;
53 }
54
55 void
56 prflush(void)
57 {
58         ulong now;
59
60         now = m->ticks;
61         while(consactive())
62                 if(m->ticks - now >= HZ)
63                         break;
64 }
65
66 static void
67 kmesgputs(char *str, int n)
68 {
69         uint nn, d;
70
71         ilock(&kmesg.lk);
72         /* take the tail of huge writes */
73         if(n > sizeof kmesg.buf){
74                 d = n - sizeof kmesg.buf;
75                 str += d;
76                 n -= d;
77         }
78
79         /* slide the buffer down to make room */
80         nn = kmesg.n;
81         if(nn + n >= sizeof kmesg.buf){
82                 d = nn + n - sizeof kmesg.buf;
83                 if(d)
84                         memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d);
85                 nn -= d;
86         }
87
88         /* copy the data in */
89         memmove(kmesg.buf+nn, str, n);
90         nn += n;
91         kmesg.n = nn;
92         iunlock(&kmesg.lk);
93 }
94
95 /*
96  *   Print a string on the console.  Convert \n to \r\n for serial
97  *   line consoles.  Locking of the queues is left up to the screen
98  *   or uart code.  Multi-line messages to serial consoles may get
99  *   interspersed with other messages.
100  */
101 static void
102 putstrn0(char *str, int n, int usewrite)
103 {
104         int m;
105         char *t;
106         int (*wq)(Queue*, void*, int);
107
108         /*
109          *  how many different output devices do we need?
110          */
111         kmesgputs(str, n);
112
113         /*
114          *  if someone is reading /dev/kprint,
115          *  put the message there.
116          *  if not and there's an attached bit mapped display,
117          *  put the message there.
118          *
119          *  if there's a serial line being used as a console,
120          *  put the message there.
121          */
122         wq = usewrite && islo() ? qwrite : qiwrite;
123         if(kprintoq != nil && !qisclosed(kprintoq))
124                 (*wq)(kprintoq, str, n);
125         else if(screenputs != nil)
126                 screenputs(str, n);
127
128         if(serialoq == nil){
129                 uartputs(str, n);
130                 return;
131         }
132
133         while(n > 0) {
134                 t = memchr(str, '\n', n);
135                 if(t != nil) {
136                         m = t-str;
137                         (*wq)(serialoq, str, m);
138                         (*wq)(serialoq, "\r\n", 2);
139                         n -= m+1;
140                         str = t+1;
141                 } else {
142                         (*wq)(serialoq, str, n);
143                         break;
144                 }
145         }
146 }
147
148 void
149 putstrn(char *str, int n)
150 {
151         putstrn0(str, n, 0);
152 }
153
154 int
155 print(char *fmt, ...)
156 {
157         int n;
158         va_list arg;
159         char buf[PRINTSIZE];
160
161         va_start(arg, fmt);
162         n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
163         va_end(arg);
164         putstrn(buf, n);
165
166         return n;
167 }
168
169 /*
170  * Want to interlock iprints to avoid interlaced output on 
171  * multiprocessor, but don't want to deadlock if one processor
172  * dies during print and another has something important to say.
173  * Make a good faith effort.
174  */
175 static Lock iprintlock;
176 static int
177 iprintcanlock(Lock *l)
178 {
179         int i;
180         
181         for(i=0; i<1000; i++){
182                 if(canlock(l))
183                         return 1;
184                 if(l->m == MACHP(m->machno))
185                         return 0;
186                 microdelay(100);
187         }
188         return 0;
189 }
190
191 int
192 iprint(char *fmt, ...)
193 {
194         int n, s, locked;
195         va_list arg;
196         char buf[PRINTSIZE];
197
198         s = splhi();
199         va_start(arg, fmt);
200         n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
201         va_end(arg);
202         locked = iprintcanlock(&iprintlock);
203         if(screenputs != nil && iprintscreenputs)
204                 screenputs(buf, n);
205         uartputs(buf, n);
206         if(locked)
207                 unlock(&iprintlock);
208         splx(s);
209
210         return n;
211 }
212
213 void
214 panic(char *fmt, ...)
215 {
216         int s;
217         va_list arg;
218         char buf[PRINTSIZE];
219
220         kprintoq = nil; /* don't try to write to /dev/kprint */
221
222         if(panicking)
223                 for(;;);
224         panicking = 1;
225
226         s = splhi();
227         strcpy(buf, "panic: ");
228         va_start(arg, fmt);
229         vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
230         va_end(arg);
231         iprint("%s\n", buf);
232         if(consdebug)
233                 (*consdebug)();
234         splx(s);
235         prflush();
236         dumpstack();
237
238         /* reboot cpu servers and headless machines when not debugging */
239         if(getconf("*debug") == nil)
240         if(cpuserver || !conf.monitor)
241                 exit(1);
242
243         /* otherwise, just hang */
244         while(islo()) idlehands();
245         for(;;);
246 }
247
248 /* libmp at least contains a few calls to sysfatal; simulate with panic */
249 void
250 sysfatal(char *fmt, ...)
251 {
252         char err[256];
253         va_list arg;
254
255         va_start(arg, fmt);
256         vseprint(err, err + sizeof err, fmt, arg);
257         va_end(arg);
258         panic("sysfatal: %s", err);
259 }
260
261 void
262 _assert(char *fmt)
263 {
264         panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt);
265 }
266
267 int
268 pprint(char *fmt, ...)
269 {
270         int n;
271         Chan *c;
272         va_list arg;
273         char buf[2*PRINTSIZE];
274
275         if(up == nil || up->fgrp == nil)
276                 return 0;
277
278         c = up->fgrp->fd[2];
279         if(c==nil || (c->flag&CMSG)!=0 || (c->mode!=OWRITE && c->mode!=ORDWR))
280                 return 0;
281         n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid);
282         va_start(arg, fmt);
283         n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
284         va_end(arg);
285
286         if(waserror())
287                 return 0;
288         devtab[c->type]->write(c, buf, n, c->offset);
289         poperror();
290
291         lock(c);
292         c->offset += n;
293         unlock(c);
294
295         return n;
296 }
297
298 enum{
299         Qdir,
300         Qbintime,
301         Qcons,
302         Qconsctl,
303         Qcputime,
304         Qdrivers,
305         Qkmesg,
306         Qkprint,
307         Qhostdomain,
308         Qhostowner,
309         Qnull,
310         Qosversion,
311         Qpid,
312         Qppid,
313         Qrandom,
314         Qreboot,
315         Qsysname,
316         Qsysstat,
317         Qtime,
318         Quser,
319         Qzero,
320         Qmordor,
321         Qconfig,
322 };
323
324 enum
325 {
326         VLNUMSIZE=      22,
327 };
328
329 static Dirtab consdir[]={
330         ".",    {Qdir, 0, QTDIR},       0,              DMDIR|0555,
331         "bintime",      {Qbintime},     24,             0664,
332         "cons",         {Qcons},        0,              0660,
333         "consctl",      {Qconsctl},     0,              0220,
334         "cputime",      {Qcputime},     6*NUMSIZE,      0444,
335         "drivers",      {Qdrivers},     0,              0444,
336         "hostdomain",   {Qhostdomain},  DOMLEN,         0664,
337         "hostowner",    {Qhostowner},   0,              0664,
338         "kmesg",        {Qkmesg},       0,              0440,
339         "kprint",       {Qkprint, 0, QTEXCL},   0,      DMEXCL|0440,
340         "null",         {Qnull},        0,              0666,
341         "osversion",    {Qosversion},   0,              0444,
342         "pid",          {Qpid},         NUMSIZE,        0444,
343         "ppid",         {Qppid},        NUMSIZE,        0444,
344         "random",       {Qrandom},      0,              0444,
345         "reboot",       {Qreboot},      0,              0220,
346         "sysname",      {Qsysname},     0,              0664,
347         "sysstat",      {Qsysstat},     0,              0664,
348         "time",         {Qtime},        NUMSIZE+3*VLNUMSIZE,    0664,
349         "user",         {Quser},        0,              0666,
350         "zero",         {Qzero},        0,              0444,
351         "config",       {Qconfig},      0,              0444,
352         "mordor",       {Qmordor},      0,              0666,
353 };
354
355 int
356 readnum(ulong off, char *buf, ulong n, ulong val, int size)
357 {
358         char tmp[64];
359
360         snprint(tmp, sizeof(tmp), "%*lud", size-1, val);
361         tmp[size-1] = ' ';
362         if(off >= size)
363                 return 0;
364         if(off+n > size)
365                 n = size-off;
366         memmove(buf, tmp+off, n);
367         return n;
368 }
369
370 int
371 readstr(ulong off, char *buf, ulong n, char *str)
372 {
373         int size;
374
375         size = strlen(str);
376         if(off >= size)
377                 return 0;
378         if(off+n > size)
379                 n = size-off;
380         memmove(buf, str+off, n);
381         return n;
382 }
383
384 static void
385 consinit(void)
386 {
387         todinit();
388         randominit();
389 }
390
391 static Chan*
392 consattach(char *spec)
393 {
394         return devattach('c', spec);
395 }
396
397 static Walkqid*
398 conswalk(Chan *c, Chan *nc, char **name, int nname)
399 {
400         return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
401 }
402
403 static int
404 consstat(Chan *c, uchar *dp, int n)
405 {
406         return devstat(c, dp, n, consdir, nelem(consdir), devgen);
407 }
408
409 static Chan*
410 consopen(Chan *c, int omode)
411 {
412         c->aux = nil;
413         c = devopen(c, omode, consdir, nelem(consdir), devgen);
414         switch((ulong)c->qid.path){
415         case Qkprint:
416                 if(tas(&kprintinuse) != 0){
417                         c->flag &= ~COPEN;
418                         error(Einuse);
419                 }
420                 if(kprintoq == nil){
421                         kprintoq = qopen(8*1024, Qcoalesce, 0, 0);
422                         if(kprintoq == nil){
423                                 c->flag &= ~COPEN;
424                                 error(Enomem);
425                         }
426                         qnoblock(kprintoq, 1);
427                 }else
428                         qreopen(kprintoq);
429                 c->iounit = qiomaxatomic;
430                 break;
431         }
432         return c;
433 }
434
435 static void
436 consclose(Chan *c)
437 {
438         switch((ulong)c->qid.path){
439         /* close of kprint allows other opens */
440         case Qkprint:
441                 if(c->flag & COPEN){
442                         kprintinuse = 0;
443                         qhangup(kprintoq, nil);
444                 }
445                 break;
446         }
447 }
448
449 static long
450 consread(Chan *c, void *buf, long n, vlong off)
451 {
452         ulong l;
453         Mach *mp;
454         char *b, *bp;
455         char tmp[256];
456         int i, k, id;
457         vlong offset = off;
458         extern char configfile[];
459
460         if(n <= 0)
461                 return n;
462
463         switch((ulong)c->qid.path){
464         case Qdir:
465                 return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
466
467         case Qcons:
468                 error(Egreg);
469
470         case Qcputime:
471                 k = offset;
472                 if(k >= 6*NUMSIZE)
473                         return 0;
474                 if(k+n > 6*NUMSIZE)
475                         n = 6*NUMSIZE - k;
476                 /* easiest to format in a separate buffer and copy out */
477                 for(i=0; i<6 && NUMSIZE*i<k+n; i++){
478                         l = up->time[i];
479                         if(i == TReal)
480                                 l = MACHP(0)->ticks - l;
481                         readnum(0, tmp+NUMSIZE*i, NUMSIZE, tk2ms(l), NUMSIZE);
482                 }
483                 memmove(buf, tmp+k, n);
484                 return n;
485
486         case Qkmesg:
487                 /*
488                  * This is unlocked to avoid tying up a process
489                  * that's writing to the buffer.  kmesg.n never 
490                  * gets smaller, so worst case the reader will
491                  * see a slurred buffer.
492                  */
493                 if(off >= kmesg.n)
494                         n = 0;
495                 else{
496                         if(off+n > kmesg.n)
497                                 n = kmesg.n - off;
498                         memmove(buf, kmesg.buf+off, n);
499                 }
500                 return n;
501                 
502         case Qkprint:
503                 return qread(kprintoq, buf, n);
504
505         case Qpid:
506                 return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
507
508         case Qppid:
509                 return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
510
511         case Qtime:
512                 return readtime((ulong)offset, buf, n);
513
514         case Qbintime:
515                 return readbintime(buf, n);
516
517         case Qhostowner:
518                 return readstr((ulong)offset, buf, n, eve);
519
520         case Qhostdomain:
521                 return readstr((ulong)offset, buf, n, hostdomain);
522
523         case Quser:
524                 return readstr((ulong)offset, buf, n, up->user);
525
526         case Qnull:
527                 return 0;
528
529         case Qconfig:
530                 return readstr((ulong)offset, buf, n, configfile);
531
532         case Qsysstat:
533                 b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1);     /* +1 for NUL */
534                 bp = b;
535                 for(id = 0; id < MAXMACH; id++) {
536                         if(active.machs[id]) {
537                                 mp = MACHP(id);
538                                 readnum(0, bp, NUMSIZE, id, NUMSIZE);
539                                 bp += NUMSIZE;
540                                 readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
541                                 bp += NUMSIZE;
542                                 readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
543                                 bp += NUMSIZE;
544                                 readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
545                                 bp += NUMSIZE;
546                                 readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
547                                 bp += NUMSIZE;
548                                 readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
549                                 bp += NUMSIZE;
550                                 readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
551                                 bp += NUMSIZE;
552                                 readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
553                                 bp += NUMSIZE;
554                                 l = mp->perf.period;
555                                 if(l == 0)
556                                         l = 1;
557                                 readnum(0, bp, NUMSIZE,
558                                         (mp->perf.avg_inidle*100)/l, NUMSIZE);
559                                 bp += NUMSIZE;
560                                 readnum(0, bp, NUMSIZE,
561                                         (mp->perf.avg_inintr*100)/l, NUMSIZE);
562                                 bp += NUMSIZE;
563                                 *bp++ = '\n';
564                         }
565                 }
566                 if(waserror()){
567                         free(b);
568                         nexterror();
569                 }
570                 n = readstr((ulong)offset, buf, n, b);
571                 free(b);
572                 poperror();
573                 return n;
574
575         case Qsysname:
576                 if(sysname == nil)
577                         return 0;
578                 return readstr((ulong)offset, buf, n, sysname);
579
580         case Qrandom:
581                 return randomread(buf, n);
582
583         case Qdrivers:
584                 b = smalloc(READSTR);
585                 k = 0;
586                 for(i = 0; devtab[i] != nil; i++)
587                         k += snprint(b+k, READSTR-k, "#%C %s\n",
588                                 devtab[i]->dc, devtab[i]->name);
589                 if(waserror()){
590                         free(b);
591                         nexterror();
592                 }
593                 n = readstr((ulong)offset, buf, n, b);
594                 poperror();
595                 free(b);
596                 return n;
597
598         case Qzero:
599                 memset(buf, 0, n);
600                 return n;
601         
602         case Qmordor:
603                 error("one does not simply read from mordor");
604                 return 0;
605
606         case Qosversion:
607                 snprint(tmp, sizeof tmp, "2000");
608                 n = readstr((ulong)offset, buf, n, tmp);
609                 return n;
610
611         default:
612                 print("consread %#llux\n", c->qid.path);
613                 error(Egreg);
614         }
615         return -1;              /* never reached */
616 }
617
618 static long
619 conswrite(Chan *c, void *va, long n, vlong off)
620 {
621         char buf[256];
622         long l, bp;
623         char *a;
624         Mach *mp;
625         int id;
626         ulong offset;
627         Cmdbuf *cb;
628         Cmdtab *ct;
629
630         a = va;
631         offset = off;
632
633         switch((ulong)c->qid.path){
634         case Qcons:
635                 /*
636                  * Can't page fault in putstrn, so copy the data locally.
637                  */
638                 l = n;
639                 while(l > 0){
640                         bp = l;
641                         if(bp > sizeof buf)
642                                 bp = sizeof buf;
643                         memmove(buf, a, bp);
644                         putstrn0(buf, bp, 1);
645                         a += bp;
646                         l -= bp;
647                 }
648                 break;
649
650         case Qconsctl:
651                 error(Egreg);
652
653         case Qtime:
654                 if(!iseve())
655                         error(Eperm);
656                 return writetime(a, n);
657
658         case Qbintime:
659                 if(!iseve())
660                         error(Eperm);
661                 return writebintime(a, n);
662
663         case Qhostowner:
664                 return hostownerwrite(a, n);
665
666         case Qhostdomain:
667                 return hostdomainwrite(a, n);
668
669         case Quser:
670                 return userwrite(a, n);
671
672         case Qnull:
673                 break;
674
675         case Qconfig:
676                 error(Eperm);
677                 break;
678
679         case Qreboot:
680                 if(!iseve())
681                         error(Eperm);
682                 cb = parsecmd(a, n);
683
684                 if(waserror()) {
685                         free(cb);
686                         nexterror();
687                 }
688                 ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
689                 switch(ct->index) {
690                 case CMreboot:
691                         rebootcmd(cb->nf-1, cb->f+1);
692                         break;
693                 case CMpanic:
694                         *(ulong*)0=0;
695                         panic("/dev/reboot");
696                 case CMrdb:
697                         if(consdebug == nil)
698                                 consdebug = rdb;
699                         consdebug();
700                         break;
701                 }
702                 poperror();
703                 free(cb);
704                 break;
705
706         case Qsysstat:
707                 for(id = 0; id < MAXMACH; id++) {
708                         if(active.machs[id]) {
709                                 mp = MACHP(id);
710                                 mp->cs = 0;
711                                 mp->intr = 0;
712                                 mp->syscall = 0;
713                                 mp->pfault = 0;
714                                 mp->tlbfault = 0;
715                                 mp->tlbpurge = 0;
716                         }
717                 }
718                 break;
719
720         case Qsysname:
721                 if(offset != 0)
722                         error(Ebadarg);
723                 if(n <= 0 || n >= sizeof buf)
724                         error(Ebadarg);
725                 strncpy(buf, a, n);
726                 buf[n] = 0;
727                 if(buf[n-1] == '\n')
728                         buf[n-1] = 0;
729                 kstrdup(&sysname, buf);
730                 break;
731         
732         case Qmordor:
733                 error("one does not simply write into mordor");
734                 return 0;
735
736         default:
737                 print("conswrite: %#llux\n", c->qid.path);
738                 error(Egreg);
739         }
740         return n;
741 }
742
743 Dev consdevtab = {
744         'c',
745         "cons",
746
747         devreset,
748         consinit,
749         devshutdown,
750         consattach,
751         conswalk,
752         consstat,
753         consopen,
754         devcreate,
755         consclose,
756         consread,
757         devbread,
758         conswrite,
759         devbwrite,
760         devremove,
761         devwstat,
762 };
763
764 static uvlong uvorder = 0x0001020304050607ULL;
765
766 static uchar*
767 le2vlong(vlong *to, uchar *f)
768 {
769         uchar *t, *o;
770         int i;
771
772         t = (uchar*)to;
773         o = (uchar*)&uvorder;
774         for(i = 0; i < sizeof(vlong); i++)
775                 t[o[i]] = f[i];
776         return f+sizeof(vlong);
777 }
778
779 static uchar*
780 vlong2le(uchar *t, vlong from)
781 {
782         uchar *f, *o;
783         int i;
784
785         f = (uchar*)&from;
786         o = (uchar*)&uvorder;
787         for(i = 0; i < sizeof(vlong); i++)
788                 t[i] = f[o[i]];
789         return t+sizeof(vlong);
790 }
791
792 static long order = 0x00010203;
793
794 static uchar*
795 le2long(long *to, uchar *f)
796 {
797         uchar *t, *o;
798         int i;
799
800         t = (uchar*)to;
801         o = (uchar*)&order;
802         for(i = 0; i < sizeof(long); i++)
803                 t[o[i]] = f[i];
804         return f+sizeof(long);
805 }
806
807 static uchar*
808 long2le(uchar *t, long from)
809 {
810         uchar *f, *o;
811         int i;
812
813         f = (uchar*)&from;
814         o = (uchar*)&order;
815         for(i = 0; i < sizeof(long); i++)
816                 t[i] = f[o[i]];
817         return t+sizeof(long);
818 }
819
820 char *Ebadtimectl = "bad time control";
821
822 /*
823  *  like the old #c/time but with added info.  Return
824  *
825  *      secs    nanosecs        fastticks       fasthz
826  */
827 static int
828 readtime(ulong off, char *buf, int n)
829 {
830         vlong   nsec, ticks;
831         long sec;
832         char str[7*NUMSIZE];
833
834         nsec = todget(&ticks);
835         if(fasthz == 0LL)
836                 fastticks((uvlong*)&fasthz);
837         sec = nsec/1000000000ULL;
838         snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
839                 NUMSIZE-1, sec,
840                 VLNUMSIZE-1, nsec,
841                 VLNUMSIZE-1, ticks,
842                 VLNUMSIZE-1, fasthz);
843         return readstr(off, buf, n, str);
844 }
845
846 /*
847  *  set the time in seconds
848  */
849 static int
850 writetime(char *buf, int n)
851 {
852         char b[13];
853         long i;
854         vlong now;
855
856         if(n >= sizeof(b))
857                 error(Ebadtimectl);
858         strncpy(b, buf, n);
859         b[n] = 0;
860         i = strtol(b, 0, 0);
861         if(i <= 0)
862                 error(Ebadtimectl);
863         now = i*1000000000LL;
864         todset(now, 0, 0);
865         return n;
866 }
867
868 /*
869  *  read binary time info.  all numbers are little endian.
870  *  ticks and nsec are syncronized.
871  */
872 static int
873 readbintime(char *buf, int n)
874 {
875         int i;
876         vlong nsec, ticks;
877         uchar *b = (uchar*)buf;
878
879         i = 0;
880         if(fasthz == 0LL)
881                 fastticks((uvlong*)&fasthz);
882         nsec = todget(&ticks);
883         if(n >= 3*sizeof(uvlong)){
884                 vlong2le(b+2*sizeof(uvlong), fasthz);
885                 i += sizeof(uvlong);
886         }
887         if(n >= 2*sizeof(uvlong)){
888                 vlong2le(b+sizeof(uvlong), ticks);
889                 i += sizeof(uvlong);
890         }
891         if(n >= 8){
892                 vlong2le(b, nsec);
893                 i += sizeof(vlong);
894         }
895         return i;
896 }
897
898 /*
899  *  set any of the following
900  *      - time in nsec
901  *      - nsec trim applied over some seconds
902  *      - clock frequency
903  */
904 static int
905 writebintime(char *buf, int n)
906 {
907         uchar *p;
908         vlong delta;
909         long period;
910
911         if(--n <= 0)
912                 error(Ebadtimectl);
913         p = (uchar*)buf + 1;
914         switch(*buf){
915         case 'n':
916                 if(n < sizeof(vlong))
917                         error(Ebadtimectl);
918                 le2vlong(&delta, p);
919                 todset(delta, 0, 0);
920                 break;
921         case 'd':
922                 if(n < sizeof(vlong)+sizeof(long))
923                         error(Ebadtimectl);
924                 p = le2vlong(&delta, p);
925                 le2long(&period, p);
926                 todset(-1, delta, period);
927                 break;
928         case 'f':
929                 if(n < sizeof(uvlong))
930                         error(Ebadtimectl);
931                 le2vlong(&fasthz, p);
932                 if(fasthz <= 0)
933                         error(Ebadtimectl);
934                 todsetfreq(fasthz);
935                 break;
936         }
937         return n+1;
938 }
939
940 void
941 cpushutdown(void)
942 {
943         int ms, once;
944
945         once = active.machs[m->machno];
946         active.machs[m->machno] = 0;
947         active.exiting = 1;
948
949         if(once)
950                 iprint("cpu%d: exiting\n", m->machno);
951
952         /* wait for any other processors to shutdown */
953         spllo();
954         for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
955                 delay(TK2MS(2));
956                 if(memchr(active.machs, 1, MAXMACH) == nil && consactive() == 0)
957                         break;
958         }
959 }