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