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