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