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