xref: /plan9-contrib/sys/src/9/pc/devarch.c (revision 5e4924093ecb86f7174bf23023955abc83fb6962)
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 	sprint(buf+i, "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
668 		m->cpuidtype, m->cpuidax, m->cpuiddx);
669 	print(buf);
670 }
671 
672 /*
673  *  figure out:
674  *	- cpu type
675  *	- whether or not we have a TSC (cycle counter)
676  *	- whether or not it supports page size extensions
677  *		(if so turn it on)
678  *	- whether or not it supports machine check exceptions
679  *		(if so turn it on)
680  *	- whether or not it supports the page global flag
681  *		(if so turn it on)
682  */
683 int
684 cpuidentify(void)
685 {
686 	char *p;
687 	int family, model, nomce;
688 	X86type *t, *tab;
689 	ulong cr4;
690 	vlong mca, mct;
691 
692 	cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
693 	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
694 		tab = x86amd;
695 	else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
696 		tab = x86winchip;
697 	else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
698 		tab = x86sis;
699 	else
700 		tab = x86intel;
701 
702 	family = X86FAMILY(m->cpuidax);
703 	model = X86MODEL(m->cpuidax);
704 	for(t=tab; t->name; t++)
705 		if((t->family == family && t->model == model)
706 		|| (t->family == family && t->model == -1)
707 		|| (t->family == -1))
708 			break;
709 
710 	m->cpuidtype = t->name;
711 
712 	/*
713 	 *  if there is one, set tsc to a known value
714 	 */
715 	if(m->cpuiddx & 0x10){
716 		m->havetsc = 1;
717 		cycles = _cycles;
718 		if(m->cpuiddx & 0x20)
719 			wrmsr(0x10, 0);
720 	}
721 
722 	/*
723  	 *  use i8253 to guess our cpu speed
724 	 */
725 	guesscpuhz(t->aalcycles);
726 
727 	/*
728 	 * If machine check exception, page size extensions or page global bit
729 	 * are supported enable them in CR4 and clear any other set extensions.
730 	 * If machine check was enabled clear out any lingering status.
731 	 */
732 	if(m->cpuiddx & 0x2088){
733 		cr4 = 0;
734 		if(m->cpuiddx & 0x08)
735 			cr4 |= 0x10;		/* page size extensions */
736 		if(p = getconf("*nomce"))
737 			nomce = strtoul(p, 0, 0);
738 		else
739 			nomce = 0;
740 		if((m->cpuiddx & 0x80) && !nomce){
741 			cr4 |= 0x40;		/* machine check enable */
742 			if(family == 5){
743 				rdmsr(0x00, &mca);
744 				rdmsr(0x01, &mct);
745 			}
746 		}
747 
748 		/*
749 		 * Detect whether the chip supports the global bit
750 		 * in page directory and page table entries.  When set
751 		 * in a particular entry, it means ``don't bother removing
752 		 * this from the TLB when CR3 changes.''
753 		 *
754 		 * We flag all kernel pages with this bit.  Doing so lessens the
755 		 * overhead of switching processes on bare hardware,
756 		 * even more so on VMware.  See mmu.c:/^memglobal.
757 		 *
758 		 * For future reference, should we ever need to do a
759 		 * full TLB flush, it can be accomplished by clearing
760 		 * the PGE bit in CR4, writing to CR3, and then
761 		 * restoring the PGE bit.
762 		 */
763 		if(m->cpuiddx & 0x2000){
764 			cr4 |= 0x80;		/* page global enable bit */
765 			m->havepge = 1;
766 		}
767 
768 		putcr4(cr4);
769 		if(m->cpuiddx & 0x80)
770 			rdmsr(0x01, &mct);
771 	}
772 
773 	cputype = t;
774 	return t->family;
775 }
776 
777 static long
778 cputyperead(Chan*, void *a, long n, vlong offset)
779 {
780 	char str[32];
781 	ulong mhz;
782 
783 	mhz = (m->cpuhz+999999)/1000000;
784 
785 	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
786 	return readstr(offset, a, n, str);
787 }
788 
789 static long
790 archctlread(Chan*, void *a, long nn, vlong offset)
791 {
792 	char buf[256];
793 	int n;
794 
795 	n = snprint(buf, sizeof buf, "cpu %s %lud%s\n",
796 		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
797 		m->havepge ? " pge" : "");
798 	n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off");
799 	n += snprint(buf+n, sizeof buf-n, "coherence ");
800 	if(coherence == mb386)
801 		n += snprint(buf+n, sizeof buf-n, "mb386\n");
802 	else if(coherence == mb586)
803 		n += snprint(buf+n, sizeof buf-n, "mb586\n");
804 	else if(coherence == nop)
805 		n += snprint(buf+n, sizeof buf-n, "nop\n");
806 	else
807 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence);
808 	n += snprint(buf+n, sizeof buf-n, "cmpswap ");
809 	if(cmpswap == cmpswap386)
810 		n += snprint(buf+n, sizeof buf-n, "cmpswap386\n");
811 	else if(cmpswap == cmpswap486)
812 		n += snprint(buf+n, sizeof buf-n, "cmpswap486\n");
813 	else
814 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap);
815 	n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off");
816 	buf[n] = 0;
817 	return readstr(offset, a, nn, buf);
818 }
819 
820 enum
821 {
822 	CMpge,
823 	CMcoherence,
824 	CMi8253set,
825 };
826 
827 static Cmdtab archctlmsg[] =
828 {
829 	CMpge,		"pge",		2,
830 	CMcoherence,	"coherence",	2,
831 	CMi8253set,	"i8253set",	2,
832 };
833 
834 static long
835 archctlwrite(Chan*, void *a, long n, vlong)
836 {
837 	Cmdbuf *cb;
838 	Cmdtab *ct;
839 
840 	cb = parsecmd(a, n);
841 	if(waserror()){
842 		free(cb);
843 		nexterror();
844 	}
845 	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
846 	switch(ct->index){
847 	case CMpge:
848 		if(!m->havepge)
849 			error("processor does not support pge");
850 		if(strcmp(cb->f[1], "on") == 0)
851 			putcr4(getcr4() | 0x80);
852 		else if(strcmp(cb->f[1], "off") == 0)
853 			putcr4(getcr4() & ~0x80);
854 		else
855 			cmderror(cb, "invalid pge ctl");
856 		break;
857 	case CMcoherence:
858 		if(strcmp(cb->f[1], "mb386") == 0)
859 			coherence = mb386;
860 		else if(strcmp(cb->f[1], "mb586") == 0){
861 			if(X86FAMILY(m->cpuidax) < 5)
862 				error("invalid coherence ctl on this cpu family");
863 			coherence = mb586;
864 		}
865 		else if(strcmp(cb->f[1], "nop") == 0){
866 			/* only safe on vmware */
867 			if(conf.nmach > 1)
868 				error("cannot disable coherence on a multiprocessor");
869 			coherence = nop;
870 		}else
871 			cmderror(cb, "invalid coherence ctl");
872 		break;
873 	case CMi8253set:
874 		if(strcmp(cb->f[1], "on") == 0)
875 			doi8253set = 1;
876 		else if(strcmp(cb->f[1], "off") == 0){
877 			doi8253set = 0;
878 			(*arch->timerset)(0);
879 		}else
880 			cmderror(cb, "invalid i2853set ctl");
881 		break;
882 	}
883 	free(cb);
884 	poperror();
885 	return n;
886 }
887 
888 void
889 archinit(void)
890 {
891 	PCArch **p;
892 
893 	arch = 0;
894 	for(p = knownarch; *p; p++){
895 		if((*p)->ident && (*p)->ident() == 0){
896 			arch = *p;
897 			break;
898 		}
899 	}
900 	if(arch == 0)
901 		arch = &archgeneric;
902 	else{
903 		if(arch->id == 0)
904 			arch->id = archgeneric.id;
905 		if(arch->reset == 0)
906 			arch->reset = archgeneric.reset;
907 		if(arch->serialpower == 0)
908 			arch->serialpower = archgeneric.serialpower;
909 		if(arch->modempower == 0)
910 			arch->modempower = archgeneric.modempower;
911 		if(arch->intrinit == 0)
912 			arch->intrinit = archgeneric.intrinit;
913 		if(arch->intrenable == 0)
914 			arch->intrenable = archgeneric.intrenable;
915 	}
916 
917 	/*
918 	 *  Decide whether to use copy-on-reference (386 and mp).
919 	 *  We get another chance to set it in mpinit() for a
920 	 *  multiprocessor.
921 	 */
922 	if(X86FAMILY(m->cpuidax) == 3)
923 		conf.copymode = 1;
924 
925 	if(X86FAMILY(m->cpuidax) >= 4)
926 		cmpswap = cmpswap486;
927 
928 	if(X86FAMILY(m->cpuidax) >= 5)
929 		coherence = mb586;
930 
931 	addarchfile("cputype", 0444, cputyperead, nil);
932 	addarchfile("archctl", 0664, archctlread, archctlwrite);
933 }
934 
935 /*
936  *  call either the pcmcia or pccard device setup
937  */
938 int
939 pcmspecial(char *idstr, ISAConf *isa)
940 {
941 	return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
942 }
943 
944 /*
945  *  call either the pcmcia or pccard device teardown
946  */
947 void
948 pcmspecialclose(int a)
949 {
950 	if (_pcmspecialclose != nil)
951 		_pcmspecialclose(a);
952 }
953 
954 /*
955  *  return value and speed of timer set in arch->clockenable
956  */
957 uvlong
958 fastticks(uvlong *hz)
959 {
960 	return (*arch->fastclock)(hz);
961 }
962 
963 /*
964  *  set next timer interrupt
965  */
966 void
967 timerset(uvlong x)
968 {
969 	if(doi8253set)
970 		(*arch->timerset)(x);
971 }
972