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