xref: /plan9/sys/src/9/port/devcons.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
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*	kbdq;			/* unprocessed console input */
15 Queue*	lineq;			/* processed console input */
16 Queue*	serialoq;		/* serial console output */
17 Queue*	kprintoq;		/* console output, for /dev/kprint */
18 ulong	kprintinuse;		/* test and set whether /dev/kprint is open */
19 int	iprintscreenputs = 1;
20 
21 int	panicking;
22 
23 static struct
24 {
25 	QLock;
26 
27 	int	raw;		/* true if we shouldn't process input */
28 	Ref	ctl;		/* number of opens to the control file */
29 	int	x;		/* index into line */
30 	char	line[1024];	/* current input line */
31 
32 	int	count;
33 	int	ctlpoff;
34 
35 	/* a place to save up characters at interrupt time before dumping them in the queue */
36 	Lock	lockputc;
37 	char	istage[1024];
38 	char	*iw;
39 	char	*ir;
40 	char	*ie;
41 } kbd = {
42 	.iw	= kbd.istage,
43 	.ir	= kbd.istage,
44 	.ie	= kbd.istage + sizeof(kbd.istage),
45 };
46 
47 char	*sysname;
48 vlong	fasthz;
49 
50 static void	seedrand(void);
51 static int	readtime(ulong, char*, int);
52 static int	readbintime(char*, int);
53 static int	writetime(char*, int);
54 static int	writebintime(char*, int);
55 
56 enum
57 {
58 	CMhalt,
59 	CMreboot,
60 	CMpanic,
61 };
62 
63 Cmdtab rebootmsg[] =
64 {
65 	CMhalt,		"halt",		1,
66 	CMreboot,	"reboot",	0,
67 	CMpanic,	"panic",	0,
68 };
69 
70 void
71 printinit(void)
72 {
73 	lineq = qopen(2*1024, 0, nil, nil);
74 	if(lineq == nil)
75 		panic("printinit");
76 	qnoblock(lineq, 1);
77 }
78 
79 int
80 consactive(void)
81 {
82 	if(serialoq)
83 		return qlen(serialoq) > 0;
84 	return 0;
85 }
86 
87 void
88 prflush(void)
89 {
90 	ulong now;
91 
92 	now = m->ticks;
93 	while(consactive())
94 		if(m->ticks - now >= HZ)
95 			break;
96 }
97 
98 /*
99  * Log console output so it can be retrieved via /dev/kmesg.
100  * This is good for catching boot-time messages after the fact.
101  */
102 struct {
103 	Lock lk;
104 //	char buf[16384];		/* normal */
105 	char buf[256*1024];		/* for acpi debugging */
106 	uint n;
107 } kmesg;
108 
109 static void
110 kmesgputs(char *str, int n)
111 {
112 	uint nn, d;
113 
114 	ilock(&kmesg.lk);
115 	/* take the tail of huge writes */
116 	if(n > sizeof kmesg.buf){
117 		d = n - sizeof kmesg.buf;
118 		str += d;
119 		n -= d;
120 	}
121 
122 	/* slide the buffer down to make room */
123 	nn = kmesg.n;
124 	if(nn + n >= sizeof kmesg.buf){
125 		d = nn + n - sizeof kmesg.buf;
126 		if(d)
127 			memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d);
128 		nn -= d;
129 	}
130 
131 	/* copy the data in */
132 	memmove(kmesg.buf+nn, str, n);
133 	nn += n;
134 	kmesg.n = nn;
135 	iunlock(&kmesg.lk);
136 }
137 
138 /*
139  *   Print a string on the console.  Convert \n to \r\n for serial
140  *   line consoles.  Locking of the queues is left up to the screen
141  *   or uart code.  Multi-line messages to serial consoles may get
142  *   interspersed with other messages.
143  */
144 static void
145 putstrn0(char *str, int n, int usewrite)
146 {
147 	int m;
148 	char *t;
149 
150 	if(!islo())
151 		usewrite = 0;
152 
153 	/*
154 	 *  how many different output devices do we need?
155 	 */
156 	kmesgputs(str, n);
157 
158 	/*
159 	 *  if someone is reading /dev/kprint,
160 	 *  put the message there.
161 	 *  if not and there's an attached bit mapped display,
162 	 *  put the message there.
163 	 *
164 	 *  if there's a serial line being used as a console,
165 	 *  put the message there.
166 	 */
167 	if(kprintoq != nil && !qisclosed(kprintoq)){
168 		if(usewrite)
169 			qwrite(kprintoq, str, n);
170 		else
171 			qiwrite(kprintoq, str, n);
172 	}else if(screenputs != nil)
173 		screenputs(str, n);
174 
175 	if(serialoq == nil){
176 		uartputs(str, n);
177 		return;
178 	}
179 
180 	while(n > 0) {
181 		t = memchr(str, '\n', n);
182 		if(t && !kbd.raw) {
183 			m = t-str;
184 			if(usewrite){
185 				qwrite(serialoq, str, m);
186 				qwrite(serialoq, "\r\n", 2);
187 			} else {
188 				qiwrite(serialoq, str, m);
189 				qiwrite(serialoq, "\r\n", 2);
190 			}
191 			n -= m+1;
192 			str = t+1;
193 		} else {
194 			if(usewrite)
195 				qwrite(serialoq, str, n);
196 			else
197 				qiwrite(serialoq, str, n);
198 			break;
199 		}
200 	}
201 }
202 
203 void
204 putstrn(char *str, int n)
205 {
206 	putstrn0(str, n, 0);
207 }
208 
209 int noprint;
210 
211 int
212 print(char *fmt, ...)
213 {
214 	int n;
215 	va_list arg;
216 	char buf[PRINTSIZE];
217 
218 	if(noprint)
219 		return -1;
220 
221 	va_start(arg, fmt);
222 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
223 	va_end(arg);
224 	putstrn(buf, n);
225 
226 	return n;
227 }
228 
229 /*
230  * Want to interlock iprints to avoid interlaced output on
231  * multiprocessor, but don't want to deadlock if one processor
232  * dies during print and another has something important to say.
233  * Make a good faith effort.
234  */
235 static Lock iprintlock;
236 static int
237 iprintcanlock(Lock *l)
238 {
239 	int i;
240 
241 	for(i=0; i<1000; i++){
242 		if(canlock(l))
243 			return 1;
244 		if(l->m == MACHP(m->machno))
245 			return 0;
246 		microdelay(100);
247 	}
248 	return 0;
249 }
250 
251 int
252 iprint(char *fmt, ...)
253 {
254 	int n, s, locked;
255 	va_list arg;
256 	char buf[PRINTSIZE];
257 
258 	s = splhi();
259 	va_start(arg, fmt);
260 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
261 	va_end(arg);
262 	locked = iprintcanlock(&iprintlock);
263 	if(screenputs != nil && iprintscreenputs)
264 		screenputs(buf, n);
265 	uartputs(buf, n);
266 	if(locked)
267 		unlock(&iprintlock);
268 	splx(s);
269 
270 	return n;
271 }
272 
273 void
274 panic(char *fmt, ...)
275 {
276 	int n, s;
277 	va_list arg;
278 	char buf[PRINTSIZE];
279 
280 	kprintoq = nil;	/* don't try to write to /dev/kprint */
281 
282 	if(panicking)
283 		for(;;);
284 	panicking = 1;
285 
286 	s = splhi();
287 	strcpy(buf, "panic: ");
288 	va_start(arg, fmt);
289 	n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf;
290 	va_end(arg);
291 	iprint("%s\n", buf);
292 	if(consdebug)
293 		(*consdebug)();
294 	splx(s);
295 	prflush();
296 	buf[n] = '\n';
297 	putstrn(buf, n+1);
298 	dumpstack();
299 
300 	exit(1);
301 }
302 
303 /* libmp at least contains a few calls to sysfatal; simulate with panic */
304 void
305 sysfatal(char *fmt, ...)
306 {
307 	char err[256];
308 	va_list arg;
309 
310 	va_start(arg, fmt);
311 	vseprint(err, err + sizeof err, fmt, arg);
312 	va_end(arg);
313 	panic("sysfatal: %s", err);
314 }
315 
316 void
317 _assert(char *fmt)
318 {
319 	panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt);
320 }
321 
322 int
323 pprint(char *fmt, ...)
324 {
325 	int n;
326 	Chan *c;
327 	va_list arg;
328 	char buf[2*PRINTSIZE];
329 
330 	if(up == nil || up->fgrp == nil)
331 		return 0;
332 
333 	c = up->fgrp->fd[2];
334 	if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR))
335 		return 0;
336 	n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid);
337 	va_start(arg, fmt);
338 	n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
339 	va_end(arg);
340 
341 	if(waserror())
342 		return 0;
343 	devtab[c->type]->write(c, buf, n, c->offset);
344 	poperror();
345 
346 	lock(c);
347 	c->offset += n;
348 	unlock(c);
349 
350 	return n;
351 }
352 
353 static void
354 echoscreen(char *buf, int n)
355 {
356 	char *e, *p;
357 	char ebuf[128];
358 	int x;
359 
360 	p = ebuf;
361 	e = ebuf + sizeof(ebuf) - 4;
362 	while(n-- > 0){
363 		if(p >= e){
364 			screenputs(ebuf, p - ebuf);
365 			p = ebuf;
366 		}
367 		x = *buf++;
368 		if(x == 0x15){
369 			*p++ = '^';
370 			*p++ = 'U';
371 			*p++ = '\n';
372 		} else
373 			*p++ = x;
374 	}
375 	if(p != ebuf)
376 		screenputs(ebuf, p - ebuf);
377 }
378 
379 static void
380 echoserialoq(char *buf, int n)
381 {
382 	char *e, *p;
383 	char ebuf[128];
384 	int x;
385 
386 	p = ebuf;
387 	e = ebuf + sizeof(ebuf) - 4;
388 	while(n-- > 0){
389 		if(p >= e){
390 			qiwrite(serialoq, ebuf, p - ebuf);
391 			p = ebuf;
392 		}
393 		x = *buf++;
394 		if(x == '\n'){
395 			*p++ = '\r';
396 			*p++ = '\n';
397 		} else if(x == 0x15){
398 			*p++ = '^';
399 			*p++ = 'U';
400 			*p++ = '\n';
401 		} else
402 			*p++ = x;
403 	}
404 	if(p != ebuf)
405 		qiwrite(serialoq, ebuf, p - ebuf);
406 }
407 
408 static void
409 echo(char *buf, int n)
410 {
411 	static int ctrlt, pid;
412 	int x;
413 	char *e, *p;
414 
415 	if(n == 0)
416 		return;
417 
418 	e = buf+n;
419 	for(p = buf; p < e; p++){
420 		switch(*p){
421 		case 0x10:	/* ^P */
422 			if(cpuserver && !kbd.ctlpoff){
423 				active.exiting = 1;
424 				return;
425 			}
426 			break;
427 		case 0x14:	/* ^T */
428 			ctrlt++;
429 			if(ctrlt > 2)
430 				ctrlt = 2;
431 			continue;
432 		}
433 
434 		if(ctrlt != 2)
435 			continue;
436 
437 		/* ^T escapes */
438 		ctrlt = 0;
439 		switch(*p){
440 		case 'S':
441 			x = splhi();
442 			dumpstack();
443 			procdump();
444 			splx(x);
445 			return;
446 		case 's':
447 			dumpstack();
448 			return;
449 		case 'x':
450 			xsummary();
451 			ixsummary();
452 			mallocsummary();
453 		//	memorysummary();
454 			pagersummary();
455 			return;
456 		case 'd':
457 			if(consdebug == nil)
458 				consdebug = rdb;
459 			else
460 				consdebug = nil;
461 			print("consdebug now %#p\n", consdebug);
462 			return;
463 		case 'D':
464 			if(consdebug == nil)
465 				consdebug = rdb;
466 			consdebug();
467 			return;
468 		case 'p':
469 			x = spllo();
470 			procdump();
471 			splx(x);
472 			return;
473 		case 'q':
474 			scheddump();
475 			return;
476 		case 'k':
477 			killbig("^t ^t k");
478 			return;
479 		case 'r':
480 			exit(0);
481 			return;
482 		}
483 	}
484 
485 	qproduce(kbdq, buf, n);
486 	if(kbd.raw)
487 		return;
488 	kmesgputs(buf, n);
489 	if(screenputs != nil)
490 		echoscreen(buf, n);
491 	if(serialoq)
492 		echoserialoq(buf, n);
493 }
494 
495 /*
496  *  Called by a uart interrupt for console input.
497  *
498  *  turn '\r' into '\n' before putting it into the queue.
499  */
500 int
501 kbdcr2nl(Queue*, int ch)
502 {
503 	char *next;
504 
505 	ilock(&kbd.lockputc);		/* just a mutex */
506 	if(ch == '\r' && !kbd.raw)
507 		ch = '\n';
508 	next = kbd.iw+1;
509 	if(next >= kbd.ie)
510 		next = kbd.istage;
511 	if(next != kbd.ir){
512 		*kbd.iw = ch;
513 		kbd.iw = next;
514 	}
515 	iunlock(&kbd.lockputc);
516 	return 0;
517 }
518 
519 /*
520  *  Put character, possibly a rune, into read queue at interrupt time.
521  *  Called at interrupt time to process a character.
522  */
523 int
524 kbdputc(Queue*, int ch)
525 {
526 	int i, n;
527 	char buf[3];
528 	Rune r;
529 	char *next;
530 
531 	if(kbd.ir == nil)
532 		return 0;		/* in case we're not inited yet */
533 
534 	ilock(&kbd.lockputc);		/* just a mutex */
535 	r = ch;
536 	n = runetochar(buf, &r);
537 	for(i = 0; i < n; i++){
538 		next = kbd.iw+1;
539 		if(next >= kbd.ie)
540 			next = kbd.istage;
541 		if(next == kbd.ir)
542 			break;
543 		*kbd.iw = buf[i];
544 		kbd.iw = next;
545 	}
546 	iunlock(&kbd.lockputc);
547 	return 0;
548 }
549 
550 /*
551  *  we save up input characters till clock time to reduce
552  *  per character interrupt overhead.
553  */
554 static void
555 kbdputcclock(void)
556 {
557 	char *iw;
558 
559 	/* this amortizes cost of qproduce */
560 	if(kbd.iw != kbd.ir){
561 		iw = kbd.iw;
562 		if(iw < kbd.ir){
563 			echo(kbd.ir, kbd.ie-kbd.ir);
564 			kbd.ir = kbd.istage;
565 		}
566 		if(kbd.ir != iw){
567 			echo(kbd.ir, iw-kbd.ir);
568 			kbd.ir = iw;
569 		}
570 	}
571 }
572 
573 enum{
574 	Qdir,
575 	Qbintime,
576 	Qcons,
577 	Qconsctl,
578 	Qcputime,
579 	Qdrivers,
580 	Qkmesg,
581 	Qkprint,
582 	Qhostdomain,
583 	Qhostowner,
584 	Qnull,
585 	Qosversion,
586 	Qpgrpid,
587 	Qpid,
588 	Qppid,
589 	Qrandom,
590 	Qreboot,
591 	Qswap,
592 	Qsysname,
593 	Qsysstat,
594 	Qtime,
595 	Quser,
596 	Qzero,
597 };
598 
599 enum
600 {
601 	VLNUMSIZE=	22,
602 };
603 
604 static Dirtab consdir[]={
605 	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
606 	"bintime",	{Qbintime},	24,		0664,
607 	"cons",		{Qcons},	0,		0660,
608 	"consctl",	{Qconsctl},	0,		0220,
609 	"cputime",	{Qcputime},	6*NUMSIZE,	0444,
610 	"drivers",	{Qdrivers},	0,		0444,
611 	"hostdomain",	{Qhostdomain},	DOMLEN,		0664,
612 	"hostowner",	{Qhostowner},	0,		0664,
613 	"kmesg",	{Qkmesg},	0,		0440,
614 	"kprint",	{Qkprint, 0, QTEXCL},	0,	DMEXCL|0440,
615 	"null",		{Qnull},	0,		0666,
616 	"osversion",	{Qosversion},	0,		0444,
617 	"pgrpid",	{Qpgrpid},	NUMSIZE,	0444,
618 	"pid",		{Qpid},		NUMSIZE,	0444,
619 	"ppid",		{Qppid},	NUMSIZE,	0444,
620 	"random",	{Qrandom},	0,		0444,
621 	"reboot",	{Qreboot},	0,		0664,
622 	"swap",		{Qswap},	0,		0664,
623 	"sysname",	{Qsysname},	0,		0664,
624 	"sysstat",	{Qsysstat},	0,		0666,
625 	"time",		{Qtime},	NUMSIZE+3*VLNUMSIZE,	0664,
626 	"user",		{Quser},	0,		0666,
627 	"zero",		{Qzero},	0,		0444,
628 };
629 
630 int
631 readnum(ulong off, char *buf, ulong n, ulong val, int size)
632 {
633 	char tmp[64];
634 
635 	snprint(tmp, sizeof(tmp), "%*lud", size-1, val);
636 	tmp[size-1] = ' ';
637 	if(off >= size)
638 		return 0;
639 	if(off+n > size)
640 		n = size-off;
641 	memmove(buf, tmp+off, n);
642 	return n;
643 }
644 
645 int
646 readstr(ulong off, char *buf, ulong n, char *str)
647 {
648 	int size;
649 
650 	size = strlen(str);
651 	if(off >= size)
652 		return 0;
653 	if(off+n > size)
654 		n = size-off;
655 	memmove(buf, str+off, n);
656 	return n;
657 }
658 
659 static void
660 consinit(void)
661 {
662 	todinit();
663 	randominit();
664 	/*
665 	 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
666 	 * processing it every 22 ms should be fine
667 	 */
668 	addclock0link(kbdputcclock, 22);
669 }
670 
671 static Chan*
672 consattach(char *spec)
673 {
674 	return devattach('c', spec);
675 }
676 
677 static Walkqid*
678 conswalk(Chan *c, Chan *nc, char **name, int nname)
679 {
680 	return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
681 }
682 
683 static int
684 consstat(Chan *c, uchar *dp, int n)
685 {
686 	return devstat(c, dp, n, consdir, nelem(consdir), devgen);
687 }
688 
689 static Chan*
690 consopen(Chan *c, int omode)
691 {
692 	c->aux = nil;
693 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
694 	switch((ulong)c->qid.path){
695 	case Qconsctl:
696 		incref(&kbd.ctl);
697 		break;
698 
699 	case Qkprint:
700 		if(tas(&kprintinuse) != 0){
701 			c->flag &= ~COPEN;
702 			error(Einuse);
703 		}
704 		if(kprintoq == nil){
705 			kprintoq = qopen(8*1024, Qcoalesce, 0, 0);
706 			if(kprintoq == nil){
707 				c->flag &= ~COPEN;
708 				error(Enomem);
709 			}
710 			qnoblock(kprintoq, 1);
711 		}else
712 			qreopen(kprintoq);
713 		c->iounit = qiomaxatomic;
714 		break;
715 	}
716 	return c;
717 }
718 
719 static void
720 consclose(Chan *c)
721 {
722 	switch((ulong)c->qid.path){
723 	/* last close of control file turns off raw */
724 	case Qconsctl:
725 		if(c->flag&COPEN){
726 			if(decref(&kbd.ctl) == 0)
727 				kbd.raw = 0;
728 		}
729 		break;
730 
731 	/* close of kprint allows other opens */
732 	case Qkprint:
733 		if(c->flag & COPEN){
734 			kprintinuse = 0;
735 			qhangup(kprintoq, nil);
736 		}
737 		break;
738 	}
739 }
740 
741 static long
742 consread(Chan *c, void *buf, long n, vlong off)
743 {
744 	ulong l;
745 	Mach *mp;
746 	char *b, *bp, ch;
747 	char tmp[256];		/* must be >= 18*NUMSIZE (Qswap) */
748 	int i, k, id, send;
749 	vlong offset = off;
750 
751 	if(n <= 0)
752 		return n;
753 
754 	switch((ulong)c->qid.path){
755 	case Qdir:
756 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
757 
758 	case Qcons:
759 		qlock(&kbd);
760 		if(waserror()) {
761 			qunlock(&kbd);
762 			nexterror();
763 		}
764 		while(!qcanread(lineq)){
765 			if(qread(kbdq, &ch, 1) == 0)
766 				continue;
767 			send = 0;
768 			if(ch == 0){
769 				/* flush output on rawoff -> rawon */
770 				if(kbd.x > 0)
771 					send = !qcanread(kbdq);
772 			}else if(kbd.raw){
773 				kbd.line[kbd.x++] = ch;
774 				send = !qcanread(kbdq);
775 			}else{
776 				switch(ch){
777 				case '\b':
778 					if(kbd.x > 0)
779 						kbd.x--;
780 					break;
781 				case 0x15:	/* ^U */
782 					kbd.x = 0;
783 					break;
784 				case '\n':
785 				case 0x04:	/* ^D */
786 					send = 1;
787 				default:
788 					if(ch != 0x04)
789 						kbd.line[kbd.x++] = ch;
790 					break;
791 				}
792 			}
793 			if(send || kbd.x == sizeof kbd.line){
794 				qwrite(lineq, kbd.line, kbd.x);
795 				kbd.x = 0;
796 			}
797 		}
798 		n = qread(lineq, buf, n);
799 		qunlock(&kbd);
800 		poperror();
801 		return n;
802 
803 	case Qcputime:
804 		k = offset;
805 		if(k >= 6*NUMSIZE)
806 			return 0;
807 		if(k+n > 6*NUMSIZE)
808 			n = 6*NUMSIZE - k;
809 		/* easiest to format in a separate buffer and copy out */
810 		for(i=0; i<6 && NUMSIZE*i<k+n; i++){
811 			l = up->time[i];
812 			if(i == TReal)
813 				l = MACHP(0)->ticks - l;
814 			l = TK2MS(l);
815 			readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
816 		}
817 		memmove(buf, tmp+k, n);
818 		return n;
819 
820 	case Qkmesg:
821 		/*
822 		 * This is unlocked to avoid tying up a process
823 		 * that's writing to the buffer.  kmesg.n never
824 		 * gets smaller, so worst case the reader will
825 		 * see a slurred buffer.
826 		 */
827 		if(off >= kmesg.n)
828 			n = 0;
829 		else{
830 			if(off+n > kmesg.n)
831 				n = kmesg.n - off;
832 			memmove(buf, kmesg.buf+off, n);
833 		}
834 		return n;
835 
836 	case Qkprint:
837 		return qread(kprintoq, buf, n);
838 
839 	case Qpgrpid:
840 		return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE);
841 
842 	case Qpid:
843 		return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
844 
845 	case Qppid:
846 		return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
847 
848 	case Qtime:
849 		return readtime((ulong)offset, buf, n);
850 
851 	case Qbintime:
852 		return readbintime(buf, n);
853 
854 	case Qhostowner:
855 		return readstr((ulong)offset, buf, n, eve);
856 
857 	case Qhostdomain:
858 		return readstr((ulong)offset, buf, n, hostdomain);
859 
860 	case Quser:
861 		return readstr((ulong)offset, buf, n, up->user);
862 
863 	case Qnull:
864 		return 0;
865 
866 	case Qsysstat:
867 		b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1);	/* +1 for NUL */
868 		bp = b;
869 		for(id = 0; id < 32; id++) {
870 			if(active.machs & (1<<id)) {
871 				mp = MACHP(id);
872 				readnum(0, bp, NUMSIZE, id, NUMSIZE);
873 				bp += NUMSIZE;
874 				readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
875 				bp += NUMSIZE;
876 				readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
877 				bp += NUMSIZE;
878 				readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
879 				bp += NUMSIZE;
880 				readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
881 				bp += NUMSIZE;
882 				readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
883 				bp += NUMSIZE;
884 				readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
885 				bp += NUMSIZE;
886 				readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
887 				bp += NUMSIZE;
888 				readnum(0, bp, NUMSIZE,
889 					(mp->perf.avg_inidle*100)/mp->perf.period,
890 					NUMSIZE);
891 				bp += NUMSIZE;
892 				readnum(0, bp, NUMSIZE,
893 					(mp->perf.avg_inintr*100)/mp->perf.period,
894 					NUMSIZE);
895 				bp += NUMSIZE;
896 				*bp++ = '\n';
897 			}
898 		}
899 		if(waserror()){
900 			free(b);
901 			nexterror();
902 		}
903 		n = readstr((ulong)offset, buf, n, b);
904 		free(b);
905 		poperror();
906 		return n;
907 
908 	case Qswap:
909 		snprint(tmp, sizeof tmp,
910 			"%lud memory\n"
911 			"%d pagesize\n"
912 			"%lud kernel\n"
913 			"%lud/%lud user\n"
914 			"%lud/%lud swap\n"
915 			"%lud/%lud kernel malloc\n"
916 			"%lud/%lud kernel draw\n",
917 			conf.npage*BY2PG,
918 			BY2PG,
919 			conf.npage-conf.upages,
920 			palloc.user-palloc.freecount, palloc.user,
921 			conf.nswap-swapalloc.free, conf.nswap,
922 			mainmem->cursize, mainmem->maxsize,
923 			imagmem->cursize, imagmem->maxsize);
924 
925 		return readstr((ulong)offset, buf, n, tmp);
926 
927 	case Qsysname:
928 		if(sysname == nil)
929 			return 0;
930 		return readstr((ulong)offset, buf, n, sysname);
931 
932 	case Qrandom:
933 		return randomread(buf, n);
934 
935 	case Qdrivers:
936 		b = malloc(READSTR);
937 		if(b == nil)
938 			error(Enomem);
939 		n = 0;
940 		for(i = 0; devtab[i] != nil; i++)
941 			n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc,  devtab[i]->name);
942 		if(waserror()){
943 			free(b);
944 			nexterror();
945 		}
946 		n = readstr((ulong)offset, buf, n, b);
947 		free(b);
948 		poperror();
949 		return n;
950 
951 	case Qzero:
952 		memset(buf, 0, n);
953 		return n;
954 
955 	case Qosversion:
956 		snprint(tmp, sizeof tmp, "2000");
957 		n = readstr((ulong)offset, buf, n, tmp);
958 		return n;
959 
960 	default:
961 		print("consread %#llux\n", c->qid.path);
962 		error(Egreg);
963 	}
964 	return -1;		/* never reached */
965 }
966 
967 static long
968 conswrite(Chan *c, void *va, long n, vlong off)
969 {
970 	char buf[256], ch;
971 	long l, bp;
972 	char *a;
973 	Mach *mp;
974 	int id, fd;
975 	Chan *swc;
976 	ulong offset;
977 	Cmdbuf *cb;
978 	Cmdtab *ct;
979 
980 	a = va;
981 	offset = off;
982 
983 	switch((ulong)c->qid.path){
984 	case Qcons:
985 		/*
986 		 * Can't page fault in putstrn, so copy the data locally.
987 		 */
988 		l = n;
989 		while(l > 0){
990 			bp = l;
991 			if(bp > sizeof buf)
992 				bp = sizeof buf;
993 			memmove(buf, a, bp);
994 			putstrn0(buf, bp, 1);
995 			a += bp;
996 			l -= bp;
997 		}
998 		break;
999 
1000 	case Qconsctl:
1001 		if(n >= sizeof(buf))
1002 			n = sizeof(buf)-1;
1003 		strncpy(buf, a, n);
1004 		buf[n] = 0;
1005 		for(a = buf; a;){
1006 			if(strncmp(a, "rawon", 5) == 0){
1007 				kbd.raw = 1;
1008 				/* clumsy hack - wake up reader */
1009 				ch = 0;
1010 				qwrite(kbdq, &ch, 1);
1011 			} else if(strncmp(a, "rawoff", 6) == 0){
1012 				kbd.raw = 0;
1013 			} else if(strncmp(a, "ctlpon", 6) == 0){
1014 				kbd.ctlpoff = 0;
1015 			} else if(strncmp(a, "ctlpoff", 7) == 0){
1016 				kbd.ctlpoff = 1;
1017 			}
1018 			if(a = strchr(a, ' '))
1019 				a++;
1020 		}
1021 		break;
1022 
1023 	case Qtime:
1024 		if(!iseve())
1025 			error(Eperm);
1026 		return writetime(a, n);
1027 
1028 	case Qbintime:
1029 		if(!iseve())
1030 			error(Eperm);
1031 		return writebintime(a, n);
1032 
1033 	case Qhostowner:
1034 		return hostownerwrite(a, n);
1035 
1036 	case Qhostdomain:
1037 		return hostdomainwrite(a, n);
1038 
1039 	case Quser:
1040 		return userwrite(a, n);
1041 
1042 	case Qnull:
1043 		break;
1044 
1045 	case Qreboot:
1046 		if(!iseve())
1047 			error(Eperm);
1048 		cb = parsecmd(a, n);
1049 
1050 		if(waserror()) {
1051 			free(cb);
1052 			nexterror();
1053 		}
1054 		ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
1055 		switch(ct->index) {
1056 		case CMhalt:
1057 			reboot(nil, 0, 0);
1058 			break;
1059 		case CMreboot:
1060 			rebootcmd(cb->nf-1, cb->f+1);
1061 			break;
1062 		case CMpanic:
1063 			*(ulong*)0=0;
1064 			panic("/dev/reboot");
1065 		}
1066 		poperror();
1067 		free(cb);
1068 		break;
1069 
1070 	case Qsysstat:
1071 		for(id = 0; id < 32; id++) {
1072 			if(active.machs & (1<<id)) {
1073 				mp = MACHP(id);
1074 				mp->cs = 0;
1075 				mp->intr = 0;
1076 				mp->syscall = 0;
1077 				mp->pfault = 0;
1078 				mp->tlbfault = 0;
1079 				mp->tlbpurge = 0;
1080 			}
1081 		}
1082 		break;
1083 
1084 	case Qswap:
1085 		if(n >= sizeof buf)
1086 			error(Egreg);
1087 		memmove(buf, va, n);	/* so we can NUL-terminate */
1088 		buf[n] = 0;
1089 		/* start a pager if not already started */
1090 		if(strncmp(buf, "start", 5) == 0){
1091 			kickpager();
1092 			break;
1093 		}
1094 		if(!iseve())
1095 			error(Eperm);
1096 		if(buf[0]<'0' || '9'<buf[0])
1097 			error(Ebadarg);
1098 		fd = strtoul(buf, 0, 0);
1099 		swc = fdtochan(fd, -1, 1, 1);
1100 		setswapchan(swc);
1101 		break;
1102 
1103 	case Qsysname:
1104 		if(offset != 0)
1105 			error(Ebadarg);
1106 		if(n <= 0 || n >= sizeof buf)
1107 			error(Ebadarg);
1108 		strncpy(buf, a, n);
1109 		buf[n] = 0;
1110 		if(buf[n-1] == '\n')
1111 			buf[n-1] = 0;
1112 		kstrdup(&sysname, buf);
1113 		break;
1114 
1115 	default:
1116 		print("conswrite: %#llux\n", c->qid.path);
1117 		error(Egreg);
1118 	}
1119 	return n;
1120 }
1121 
1122 Dev consdevtab = {
1123 	'c',
1124 	"cons",
1125 
1126 	devreset,
1127 	consinit,
1128 	devshutdown,
1129 	consattach,
1130 	conswalk,
1131 	consstat,
1132 	consopen,
1133 	devcreate,
1134 	consclose,
1135 	consread,
1136 	devbread,
1137 	conswrite,
1138 	devbwrite,
1139 	devremove,
1140 	devwstat,
1141 };
1142 
1143 static	ulong	randn;
1144 
1145 static void
1146 seedrand(void)
1147 {
1148 	if(!waserror()){
1149 		randomread((void*)&randn, sizeof(randn));
1150 		poperror();
1151 	}
1152 }
1153 
1154 int
1155 nrand(int n)
1156 {
1157 	if(randn == 0)
1158 		seedrand();
1159 	randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
1160 	return (randn>>16) % n;
1161 }
1162 
1163 int
1164 rand(void)
1165 {
1166 	nrand(1);
1167 	return randn;
1168 }
1169 
1170 static uvlong uvorder = 0x0001020304050607ULL;
1171 
1172 static uchar*
1173 le2vlong(vlong *to, uchar *f)
1174 {
1175 	uchar *t, *o;
1176 	int i;
1177 
1178 	t = (uchar*)to;
1179 	o = (uchar*)&uvorder;
1180 	for(i = 0; i < sizeof(vlong); i++)
1181 		t[o[i]] = f[i];
1182 	return f+sizeof(vlong);
1183 }
1184 
1185 static uchar*
1186 vlong2le(uchar *t, vlong from)
1187 {
1188 	uchar *f, *o;
1189 	int i;
1190 
1191 	f = (uchar*)&from;
1192 	o = (uchar*)&uvorder;
1193 	for(i = 0; i < sizeof(vlong); i++)
1194 		t[i] = f[o[i]];
1195 	return t+sizeof(vlong);
1196 }
1197 
1198 static long order = 0x00010203;
1199 
1200 static uchar*
1201 le2long(long *to, uchar *f)
1202 {
1203 	uchar *t, *o;
1204 	int i;
1205 
1206 	t = (uchar*)to;
1207 	o = (uchar*)&order;
1208 	for(i = 0; i < sizeof(long); i++)
1209 		t[o[i]] = f[i];
1210 	return f+sizeof(long);
1211 }
1212 
1213 static uchar*
1214 long2le(uchar *t, long from)
1215 {
1216 	uchar *f, *o;
1217 	int i;
1218 
1219 	f = (uchar*)&from;
1220 	o = (uchar*)&order;
1221 	for(i = 0; i < sizeof(long); i++)
1222 		t[i] = f[o[i]];
1223 	return t+sizeof(long);
1224 }
1225 
1226 char *Ebadtimectl = "bad time control";
1227 
1228 /*
1229  *  like the old #c/time but with added info.  Return
1230  *
1231  *	secs	nanosecs	fastticks	fasthz
1232  */
1233 static int
1234 readtime(ulong off, char *buf, int n)
1235 {
1236 	vlong	nsec, ticks;
1237 	long sec;
1238 	char str[7*NUMSIZE];
1239 
1240 	nsec = todget(&ticks);
1241 	if(fasthz == 0LL)
1242 		fastticks((uvlong*)&fasthz);
1243 	sec = nsec/1000000000ULL;
1244 	snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
1245 		NUMSIZE-1, sec,
1246 		VLNUMSIZE-1, nsec,
1247 		VLNUMSIZE-1, ticks,
1248 		VLNUMSIZE-1, fasthz);
1249 	return readstr(off, buf, n, str);
1250 }
1251 
1252 /*
1253  *  set the time in seconds
1254  */
1255 static int
1256 writetime(char *buf, int n)
1257 {
1258 	char b[13];
1259 	long i;
1260 	vlong now;
1261 
1262 	if(n >= sizeof(b))
1263 		error(Ebadtimectl);
1264 	strncpy(b, buf, n);
1265 	b[n] = 0;
1266 	i = strtol(b, 0, 0);
1267 	if(i <= 0)
1268 		error(Ebadtimectl);
1269 	now = i*1000000000LL;
1270 	todset(now, 0, 0);
1271 	return n;
1272 }
1273 
1274 /*
1275  *  read binary time info.  all numbers are little endian.
1276  *  ticks and nsec are syncronized.
1277  */
1278 static int
1279 readbintime(char *buf, int n)
1280 {
1281 	int i;
1282 	vlong nsec, ticks;
1283 	uchar *b = (uchar*)buf;
1284 
1285 	i = 0;
1286 	if(fasthz == 0LL)
1287 		fastticks((uvlong*)&fasthz);
1288 	nsec = todget(&ticks);
1289 	if(n >= 3*sizeof(uvlong)){
1290 		vlong2le(b+2*sizeof(uvlong), fasthz);
1291 		i += sizeof(uvlong);
1292 	}
1293 	if(n >= 2*sizeof(uvlong)){
1294 		vlong2le(b+sizeof(uvlong), ticks);
1295 		i += sizeof(uvlong);
1296 	}
1297 	if(n >= 8){
1298 		vlong2le(b, nsec);
1299 		i += sizeof(vlong);
1300 	}
1301 	return i;
1302 }
1303 
1304 /*
1305  *  set any of the following
1306  *	- time in nsec
1307  *	- nsec trim applied over some seconds
1308  *	- clock frequency
1309  */
1310 static int
1311 writebintime(char *buf, int n)
1312 {
1313 	uchar *p;
1314 	vlong delta;
1315 	long period;
1316 
1317 	n--;
1318 	p = (uchar*)buf + 1;
1319 	switch(*buf){
1320 	case 'n':
1321 		if(n < sizeof(vlong))
1322 			error(Ebadtimectl);
1323 		le2vlong(&delta, p);
1324 		todset(delta, 0, 0);
1325 		break;
1326 	case 'd':
1327 		if(n < sizeof(vlong)+sizeof(long))
1328 			error(Ebadtimectl);
1329 		p = le2vlong(&delta, p);
1330 		le2long(&period, p);
1331 		todset(-1, delta, period);
1332 		break;
1333 	case 'f':
1334 		if(n < sizeof(uvlong))
1335 			error(Ebadtimectl);
1336 		le2vlong(&fasthz, p);
1337 		if(fasthz <= 0)
1338 			error(Ebadtimectl);
1339 		todsetfreq(fasthz);
1340 		break;
1341 	}
1342 	return n;
1343 }
1344