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