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