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