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