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