xref: /plan9/sys/src/9/pc/devarch.c (revision 94e3d7af89050c96afb15cdc7981d439c8ed74fb)
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 port, int size, int align, char *tag)
148 {
149 	IOMap *m, **l;
150 	int i;
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  * On a uniprocessor, you'd think that coherence could be nop,
501  * but it can't.  We still need a barrier when using coherence() in
502  * device drivers.
503  *
504  * On VMware, it's safe (and a huge win) to set this to nop.
505  * Aux/vmware does this via the #P/archctl file.
506  */
507 void (*coherence)(void) = nop;
508 
509 PCArch* arch;
510 extern PCArch* knownarch[];
511 
512 PCArch archgeneric = {
513 .id=		"generic",
514 .ident=		0,
515 .reset=		i8042reset,
516 .serialpower=	unimplemented,
517 .modempower=	unimplemented,
518 
519 .intrinit=	i8259init,
520 .intrenable=	i8259enable,
521 .intrvecno=	i8259vecno,
522 .intrdisable=	i8259disable,
523 
524 .clockenable=	i8253enable,
525 .fastclock=	i8253read,
526 .timerset=	i8253timerset,
527 };
528 
529 typedef struct X86type X86type;
530 struct X86type {
531 	int	family;
532 	int	model;
533 	int	aalcycles;
534 	char*	name;
535 };
536 
537 static X86type x86intel[] =
538 {
539 	{ 4,	0,	22,	"486DX", },	/* known chips */
540 	{ 4,	1,	22,	"486DX50", },
541 	{ 4,	2,	22,	"486SX", },
542 	{ 4,	3,	22,	"486DX2", },
543 	{ 4,	4,	22,	"486SL", },
544 	{ 4,	5,	22,	"486SX2", },
545 	{ 4,	7,	22,	"DX2WB", },	/* P24D */
546 	{ 4,	8,	22,	"DX4", },	/* P24C */
547 	{ 4,	9,	22,	"DX4WB", },	/* P24CT */
548 	{ 5,	0,	23,	"P5", },
549 	{ 5,	1,	23,	"P5", },
550 	{ 5,	2,	23,	"P54C", },
551 	{ 5,	3,	23,	"P24T", },
552 	{ 5,	4,	23,	"P55C MMX", },
553 	{ 5,	7,	23,	"P54C VRT", },
554 	{ 6,	1,	16,	"PentiumPro", },/* trial and error */
555 	{ 6,	3,	16,	"PentiumII", },
556 	{ 6,	5,	16,	"PentiumII/Xeon", },
557 	{ 6,	6,	16,	"Celeron", },
558 	{ 6,	7,	16,	"PentiumIII/Xeon", },
559 	{ 6,	8,	16,	"PentiumIII/Xeon", },
560 	{ 6,	0xB,	16,	"PentiumIII/Xeon", },
561 	{ 0xF,	1,	16,	"P4", },	/* P4 */
562 	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
563 
564 	{ 3,	-1,	32,	"386", },	/* family defaults */
565 	{ 4,	-1,	22,	"486", },
566 	{ 5,	-1,	23,	"P5", },
567 	{ 6,	-1,	16,	"P6", },
568 	{ 0xF,	-1,	16,	"P4", },	/* P4 */
569 
570 	{ -1,	-1,	16,	"unknown", },	/* total default */
571 };
572 
573 /*
574  * The AMD processors all implement the CPUID instruction.
575  * The later ones also return the processor name via functions
576  * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
577  * and DX:
578  *	K5	"AMD-K5(tm) Processor"
579  *	K6	"AMD-K6tm w/ multimedia extensions"
580  *	K6 3D	"AMD-K6(tm) 3D processor"
581  *	K6 3D+	?
582  */
583 static X86type x86amd[] =
584 {
585 	{ 5,	0,	23,	"AMD-K5", },	/* guesswork */
586 	{ 5,	1,	23,	"AMD-K5", },	/* guesswork */
587 	{ 5,	2,	23,	"AMD-K5", },	/* guesswork */
588 	{ 5,	3,	23,	"AMD-K5", },	/* guesswork */
589 	{ 5,	6,	11,	"AMD-K6", },	/* trial and error */
590 	{ 5,	7,	11,	"AMD-K6", },	/* trial and error */
591 	{ 5,	8,	11,	"AMD-K6-2", },	/* trial and error */
592 	{ 5,	9,	11,	"AMD-K6-III", },/* trial and error */
593 
594 	{ 6,	1,	11,	"AMD-Athlon", },/* trial and error */
595 	{ 6,	2,	11,	"AMD-Athlon", },/* trial and error */
596 
597 	{ 4,	-1,	22,	"Am486", },	/* guesswork */
598 	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
599 	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
600 	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
601 
602 	{ -1,	-1,	11,	"unknown", },	/* total default */
603 };
604 
605 /*
606  * WinChip 240MHz
607  */
608 static X86type x86winchip[] =
609 {
610 	{5,	4,	23,	"Winchip",},	/* guesswork */
611 	{6,	7,	23,	"Via C3 Samuel 2 or Ezra",},
612 	{6,	8,	23,	"Via C3 Ezra-T",},
613 	{ -1,	-1,	23,	"unknown", },	/* total default */
614 };
615 
616 /*
617  * SiS 55x
618  */
619 static X86type x86sis[] =
620 {
621 	{5,	0,	23,	"SiS 55x",},	/* guesswork */
622 	{ -1,	-1,	23,	"unknown", },	/* total default */
623 };
624 
625 static X86type *cputype;
626 
627 static void	simplecycles(uvlong*);
628 void	(*cycles)(uvlong*) = simplecycles;
629 void	_cycles(uvlong*);	/* in l.s */
630 
631 static void
632 simplecycles(uvlong*x)
633 {
634 	*x = m->ticks;
635 }
636 
637 void
638 cpuidprint(void)
639 {
640 	int i;
641 	char buf[128];
642 
643 	i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz);
644 	if(m->cpuidid[0])
645 		i += sprint(buf+i, "%12.12s ", m->cpuidid);
646 	sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
647 		m->cpuidtype, m->cpuidax, m->cpuiddx);
648 	print(buf);
649 }
650 
651 /*
652  *  figure out:
653  *	- cpu type
654  *	- whether or not we have a TSC (cycle counter)
655  *	- whether or not it supports page size extensions
656  *		(if so turn it on)
657  *	- whether or not it supports machine check exceptions
658  *		(if so turn it on)
659  *	- whether or not it supports the page global flag
660  *		(if so turn it on)
661  */
662 int
663 cpuidentify(void)
664 {
665 	char *p;
666 	int family, model, nomce;
667 	X86type *t, *tab;
668 	ulong cr4;
669 	vlong mca, mct;
670 
671 	cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
672 	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
673 		tab = x86amd;
674 	else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
675 		tab = x86winchip;
676 	else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
677 		tab = x86sis;
678 	else
679 		tab = x86intel;
680 
681 	family = X86FAMILY(m->cpuidax);
682 	model = X86MODEL(m->cpuidax);
683 	for(t=tab; t->name; t++)
684 		if((t->family == family && t->model == model)
685 		|| (t->family == family && t->model == -1)
686 		|| (t->family == -1))
687 			break;
688 
689 	m->cpuidtype = t->name;
690 
691 	/*
692 	 *  if there is one, set tsc to a known value
693 	 */
694 	if(m->cpuiddx & 0x10){
695 		m->havetsc = 1;
696 		cycles = _cycles;
697 		if(m->cpuiddx & 0x20)
698 			wrmsr(0x10, 0);
699 	}
700 
701 	/*
702  	 *  use i8253 to guess our cpu speed
703 	 */
704 	guesscpuhz(t->aalcycles);
705 
706 	/*
707 	 * If machine check exception, page size extensions or page global bit
708 	 * are supported enable them in CR4 and clear any other set extensions.
709 	 * If machine check was enabled clear out any lingering status.
710 	 */
711 	if(m->cpuiddx & 0x2088){
712 		cr4 = 0;
713 		if(m->cpuiddx & 0x08)
714 			cr4 |= 0x10;		/* page size extensions */
715 		if(p = getconf("*nomce"))
716 			nomce = strtoul(p, 0, 0);
717 		else
718 			nomce = 0;
719 		if((m->cpuiddx & 0x80) && !nomce){
720 			cr4 |= 0x40;		/* machine check enable */
721 			if(family == 5){
722 				rdmsr(0x00, &mca);
723 				rdmsr(0x01, &mct);
724 			}
725 		}
726 
727 		/*
728 		 * Detect whether the chip supports the global bit
729 		 * in page directory and page table entries.  When set
730 		 * in a particular entry, it means ``don't bother removing
731 		 * this from the TLB when CR3 changes.''
732 		 *
733 		 * We flag all kernel pages with this bit.  Doing so lessens the
734 		 * overhead of switching processes on bare hardware,
735 		 * even more so on VMware.  See mmu.c:/^memglobal.
736 		 *
737 		 * For future reference, should we ever need to do a
738 		 * full TLB flush, it can be accomplished by clearing
739 		 * the PGE bit in CR4, writing to CR3, and then
740 		 * restoring the PGE bit.
741 		 */
742 		if(m->cpuiddx & 0x2000){
743 			cr4 |= 0x80;		/* page global enable bit */
744 			m->havepge = 1;
745 		}
746 
747 		putcr4(cr4);
748 		if(m->cpuiddx & 0x80)
749 			rdmsr(0x01, &mct);
750 	}
751 
752 	cputype = t;
753 	return t->family;
754 }
755 
756 static long
757 cputyperead(Chan*, void *a, long n, vlong offset)
758 {
759 	char str[32];
760 	ulong mhz;
761 
762 	mhz = (m->cpuhz+999999)/1000000;
763 
764 	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
765 	return readstr(offset, a, n, str);
766 }
767 
768 static long
769 archctlread(Chan*, void *a, long nn, vlong offset)
770 {
771 	char buf[256];
772 	int n;
773 
774 	n = snprint(buf, sizeof buf, "cpu %s %lud%s\n",
775 		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
776 		m->havepge ? " pge" : "");
777 	n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off");
778 	n += snprint(buf+n, sizeof buf-n, "coherence ");
779 	if(coherence == mb386)
780 		n += snprint(buf+n, sizeof buf-n, "mb386\n");
781 	else if(coherence == mb586)
782 		n += snprint(buf+n, sizeof buf-n, "mb586\n");
783 	else if(coherence == nop)
784 		n += snprint(buf+n, sizeof buf-n, "nop\n");
785 	else
786 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence);
787 	n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off");
788 	buf[n] = 0;
789 	return readstr(offset, a, nn, buf);
790 }
791 
792 enum
793 {
794 	CMpge,
795 	CMcoherence,
796 	CMi8253set,
797 };
798 
799 static Cmdtab archctlmsg[] =
800 {
801 	CMpge,		"pge",		2,
802 	CMcoherence,	"coherence",	2,
803 	CMi8253set,	"i8253set",	2,
804 };
805 
806 static long
807 archctlwrite(Chan*, void *a, long n, vlong)
808 {
809 	Cmdbuf *cb;
810 	Cmdtab *ct;
811 
812 	cb = parsecmd(a, n);
813 	if(waserror()){
814 		free(cb);
815 		nexterror();
816 	}
817 	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
818 	switch(ct->index){
819 	case CMpge:
820 		if(!m->havepge)
821 			error("processor does not support pge");
822 		if(strcmp(cb->f[1], "on") == 0)
823 			putcr4(getcr4() | 0x80);
824 		else if(strcmp(cb->f[1], "off") == 0)
825 			putcr4(getcr4() & ~0x80);
826 		else
827 			cmderror(cb, "invalid pge ctl");
828 		break;
829 	case CMcoherence:
830 		if(strcmp(cb->f[1], "mb386") == 0)
831 			coherence = mb386;
832 		else if(strcmp(cb->f[1], "mb586") == 0){
833 			if(X86FAMILY(m->cpuidax) < 5)
834 				error("invalid coherence ctl on this cpu family");
835 			coherence = mb586;
836 		}
837 		else if(strcmp(cb->f[1], "nop") == 0){
838 			/* only safe on vmware */
839 			if(conf.nmach > 1)
840 				error("cannot disable coherence on a multiprocessor");
841 			coherence = nop;
842 		}else
843 			cmderror(cb, "invalid coherence ctl");
844 		break;
845 	case CMi8253set:
846 		if(strcmp(cb->f[1], "on") == 0)
847 			doi8253set = 1;
848 		else if(strcmp(cb->f[1], "off") == 0){
849 			doi8253set = 0;
850 			(*arch->timerset)(0);
851 		}else
852 			cmderror(cb, "invalid i2853set ctl");
853 		break;
854 	}
855 	free(cb);
856 	poperror();
857 	return n;
858 }
859 
860 void
861 archinit(void)
862 {
863 	PCArch **p;
864 
865 	arch = 0;
866 	for(p = knownarch; *p; p++){
867 		if((*p)->ident && (*p)->ident() == 0){
868 			arch = *p;
869 			break;
870 		}
871 	}
872 	if(arch == 0)
873 		arch = &archgeneric;
874 	else{
875 		if(arch->id == 0)
876 			arch->id = archgeneric.id;
877 		if(arch->reset == 0)
878 			arch->reset = archgeneric.reset;
879 		if(arch->serialpower == 0)
880 			arch->serialpower = archgeneric.serialpower;
881 		if(arch->modempower == 0)
882 			arch->modempower = archgeneric.modempower;
883 		if(arch->intrinit == 0)
884 			arch->intrinit = archgeneric.intrinit;
885 		if(arch->intrenable == 0)
886 			arch->intrenable = archgeneric.intrenable;
887 	}
888 
889 	/*
890 	 *  Decide whether to use copy-on-reference (386 and mp).
891 	 *  We get another chance to set it in mpinit() for a
892 	 *  multiprocessor.
893 	 */
894 	if(X86FAMILY(m->cpuidax) == 3)
895 		conf.copymode = 1;
896 
897 	if(X86FAMILY(m->cpuidax) >= 5)
898 		coherence = mb586;
899 
900 	addarchfile("cputype", 0444, cputyperead, nil);
901 	addarchfile("archctl", 0664, archctlread, archctlwrite);
902 }
903 
904 /*
905  *  call either the pcmcia or pccard device setup
906  */
907 int
908 pcmspecial(char *idstr, ISAConf *isa)
909 {
910 	return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
911 }
912 
913 /*
914  *  call either the pcmcia or pccard device teardown
915  */
916 void
917 pcmspecialclose(int a)
918 {
919 	if (_pcmspecialclose != nil)
920 		_pcmspecialclose(a);
921 }
922 
923 /*
924  *  return value and speed of timer set in arch->clockenable
925  */
926 uvlong
927 fastticks(uvlong *hz)
928 {
929 	return (*arch->fastclock)(hz);
930 }
931 
932 /*
933  *  set next timer interrupt
934  */
935 void
936 timerset(uvlong x)
937 {
938 	if(doi8253set)
939 		(*arch->timerset)(x);
940 }
941