xref: /plan9-contrib/sys/src/9/pc/devarch.c (revision ed2a258a218962e0b4e0f5cbbab48c6ce1bcbf02)
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 	{ 0xF,	1,	16,	"P4", },	/* P4 */
584 	{ 0xF,	2,	16,	"PentiumIV/Xeon", },
585 
586 	{ 3,	-1,	32,	"386", },	/* family defaults */
587 	{ 4,	-1,	22,	"486", },
588 	{ 5,	-1,	23,	"P5", },
589 	{ 6,	-1,	16,	"P6", },
590 	{ 0xF,	-1,	16,	"P4", },	/* P4 */
591 
592 	{ -1,	-1,	16,	"unknown", },	/* total default */
593 };
594 
595 /*
596  * The AMD processors all implement the CPUID instruction.
597  * The later ones also return the processor name via functions
598  * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
599  * and DX:
600  *	K5	"AMD-K5(tm) Processor"
601  *	K6	"AMD-K6tm w/ multimedia extensions"
602  *	K6 3D	"AMD-K6(tm) 3D processor"
603  *	K6 3D+	?
604  */
605 static X86type x86amd[] =
606 {
607 	{ 5,	0,	23,	"AMD-K5", },	/* guesswork */
608 	{ 5,	1,	23,	"AMD-K5", },	/* guesswork */
609 	{ 5,	2,	23,	"AMD-K5", },	/* guesswork */
610 	{ 5,	3,	23,	"AMD-K5", },	/* guesswork */
611 	{ 5,	6,	11,	"AMD-K6", },	/* trial and error */
612 	{ 5,	7,	11,	"AMD-K6", },	/* trial and error */
613 	{ 5,	8,	11,	"AMD-K6-2", },	/* trial and error */
614 	{ 5,	9,	11,	"AMD-K6-III", },/* trial and error */
615 
616 	{ 6,	1,	11,	"AMD-Athlon", },/* trial and error */
617 	{ 6,	2,	11,	"AMD-Athlon", },/* trial and error */
618 
619 	{ 4,	-1,	22,	"Am486", },	/* guesswork */
620 	{ 5,	-1,	23,	"AMD-K5/K6", },	/* guesswork */
621 	{ 6,	-1,	11,	"AMD-Athlon", },/* guesswork */
622 	{ 0xF,	-1,	11,	"AMD64", },	/* guesswork */
623 
624 	{ -1,	-1,	11,	"unknown", },	/* total default */
625 };
626 
627 /*
628  * WinChip 240MHz
629  */
630 static X86type x86winchip[] =
631 {
632 	{5,	4,	23,	"Winchip",},	/* guesswork */
633 	{6,	7,	23,	"Via C3 Samuel 2 or Ezra",},
634 	{6,	8,	23,	"Via C3 Ezra-T",},
635 	{6,	9,	23,	"Via C3 Eden-N",},
636 	{ -1,	-1,	23,	"unknown", },	/* total default */
637 };
638 
639 /*
640  * SiS 55x
641  */
642 static X86type x86sis[] =
643 {
644 	{5,	0,	23,	"SiS 55x",},	/* guesswork */
645 	{ -1,	-1,	23,	"unknown", },	/* total default */
646 };
647 
648 static X86type *cputype;
649 
650 static void	simplecycles(uvlong*);
651 void	(*cycles)(uvlong*) = simplecycles;
652 void	_cycles(uvlong*);	/* in l.s */
653 
654 static void
655 simplecycles(uvlong*x)
656 {
657 	*x = m->ticks;
658 }
659 
660 void
661 cpuidprint(void)
662 {
663 	int i;
664 	char buf[128];
665 
666 	i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz);
667 	if(m->cpuidid[0])
668 		i += sprint(buf+i, "%12.12s ", m->cpuidid);
669 	seprint(buf+i, buf + sizeof buf - 1,
670 		"%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
671 		m->cpuidtype, m->cpuidax, m->cpuiddx);
672 	print(buf);
673 }
674 
675 /*
676  *  figure out:
677  *	- cpu type
678  *	- whether or not we have a TSC (cycle counter)
679  *	- whether or not it supports page size extensions
680  *		(if so turn it on)
681  *	- whether or not it supports machine check exceptions
682  *		(if so turn it on)
683  *	- whether or not it supports the page global flag
684  *		(if so turn it on)
685  */
686 int
687 cpuidentify(void)
688 {
689 	char *p;
690 	int family, model, nomce;
691 	X86type *t, *tab;
692 	ulong cr4;
693 	vlong mca, mct;
694 
695 	cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
696 	if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
697 		tab = x86amd;
698 	else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
699 		tab = x86winchip;
700 	else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
701 		tab = x86sis;
702 	else
703 		tab = x86intel;
704 
705 	family = X86FAMILY(m->cpuidax);
706 	model = X86MODEL(m->cpuidax);
707 	for(t=tab; t->name; t++)
708 		if((t->family == family && t->model == model)
709 		|| (t->family == family && t->model == -1)
710 		|| (t->family == -1))
711 			break;
712 
713 	m->cpuidtype = t->name;
714 
715 	/*
716 	 *  if there is one, set tsc to a known value
717 	 */
718 	if(m->cpuiddx & 0x10){
719 		m->havetsc = 1;
720 		cycles = _cycles;
721 		if(m->cpuiddx & 0x20)
722 			wrmsr(0x10, 0);
723 	}
724 
725 	/*
726  	 *  use i8253 to guess our cpu speed
727 	 */
728 	guesscpuhz(t->aalcycles);
729 
730 	/*
731 	 * If machine check exception, page size extensions or page global bit
732 	 * are supported enable them in CR4 and clear any other set extensions.
733 	 * If machine check was enabled clear out any lingering status.
734 	 */
735 	if(m->cpuiddx & 0x2088){
736 		cr4 = 0;
737 		if(m->cpuiddx & 0x08)
738 			cr4 |= 0x10;		/* page size extensions */
739 		if(p = getconf("*nomce"))
740 			nomce = strtoul(p, 0, 0);
741 		else
742 			nomce = 0;
743 		if((m->cpuiddx & 0x80) && !nomce){
744 			cr4 |= 0x40;		/* machine check enable */
745 			if(family == 5){
746 				rdmsr(0x00, &mca);
747 				rdmsr(0x01, &mct);
748 			}
749 		}
750 
751 		/*
752 		 * Detect whether the chip supports the global bit
753 		 * in page directory and page table entries.  When set
754 		 * in a particular entry, it means ``don't bother removing
755 		 * this from the TLB when CR3 changes.''
756 		 *
757 		 * We flag all kernel pages with this bit.  Doing so lessens the
758 		 * overhead of switching processes on bare hardware,
759 		 * even more so on VMware.  See mmu.c:/^memglobal.
760 		 *
761 		 * For future reference, should we ever need to do a
762 		 * full TLB flush, it can be accomplished by clearing
763 		 * the PGE bit in CR4, writing to CR3, and then
764 		 * restoring the PGE bit.
765 		 */
766 		if(m->cpuiddx & 0x2000){
767 			cr4 |= 0x80;		/* page global enable bit */
768 			m->havepge = 1;
769 		}
770 
771 		putcr4(cr4);
772 		if(m->cpuiddx & 0x80)
773 			rdmsr(0x01, &mct);
774 	}
775 
776 	cputype = t;
777 	return t->family;
778 }
779 
780 static long
781 cputyperead(Chan*, void *a, long n, vlong offset)
782 {
783 	char str[32];
784 	ulong mhz;
785 
786 	mhz = (m->cpuhz+999999)/1000000;
787 
788 	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
789 	return readstr(offset, a, n, str);
790 }
791 
792 static long
793 archctlread(Chan*, void *a, long nn, vlong offset)
794 {
795 	char buf[256];
796 	int n;
797 
798 	n = snprint(buf, sizeof buf, "cpu %s %lud%s\n",
799 		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
800 		m->havepge ? " pge" : "");
801 	n += snprint(buf+n, sizeof buf-n, "pge %s\n", getcr4()&0x80 ? "on" : "off");
802 	n += snprint(buf+n, sizeof buf-n, "coherence ");
803 	if(coherence == mb386)
804 		n += snprint(buf+n, sizeof buf-n, "mb386\n");
805 	else if(coherence == mb586)
806 		n += snprint(buf+n, sizeof buf-n, "mb586\n");
807 	else if(coherence == nop)
808 		n += snprint(buf+n, sizeof buf-n, "nop\n");
809 	else
810 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", coherence);
811 	n += snprint(buf+n, sizeof buf-n, "cmpswap ");
812 	if(cmpswap == cmpswap386)
813 		n += snprint(buf+n, sizeof buf-n, "cmpswap386\n");
814 	else if(cmpswap == cmpswap486)
815 		n += snprint(buf+n, sizeof buf-n, "cmpswap486\n");
816 	else
817 		n += snprint(buf+n, sizeof buf-n, "0x%p\n", cmpswap);
818 	n += snprint(buf+n, sizeof buf-n, "i8253set %s\n", doi8253set ? "on" : "off");
819 	buf[n] = 0;
820 	return readstr(offset, a, nn, buf);
821 }
822 
823 enum
824 {
825 	CMpge,
826 	CMcoherence,
827 	CMi8253set,
828 };
829 
830 static Cmdtab archctlmsg[] =
831 {
832 	CMpge,		"pge",		2,
833 	CMcoherence,	"coherence",	2,
834 	CMi8253set,	"i8253set",	2,
835 };
836 
837 static long
838 archctlwrite(Chan*, void *a, long n, vlong)
839 {
840 	Cmdbuf *cb;
841 	Cmdtab *ct;
842 
843 	cb = parsecmd(a, n);
844 	if(waserror()){
845 		free(cb);
846 		nexterror();
847 	}
848 	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
849 	switch(ct->index){
850 	case CMpge:
851 		if(!m->havepge)
852 			error("processor does not support pge");
853 		if(strcmp(cb->f[1], "on") == 0)
854 			putcr4(getcr4() | 0x80);
855 		else if(strcmp(cb->f[1], "off") == 0)
856 			putcr4(getcr4() & ~0x80);
857 		else
858 			cmderror(cb, "invalid pge ctl");
859 		break;
860 	case CMcoherence:
861 		if(strcmp(cb->f[1], "mb386") == 0)
862 			coherence = mb386;
863 		else if(strcmp(cb->f[1], "mb586") == 0){
864 			if(X86FAMILY(m->cpuidax) < 5)
865 				error("invalid coherence ctl on this cpu family");
866 			coherence = mb586;
867 		}
868 		else if(strcmp(cb->f[1], "nop") == 0){
869 			/* only safe on vmware */
870 			if(conf.nmach > 1)
871 				error("cannot disable coherence on a multiprocessor");
872 			coherence = nop;
873 		}else
874 			cmderror(cb, "invalid coherence ctl");
875 		break;
876 	case CMi8253set:
877 		if(strcmp(cb->f[1], "on") == 0)
878 			doi8253set = 1;
879 		else if(strcmp(cb->f[1], "off") == 0){
880 			doi8253set = 0;
881 			(*arch->timerset)(0);
882 		}else
883 			cmderror(cb, "invalid i2853set ctl");
884 		break;
885 	}
886 	free(cb);
887 	poperror();
888 	return n;
889 }
890 
891 void
892 archinit(void)
893 {
894 	PCArch **p;
895 
896 	arch = 0;
897 	for(p = knownarch; *p; p++){
898 		if((*p)->ident && (*p)->ident() == 0){
899 			arch = *p;
900 			break;
901 		}
902 	}
903 	if(arch == 0)
904 		arch = &archgeneric;
905 	else{
906 		if(arch->id == 0)
907 			arch->id = archgeneric.id;
908 		if(arch->reset == 0)
909 			arch->reset = archgeneric.reset;
910 		if(arch->serialpower == 0)
911 			arch->serialpower = archgeneric.serialpower;
912 		if(arch->modempower == 0)
913 			arch->modempower = archgeneric.modempower;
914 		if(arch->intrinit == 0)
915 			arch->intrinit = archgeneric.intrinit;
916 		if(arch->intrenable == 0)
917 			arch->intrenable = archgeneric.intrenable;
918 	}
919 
920 	/*
921 	 *  Decide whether to use copy-on-reference (386 and mp).
922 	 *  We get another chance to set it in mpinit() for a
923 	 *  multiprocessor.
924 	 */
925 	if(X86FAMILY(m->cpuidax) == 3)
926 		conf.copymode = 1;
927 
928 	if(X86FAMILY(m->cpuidax) >= 4)
929 		cmpswap = cmpswap486;
930 
931 	if(X86FAMILY(m->cpuidax) >= 5)
932 		coherence = mb586;
933 
934 	addarchfile("cputype", 0444, cputyperead, nil);
935 	addarchfile("archctl", 0664, archctlread, archctlwrite);
936 }
937 
938 /*
939  *  call either the pcmcia or pccard device setup
940  */
941 int
942 pcmspecial(char *idstr, ISAConf *isa)
943 {
944 	return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
945 }
946 
947 /*
948  *  call either the pcmcia or pccard device teardown
949  */
950 void
951 pcmspecialclose(int a)
952 {
953 	if (_pcmspecialclose != nil)
954 		_pcmspecialclose(a);
955 }
956 
957 /*
958  *  return value and speed of timer set in arch->clockenable
959  */
960 uvlong
961 fastticks(uvlong *hz)
962 {
963 	return (*arch->fastclock)(hz);
964 }
965 
966 ulong
967 µs(void)
968 {
969 	return fastticks2us((*arch->fastclock)(nil));
970 }
971 
972 /*
973  *  set next timer interrupt
974  */
975 void
976 timerset(uvlong x)
977 {
978 	if(doi8253set)
979 		(*arch->timerset)(x);
980 }
981