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