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