]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devarch.c
pc, pc64: include PCArch.id in #P/archctl simplify #P/ioalloc read handler
[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         /*
788          *  use i8253 to guess our cpu speed
789          */
790         guesscpuhz(t->aalcycles);
791
792         /*
793          * If machine check exception, page size extensions or page global bit
794          * are supported enable them in CR4 and clear any other set extensions.
795          * If machine check was enabled clear out any lingering status.
796          */
797         if(m->cpuiddx & (Pge|Mce|Pse)){
798                 cr4 = getcr4();
799                 if(m->cpuiddx & Pse)
800                         cr4 |= 0x10;            /* page size extensions */
801                 if(p = getconf("*nomce"))
802                         nomce = strtoul(p, 0, 0);
803                 else
804                         nomce = 0;
805                 if((m->cpuiddx & Mce) != 0 && !nomce){
806                         if((m->cpuiddx & Mca) != 0){
807                                 vlong cap;
808                                 int bank;
809
810                                 cap = 0;
811                                 rdmsr(0x179, &cap);
812
813                                 if(cap & 0x100)
814                                         wrmsr(0x17B, ~0ULL);    /* enable all mca features */
815
816                                 bank = cap & 0xFF;
817                                 if(bank > 64)
818                                         bank = 64;
819
820                                 /* init MCi .. MC1 (except MC0) */
821                                 while(--bank > 0){
822                                         wrmsr(0x400 + bank*4, ~0ULL);
823                                         wrmsr(0x401 + bank*4, 0);
824                                 }
825
826                                 if(family != 6 || model >= 0x1A)
827                                         wrmsr(0x400, ~0ULL);
828
829                                 wrmsr(0x401, 0);
830                         }
831                         else if(family == 5){
832                                 rdmsr(0x00, &mca);
833                                 rdmsr(0x01, &mct);
834                         }
835                         cr4 |= 0x40;            /* machine check enable */
836                 }
837
838                 /*
839                  * Detect whether the chip supports the global bit
840                  * in page directory and page table entries.  When set
841                  * in a particular entry, it means ``don't bother removing
842                  * this from the TLB when CR3 changes.''
843                  *
844                  * We flag all kernel pages with this bit.  Doing so lessens the
845                  * overhead of switching processes on bare hardware,
846                  * even more so on VMware.  See mmu.c:/^memglobal.
847                  *
848                  * For future reference, should we ever need to do a
849                  * full TLB flush, it can be accomplished by clearing
850                  * the PGE bit in CR4, writing to CR3, and then
851                  * restoring the PGE bit.
852                  */
853                 if(m->cpuiddx & Pge){
854                         cr4 |= 0x80;            /* page global enable bit */
855                         m->havepge = 1;
856                 }
857
858                 putcr4(cr4);
859
860                 if((m->cpuiddx & (Mca|Mce)) == Mce)
861                         rdmsr(0x01, &mct);
862         }
863
864         if(m->cpuiddx & Mtrr)
865                 mtrrsync();
866
867         if(m->cpuiddx & Fxsr){                  /* have sse fp? */
868                 fpsave = fpssesave;
869                 fprestore = fpsserestore;
870                 putcr4(getcr4() | CR4Osfxsr|CR4Oxmmex);
871         } else {
872                 fpsave = fpx87save;
873                 fprestore = fpx87restore;
874         }
875
876         cputype = t;
877         return t->family;
878 }
879
880 static long
881 cputyperead(Chan*, void *a, long n, vlong offset)
882 {
883         char str[32];
884         ulong mhz;
885
886         mhz = (m->cpuhz+999999)/1000000;
887
888         snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
889         return readstr(offset, a, n, str);
890 }
891
892 static long
893 archctlread(Chan*, void *a, long nn, vlong offset)
894 {
895         int n;
896         char *buf, *p, *ep;
897
898         p = buf = smalloc(READSTR);
899         ep = p + READSTR;
900         p = seprint(p, ep, "cpu %s %lud%s\n",
901                 cputype->name, (ulong)(m->cpuhz+999999)/1000000,
902                 m->havepge ? " pge" : "");
903         p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
904         p = seprint(p, ep, "coherence ");
905         if(coherence == mb386)
906                 p = seprint(p, ep, "mb386\n");
907         else if(coherence == mb586)
908                 p = seprint(p, ep, "mb586\n");
909         else if(coherence == mfence)
910                 p = seprint(p, ep, "mfence\n");
911         else if(coherence == nop)
912                 p = seprint(p, ep, "nop\n");
913         else
914                 p = seprint(p, ep, "0x%p\n", coherence);
915         p = seprint(p, ep, "cmpswap ");
916         if(cmpswap == cmpswap386)
917                 p = seprint(p, ep, "cmpswap386\n");
918         else if(cmpswap == cmpswap486)
919                 p = seprint(p, ep, "cmpswap486\n");
920         else
921                 p = seprint(p, ep, "0x%p\n", cmpswap);
922         p = seprint(p, ep, "i8253set %s\n", doi8253set ? "on" : "off");
923         p = seprint(p, ep, "arch %s\n", arch->id);
924         n = p - buf;
925         n += mtrrprint(p, ep - p);
926         buf[n] = '\0';
927
928         n = readstr(offset, a, nn, buf);
929         free(buf);
930         return n;
931 }
932
933 enum
934 {
935         CMpge,
936         CMcoherence,
937         CMi8253set,
938         CMcache,
939 };
940
941 static Cmdtab archctlmsg[] =
942 {
943         CMpge,          "pge",          2,
944         CMcoherence,    "coherence",    2,
945         CMi8253set,     "i8253set",     2,
946         CMcache,        "cache",        4,
947 };
948
949 static long
950 archctlwrite(Chan*, void *a, long n, vlong)
951 {
952         uvlong base, size;
953         Cmdbuf *cb;
954         Cmdtab *ct;
955         char *ep;
956
957         cb = parsecmd(a, n);
958         if(waserror()){
959                 free(cb);
960                 nexterror();
961         }
962         ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
963         switch(ct->index){
964         case CMpge:
965                 if(!m->havepge)
966                         error("processor does not support pge");
967                 if(strcmp(cb->f[1], "on") == 0)
968                         putcr4(getcr4() | 0x80);
969                 else if(strcmp(cb->f[1], "off") == 0)
970                         putcr4(getcr4() & ~0x80);
971                 else
972                         cmderror(cb, "invalid pge ctl");
973                 break;
974         case CMcoherence:
975                 if(strcmp(cb->f[1], "mb386") == 0)
976                         coherence = mb386;
977                 else if(strcmp(cb->f[1], "mb586") == 0){
978                         if(X86FAMILY(m->cpuidax) < 5)
979                                 error("invalid coherence ctl on this cpu family");
980                         coherence = mb586;
981                 }else if(strcmp(cb->f[1], "mfence") == 0){
982                         if((m->cpuiddx & Sse2) == 0)
983                                 error("invalid coherence ctl on this cpu family");
984                         coherence = mfence;
985                 }else if(strcmp(cb->f[1], "nop") == 0){
986                         /* only safe on vmware */
987                         if(conf.nmach > 1)
988                                 error("cannot disable coherence on a multiprocessor");
989                         coherence = nop;
990                 }else
991                         cmderror(cb, "invalid coherence ctl");
992                 break;
993         case CMi8253set:
994                 if(strcmp(cb->f[1], "on") == 0)
995                         doi8253set = 1;
996                 else if(strcmp(cb->f[1], "off") == 0){
997                         doi8253set = 0;
998                         (*arch->timerset)(0);
999                 }else
1000                         cmderror(cb, "invalid i2853set ctl");
1001                 break;
1002         case CMcache:
1003                 base = strtoull(cb->f[1], &ep, 0);
1004                 if(*ep)
1005                         error("cache: parse error: base not a number?");
1006                 size = strtoull(cb->f[2], &ep, 0);
1007                 if(*ep)
1008                         error("cache: parse error: size not a number?");
1009                 ep = mtrr(base, size, cb->f[3]);
1010                 if(ep != nil)
1011                         error(ep);
1012                 break;
1013         }
1014         free(cb);
1015         poperror();
1016         return n;
1017 }
1018
1019 static long
1020 rmemrw(int isr, void *a, long n, vlong off)
1021 {
1022         uintptr addr = off;
1023
1024         if(off < 0 || n < 0)
1025                 error("bad offset/count");
1026         if(isr){
1027                 if(addr >= MB)
1028                         return 0;
1029                 if(addr+n > MB)
1030                         n = MB - addr;
1031                 memmove(a, KADDR(addr), n);
1032         }else{
1033                 /* allow vga framebuf's write access */
1034                 if(addr >= MB || addr+n > MB ||
1035                     (addr < 0xA0000 || addr+n > 0xB0000+0x10000))
1036                         error("bad offset/count in write");
1037                 memmove(KADDR(addr), a, n);
1038         }
1039         return n;
1040 }
1041
1042 static long
1043 rmemread(Chan*, void *a, long n, vlong off)
1044 {
1045         return rmemrw(1, a, n, off);
1046 }
1047
1048 static long
1049 rmemwrite(Chan*, void *a, long n, vlong off)
1050 {
1051         return rmemrw(0, a, n, off);
1052 }
1053
1054 void
1055 archinit(void)
1056 {
1057         PCArch **p;
1058
1059         arch = &archgeneric;
1060         for(p = knownarch; *p != nil; p++){
1061                 if((*p)->ident != nil && (*p)->ident() == 0){
1062                         arch = *p;
1063                         break;
1064                 }
1065         }
1066         if(arch != &archgeneric){
1067                 if(arch->id == nil)
1068                         arch->id = archgeneric.id;
1069                 if(arch->reset == nil)
1070                         arch->reset = archgeneric.reset;
1071                 if(arch->serialpower == nil)
1072                         arch->serialpower = archgeneric.serialpower;
1073                 if(arch->modempower == nil)
1074                         arch->modempower = archgeneric.modempower;
1075                 if(arch->intrinit == nil)
1076                         arch->intrinit = archgeneric.intrinit;
1077                 if(arch->intrenable == nil)
1078                         arch->intrenable = archgeneric.intrenable;
1079         }
1080
1081         /*
1082          *  Decide whether to use copy-on-reference (386 and mp).
1083          *  We get another chance to set it in mpinit() for a
1084          *  multiprocessor.
1085          */
1086         if(X86FAMILY(m->cpuidax) == 3)
1087                 conf.copymode = 1;
1088
1089         if(X86FAMILY(m->cpuidax) >= 4)
1090                 cmpswap = cmpswap486;
1091
1092         if(X86FAMILY(m->cpuidax) >= 5)
1093                 coherence = mb586;
1094
1095         if(m->cpuiddx & Sse2)
1096                 coherence = mfence;
1097
1098         addarchfile("cputype", 0444, cputyperead, nil);
1099         addarchfile("archctl", 0664, archctlread, archctlwrite);
1100         addarchfile("realmodemem", 0660, rmemread, rmemwrite);
1101 }
1102
1103 /*
1104  *  call either the pcmcia or pccard device setup
1105  */
1106 int
1107 pcmspecial(char *idstr, ISAConf *isa)
1108 {
1109         return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
1110 }
1111
1112 /*
1113  *  call either the pcmcia or pccard device teardown
1114  */
1115 void
1116 pcmspecialclose(int a)
1117 {
1118         if (_pcmspecialclose != nil)
1119                 _pcmspecialclose(a);
1120 }
1121
1122 /*
1123  *  return value and speed of timer set in arch->clockenable
1124  */
1125 uvlong
1126 fastticks(uvlong *hz)
1127 {
1128         return (*arch->fastclock)(hz);
1129 }
1130
1131 ulong
1132 µs(void)
1133 {
1134         return fastticks2us((*arch->fastclock)(nil));
1135 }
1136
1137 /*
1138  *  set next timer interrupt
1139  */
1140 void
1141 timerset(Tval x)
1142 {
1143         if(doi8253set)
1144                 (*arch->timerset)(x);
1145 }
1146
1147 /*
1148  *  put the processor in the halt state if we've no processes to run.
1149  *  an interrupt will get us going again.
1150  *
1151  *  halting in an smp system can result in a startup latency for
1152  *  processes that become ready.
1153  *  if idle_spin is zero, we care more about saving energy
1154  *  than reducing this latency.
1155  *
1156  *  the performance loss with idle_spin == 0 seems to be slight
1157  *  and it reduces lock contention (thus system time and real time)
1158  *  on many-core systems with large values of NPROC.
1159  */
1160 void
1161 idlehands(void)
1162 {
1163         extern int nrdy, idle_spin;
1164
1165         if(conf.nmach == 1)
1166                 halt();
1167         else if(m->cpuidcx & Monitor)
1168                 mwait(&nrdy);
1169         else if(idle_spin == 0)
1170                 halt();
1171 }
1172
1173 int
1174 isaconfig(char *class, int ctlrno, ISAConf *isa)
1175 {
1176         char cc[32], *p;
1177         int i;
1178
1179         snprint(cc, sizeof cc, "%s%d", class, ctlrno);
1180         p = getconf(cc);
1181         if(p == nil)
1182                 return 0;
1183
1184         isa->type = "";
1185         isa->nopt = tokenize(p, isa->opt, NISAOPT);
1186         for(i = 0; i < isa->nopt; i++){
1187                 p = isa->opt[i];
1188                 if(cistrncmp(p, "type=", 5) == 0)
1189                         isa->type = p + 5;
1190                 else if(cistrncmp(p, "port=", 5) == 0)
1191                         isa->port = strtoul(p+5, &p, 0);
1192                 else if(cistrncmp(p, "irq=", 4) == 0)
1193                         isa->irq = strtoul(p+4, &p, 0);
1194                 else if(cistrncmp(p, "dma=", 4) == 0)
1195                         isa->dma = strtoul(p+4, &p, 0);
1196                 else if(cistrncmp(p, "mem=", 4) == 0)
1197                         isa->mem = strtoul(p+4, &p, 0);
1198                 else if(cistrncmp(p, "size=", 5) == 0)
1199                         isa->size = strtoul(p+5, &p, 0);
1200                 else if(cistrncmp(p, "freq=", 5) == 0)
1201                         isa->freq = strtoul(p+5, &p, 0);
1202         }
1203         return 1;
1204 }
1205
1206 void
1207 dumpmcregs(void)
1208 {
1209         vlong v, w;
1210         int bank;
1211
1212         if((m->cpuiddx & (Mce|Cpumsr)) != (Mce|Cpumsr))
1213                 return;
1214         if((m->cpuiddx & Mca) == 0){
1215                 rdmsr(0x00, &v);
1216                 rdmsr(0x01, &w);
1217                 iprint("MCA %8.8llux MCT %8.8llux\n", v, w);
1218                 return;
1219         }
1220         rdmsr(0x179, &v);
1221         rdmsr(0x17A, &w);
1222         iprint("MCG CAP %.16llux STATUS %.16llux\n", v, w);
1223
1224         bank = v & 0xFF;
1225         if(bank > 64)
1226                 bank = 64;
1227         while(--bank >= 0){
1228                 rdmsr(0x401 + bank*4, &v);
1229                 if((v & (1ull << 63)) == 0)
1230                         continue;
1231                 iprint("MC%d STATUS %.16llux", bank, v);
1232                 if(v & (1ull << 58)){
1233                         rdmsr(0x402 + bank*4, &w);
1234                         iprint(" ADDR %.16llux", w);
1235                 }
1236                 if(v & (1ull << 59)){
1237                         rdmsr(0x403 + bank*4, &w);
1238                         iprint(" MISC %.16llux", w);
1239                 }
1240                 iprint("\n");
1241         }
1242 }