Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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