xref: /plan9-contrib/sys/src/9/pc/devarch.c (revision 3f9c83932f326ae8b6d81b36429957bc06a9813e)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9 
10 typedef struct IOMap IOMap;
11 struct IOMap
12 {
13 	IOMap	*next;
14 	int	reserved;
15 	char	tag[13];
16 	ulong	start;
17 	ulong	end;
18 };
19 
20 static struct
21 {
22 	Lock;
23 	IOMap	*m;
24 	IOMap	*free;
25 	IOMap	maps[32];		// some initial free maps
26 
27 	QLock	ql;			// lock for reading map
28 } iomap;
29 
30 enum {
31 	Qdir = 0,
32 	Qioalloc = 1,
33 	Qiob,
34 	Qiow,
35 	Qiol,
36 	Qbase,
37 
38 	Qmax = 16,
39 };
40 
41 typedef long Rdwrfn(Chan*, void*, long, vlong);
42 
43 static Rdwrfn *readfn[Qmax];
44 static Rdwrfn *writefn[Qmax];
45 
46 static Dirtab archdir[Qmax] = {
47 	".",		{ Qdir, 0, QTDIR },	0,	0555,
48 	"ioalloc",	{ Qioalloc, 0 },	0,	0444,
49 	"iob",		{ Qiob, 0 },		0,	0660,
50 	"iow",		{ Qiow, 0 },		0,	0660,
51 	"iol",		{ Qiol, 0 },		0,	0660,
52 };
53 Lock archwlock;	/* the lock is only for changing archdir */
54 int narchdir = Qbase;
55 int (*_pcmspecial)(char*, ISAConf*);
56 void (*_pcmspecialclose)(int);
57 
58 static int doi8253set = 1;
59 
60 /*
61  * Add a file to the #P listing.  Once added, you can't delete it.
62  * You can't add a file with the same name as one already there,
63  * and you get a pointer to the Dirtab entry so you can do things
64  * like change the Qid version.  Changing the Qid path is disallowed.
65  */
66 Dirtab*
67 addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
68 {
69 	int i;
70 	Dirtab d;
71 	Dirtab *dp;
72 
73 	memset(&d, 0, sizeof d);
74 	strcpy(d.name, name);
75 	d.perm = perm;
76 
77 	lock(&archwlock);
78 	if(narchdir >= Qmax){
79 		unlock(&archwlock);
80 		return nil;
81 	}
82 
83 	for(i=0; i<narchdir; i++)
84 		if(strcmp(archdir[i].name, name) == 0){
85 			unlock(&archwlock);
86 			return nil;
87 		}
88 
89 	d.qid.path = narchdir;
90 	archdir[narchdir] = d;
91 	readfn[narchdir] = rdfn;
92 	writefn[narchdir] = wrfn;
93 	dp = &archdir[narchdir++];
94 	unlock(&archwlock);
95 
96 	return dp;
97 }
98 
99 void
100 ioinit(void)
101 {
102 	char *excluded;
103 	int i;
104 
105 	for(i = 0; i < nelem(iomap.maps)-1; i++)
106 		iomap.maps[i].next = &iomap.maps[i+1];
107 	iomap.maps[i].next = nil;
108 	iomap.free = iomap.maps;
109 
110 	/*
111 	 * This is necessary to make the IBM X20 boot.
112 	 * Have not tracked down the reason.
113 	 */
114 	ioalloc(0x0fff, 1, 0, "dummy");	// i82557 is at 0x1000, the dummy
115 					// entry is needed for swappable devs.
116 
117 	if ((excluded = getconf("ioexclude")) != nil) {
118 		char *s;
119 
120 		s = excluded;
121 		while (s && *s != '\0' && *s != '\n') {
122 			char *ends;
123 			int io_s, io_e;
124 
125 			io_s = (int)strtol(s, &ends, 0);
126 			if (ends == nil || ends == s || *ends != '-') {
127 				print("ioinit: cannot parse option string\n");
128 				break;
129 			}
130 			s = ++ends;
131 
132 			io_e = (int)strtol(s, &ends, 0);
133 			if (ends && *ends == ',')
134 				*ends++ = '\0';
135 			s = ends;
136 
137 			ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
138 		}
139 	}
140 
141 }
142 
143 // Reserve a range to be ioalloced later.
144 // This is in particular useful for exchangable cards, such
145 // as pcmcia and cardbus cards.
146 int
147 ioreserve(int, int size, int align, char *tag)
148 {
149 	IOMap *m, **l;
150 	int i, port;
151 
152 	lock(&iomap);
153 	// find a free port above 0x400 and below 0x1000
154 	port = 0x400;
155 	for(l = &iomap.m; *l; l = &(*l)->next){
156 		m = *l;
157 		if (m->start < 0x400) continue;
158 		i = m->start - port;
159 		if(i > size)
160 			break;
161 		if(align > 0)
162 			port = ((port+align-1)/align)*align;
163 		else
164 			port = m->end;
165 	}
166 	if(*l == nil){
167 		unlock(&iomap);
168 		return -1;
169 	}
170 	m = iomap.free;
171 	if(m == nil){
172 		print("ioalloc: out of maps");
173 		unlock(&iomap);
174 		return port;
175 	}
176 	iomap.free = m->next;
177 	m->next = *l;
178 	m->start = port;
179 	m->end = port + size;
180 	m->reserved = 1;
181 	strncpy(m->tag, tag, sizeof(m->tag));
182 	m->tag[sizeof(m->tag)-1] = 0;
183 	*l = m;
184 
185 	archdir[0].qid.vers++;
186 
187 	unlock(&iomap);
188 	return m->start;
189 }
190 
191 //
192 //	alloc some io port space and remember who it was
193 //	alloced to.  if port < 0, find a free region.
194 //
195 int
196 ioalloc(int port, int size, int align, char *tag)
197 {
198 	IOMap *m, **l;
199 	int i;
200 
201 	lock(&iomap);
202 	if(port < 0){
203 		// find a free port above 0x400 and below 0x1000
204 		port = 0x400;
205 		for(l = &iomap.m; *l; l = &(*l)->next){
206 			m = *l;
207 			if (m->start < 0x400) continue;
208 			i = m->start - port;
209 			if(i > size)
210 				break;
211 			if(align > 0)
212 				port = ((port+align-1)/align)*align;
213 			else
214 				port = m->end;
215 		}
216 		if(*l == nil){
217 			unlock(&iomap);
218 			return -1;
219 		}
220 	} else {
221 		// Only 64KB I/O space on the x86.
222 		if((port+size) > 0x10000){
223 			unlock(&iomap);
224 			return -1;
225 		}
226 		// see if the space clashes with previously allocated ports
227 		for(l = &iomap.m; *l; l = &(*l)->next){
228 			m = *l;
229 			if(m->end <= port)
230 				continue;
231 			if(m->reserved && m->start == port && m->end == port + size) {
232 				m->reserved = 0;
233 				unlock(&iomap);
234 				return m->start;
235 			}
236 			if(m->start >= port+size)
237 				break;
238 			unlock(&iomap);
239 			return -1;
240 		}
241 	}
242 	m = iomap.free;
243 	if(m == nil){
244 		print("ioalloc: out of maps");
245 		unlock(&iomap);
246 		return port;
247 	}
248 	iomap.free = m->next;
249 	m->next = *l;
250 	m->start = port;
251 	m->end = port + size;
252 	strncpy(m->tag, tag, sizeof(m->tag));
253 	m->tag[sizeof(m->tag)-1] = 0;
254 	*l = m;
255 
256 	archdir[0].qid.vers++;
257 
258 	unlock(&iomap);
259 	return m->start;
260 }
261 
262 void
263 iofree(int port)
264 {
265 	IOMap *m, **l;
266 
267 	lock(&iomap);
268 	for(l = &iomap.m; *l; l = &(*l)->next){
269 		if((*l)->start == port){
270 			m = *l;
271 			*l = m->next;
272 			m->next = iomap.free;
273 			iomap.free = m;
274 			break;
275 		}
276 		if((*l)->start > port)
277 			break;
278 	}
279 	archdir[0].qid.vers++;
280 	unlock(&iomap);
281 }
282 
283 int
284 iounused(int start, int end)
285 {
286 	IOMap *m;
287 
288 	for(m = iomap.m; m; m = m->next){
289 		if(start >= m->start && start < m->end
290 		|| start <= m->start && end > m->start)
291 			return 0;
292 	}
293 	return 1;
294 }
295 
296 static void
297 checkport(int start, int end)
298 {
299 	/* standard vga regs are OK */
300 	if(start >= 0x2b0 && end <= 0x2df+1)
301 		return;
302 	if(start >= 0x3c0 && end <= 0x3da+1)
303 		return;
304 
305 	if(iounused(start, end))
306 		return;
307 	error(Eperm);
308 }
309 
310 static Chan*
311 archattach(char* spec)
312 {
313 	return devattach('P', spec);
314 }
315 
316 Walkqid*
317 archwalk(Chan* c, Chan *nc, char** name, int nname)
318 {
319 	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
320 }
321 
322 static int
323 archstat(Chan* c, uchar* dp, int n)
324 {
325 	return devstat(c, dp, n, archdir, narchdir, devgen);
326 }
327 
328 static Chan*
329 archopen(Chan* c, int omode)
330 {
331 	return devopen(c, omode, archdir, narchdir, devgen);
332 }
333 
334 static void
335 archclose(Chan*)
336 {
337 }
338 
339 enum
340 {
341 	Linelen= 31,
342 };
343 
344 static long
345 archread(Chan *c, void *a, long n, vlong offset)
346 {
347 	char *buf, *p;
348 	int port;
349 	ushort *sp;
350 	ulong *lp;
351 	IOMap *m;
352 	Rdwrfn *fn;
353 
354 	switch((ulong)c->qid.path){
355 
356 	case Qdir:
357 		return devdirread(c, a, n, archdir, narchdir, devgen);
358 
359 	case Qiob:
360 		port = offset;
361 		checkport(offset, offset+n);
362 		for(p = a; port < offset+n; port++)
363 			*p++ = inb(port);
364 		return n;
365 
366 	case Qiow:
367 		if(n & 1)
368 			error(Ebadarg);
369 		checkport(offset, offset+n);
370 		sp = a;
371 		for(port = offset; port < offset+n; port += 2)
372 			*sp++ = ins(port);
373 		return n;
374 
375 	case Qiol:
376 		if(n & 3)
377 			error(Ebadarg);
378 		checkport(offset, offset+n);
379 		lp = a;
380 		for(port = offset; port < offset+n; port += 4)
381 			*lp++ = inl(port);
382 		return n;
383 
384 	case Qioalloc:
385 		break;
386 
387 	default:
388 		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
389 			return fn(c, a, n, offset);
390 		error(Eperm);
391 		break;
392 	}
393 
394 	if((buf = malloc(n)) == nil)
395 		error(Enomem);
396 	p = buf;
397 	n = n/Linelen;
398 	offset = offset/Linelen;
399 
400 	lock(&iomap);
401 	for(m = iomap.m; n > 0 && m != nil; m = m->next){
402 		if(offset-- > 0)
403 			continue;
404 		sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
405 		p += Linelen;
406 		n--;
407 	}
408 	unlock(&iomap);
409 
410 	n = p - buf;
411 	memmove(a, buf, n);
412 	free(buf);
413 
414 	return n;
415 }
416 
417 static long
418 archwrite(Chan *c, void *a, long n, vlong offset)
419 {
420 	char *p;
421 	int port;
422 	ushort *sp;
423 	ulong *lp;
424 	Rdwrfn *fn;
425 
426 	switch((ulong)c->qid.path){
427 
428 	case Qiob:
429 		p = a;
430 		checkport(offset, offset+n);
431 		for(port = offset; port < offset+n; port++)
432 			outb(port, *p++);
433 		return n;
434 
435 	case Qiow:
436 		if(n & 1)
437 			error(Ebadarg);
438 		checkport(offset, offset+n);
439 		sp = a;
440 		for(port = offset; port < offset+n; port += 2)
441 			outs(port, *sp++);
442 		return n;
443 
444 	case Qiol:
445 		if(n & 3)
446 			error(Ebadarg);
447 		checkport(offset, offset+n);
448 		lp = a;
449 		for(port = offset; port < offset+n; port += 4)
450 			outl(port, *lp++);
451 		return n;
452 
453 	default:
454 		if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
455 			return fn(c, a, n, offset);
456 		error(Eperm);
457 		break;
458 	}
459 	return 0;
460 }
461 
462 Dev archdevtab = {
463 	'P',
464 	"arch",
465 
466 	devreset,
467 	devinit,
468 	devshutdown,
469 	archattach,
470 	archwalk,
471 	archstat,
472 	archopen,
473 	devcreate,
474 	archclose,
475 	archread,
476 	devbread,
477 	archwrite,
478 	devbwrite,
479 	devremove,
480 	devwstat,
481 };
482 
483 /*
484  *  the following is a generic version of the
485  *  architecture specific stuff
486  */
487 
488 static int
489 unimplemented(int)
490 {
491 	return 0;
492 }
493 
494 static void
495 nop(void)
496 {
497 }
498 
499 /*
500  * 386 has no compare-and-swap instruction.
501  * Run it with interrupts turned off instead.
502  */
503 static int
504 cmpswap386(long *addr, long old, long new)
505 {
506 	int r, s;
507 
508 	s = splhi();
509 	if(r = (*addr == old))
510 		*addr = new;
511 	splx(s);
512 	return r;
513 }
514 
515 /*
516  * On a uniprocessor, you'd think that coherence could be nop,
517  * but it can't.  We still need a barrier when using coherence() in
518  * device drivers.
519  *
520  * On VMware, it's safe (and a huge win) to set this to nop.
521  * Aux/vmware does this via the #P/archctl file.
522  */
523 void (*coherence)(void) = nop;
524 
525 int (*cmpswap)(long*, long, long) = cmpswap386;
526 
527 PCArch* arch;
528 extern PCArch* knownarch[];
529 
530 PCArch archgeneric = {
531 .id=		"generic",
532 .ident=		0,
533 .reset=		i8042reset,
534 .serialpower=	unimplemented,
535 .modempower=	unimplemented,
536 
537 .intrinit=	i8259init,
538 .intrenable=	i8259enable,
539 .intrvecno=	i8259vecno,
540 .intrdisable=	i8259disable,
541 .intron=		i8259on,
542 .introff=		i8259off,
543 
544 .clockenable=	i8253enable,
545 .fastclock=	i8253read,
546 .timerset=	i8253timerset,
547 };
548 
549 typedef struct X86type X86type;
550 struct X86type {
551 	int	family;
552 	int	model;
553 	int	aalcycles;
554 	char*	name;
555 };
556 
557 static X86type x86intel[] =
558 {
559 	{ 4,	0,	22,	"486DX", },	/* known chips */
560 	{ 4,	1,	22,	"486DX50", },
561 	{ 4,	2,	22,	"486SX", },
562 	{ 4,	3,	22,	"486DX2", },
563 	{ 4,	4,	22,	"486SL", },
564 	{ 4,	5,	22,	"486SX2", },
565 	{ 4,	7,	22,	"DX2WB", },	/* P24D */
566 	{ 4,	8,	22,	"DX4", },	/* P24C */
567 	{ 4,	9,	22,	"DX4WB", },	/* P24CT */
568 	{ 5,	0,	23,	"P5", },
569 	{ 5,	1,	23,	"P5", },
570 	{ 5,	2,	23,	"P54C", },
571 	{ 5,	3,	23,	"P24T", },
572 	{ 5,	4,	23,	"P55C MMX", },
573 	{ 5,	7,	23,	"P54C VRT", },
574 	{ 6,	1,	16,	"PentiumPro", },/* trial and error */
575 	{ 6,	3,	16,	"PentiumII", },
576 	{ 6,	5,	16,	"PentiumII/Xeon", },
577 	{ 6,	6,	16,	"Celeron", },
578 	{ 6,	7,	16,	"PentiumIII/Xeon", },
579 	{ 6,	8,	16,	"PentiumIII/Xeon", },
580 	{ 6,	0xB,	16,	"PentiumIII/Xeon", },
581 	{ 0xF,	1,	16,	"P4", },	/* P4 */
582 	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
583 
584 	{ 3,	-1,	32,	"386", },	/* family defaults */
585 	{ 4,	-1,	22,	"486", },
586 	{ 5,	-1,	23,	"P5", },
587 	{ 6,	-1,	16,	"P6", },
588 	{ 0xF,	-1,	16,	"P4", },	/* P4 */
589 
590 	{ -1,	-1,	16,	"unknown", },	/* total default */
591 };
592 
593 /*
594  * The AMD processors all implement the CPUID instruction.
595  * The later ones also return the processor name via functions
596  * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
597  * and DX:
598  *	K5	"AMD-K5(tm) Processor"
599  *	K6	"AMD-K6tm w/ multimedia extensions"
600  *	K6 3D	"AMD-K6(tm) 3D processor"
601  *	K6 3D+	?
602  */
603 static X86type x86amd[] =
604 {
605 	{ 5,	0,	23,	"AMD-K5", },	/* guesswork */
606 	{ 5,	1,	23,	"AMD-K5", },	/* guesswork */
607 	{ 5,	2,	23,	"AMD-K5", },	/* guesswork */
608 	{ 5,	3,	23,	"AMD-K5", },	/* guesswork */
609 	{ 5,	6,	11,	"AMD-K6", },	/* trial and error */
610 	{ 5,	7,	11,	"AMD-K6", },	/* trial and error */
611 	{ 5,	8,	11,	"AMD-K6-2", },	/* trial and error */
612 	{ 5,	9,	11,	"AMD-K6-III", },/* trial and error */
613 
614 	{ 6,	1,	11,	"AMD-Athlon", },/* trial and error */
615 	{ 6,	2,	11,	"AMD-Athlon", },/* trial and error */
616 
617 	{ 4,	-1,	22,	"Am486", },	/* guesswork */
618 	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
619 	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
620 	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
621 
622 	{ -1,	-1,	11,	"unknown", },	/* total default */
623 };
624 
625 /*
626  * WinChip 240MHz
627  */
628 static X86type x86winchip[] =
629 {
630 	{5,	4,	23,	"Winchip",},	/* guesswork */
631 	{6,	7,	23,	"Via C3 Samuel 2 or Ezra",},
632 	{6,	8,	23,	"Via C3 Ezra-T",},
633 	{6,	9,	23,	"Via C3 Eden-N",},
634 	{ -1,	-1,	23,	"unknown", },	/* total default */
635 };
636 
637 /*
638  * SiS 55x
639  */
640 static X86type x86sis[] =
641 {
642 	{5,	0,	23,	"SiS 55x",},	/* guesswork */
643 	{ -1,	-1,	23,	"unknown", },	/* total default */
644 };
645 
646 static X86type *cputype;
647 
648 static void	simplecycles(uvlong*);
649 void	(*cycles)(uvlong*) = simplecycles;
650 void	_cycles(uvlong*);	/* in l.s */
651 
652 static void
653 simplecycles(uvlong*x)
654 {
655 	*x = m->ticks;
656 }
657 
658 void
659 cpuidprint(void)
660 {
661 	int i;
662 	char buf[128];
663 
664 	i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz);
665 	if(m->cpuidid[0])
666 		i += sprint(buf+i, "%12.12s ", m->cpuidid);
667 	seprint(buf+i, buf + sizeof buf - 1,
668 		"%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
669 		m->cpuidtype, m->cpuidax, m->cpuiddx);
670 	print(buf);
671 }
672 
673 /*
674  *  figure out:
675  *	- cpu type
676  *	- whether or not we have a TSC (cycle counter)
677  *	- whether or not it supports page size extensions
678  *		(if so turn it on)
679  *	- whether or not it supports machine check exceptions
680  *		(if so turn it on)
681  *	- whether or not it supports the page global flag
682  *		(if so turn it on)
683  */
684 int
685 cpuidentify(void)
686 {
687 	char *p;
688 	int family, model, nomce;
689 	X86type *t, *tab;
690 	ulong cr4;
691 	vlong mca, mct;
692 
693 	cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
694 	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
695 		tab = x86amd;
696 	else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
697 		tab = x86winchip;
698 	else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
699 		tab = x86sis;
700 	else
701 		tab = x86intel;
702 
703 	family = X86FAMILY(m->cpuidax);
704 	model = X86MODEL(m->cpuidax);
705 	for(t=tab; t->name; t++)
706 		if((t->family == family && t->model == model)
707 		|| (t->family == family && t->model == -1)
708 		|| (t->family == -1))
709 			break;
710 
711 	m->cpuidtype = t->name;
712 
713 	/*
714 	 *  if there is one, set tsc to a known value
715 	 */
716 	if(m->cpuiddx & 0x10){
717 		m->havetsc = 1;
718 		cycles = _cycles;
719 		if(m->cpuiddx & 0x20)
720 			wrmsr(0x10, 0);
721 	}
722 
723 	/*
724  	 *  use i8253 to guess our cpu speed
725 	 */
726 	guesscpuhz(t->aalcycles);
727 
728 	/*
729 	 * If machine check exception, page size extensions or page global bit
730 	 * are supported enable them in CR4 and clear any other set extensions.
731 	 * If machine check was enabled clear out any lingering status.
732 	 */
733 	if(m->cpuiddx & 0x2088){
734 		cr4 = 0;
735 		if(m->cpuiddx & 0x08)
736 			cr4 |= 0x10;		/* page size extensions */
737 		if(p = getconf("*nomce"))
738 			nomce = strtoul(p, 0, 0);
739 		else
740 			nomce = 0;
741 		if((m->cpuiddx & 0x80) && !nomce){
742 			cr4 |= 0x40;		/* machine check enable */
743 			if(family == 5){
744 				rdmsr(0x00, &mca);
745 				rdmsr(0x01, &mct);
746 			}
747 		}
748 
749 		/*
750 		 * Detect whether the chip supports the global bit
751 		 * in page directory and page table entries.  When set
752 		 * in a particular entry, it means ``don't bother removing
753 		 * this from the TLB when CR3 changes.''
754 		 *
755 		 * We flag all kernel pages with this bit.  Doing so lessens the
756 		 * overhead of switching processes on bare hardware,
757 		 * even more so on VMware.  See mmu.c:/^memglobal.
758 		 *
759 		 * For future reference, should we ever need to do a
760 		 * full TLB flush, it can be accomplished by clearing
761 		 * the PGE bit in CR4, writing to CR3, and then
762 		 * restoring the PGE bit.
763 		 */
764 		if(m->cpuiddx & 0x2000){
765 			cr4 |= 0x80;		/* page global enable bit */
766 			m->havepge = 1;
767 		}
768 
769 		putcr4(cr4);
770 		if(m->cpuiddx & 0x80)
771 			rdmsr(0x01, &mct);
772 	}
773 
774 	cputype = t;
775 	return t->family;
776 }
777 
778 static long
779 cputyperead(Chan*, void *a, long n, vlong offset)
780 {
781 	char str[32];
782 	ulong mhz;
783 
784 	mhz = (m->cpuhz+999999)/1000000;
785 
786 	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
787 	return readstr(offset, a, n, str);
788 }
789 
790 static long
791 archctlread(Chan*, void *a, long nn, vlong offset)
792 {
793 	char buf[256];
794 	int n;
795 
796 	n = snprint(buf, sizeof buf, "cpu %s %lud%s\n",
797 		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
798 		m->havepge ? " pge" : "");
799 	n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off");
800 	n += snprint(buf+n, sizeof buf-n, "coherence ");
801 	if(coherence == mb386)
802 		n += snprint(buf+n, sizeof buf-n, "mb386\n");
803 	else if(coherence == mb586)
804 		n += snprint(buf+n, sizeof buf-n, "mb586\n");
805 	else if(coherence == nop)
806 		n += snprint(buf+n, sizeof buf-n, "nop\n");
807 	else
808 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence);
809 	n += snprint(buf+n, sizeof buf-n, "cmpswap ");
810 	if(cmpswap == cmpswap386)
811 		n += snprint(buf+n, sizeof buf-n, "cmpswap386\n");
812 	else if(cmpswap == cmpswap486)
813 		n += snprint(buf+n, sizeof buf-n, "cmpswap486\n");
814 	else
815 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap);
816 	n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off");
817 	buf[n] = 0;
818 	return readstr(offset, a, nn, buf);
819 }
820 
821 enum
822 {
823 	CMpge,
824 	CMcoherence,
825 	CMi8253set,
826 };
827 
828 static Cmdtab archctlmsg[] =
829 {
830 	CMpge,		"pge",		2,
831 	CMcoherence,	"coherence",	2,
832 	CMi8253set,	"i8253set",	2,
833 };
834 
835 static long
836 archctlwrite(Chan*, void *a, long n, vlong)
837 {
838 	Cmdbuf *cb;
839 	Cmdtab *ct;
840 
841 	cb = parsecmd(a, n);
842 	if(waserror()){
843 		free(cb);
844 		nexterror();
845 	}
846 	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
847 	switch(ct->index){
848 	case CMpge:
849 		if(!m->havepge)
850 			error("processor does not support pge");
851 		if(strcmp(cb->f[1], "on") == 0)
852 			putcr4(getcr4() | 0x80);
853 		else if(strcmp(cb->f[1], "off") == 0)
854 			putcr4(getcr4() & ~0x80);
855 		else
856 			cmderror(cb, "invalid pge ctl");
857 		break;
858 	case CMcoherence:
859 		if(strcmp(cb->f[1], "mb386") == 0)
860 			coherence = mb386;
861 		else if(strcmp(cb->f[1], "mb586") == 0){
862 			if(X86FAMILY(m->cpuidax) < 5)
863 				error("invalid coherence ctl on this cpu family");
864 			coherence = mb586;
865 		}
866 		else if(strcmp(cb->f[1], "nop") == 0){
867 			/* only safe on vmware */
868 			if(conf.nmach > 1)
869 				error("cannot disable coherence on a multiprocessor");
870 			coherence = nop;
871 		}else
872 			cmderror(cb, "invalid coherence ctl");
873 		break;
874 	case CMi8253set:
875 		if(strcmp(cb->f[1], "on") == 0)
876 			doi8253set = 1;
877 		else if(strcmp(cb->f[1], "off") == 0){
878 			doi8253set = 0;
879 			(*arch->timerset)(0);
880 		}else
881 			cmderror(cb, "invalid i2853set ctl");
882 		break;
883 	}
884 	free(cb);
885 	poperror();
886 	return n;
887 }
888 
889 void
890 archinit(void)
891 {
892 	PCArch **p;
893 
894 	arch = 0;
895 	for(p = knownarch; *p; p++){
896 		if((*p)->ident && (*p)->ident() == 0){
897 			arch = *p;
898 			break;
899 		}
900 	}
901 	if(arch == 0)
902 		arch = &archgeneric;
903 	else{
904 		if(arch->id == 0)
905 			arch->id = archgeneric.id;
906 		if(arch->reset == 0)
907 			arch->reset = archgeneric.reset;
908 		if(arch->serialpower == 0)
909 			arch->serialpower = archgeneric.serialpower;
910 		if(arch->modempower == 0)
911 			arch->modempower = archgeneric.modempower;
912 		if(arch->intrinit == 0)
913 			arch->intrinit = archgeneric.intrinit;
914 		if(arch->intrenable == 0)
915 			arch->intrenable = archgeneric.intrenable;
916 	}
917 
918 	/*
919 	 *  Decide whether to use copy-on-reference (386 and mp).
920 	 *  We get another chance to set it in mpinit() for a
921 	 *  multiprocessor.
922 	 */
923 	if(X86FAMILY(m->cpuidax) == 3)
924 		conf.copymode = 1;
925 
926 	if(X86FAMILY(m->cpuidax) >= 4)
927 		cmpswap = cmpswap486;
928 
929 	if(X86FAMILY(m->cpuidax) >= 5)
930 		coherence = mb586;
931 
932 	addarchfile("cputype", 0444, cputyperead, nil);
933 	addarchfile("archctl", 0664, archctlread, archctlwrite);
934 }
935 
936 /*
937  *  call either the pcmcia or pccard device setup
938  */
939 int
940 pcmspecial(char *idstr, ISAConf *isa)
941 {
942 	return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
943 }
944 
945 /*
946  *  call either the pcmcia or pccard device teardown
947  */
948 void
949 pcmspecialclose(int a)
950 {
951 	if (_pcmspecialclose != nil)
952 		_pcmspecialclose(a);
953 }
954 
955 /*
956  *  return value and speed of timer set in arch->clockenable
957  */
958 uvlong
959 fastticks(uvlong *hz)
960 {
961 	return (*arch->fastclock)(hz);
962 }
963 
964 ulong
965 µs(void)
966 {
967 	return fastticks2us((*arch->fastclock)(nil));
968 }
969 
970 /*
971  *  set next timer interrupt
972  */
973 void
974 timerset(uvlong x)
975 {
976 	if(doi8253set)
977 		(*arch->timerset)(x);
978 }
979