]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devarch.c
merge
[plan9front.git] / sys / src / 9 / pc / devarch.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9
10 typedef struct IOMap IOMap;
11 struct IOMap
12 {
13         IOMap   *next;
14         int     reserved;
15         char    tag[13];
16         ulong   start;
17         ulong   end;
18 };
19
20 static struct
21 {
22         Lock;
23         IOMap   *m;
24         IOMap   *free;
25         IOMap   maps[32];       /* some initial free maps */
26
27         QLock   ql;             /* lock for reading map */
28 } iomap;
29
30 enum {
31         Qdir = 0,
32         Qioalloc = 1,
33         Qiob,
34         Qiow,
35         Qiol,
36         Qmsr,
37         Qbase,
38
39         Qmax = 16,
40 };
41
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         if(strcmp(m->cpuidid, "GenuineIntel") == 0 && (m->cpuidcx & Rdrnd) != 0)
876                 hwrandbuf = rdrandbuf;
877         else
878                 hwrandbuf = nil;
879
880         cputype = t;
881         return t->family;
882 }
883
884 static long
885 cputyperead(Chan*, void *a, long n, vlong offset)
886 {
887         char str[32];
888         ulong mhz;
889
890         mhz = (m->cpuhz+999999)/1000000;
891
892         snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
893         return readstr(offset, a, n, str);
894 }
895
896 static long
897 archctlread(Chan*, void *a, long nn, vlong offset)
898 {
899         int n;
900         char *buf, *p, *ep;
901
902         p = buf = smalloc(READSTR);
903         ep = p + READSTR;
904         p = seprint(p, ep, "cpu %s %lud%s\n",
905                 cputype->name, (ulong)(m->cpuhz+999999)/1000000,
906                 m->havepge ? " pge" : "");
907         p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
908         p = seprint(p, ep, "coherence ");
909         if(coherence == mb386)
910                 p = seprint(p, ep, "mb386\n");
911         else if(coherence == mb586)
912                 p = seprint(p, ep, "mb586\n");
913         else if(coherence == mfence)
914                 p = seprint(p, ep, "mfence\n");
915         else if(coherence == nop)
916                 p = seprint(p, ep, "nop\n");
917         else
918                 p = seprint(p, ep, "0x%p\n", coherence);
919         p = seprint(p, ep, "cmpswap ");
920         if(cmpswap == cmpswap386)
921                 p = seprint(p, ep, "cmpswap386\n");
922         else if(cmpswap == cmpswap486)
923                 p = seprint(p, ep, "cmpswap486\n");
924         else
925                 p = seprint(p, ep, "0x%p\n", cmpswap);
926         p = seprint(p, ep, "i8253set %s\n", doi8253set ? "on" : "off");
927         p = seprint(p, ep, "arch %s\n", arch->id);
928         n = p - buf;
929         n += mtrrprint(p, ep - p);
930         buf[n] = '\0';
931
932         n = readstr(offset, a, nn, buf);
933         free(buf);
934         return n;
935 }
936
937 enum
938 {
939         CMpge,
940         CMcoherence,
941         CMi8253set,
942         CMcache,
943 };
944
945 static Cmdtab archctlmsg[] =
946 {
947         CMpge,          "pge",          2,
948         CMcoherence,    "coherence",    2,
949         CMi8253set,     "i8253set",     2,
950         CMcache,        "cache",        4,
951 };
952
953 static long
954 archctlwrite(Chan*, void *a, long n, vlong)
955 {
956         uvlong base, size;
957         Cmdbuf *cb;
958         Cmdtab *ct;
959         char *ep;
960
961         cb = parsecmd(a, n);
962         if(waserror()){
963                 free(cb);
964                 nexterror();
965         }
966         ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
967         switch(ct->index){
968         case CMpge:
969                 if(!m->havepge)
970                         error("processor does not support pge");
971                 if(strcmp(cb->f[1], "on") == 0)
972                         putcr4(getcr4() | 0x80);
973                 else if(strcmp(cb->f[1], "off") == 0)
974                         putcr4(getcr4() & ~0x80);
975                 else
976                         cmderror(cb, "invalid pge ctl");
977                 break;
978         case CMcoherence:
979                 if(strcmp(cb->f[1], "mb386") == 0)
980                         coherence = mb386;
981                 else if(strcmp(cb->f[1], "mb586") == 0){
982                         if(X86FAMILY(m->cpuidax) < 5)
983                                 error("invalid coherence ctl on this cpu family");
984                         coherence = mb586;
985                 }else if(strcmp(cb->f[1], "mfence") == 0){
986                         if((m->cpuiddx & Sse2) == 0)
987                                 error("invalid coherence ctl on this cpu family");
988                         coherence = mfence;
989                 }else if(strcmp(cb->f[1], "nop") == 0){
990                         /* only safe on vmware */
991                         if(conf.nmach > 1)
992                                 error("cannot disable coherence on a multiprocessor");
993                         coherence = nop;
994                 }else
995                         cmderror(cb, "invalid coherence ctl");
996                 break;
997         case CMi8253set:
998                 if(strcmp(cb->f[1], "on") == 0)
999                         doi8253set = 1;
1000                 else if(strcmp(cb->f[1], "off") == 0){
1001                         doi8253set = 0;
1002                         (*arch->timerset)(0);
1003                 }else
1004                         cmderror(cb, "invalid i2853set ctl");
1005                 break;
1006         case CMcache:
1007                 base = strtoull(cb->f[1], &ep, 0);
1008                 if(*ep)
1009                         error("cache: parse error: base not a number?");
1010                 size = strtoull(cb->f[2], &ep, 0);
1011                 if(*ep)
1012                         error("cache: parse error: size not a number?");
1013                 ep = mtrr(base, size, cb->f[3]);
1014                 if(ep != nil)
1015                         error(ep);
1016                 break;
1017         }
1018         free(cb);
1019         poperror();
1020         return n;
1021 }
1022
1023 static long
1024 rmemrw(int isr, void *a, long n, vlong off)
1025 {
1026         uintptr addr = off;
1027
1028         if(off < 0 || n < 0)
1029                 error("bad offset/count");
1030         if(isr){
1031                 if(addr >= MB)
1032                         return 0;
1033                 if(addr+n > MB)
1034                         n = MB - addr;
1035                 memmove(a, KADDR(addr), n);
1036         }else{
1037                 /* allow vga framebuf's write access */
1038                 if(addr >= MB || addr+n > MB ||
1039                     (addr < 0xA0000 || addr+n > 0xB0000+0x10000))
1040                         error("bad offset/count in write");
1041                 memmove(KADDR(addr), a, n);
1042         }
1043         return n;
1044 }
1045
1046 static long
1047 rmemread(Chan*, void *a, long n, vlong off)
1048 {
1049         return rmemrw(1, a, n, off);
1050 }
1051
1052 static long
1053 rmemwrite(Chan*, void *a, long n, vlong off)
1054 {
1055         return rmemrw(0, a, n, off);
1056 }
1057
1058 void
1059 archinit(void)
1060 {
1061         PCArch **p;
1062
1063         arch = &archgeneric;
1064         for(p = knownarch; *p != nil; p++){
1065                 if((*p)->ident != nil && (*p)->ident() == 0){
1066                         arch = *p;
1067                         break;
1068                 }
1069         }
1070         if(arch != &archgeneric){
1071                 if(arch->id == nil)
1072                         arch->id = archgeneric.id;
1073                 if(arch->reset == nil)
1074                         arch->reset = archgeneric.reset;
1075                 if(arch->serialpower == nil)
1076                         arch->serialpower = archgeneric.serialpower;
1077                 if(arch->modempower == nil)
1078                         arch->modempower = archgeneric.modempower;
1079                 if(arch->intrinit == nil)
1080                         arch->intrinit = archgeneric.intrinit;
1081                 if(arch->intrenable == nil)
1082                         arch->intrenable = archgeneric.intrenable;
1083         }
1084
1085         /*
1086          *  Decide whether to use copy-on-reference (386 and mp).
1087          *  We get another chance to set it in mpinit() for a
1088          *  multiprocessor.
1089          */
1090         if(X86FAMILY(m->cpuidax) == 3)
1091                 conf.copymode = 1;
1092
1093         if(X86FAMILY(m->cpuidax) >= 4)
1094                 cmpswap = cmpswap486;
1095
1096         if(X86FAMILY(m->cpuidax) >= 5)
1097                 coherence = mb586;
1098
1099         if(m->cpuiddx & Sse2)
1100                 coherence = mfence;
1101
1102         addarchfile("cputype", 0444, cputyperead, nil);
1103         addarchfile("archctl", 0664, archctlread, archctlwrite);
1104         addarchfile("realmodemem", 0660, rmemread, rmemwrite);
1105 }
1106
1107 /*
1108  *  call either the pcmcia or pccard device setup
1109  */
1110 int
1111 pcmspecial(char *idstr, ISAConf *isa)
1112 {
1113         return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
1114 }
1115
1116 /*
1117  *  call either the pcmcia or pccard device teardown
1118  */
1119 void
1120 pcmspecialclose(int a)
1121 {
1122         if (_pcmspecialclose != nil)
1123                 _pcmspecialclose(a);
1124 }
1125
1126 /*
1127  *  return value and speed of timer set in arch->clockenable
1128  */
1129 uvlong
1130 fastticks(uvlong *hz)
1131 {
1132         return (*arch->fastclock)(hz);
1133 }
1134
1135 ulong
1136 µs(void)
1137 {
1138         return fastticks2us((*arch->fastclock)(nil));
1139 }
1140
1141 /*
1142  *  set next timer interrupt
1143  */
1144 void
1145 timerset(Tval x)
1146 {
1147         if(doi8253set)
1148                 (*arch->timerset)(x);
1149 }
1150
1151 /*
1152  *  put the processor in the halt state if we've no processes to run.
1153  *  an interrupt will get us going again.
1154  *
1155  *  halting in an smp system can result in a startup latency for
1156  *  processes that become ready.
1157  *  if idle_spin is zero, we care more about saving energy
1158  *  than reducing this latency.
1159  *
1160  *  the performance loss with idle_spin == 0 seems to be slight
1161  *  and it reduces lock contention (thus system time and real time)
1162  *  on many-core systems with large values of NPROC.
1163  */
1164 void
1165 idlehands(void)
1166 {
1167         extern int nrdy, idle_spin;
1168
1169         if(conf.nmach == 1)
1170                 halt();
1171         else if(m->cpuidcx & Monitor)
1172                 mwait(&nrdy);
1173         else if(idle_spin == 0)
1174                 halt();
1175 }
1176
1177 int
1178 isaconfig(char *class, int ctlrno, ISAConf *isa)
1179 {
1180         char cc[32], *p;
1181         int i;
1182
1183         snprint(cc, sizeof cc, "%s%d", class, ctlrno);
1184         p = getconf(cc);
1185         if(p == nil)
1186                 return 0;
1187
1188         isa->type = "";
1189         isa->nopt = tokenize(p, isa->opt, NISAOPT);
1190         for(i = 0; i < isa->nopt; i++){
1191                 p = isa->opt[i];
1192                 if(cistrncmp(p, "type=", 5) == 0)
1193                         isa->type = p + 5;
1194                 else if(cistrncmp(p, "port=", 5) == 0)
1195                         isa->port = strtoul(p+5, &p, 0);
1196                 else if(cistrncmp(p, "irq=", 4) == 0)
1197                         isa->irq = strtoul(p+4, &p, 0);
1198                 else if(cistrncmp(p, "dma=", 4) == 0)
1199                         isa->dma = strtoul(p+4, &p, 0);
1200                 else if(cistrncmp(p, "mem=", 4) == 0)
1201                         isa->mem = strtoul(p+4, &p, 0);
1202                 else if(cistrncmp(p, "size=", 5) == 0)
1203                         isa->size = strtoul(p+5, &p, 0);
1204                 else if(cistrncmp(p, "freq=", 5) == 0)
1205                         isa->freq = strtoul(p+5, &p, 0);
1206         }
1207         return 1;
1208 }
1209
1210 void
1211 dumpmcregs(void)
1212 {
1213         vlong v, w;
1214         int bank;
1215
1216         if((m->cpuiddx & (Mce|Cpumsr)) != (Mce|Cpumsr))
1217                 return;
1218         if((m->cpuiddx & Mca) == 0){
1219                 rdmsr(0x00, &v);
1220                 rdmsr(0x01, &w);
1221                 iprint("MCA %8.8llux MCT %8.8llux\n", v, w);
1222                 return;
1223         }
1224         rdmsr(0x179, &v);
1225         rdmsr(0x17A, &w);
1226         iprint("MCG CAP %.16llux STATUS %.16llux\n", v, w);
1227
1228         bank = v & 0xFF;
1229         if(bank > 64)
1230                 bank = 64;
1231         while(--bank >= 0){
1232                 rdmsr(0x401 + bank*4, &v);
1233                 if((v & (1ull << 63)) == 0)
1234                         continue;
1235                 iprint("MC%d STATUS %.16llux", bank, v);
1236                 if(v & (1ull << 58)){
1237                         rdmsr(0x402 + bank*4, &w);
1238                         iprint(" ADDR %.16llux", w);
1239                 }
1240                 if(v & (1ull << 59)){
1241                         rdmsr(0x403 + bank*4, &w);
1242                         iprint(" MISC %.16llux", w);
1243                 }
1244                 iprint("\n");
1245         }
1246 }