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