17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier #include "ureg.h"
87dd7cddfSDavid du Colombier #include "../port/error.h"
97dd7cddfSDavid du Colombier
107dd7cddfSDavid du Colombier typedef struct IOMap IOMap;
117dd7cddfSDavid du Colombier struct IOMap
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier IOMap *next;
149a747e4fSDavid du Colombier int reserved;
157dd7cddfSDavid du Colombier char tag[13];
167dd7cddfSDavid du Colombier ulong start;
177dd7cddfSDavid du Colombier ulong end;
187dd7cddfSDavid du Colombier };
197dd7cddfSDavid du Colombier
207dd7cddfSDavid du Colombier static struct
217dd7cddfSDavid du Colombier {
227dd7cddfSDavid du Colombier Lock;
237dd7cddfSDavid du Colombier IOMap *m;
247dd7cddfSDavid du Colombier IOMap *free;
2541dd6b47SDavid du Colombier IOMap maps[32]; /* some initial free maps */
267dd7cddfSDavid du Colombier
2741dd6b47SDavid du Colombier QLock ql; /* lock for reading map */
287dd7cddfSDavid du Colombier } iomap;
297dd7cddfSDavid du Colombier
307dd7cddfSDavid du Colombier enum {
319a747e4fSDavid du Colombier Qdir = 0,
329a747e4fSDavid du Colombier Qioalloc = 1,
337dd7cddfSDavid du Colombier Qiob,
347dd7cddfSDavid du Colombier Qiow,
357dd7cddfSDavid du Colombier Qiol,
3680ee5cbfSDavid du Colombier Qbase,
3780ee5cbfSDavid du Colombier
3880ee5cbfSDavid du Colombier Qmax = 16,
397dd7cddfSDavid du Colombier };
4091e577b2SDavid du Colombier
4191e577b2SDavid du Colombier enum {
4291e577b2SDavid du Colombier CR4Osfxsr = 1 << 9,
4391e577b2SDavid du Colombier };
4491e577b2SDavid du Colombier
4526d1d1dfSDavid du Colombier enum { /* cpuid standard function codes */
4626d1d1dfSDavid du Colombier Highstdfunc = 0, /* also returns vendor string */
4726d1d1dfSDavid du Colombier Procsig,
4826d1d1dfSDavid du Colombier Proctlbcache,
4926d1d1dfSDavid du Colombier Procserial,
5026d1d1dfSDavid du Colombier };
517dd7cddfSDavid du Colombier
5280ee5cbfSDavid du Colombier typedef long Rdwrfn(Chan*, void*, long, vlong);
5380ee5cbfSDavid du Colombier
5480ee5cbfSDavid du Colombier static Rdwrfn *readfn[Qmax];
5580ee5cbfSDavid du Colombier static Rdwrfn *writefn[Qmax];
5680ee5cbfSDavid du Colombier
5780ee5cbfSDavid du Colombier static Dirtab archdir[Qmax] = {
589a747e4fSDavid du Colombier ".", { Qdir, 0, QTDIR }, 0, 0555,
597dd7cddfSDavid du Colombier "ioalloc", { Qioalloc, 0 }, 0, 0444,
607dd7cddfSDavid du Colombier "iob", { Qiob, 0 }, 0, 0660,
617dd7cddfSDavid du Colombier "iow", { Qiow, 0 }, 0, 0660,
627dd7cddfSDavid du Colombier "iol", { Qiol, 0 }, 0, 0660,
637dd7cddfSDavid du Colombier };
6480ee5cbfSDavid du Colombier Lock archwlock; /* the lock is only for changing archdir */
6580ee5cbfSDavid du Colombier int narchdir = Qbase;
669a747e4fSDavid du Colombier int (*_pcmspecial)(char*, ISAConf*);
679a747e4fSDavid du Colombier void (*_pcmspecialclose)(int);
689a747e4fSDavid du Colombier
69ed250ae1SDavid du Colombier static int doi8253set = 1;
7080ee5cbfSDavid du Colombier
7180ee5cbfSDavid du Colombier /*
7280ee5cbfSDavid du Colombier * Add a file to the #P listing. Once added, you can't delete it.
7380ee5cbfSDavid du Colombier * You can't add a file with the same name as one already there,
7480ee5cbfSDavid du Colombier * and you get a pointer to the Dirtab entry so you can do things
7580ee5cbfSDavid du Colombier * like change the Qid version. Changing the Qid path is disallowed.
7680ee5cbfSDavid du Colombier */
7780ee5cbfSDavid du Colombier Dirtab*
addarchfile(char * name,int perm,Rdwrfn * rdfn,Rdwrfn * wrfn)7880ee5cbfSDavid du Colombier addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
7980ee5cbfSDavid du Colombier {
8080ee5cbfSDavid du Colombier int i;
8180ee5cbfSDavid du Colombier Dirtab d;
8280ee5cbfSDavid du Colombier Dirtab *dp;
8380ee5cbfSDavid du Colombier
8480ee5cbfSDavid du Colombier memset(&d, 0, sizeof d);
8580ee5cbfSDavid du Colombier strcpy(d.name, name);
8680ee5cbfSDavid du Colombier d.perm = perm;
8780ee5cbfSDavid du Colombier
8880ee5cbfSDavid du Colombier lock(&archwlock);
8980ee5cbfSDavid du Colombier if(narchdir >= Qmax){
9080ee5cbfSDavid du Colombier unlock(&archwlock);
9180ee5cbfSDavid du Colombier return nil;
9280ee5cbfSDavid du Colombier }
9380ee5cbfSDavid du Colombier
9480ee5cbfSDavid du Colombier for(i=0; i<narchdir; i++)
9580ee5cbfSDavid du Colombier if(strcmp(archdir[i].name, name) == 0){
9680ee5cbfSDavid du Colombier unlock(&archwlock);
9780ee5cbfSDavid du Colombier return nil;
9880ee5cbfSDavid du Colombier }
9980ee5cbfSDavid du Colombier
10080ee5cbfSDavid du Colombier d.qid.path = narchdir;
10180ee5cbfSDavid du Colombier archdir[narchdir] = d;
10280ee5cbfSDavid du Colombier readfn[narchdir] = rdfn;
10380ee5cbfSDavid du Colombier writefn[narchdir] = wrfn;
10480ee5cbfSDavid du Colombier dp = &archdir[narchdir++];
10580ee5cbfSDavid du Colombier unlock(&archwlock);
10680ee5cbfSDavid du Colombier
10780ee5cbfSDavid du Colombier return dp;
10880ee5cbfSDavid du Colombier }
1097dd7cddfSDavid du Colombier
1107dd7cddfSDavid du Colombier void
ioinit(void)1117dd7cddfSDavid du Colombier ioinit(void)
1127dd7cddfSDavid du Colombier {
1139a747e4fSDavid du Colombier char *excluded;
1147dd7cddfSDavid du Colombier int i;
1157dd7cddfSDavid du Colombier
1167dd7cddfSDavid du Colombier for(i = 0; i < nelem(iomap.maps)-1; i++)
1177dd7cddfSDavid du Colombier iomap.maps[i].next = &iomap.maps[i+1];
1187dd7cddfSDavid du Colombier iomap.maps[i].next = nil;
1197dd7cddfSDavid du Colombier iomap.free = iomap.maps;
1207dd7cddfSDavid du Colombier
121ef9eff0bSDavid du Colombier /*
122e40528acSDavid du Colombier * This is necessary to make the IBM X20 boot.
123e40528acSDavid du Colombier * Have not tracked down the reason.
12441dd6b47SDavid du Colombier * i82557 is at 0x1000, the dummy entry is needed for swappable devs.
125dc5a79c1SDavid du Colombier */
12641dd6b47SDavid du Colombier ioalloc(0x0fff, 1, 0, "dummy");
1279a747e4fSDavid du Colombier
1289a747e4fSDavid du Colombier if ((excluded = getconf("ioexclude")) != nil) {
1299a747e4fSDavid du Colombier char *s;
1309a747e4fSDavid du Colombier
1319a747e4fSDavid du Colombier s = excluded;
1329a747e4fSDavid du Colombier while (s && *s != '\0' && *s != '\n') {
1339a747e4fSDavid du Colombier char *ends;
1349a747e4fSDavid du Colombier int io_s, io_e;
1359a747e4fSDavid du Colombier
1369a747e4fSDavid du Colombier io_s = (int)strtol(s, &ends, 0);
1379a747e4fSDavid du Colombier if (ends == nil || ends == s || *ends != '-') {
1389a747e4fSDavid du Colombier print("ioinit: cannot parse option string\n");
1399a747e4fSDavid du Colombier break;
1409a747e4fSDavid du Colombier }
1419a747e4fSDavid du Colombier s = ++ends;
1429a747e4fSDavid du Colombier
1439a747e4fSDavid du Colombier io_e = (int)strtol(s, &ends, 0);
1449a747e4fSDavid du Colombier if (ends && *ends == ',')
1459a747e4fSDavid du Colombier *ends++ = '\0';
1469a747e4fSDavid du Colombier s = ends;
1479a747e4fSDavid du Colombier
1489a747e4fSDavid du Colombier ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
1499a747e4fSDavid du Colombier }
1509a747e4fSDavid du Colombier }
1519a747e4fSDavid du Colombier
1529a747e4fSDavid du Colombier }
1539a747e4fSDavid du Colombier
15441dd6b47SDavid du Colombier /*
15541dd6b47SDavid du Colombier * Reserve a range to be ioalloced later.
15641dd6b47SDavid du Colombier * This is in particular useful for exchangable cards, such
15741dd6b47SDavid du Colombier * as pcmcia and cardbus cards.
15841dd6b47SDavid du Colombier */
1599a747e4fSDavid du Colombier int
ioreserve(int,int size,int align,char * tag)1605d82c6aeSDavid du Colombier ioreserve(int, int size, int align, char *tag)
1619a747e4fSDavid du Colombier {
1629a747e4fSDavid du Colombier IOMap *m, **l;
1635d82c6aeSDavid du Colombier int i, port;
1649a747e4fSDavid du Colombier
1659a747e4fSDavid du Colombier lock(&iomap);
16641dd6b47SDavid du Colombier /* find a free port above 0x400 and below 0x1000 */
1679a747e4fSDavid du Colombier port = 0x400;
1689a747e4fSDavid du Colombier for(l = &iomap.m; *l; l = &(*l)->next){
1699a747e4fSDavid du Colombier m = *l;
1709a747e4fSDavid du Colombier if (m->start < 0x400) continue;
1719a747e4fSDavid du Colombier i = m->start - port;
1729a747e4fSDavid du Colombier if(i > size)
1739a747e4fSDavid du Colombier break;
1749a747e4fSDavid du Colombier if(align > 0)
1759a747e4fSDavid du Colombier port = ((port+align-1)/align)*align;
1769a747e4fSDavid du Colombier else
1779a747e4fSDavid du Colombier port = m->end;
1789a747e4fSDavid du Colombier }
1799a747e4fSDavid du Colombier if(*l == nil){
1809a747e4fSDavid du Colombier unlock(&iomap);
1819a747e4fSDavid du Colombier return -1;
1829a747e4fSDavid du Colombier }
1839a747e4fSDavid du Colombier m = iomap.free;
1849a747e4fSDavid du Colombier if(m == nil){
1859a747e4fSDavid du Colombier print("ioalloc: out of maps");
1869a747e4fSDavid du Colombier unlock(&iomap);
1879a747e4fSDavid du Colombier return port;
1889a747e4fSDavid du Colombier }
1899a747e4fSDavid du Colombier iomap.free = m->next;
1909a747e4fSDavid du Colombier m->next = *l;
1919a747e4fSDavid du Colombier m->start = port;
1929a747e4fSDavid du Colombier m->end = port + size;
1939a747e4fSDavid du Colombier m->reserved = 1;
1949a747e4fSDavid du Colombier strncpy(m->tag, tag, sizeof(m->tag));
1959a747e4fSDavid du Colombier m->tag[sizeof(m->tag)-1] = 0;
1969a747e4fSDavid du Colombier *l = m;
1979a747e4fSDavid du Colombier
1989a747e4fSDavid du Colombier archdir[0].qid.vers++;
1999a747e4fSDavid du Colombier
2009a747e4fSDavid du Colombier unlock(&iomap);
2019a747e4fSDavid du Colombier return m->start;
2027dd7cddfSDavid du Colombier }
2037dd7cddfSDavid du Colombier
20441dd6b47SDavid du Colombier /*
20541dd6b47SDavid du Colombier * alloc some io port space and remember who it was
20641dd6b47SDavid du Colombier * alloced to. if port < 0, find a free region.
20741dd6b47SDavid du Colombier */
2087dd7cddfSDavid du Colombier int
ioalloc(int port,int size,int align,char * tag)2097dd7cddfSDavid du Colombier ioalloc(int port, int size, int align, char *tag)
2107dd7cddfSDavid du Colombier {
2117dd7cddfSDavid du Colombier IOMap *m, **l;
2127dd7cddfSDavid du Colombier int i;
2137dd7cddfSDavid du Colombier
2147dd7cddfSDavid du Colombier lock(&iomap);
2157dd7cddfSDavid du Colombier if(port < 0){
21641dd6b47SDavid du Colombier /* find a free port above 0x400 and below 0x1000 */
2177dd7cddfSDavid du Colombier port = 0x400;
2187dd7cddfSDavid du Colombier for(l = &iomap.m; *l; l = &(*l)->next){
2197dd7cddfSDavid du Colombier m = *l;
2209a747e4fSDavid du Colombier if (m->start < 0x400) continue;
2217dd7cddfSDavid du Colombier i = m->start - port;
2227dd7cddfSDavid du Colombier if(i > size)
2237dd7cddfSDavid du Colombier break;
2247dd7cddfSDavid du Colombier if(align > 0)
2257dd7cddfSDavid du Colombier port = ((port+align-1)/align)*align;
2267dd7cddfSDavid du Colombier else
2277dd7cddfSDavid du Colombier port = m->end;
2287dd7cddfSDavid du Colombier }
2297dd7cddfSDavid du Colombier if(*l == nil){
2307dd7cddfSDavid du Colombier unlock(&iomap);
2317dd7cddfSDavid du Colombier return -1;
2327dd7cddfSDavid du Colombier }
2337dd7cddfSDavid du Colombier } else {
23441dd6b47SDavid du Colombier /* Only 64KB I/O space on the x86. */
235ed250ae1SDavid du Colombier if((port+size) > 0x10000){
2369a747e4fSDavid du Colombier unlock(&iomap);
2379a747e4fSDavid du Colombier return -1;
2389a747e4fSDavid du Colombier }
23941dd6b47SDavid du Colombier /* see if the space clashes with previously allocated ports */
2407dd7cddfSDavid du Colombier for(l = &iomap.m; *l; l = &(*l)->next){
2417dd7cddfSDavid du Colombier m = *l;
2427dd7cddfSDavid du Colombier if(m->end <= port)
2437dd7cddfSDavid du Colombier continue;
2449a747e4fSDavid du Colombier if(m->reserved && m->start == port && m->end == port + size) {
2459a747e4fSDavid du Colombier m->reserved = 0;
2469a747e4fSDavid du Colombier unlock(&iomap);
2479a747e4fSDavid du Colombier return m->start;
2489a747e4fSDavid du Colombier }
2497dd7cddfSDavid du Colombier if(m->start >= port+size)
2507dd7cddfSDavid du Colombier break;
2517dd7cddfSDavid du Colombier unlock(&iomap);
2527dd7cddfSDavid du Colombier return -1;
2537dd7cddfSDavid du Colombier }
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier m = iomap.free;
2567dd7cddfSDavid du Colombier if(m == nil){
2577dd7cddfSDavid du Colombier print("ioalloc: out of maps");
2587dd7cddfSDavid du Colombier unlock(&iomap);
2597dd7cddfSDavid du Colombier return port;
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier iomap.free = m->next;
2627dd7cddfSDavid du Colombier m->next = *l;
2637dd7cddfSDavid du Colombier m->start = port;
2647dd7cddfSDavid du Colombier m->end = port + size;
2657dd7cddfSDavid du Colombier strncpy(m->tag, tag, sizeof(m->tag));
2667dd7cddfSDavid du Colombier m->tag[sizeof(m->tag)-1] = 0;
2677dd7cddfSDavid du Colombier *l = m;
2687dd7cddfSDavid du Colombier
26980ee5cbfSDavid du Colombier archdir[0].qid.vers++;
2707dd7cddfSDavid du Colombier
2717dd7cddfSDavid du Colombier unlock(&iomap);
2727dd7cddfSDavid du Colombier return m->start;
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier
2757dd7cddfSDavid du Colombier void
iofree(int port)2767dd7cddfSDavid du Colombier iofree(int port)
2777dd7cddfSDavid du Colombier {
2787dd7cddfSDavid du Colombier IOMap *m, **l;
2797dd7cddfSDavid du Colombier
2807dd7cddfSDavid du Colombier lock(&iomap);
2817dd7cddfSDavid du Colombier for(l = &iomap.m; *l; l = &(*l)->next){
2827dd7cddfSDavid du Colombier if((*l)->start == port){
2837dd7cddfSDavid du Colombier m = *l;
2847dd7cddfSDavid du Colombier *l = m->next;
2857dd7cddfSDavid du Colombier m->next = iomap.free;
2867dd7cddfSDavid du Colombier iomap.free = m;
2877dd7cddfSDavid du Colombier break;
2887dd7cddfSDavid du Colombier }
2897dd7cddfSDavid du Colombier if((*l)->start > port)
2907dd7cddfSDavid du Colombier break;
2917dd7cddfSDavid du Colombier }
29280ee5cbfSDavid du Colombier archdir[0].qid.vers++;
2937dd7cddfSDavid du Colombier unlock(&iomap);
2947dd7cddfSDavid du Colombier }
2957dd7cddfSDavid du Colombier
2967dd7cddfSDavid du Colombier int
iounused(int start,int end)2977dd7cddfSDavid du Colombier iounused(int start, int end)
2987dd7cddfSDavid du Colombier {
2997dd7cddfSDavid du Colombier IOMap *m;
3007dd7cddfSDavid du Colombier
3017dd7cddfSDavid du Colombier for(m = iomap.m; m; m = m->next){
3027dd7cddfSDavid du Colombier if(start >= m->start && start < m->end
3037dd7cddfSDavid du Colombier || start <= m->start && end > m->start)
3047dd7cddfSDavid du Colombier return 0;
3057dd7cddfSDavid du Colombier }
3067dd7cddfSDavid du Colombier return 1;
3077dd7cddfSDavid du Colombier }
3087dd7cddfSDavid du Colombier
3097dd7cddfSDavid du Colombier static void
checkport(int start,int end)3107dd7cddfSDavid du Colombier checkport(int start, int end)
3117dd7cddfSDavid du Colombier {
3127dd7cddfSDavid du Colombier /* standard vga regs are OK */
3137dd7cddfSDavid du Colombier if(start >= 0x2b0 && end <= 0x2df+1)
3147dd7cddfSDavid du Colombier return;
3157dd7cddfSDavid du Colombier if(start >= 0x3c0 && end <= 0x3da+1)
3167dd7cddfSDavid du Colombier return;
3177dd7cddfSDavid du Colombier
3187dd7cddfSDavid du Colombier if(iounused(start, end))
3197dd7cddfSDavid du Colombier return;
3207dd7cddfSDavid du Colombier error(Eperm);
3217dd7cddfSDavid du Colombier }
3227dd7cddfSDavid du Colombier
3237dd7cddfSDavid du Colombier static Chan*
archattach(char * spec)3247dd7cddfSDavid du Colombier archattach(char* spec)
3257dd7cddfSDavid du Colombier {
3267dd7cddfSDavid du Colombier return devattach('P', spec);
3277dd7cddfSDavid du Colombier }
3287dd7cddfSDavid du Colombier
3299a747e4fSDavid du Colombier Walkqid*
archwalk(Chan * c,Chan * nc,char ** name,int nname)3309a747e4fSDavid du Colombier archwalk(Chan* c, Chan *nc, char** name, int nname)
3317dd7cddfSDavid du Colombier {
3329a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
3337dd7cddfSDavid du Colombier }
3347dd7cddfSDavid du Colombier
3359a747e4fSDavid du Colombier static int
archstat(Chan * c,uchar * dp,int n)3369a747e4fSDavid du Colombier archstat(Chan* c, uchar* dp, int n)
3377dd7cddfSDavid du Colombier {
3389a747e4fSDavid du Colombier return devstat(c, dp, n, archdir, narchdir, devgen);
3397dd7cddfSDavid du Colombier }
3407dd7cddfSDavid du Colombier
3417dd7cddfSDavid du Colombier static Chan*
archopen(Chan * c,int omode)3427dd7cddfSDavid du Colombier archopen(Chan* c, int omode)
3437dd7cddfSDavid du Colombier {
34480ee5cbfSDavid du Colombier return devopen(c, omode, archdir, narchdir, devgen);
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier static void
archclose(Chan *)3487dd7cddfSDavid du Colombier archclose(Chan*)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier }
3517dd7cddfSDavid du Colombier
3527dd7cddfSDavid du Colombier enum
3537dd7cddfSDavid du Colombier {
3547dd7cddfSDavid du Colombier Linelen= 31,
3557dd7cddfSDavid du Colombier };
3567dd7cddfSDavid du Colombier
3577dd7cddfSDavid du Colombier static long
archread(Chan * c,void * a,long n,vlong offset)3587dd7cddfSDavid du Colombier archread(Chan *c, void *a, long n, vlong offset)
3597dd7cddfSDavid du Colombier {
360ef9eff0bSDavid du Colombier char *buf, *p;
3617dd7cddfSDavid du Colombier int port;
3627dd7cddfSDavid du Colombier ushort *sp;
3637dd7cddfSDavid du Colombier ulong *lp;
36480ee5cbfSDavid du Colombier IOMap *m;
36580ee5cbfSDavid du Colombier Rdwrfn *fn;
3667dd7cddfSDavid du Colombier
3679a747e4fSDavid du Colombier switch((ulong)c->qid.path){
3687dd7cddfSDavid du Colombier
3697dd7cddfSDavid du Colombier case Qdir:
37080ee5cbfSDavid du Colombier return devdirread(c, a, n, archdir, narchdir, devgen);
3717dd7cddfSDavid du Colombier
3727dd7cddfSDavid du Colombier case Qiob:
3737dd7cddfSDavid du Colombier port = offset;
3747dd7cddfSDavid du Colombier checkport(offset, offset+n);
3757dd7cddfSDavid du Colombier for(p = a; port < offset+n; port++)
3767dd7cddfSDavid du Colombier *p++ = inb(port);
3777dd7cddfSDavid du Colombier return n;
3787dd7cddfSDavid du Colombier
3797dd7cddfSDavid du Colombier case Qiow:
3809a747e4fSDavid du Colombier if(n & 1)
3817dd7cddfSDavid du Colombier error(Ebadarg);
38280ee5cbfSDavid du Colombier checkport(offset, offset+n);
3837dd7cddfSDavid du Colombier sp = a;
3847dd7cddfSDavid du Colombier for(port = offset; port < offset+n; port += 2)
3857dd7cddfSDavid du Colombier *sp++ = ins(port);
3869a747e4fSDavid du Colombier return n;
3877dd7cddfSDavid du Colombier
3887dd7cddfSDavid du Colombier case Qiol:
3899a747e4fSDavid du Colombier if(n & 3)
3907dd7cddfSDavid du Colombier error(Ebadarg);
39180ee5cbfSDavid du Colombier checkport(offset, offset+n);
3927dd7cddfSDavid du Colombier lp = a;
3937dd7cddfSDavid du Colombier for(port = offset; port < offset+n; port += 4)
3947dd7cddfSDavid du Colombier *lp++ = inl(port);
3959a747e4fSDavid du Colombier return n;
3967dd7cddfSDavid du Colombier
3977dd7cddfSDavid du Colombier case Qioalloc:
3987dd7cddfSDavid du Colombier break;
3997dd7cddfSDavid du Colombier
4007dd7cddfSDavid du Colombier default:
40180ee5cbfSDavid du Colombier if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
40280ee5cbfSDavid du Colombier return fn(c, a, n, offset);
4037dd7cddfSDavid du Colombier error(Eperm);
4047dd7cddfSDavid du Colombier break;
4057dd7cddfSDavid du Colombier }
4067dd7cddfSDavid du Colombier
4076b6b9ac8SDavid du Colombier if((buf = malloc(n)) == nil)
408ef9eff0bSDavid du Colombier error(Enomem);
409ef9eff0bSDavid du Colombier p = buf;
4107dd7cddfSDavid du Colombier n = n/Linelen;
411ef9eff0bSDavid du Colombier offset = offset/Linelen;
412ef9eff0bSDavid du Colombier
4137dd7cddfSDavid du Colombier lock(&iomap);
4147dd7cddfSDavid du Colombier for(m = iomap.m; n > 0 && m != nil; m = m->next){
4157dd7cddfSDavid du Colombier if(offset-- > 0)
4167dd7cddfSDavid du Colombier continue;
417*4e3613abSDavid du Colombier seprint(p, &buf[n], "%8lux %8lux %-12.12s\n", m->start,
418*4e3613abSDavid du Colombier m->end-1, m->tag);
4197dd7cddfSDavid du Colombier p += Linelen;
4207dd7cddfSDavid du Colombier n--;
4217dd7cddfSDavid du Colombier }
4227dd7cddfSDavid du Colombier unlock(&iomap);
4237dd7cddfSDavid du Colombier
424ef9eff0bSDavid du Colombier n = p - buf;
425ef9eff0bSDavid du Colombier memmove(a, buf, n);
426ef9eff0bSDavid du Colombier free(buf);
427ef9eff0bSDavid du Colombier
428ef9eff0bSDavid du Colombier return n;
4297dd7cddfSDavid du Colombier }
4307dd7cddfSDavid du Colombier
4317dd7cddfSDavid du Colombier static long
archwrite(Chan * c,void * a,long n,vlong offset)4327dd7cddfSDavid du Colombier archwrite(Chan *c, void *a, long n, vlong offset)
4337dd7cddfSDavid du Colombier {
43480ee5cbfSDavid du Colombier char *p;
4357dd7cddfSDavid du Colombier int port;
4367dd7cddfSDavid du Colombier ushort *sp;
4377dd7cddfSDavid du Colombier ulong *lp;
43880ee5cbfSDavid du Colombier Rdwrfn *fn;
4397dd7cddfSDavid du Colombier
4409a747e4fSDavid du Colombier switch((ulong)c->qid.path){
4417dd7cddfSDavid du Colombier
4427dd7cddfSDavid du Colombier case Qiob:
4437dd7cddfSDavid du Colombier p = a;
4447dd7cddfSDavid du Colombier checkport(offset, offset+n);
4457dd7cddfSDavid du Colombier for(port = offset; port < offset+n; port++)
4467dd7cddfSDavid du Colombier outb(port, *p++);
4477dd7cddfSDavid du Colombier return n;
4487dd7cddfSDavid du Colombier
4497dd7cddfSDavid du Colombier case Qiow:
4509a747e4fSDavid du Colombier if(n & 1)
4517dd7cddfSDavid du Colombier error(Ebadarg);
45280ee5cbfSDavid du Colombier checkport(offset, offset+n);
4537dd7cddfSDavid du Colombier sp = a;
4547dd7cddfSDavid du Colombier for(port = offset; port < offset+n; port += 2)
4557dd7cddfSDavid du Colombier outs(port, *sp++);
4569a747e4fSDavid du Colombier return n;
4577dd7cddfSDavid du Colombier
4587dd7cddfSDavid du Colombier case Qiol:
4599a747e4fSDavid du Colombier if(n & 3)
4607dd7cddfSDavid du Colombier error(Ebadarg);
46180ee5cbfSDavid du Colombier checkport(offset, offset+n);
4627dd7cddfSDavid du Colombier lp = a;
4637dd7cddfSDavid du Colombier for(port = offset; port < offset+n; port += 4)
4647dd7cddfSDavid du Colombier outl(port, *lp++);
4659a747e4fSDavid du Colombier return n;
4667dd7cddfSDavid du Colombier
4677dd7cddfSDavid du Colombier default:
46880ee5cbfSDavid du Colombier if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
46980ee5cbfSDavid du Colombier return fn(c, a, n, offset);
4707dd7cddfSDavid du Colombier error(Eperm);
4717dd7cddfSDavid du Colombier break;
4727dd7cddfSDavid du Colombier }
4737dd7cddfSDavid du Colombier return 0;
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier
4767dd7cddfSDavid du Colombier Dev archdevtab = {
4777dd7cddfSDavid du Colombier 'P',
4787dd7cddfSDavid du Colombier "arch",
4797dd7cddfSDavid du Colombier
4807dd7cddfSDavid du Colombier devreset,
4817dd7cddfSDavid du Colombier devinit,
4829a747e4fSDavid du Colombier devshutdown,
4837dd7cddfSDavid du Colombier archattach,
4847dd7cddfSDavid du Colombier archwalk,
4857dd7cddfSDavid du Colombier archstat,
4867dd7cddfSDavid du Colombier archopen,
4877dd7cddfSDavid du Colombier devcreate,
4887dd7cddfSDavid du Colombier archclose,
4897dd7cddfSDavid du Colombier archread,
4907dd7cddfSDavid du Colombier devbread,
4917dd7cddfSDavid du Colombier archwrite,
4927dd7cddfSDavid du Colombier devbwrite,
4937dd7cddfSDavid du Colombier devremove,
4947dd7cddfSDavid du Colombier devwstat,
4957dd7cddfSDavid du Colombier };
4967dd7cddfSDavid du Colombier
4977dd7cddfSDavid du Colombier /*
4987dd7cddfSDavid du Colombier * the following is a generic version of the
4997dd7cddfSDavid du Colombier * architecture specific stuff
5007dd7cddfSDavid du Colombier */
5017dd7cddfSDavid du Colombier
5027dd7cddfSDavid du Colombier static int
unimplemented(int)5037dd7cddfSDavid du Colombier unimplemented(int)
5047dd7cddfSDavid du Colombier {
5057dd7cddfSDavid du Colombier return 0;
5067dd7cddfSDavid du Colombier }
5077dd7cddfSDavid du Colombier
5087dd7cddfSDavid du Colombier static void
nop(void)5097dd7cddfSDavid du Colombier nop(void)
5107dd7cddfSDavid du Colombier {
5117dd7cddfSDavid du Colombier }
5127dd7cddfSDavid du Colombier
513c3ca4b29SDavid du Colombier static void
archreset(void)514c3ca4b29SDavid du Colombier archreset(void)
515c3ca4b29SDavid du Colombier {
516c3ca4b29SDavid du Colombier i8042reset();
517c3ca4b29SDavid du Colombier
518c3ca4b29SDavid du Colombier /*
519c3ca4b29SDavid du Colombier * Often the BIOS hangs during restart if a conventional 8042
520c3ca4b29SDavid du Colombier * warm-boot sequence is tried. The following is Intel specific and
521c3ca4b29SDavid du Colombier * seems to perform a cold-boot, but at least it comes back.
522c3ca4b29SDavid du Colombier * And sometimes there is no keyboard...
523c3ca4b29SDavid du Colombier *
524c3ca4b29SDavid du Colombier * The reset register (0xcf9) is usually in one of the bridge
525c3ca4b29SDavid du Colombier * chips. The actual location and sequence could be extracted from
526c3ca4b29SDavid du Colombier * ACPI but why bother, this is the end of the line anyway.
527c3ca4b29SDavid du Colombier */
528c3ca4b29SDavid du Colombier print("Takes a licking and keeps on ticking...\n");
529c3ca4b29SDavid du Colombier *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */
530c3ca4b29SDavid du Colombier outb(0xcf9, 0x02);
531c3ca4b29SDavid du Colombier outb(0xcf9, 0x06);
532c3ca4b29SDavid du Colombier
533c3ca4b29SDavid du Colombier for(;;)
534c3ca4b29SDavid du Colombier idle();
535c3ca4b29SDavid du Colombier }
536c3ca4b29SDavid du Colombier
537fb7f0c93SDavid du Colombier /*
5383c2ddefeSDavid du Colombier * 386 has no compare-and-swap instruction.
5393c2ddefeSDavid du Colombier * Run it with interrupts turned off instead.
5403c2ddefeSDavid du Colombier */
5413c2ddefeSDavid du Colombier static int
cmpswap386(long * addr,long old,long new)5423c2ddefeSDavid du Colombier cmpswap386(long *addr, long old, long new)
5433c2ddefeSDavid du Colombier {
5443c2ddefeSDavid du Colombier int r, s;
5453c2ddefeSDavid du Colombier
5463c2ddefeSDavid du Colombier s = splhi();
5473c2ddefeSDavid du Colombier if(r = (*addr == old))
5483c2ddefeSDavid du Colombier *addr = new;
5493c2ddefeSDavid du Colombier splx(s);
5503c2ddefeSDavid du Colombier return r;
5513c2ddefeSDavid du Colombier }
5523c2ddefeSDavid du Colombier
5533c2ddefeSDavid du Colombier /*
554fb7f0c93SDavid du Colombier * On a uniprocessor, you'd think that coherence could be nop,
555ed250ae1SDavid du Colombier * but it can't. We still need a barrier when using coherence() in
556fb7f0c93SDavid du Colombier * device drivers.
557fb7f0c93SDavid du Colombier *
558fb7f0c93SDavid du Colombier * On VMware, it's safe (and a huge win) to set this to nop.
559fb7f0c93SDavid du Colombier * Aux/vmware does this via the #P/archctl file.
560fb7f0c93SDavid du Colombier */
561ed250ae1SDavid du Colombier void (*coherence)(void) = nop;
5627dd7cddfSDavid du Colombier
5633c2ddefeSDavid du Colombier int (*cmpswap)(long*, long, long) = cmpswap386;
5643c2ddefeSDavid du Colombier
5657dd7cddfSDavid du Colombier PCArch* arch;
5667dd7cddfSDavid du Colombier extern PCArch* knownarch[];
5677dd7cddfSDavid du Colombier
5687dd7cddfSDavid du Colombier PCArch archgeneric = {
569d9306527SDavid du Colombier .id= "generic",
570d9306527SDavid du Colombier .ident= 0,
571c3ca4b29SDavid du Colombier .reset= archreset,
572d9306527SDavid du Colombier .serialpower= unimplemented,
573d9306527SDavid du Colombier .modempower= unimplemented,
5747dd7cddfSDavid du Colombier
575d9306527SDavid du Colombier .intrinit= i8259init,
576d9306527SDavid du Colombier .intrenable= i8259enable,
577d9306527SDavid du Colombier .intrvecno= i8259vecno,
578d9306527SDavid du Colombier .intrdisable= i8259disable,
5794de34a7eSDavid du Colombier .intron= i8259on,
5804de34a7eSDavid du Colombier .introff= i8259off,
5817dd7cddfSDavid du Colombier
582d9306527SDavid du Colombier .clockenable= i8253enable,
583d9306527SDavid du Colombier .fastclock= i8253read,
584d9306527SDavid du Colombier .timerset= i8253timerset,
5857dd7cddfSDavid du Colombier };
5867dd7cddfSDavid du Colombier
587ed250ae1SDavid du Colombier typedef struct X86type X86type;
588ed250ae1SDavid du Colombier struct X86type {
5897dd7cddfSDavid du Colombier int family;
5907dd7cddfSDavid du Colombier int model;
5917dd7cddfSDavid du Colombier int aalcycles;
5927dd7cddfSDavid du Colombier char* name;
593ed250ae1SDavid du Colombier };
5947dd7cddfSDavid du Colombier
595b4124be8SDavid du Colombier /* cpuid ax is 0x0ffMTFmS, where 0xffF is family, 0xMm is model */
5967dd7cddfSDavid du Colombier static X86type x86intel[] =
5977dd7cddfSDavid du Colombier {
5987dd7cddfSDavid du Colombier { 4, 0, 22, "486DX", }, /* known chips */
5997dd7cddfSDavid du Colombier { 4, 1, 22, "486DX50", },
6007dd7cddfSDavid du Colombier { 4, 2, 22, "486SX", },
6017dd7cddfSDavid du Colombier { 4, 3, 22, "486DX2", },
6027dd7cddfSDavid du Colombier { 4, 4, 22, "486SL", },
6037dd7cddfSDavid du Colombier { 4, 5, 22, "486SX2", },
6047dd7cddfSDavid du Colombier { 4, 7, 22, "DX2WB", }, /* P24D */
6057dd7cddfSDavid du Colombier { 4, 8, 22, "DX4", }, /* P24C */
6067dd7cddfSDavid du Colombier { 4, 9, 22, "DX4WB", }, /* P24CT */
6077dd7cddfSDavid du Colombier { 5, 0, 23, "P5", },
6087dd7cddfSDavid du Colombier { 5, 1, 23, "P5", },
6097dd7cddfSDavid du Colombier { 5, 2, 23, "P54C", },
6107dd7cddfSDavid du Colombier { 5, 3, 23, "P24T", },
6117dd7cddfSDavid du Colombier { 5, 4, 23, "P55C MMX", },
6127dd7cddfSDavid du Colombier { 5, 7, 23, "P54C VRT", },
6137dd7cddfSDavid du Colombier { 6, 1, 16, "PentiumPro", },/* trial and error */
6147dd7cddfSDavid du Colombier { 6, 3, 16, "PentiumII", },
6157dd7cddfSDavid du Colombier { 6, 5, 16, "PentiumII/Xeon", },
6167dd7cddfSDavid du Colombier { 6, 6, 16, "Celeron", },
6177dd7cddfSDavid du Colombier { 6, 7, 16, "PentiumIII/Xeon", },
6187dd7cddfSDavid du Colombier { 6, 8, 16, "PentiumIII/Xeon", },
6193ff48bf5SDavid du Colombier { 6, 0xB, 16, "PentiumIII/Xeon", },
620b4124be8SDavid du Colombier { 6, 0xF, 16, "Core 2/Xeon", },
621c39c2eb3SDavid du Colombier { 6, 0x16, 16, "Celeron", },
622c39c2eb3SDavid du Colombier { 6, 0x17, 16, "Core 2/Xeon", },
623c39c2eb3SDavid du Colombier { 6, 0x1A, 16, "Core i7/Xeon", },
624c39c2eb3SDavid du Colombier { 6, 0x1C, 16, "Atom", },
625c39c2eb3SDavid du Colombier { 6, 0x1D, 16, "Xeon MP", },
626b4124be8SDavid du Colombier { 6, 0x1E, 16, "Core i5/i7/Xeon", },
627b4124be8SDavid du Colombier { 6, 0x1F, 16, "Core i7/Xeon", },
628b4124be8SDavid du Colombier { 6, 0x22, 16, "Core i7", },
629b4124be8SDavid du Colombier { 6, 0x25, 16, "Core i3/i5/i7", },
630b4124be8SDavid du Colombier { 6, 0x2A, 16, "Core i7", },
631b4124be8SDavid du Colombier { 6, 0x2C, 16, "Core i7/Xeon", },
632b4124be8SDavid du Colombier { 6, 0x2D, 16, "Core i7", },
633b4124be8SDavid du Colombier { 6, 0x2E, 16, "Xeon MP", },
634b4124be8SDavid du Colombier { 6, 0x2F, 16, "Xeon MP", },
635b4124be8SDavid du Colombier { 6, 0x3A, 16, "Core i7", },
6369a747e4fSDavid du Colombier { 0xF, 1, 16, "P4", }, /* P4 */
637a30303efSDavid du Colombier { 0xF, 2, 16, "PentiumIV/Xeon", },
638c39c2eb3SDavid du Colombier { 0xF, 6, 16, "PentiumIV/Xeon", },
6397dd7cddfSDavid du Colombier
6407dd7cddfSDavid du Colombier { 3, -1, 32, "386", }, /* family defaults */
6417dd7cddfSDavid du Colombier { 4, -1, 22, "486", },
6427dd7cddfSDavid du Colombier { 5, -1, 23, "P5", },
6437dd7cddfSDavid du Colombier { 6, -1, 16, "P6", },
6449a747e4fSDavid du Colombier { 0xF, -1, 16, "P4", }, /* P4 */
6457dd7cddfSDavid du Colombier
646282e677fSDavid du Colombier { -1, -1, 16, "unknown", }, /* total default */
6477dd7cddfSDavid du Colombier };
6487dd7cddfSDavid du Colombier
6497dd7cddfSDavid du Colombier /*
6507dd7cddfSDavid du Colombier * The AMD processors all implement the CPUID instruction.
6517dd7cddfSDavid du Colombier * The later ones also return the processor name via functions
6527dd7cddfSDavid du Colombier * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
6537dd7cddfSDavid du Colombier * and DX:
6547dd7cddfSDavid du Colombier * K5 "AMD-K5(tm) Processor"
6557dd7cddfSDavid du Colombier * K6 "AMD-K6tm w/ multimedia extensions"
6567dd7cddfSDavid du Colombier * K6 3D "AMD-K6(tm) 3D processor"
6577dd7cddfSDavid du Colombier * K6 3D+ ?
6587dd7cddfSDavid du Colombier */
6597dd7cddfSDavid du Colombier static X86type x86amd[] =
6607dd7cddfSDavid du Colombier {
6617dd7cddfSDavid du Colombier { 5, 0, 23, "AMD-K5", }, /* guesswork */
6627dd7cddfSDavid du Colombier { 5, 1, 23, "AMD-K5", }, /* guesswork */
6637dd7cddfSDavid du Colombier { 5, 2, 23, "AMD-K5", }, /* guesswork */
6647dd7cddfSDavid du Colombier { 5, 3, 23, "AMD-K5", }, /* guesswork */
6650dd39f6eSDavid du Colombier { 5, 4, 23, "AMD Geode GX1", }, /* guesswork */
6660dd39f6eSDavid du Colombier { 5, 5, 23, "AMD Geode GX2", }, /* guesswork */
6677dd7cddfSDavid du Colombier { 5, 6, 11, "AMD-K6", }, /* trial and error */
6687dd7cddfSDavid du Colombier { 5, 7, 11, "AMD-K6", }, /* trial and error */
6697dd7cddfSDavid du Colombier { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
6707dd7cddfSDavid du Colombier { 5, 9, 11, "AMD-K6-III", },/* trial and error */
6710dd39f6eSDavid du Colombier { 5, 0xa, 23, "AMD Geode LX", }, /* guesswork */
6727dd7cddfSDavid du Colombier
6737dd7cddfSDavid du Colombier { 6, 1, 11, "AMD-Athlon", },/* trial and error */
6747dd7cddfSDavid du Colombier { 6, 2, 11, "AMD-Athlon", },/* trial and error */
6757dd7cddfSDavid du Colombier
676c39c2eb3SDavid du Colombier { 0x1F, 9, 11, "AMD-K10 Opteron G34", },/* guesswork */
677c39c2eb3SDavid du Colombier
6787dd7cddfSDavid du Colombier { 4, -1, 22, "Am486", }, /* guesswork */
6797dd7cddfSDavid du Colombier { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
6807dd7cddfSDavid du Colombier { 6, -1, 11, "AMD-Athlon", },/* guesswork */
681c39c2eb3SDavid du Colombier { 0xF, -1, 11, "AMD-K8", }, /* guesswork */
682c39c2eb3SDavid du Colombier { 0x1F, -1, 11, "AMD-K10", }, /* guesswork */
6837dd7cddfSDavid du Colombier
684282e677fSDavid du Colombier { -1, -1, 11, "unknown", }, /* total default */
6857dd7cddfSDavid du Colombier };
6867dd7cddfSDavid du Colombier
68759cc4ca5SDavid du Colombier /*
68859cc4ca5SDavid du Colombier * WinChip 240MHz
68959cc4ca5SDavid du Colombier */
69059cc4ca5SDavid du Colombier static X86type x86winchip[] =
69159cc4ca5SDavid du Colombier {
69259cc4ca5SDavid du Colombier {5, 4, 23, "Winchip",}, /* guesswork */
69365fa3f8bSDavid du Colombier {6, 7, 23, "Via C3 Samuel 2 or Ezra",},
69465fa3f8bSDavid du Colombier {6, 8, 23, "Via C3 Ezra-T",},
69543f160e5SDavid du Colombier {6, 9, 23, "Via C3 Eden-N",},
69659cc4ca5SDavid du Colombier { -1, -1, 23, "unknown", }, /* total default */
69759cc4ca5SDavid du Colombier };
69859cc4ca5SDavid du Colombier
699f5a5e725SDavid du Colombier /*
700f5a5e725SDavid du Colombier * SiS 55x
701f5a5e725SDavid du Colombier */
702f5a5e725SDavid du Colombier static X86type x86sis[] =
703f5a5e725SDavid du Colombier {
704f5a5e725SDavid du Colombier {5, 0, 23, "SiS 55x",}, /* guesswork */
705f5a5e725SDavid du Colombier { -1, -1, 23, "unknown", }, /* total default */
706f5a5e725SDavid du Colombier };
70759cc4ca5SDavid du Colombier
7087dd7cddfSDavid du Colombier static X86type *cputype;
7097dd7cddfSDavid du Colombier
710179dd269SDavid du Colombier static void simplecycles(uvlong*);
711179dd269SDavid du Colombier void (*cycles)(uvlong*) = simplecycles;
712179dd269SDavid du Colombier void _cycles(uvlong*); /* in l.s */
713179dd269SDavid du Colombier
714179dd269SDavid du Colombier static void
simplecycles(uvlong * x)715179dd269SDavid du Colombier simplecycles(uvlong*x)
716179dd269SDavid du Colombier {
717179dd269SDavid du Colombier *x = m->ticks;
718179dd269SDavid du Colombier }
719179dd269SDavid du Colombier
7207dd7cddfSDavid du Colombier void
cpuidprint(void)7217dd7cddfSDavid du Colombier cpuidprint(void)
7227dd7cddfSDavid du Colombier {
7237dd7cddfSDavid du Colombier int i;
7247dd7cddfSDavid du Colombier char buf[128];
7257dd7cddfSDavid du Colombier
726*4e3613abSDavid du Colombier i = snprint(buf, sizeof buf, "cpu%d: %s%dMHz ", m->machno,
727*4e3613abSDavid du Colombier m->machno < 10? " ": "", m->cpumhz);
7287dd7cddfSDavid du Colombier if(m->cpuidid[0])
729061a3f44SDavid du Colombier i += sprint(buf+i, "%12.12s ", m->cpuidid);
730208510e1SDavid du Colombier seprint(buf+i, buf + sizeof buf - 1,
731208510e1SDavid du Colombier "%s (cpuid: AX 0x%4.4uX DX 0x%4.4uX)\n",
7327dd7cddfSDavid du Colombier m->cpuidtype, m->cpuidax, m->cpuiddx);
7337dd7cddfSDavid du Colombier print(buf);
7347dd7cddfSDavid du Colombier }
7357dd7cddfSDavid du Colombier
7369a747e4fSDavid du Colombier /*
7379a747e4fSDavid du Colombier * figure out:
7389a747e4fSDavid du Colombier * - cpu type
7399a747e4fSDavid du Colombier * - whether or not we have a TSC (cycle counter)
7406b6b9ac8SDavid du Colombier * - whether or not it supports page size extensions
7419a747e4fSDavid du Colombier * (if so turn it on)
7426b6b9ac8SDavid du Colombier * - whether or not it supports machine check exceptions
7436b6b9ac8SDavid du Colombier * (if so turn it on)
7446b6b9ac8SDavid du Colombier * - whether or not it supports the page global flag
7459a747e4fSDavid du Colombier * (if so turn it on)
7469a747e4fSDavid du Colombier */
7477dd7cddfSDavid du Colombier int
cpuidentify(void)7487dd7cddfSDavid du Colombier cpuidentify(void)
7497dd7cddfSDavid du Colombier {
75059cc4ca5SDavid du Colombier char *p;
75159cc4ca5SDavid du Colombier int family, model, nomce;
7529a747e4fSDavid du Colombier X86type *t, *tab;
7537dd7cddfSDavid du Colombier ulong cr4;
75426d1d1dfSDavid du Colombier ulong regs[4];
75559cc4ca5SDavid du Colombier vlong mca, mct;
7567dd7cddfSDavid du Colombier
75726d1d1dfSDavid du Colombier cpuid(Highstdfunc, regs);
75826d1d1dfSDavid du Colombier memmove(m->cpuidid, ®s[1], BY2WD); /* bx */
75926d1d1dfSDavid du Colombier memmove(m->cpuidid+4, ®s[3], BY2WD); /* dx */
76026d1d1dfSDavid du Colombier memmove(m->cpuidid+8, ®s[2], BY2WD); /* cx */
76126d1d1dfSDavid du Colombier m->cpuidid[12] = '\0';
76226d1d1dfSDavid du Colombier
76326d1d1dfSDavid du Colombier cpuid(Procsig, regs);
76426d1d1dfSDavid du Colombier m->cpuidax = regs[0];
76526d1d1dfSDavid du Colombier m->cpuiddx = regs[3];
76626d1d1dfSDavid du Colombier
7670dd39f6eSDavid du Colombier if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0 ||
7680dd39f6eSDavid du Colombier strncmp(m->cpuidid, "Geode by NSC", 12) == 0)
7699a747e4fSDavid du Colombier tab = x86amd;
77059cc4ca5SDavid du Colombier else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
7719a747e4fSDavid du Colombier tab = x86winchip;
772f5a5e725SDavid du Colombier else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
773f5a5e725SDavid du Colombier tab = x86sis;
7747dd7cddfSDavid du Colombier else
7759a747e4fSDavid du Colombier tab = x86intel;
7769a747e4fSDavid du Colombier
7777dd7cddfSDavid du Colombier family = X86FAMILY(m->cpuidax);
7787dd7cddfSDavid du Colombier model = X86MODEL(m->cpuidax);
7799a747e4fSDavid du Colombier for(t=tab; t->name; t++)
7807dd7cddfSDavid du Colombier if((t->family == family && t->model == model)
7817dd7cddfSDavid du Colombier || (t->family == family && t->model == -1)
7827dd7cddfSDavid du Colombier || (t->family == -1))
7837dd7cddfSDavid du Colombier break;
7849a747e4fSDavid du Colombier
7857dd7cddfSDavid du Colombier m->cpuidtype = t->name;
7869a747e4fSDavid du Colombier
7879a747e4fSDavid du Colombier /*
7889a747e4fSDavid du Colombier * if there is one, set tsc to a known value
7899a747e4fSDavid du Colombier */
790b1707c5dSDavid du Colombier if(m->cpuiddx & Tsc){
791f5a5e725SDavid du Colombier m->havetsc = 1;
792179dd269SDavid du Colombier cycles = _cycles;
793b1707c5dSDavid du Colombier if(m->cpuiddx & Cpumsr)
7949a747e4fSDavid du Colombier wrmsr(0x10, 0);
795f5a5e725SDavid du Colombier }
7969a747e4fSDavid du Colombier
7979a747e4fSDavid du Colombier /*
7989a747e4fSDavid du Colombier * use i8253 to guess our cpu speed
7999a747e4fSDavid du Colombier */
8009a747e4fSDavid du Colombier guesscpuhz(t->aalcycles);
8017dd7cddfSDavid du Colombier
8027dd7cddfSDavid du Colombier /*
8036b6b9ac8SDavid du Colombier * If machine check exception, page size extensions or page global bit
8046b6b9ac8SDavid du Colombier * are supported enable them in CR4 and clear any other set extensions.
8057dd7cddfSDavid du Colombier * If machine check was enabled clear out any lingering status.
8067dd7cddfSDavid du Colombier */
80761fd6f66SDavid du Colombier if(m->cpuiddx & (Pge|Mce|Pse)){
8087dd7cddfSDavid du Colombier cr4 = 0;
80961fd6f66SDavid du Colombier if(m->cpuiddx & Pse)
8107dd7cddfSDavid du Colombier cr4 |= 0x10; /* page size extensions */
81159cc4ca5SDavid du Colombier if(p = getconf("*nomce"))
81259cc4ca5SDavid du Colombier nomce = strtoul(p, 0, 0);
81359cc4ca5SDavid du Colombier else
81459cc4ca5SDavid du Colombier nomce = 0;
815b1707c5dSDavid du Colombier if((m->cpuiddx & Mce) && !nomce){
8167dd7cddfSDavid du Colombier cr4 |= 0x40; /* machine check enable */
81759cc4ca5SDavid du Colombier if(family == 5){
81859cc4ca5SDavid du Colombier rdmsr(0x00, &mca);
81959cc4ca5SDavid du Colombier rdmsr(0x01, &mct);
82059cc4ca5SDavid du Colombier }
82159cc4ca5SDavid du Colombier }
8227dd7cddfSDavid du Colombier
8239a747e4fSDavid du Colombier /*
8249a747e4fSDavid du Colombier * Detect whether the chip supports the global bit
8259a747e4fSDavid du Colombier * in page directory and page table entries. When set
8269a747e4fSDavid du Colombier * in a particular entry, it means ``don't bother removing
8279a747e4fSDavid du Colombier * this from the TLB when CR3 changes.''
8289a747e4fSDavid du Colombier *
8299a747e4fSDavid du Colombier * We flag all kernel pages with this bit. Doing so lessens the
8309a747e4fSDavid du Colombier * overhead of switching processes on bare hardware,
8319a747e4fSDavid du Colombier * even more so on VMware. See mmu.c:/^memglobal.
8329a747e4fSDavid du Colombier *
8339a747e4fSDavid du Colombier * For future reference, should we ever need to do a
8349a747e4fSDavid du Colombier * full TLB flush, it can be accomplished by clearing
8359a747e4fSDavid du Colombier * the PGE bit in CR4, writing to CR3, and then
8369a747e4fSDavid du Colombier * restoring the PGE bit.
8379a747e4fSDavid du Colombier */
838b1707c5dSDavid du Colombier if(m->cpuiddx & Pge){
8399a747e4fSDavid du Colombier cr4 |= 0x80; /* page global enable bit */
8409a747e4fSDavid du Colombier m->havepge = 1;
8419a747e4fSDavid du Colombier }
8429a747e4fSDavid du Colombier
8436b6b9ac8SDavid du Colombier putcr4(cr4);
844b1707c5dSDavid du Colombier if(m->cpuiddx & Mce)
8456b6b9ac8SDavid du Colombier rdmsr(0x01, &mct);
8466b6b9ac8SDavid du Colombier }
8476b6b9ac8SDavid du Colombier
84891e577b2SDavid du Colombier if(m->cpuiddx & Fxsr){ /* have sse fp? */
84991e577b2SDavid du Colombier fpsave = fpssesave;
85091e577b2SDavid du Colombier fprestore = fpsserestore;
85191e577b2SDavid du Colombier putcr4(getcr4() | CR4Osfxsr);
85291e577b2SDavid du Colombier } else {
85391e577b2SDavid du Colombier fpsave = fpx87save;
85491e577b2SDavid du Colombier fprestore = fpx87restore;
85591e577b2SDavid du Colombier }
85691e577b2SDavid du Colombier
8577dd7cddfSDavid du Colombier cputype = t;
8587dd7cddfSDavid du Colombier return t->family;
8597dd7cddfSDavid du Colombier }
8607dd7cddfSDavid du Colombier
8617dd7cddfSDavid du Colombier static long
cputyperead(Chan *,void * a,long n,vlong offset)86280ee5cbfSDavid du Colombier cputyperead(Chan*, void *a, long n, vlong offset)
8637dd7cddfSDavid du Colombier {
8647dd7cddfSDavid du Colombier char str[32];
8657dd7cddfSDavid du Colombier ulong mhz;
8667dd7cddfSDavid du Colombier
8677dd7cddfSDavid du Colombier mhz = (m->cpuhz+999999)/1000000;
8687dd7cddfSDavid du Colombier
8697dd7cddfSDavid du Colombier snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
8707dd7cddfSDavid du Colombier return readstr(offset, a, n, str);
8717dd7cddfSDavid du Colombier }
8727dd7cddfSDavid du Colombier
8739a747e4fSDavid du Colombier static long
archctlread(Chan *,void * a,long nn,vlong offset)874fb7f0c93SDavid du Colombier archctlread(Chan*, void *a, long nn, vlong offset)
8759a747e4fSDavid du Colombier {
876fb7f0c93SDavid du Colombier int n;
877e4ac449cSDavid du Colombier char *buf, *p, *ep;
8789a747e4fSDavid du Colombier
879e4ac449cSDavid du Colombier p = buf = malloc(READSTR);
880aa72973aSDavid du Colombier if(p == nil)
881aa72973aSDavid du Colombier error(Enomem);
882e4ac449cSDavid du Colombier ep = p + READSTR;
883e4ac449cSDavid du Colombier p = seprint(p, ep, "cpu %s %lud%s\n",
884fb7f0c93SDavid du Colombier cputype->name, (ulong)(m->cpuhz+999999)/1000000,
885fb7f0c93SDavid du Colombier m->havepge ? " pge" : "");
886e4ac449cSDavid du Colombier p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
887e4ac449cSDavid du Colombier p = seprint(p, ep, "coherence ");
888ed250ae1SDavid du Colombier if(coherence == mb386)
889e4ac449cSDavid du Colombier p = seprint(p, ep, "mb386\n");
890ed250ae1SDavid du Colombier else if(coherence == mb586)
891e4ac449cSDavid du Colombier p = seprint(p, ep, "mb586\n");
892b1707c5dSDavid du Colombier else if(coherence == mfence)
893e4ac449cSDavid du Colombier p = seprint(p, ep, "mfence\n");
894ed250ae1SDavid du Colombier else if(coherence == nop)
895e4ac449cSDavid du Colombier p = seprint(p, ep, "nop\n");
896ed250ae1SDavid du Colombier else
897e4ac449cSDavid du Colombier p = seprint(p, ep, "0x%p\n", coherence);
898e4ac449cSDavid du Colombier p = seprint(p, ep, "cmpswap ");
8993c2ddefeSDavid du Colombier if(cmpswap == cmpswap386)
900e4ac449cSDavid du Colombier p = seprint(p, ep, "cmpswap386\n");
9013c2ddefeSDavid du Colombier else if(cmpswap == cmpswap486)
902e4ac449cSDavid du Colombier p = seprint(p, ep, "cmpswap486\n");
9033c2ddefeSDavid du Colombier else
904e4ac449cSDavid du Colombier p = seprint(p, ep, "0x%p\n", cmpswap);
905e4ac449cSDavid du Colombier p = seprint(p, ep, "i8253set %s\n", doi8253set ? "on" : "off");
906e4ac449cSDavid du Colombier n = p - buf;
907e4ac449cSDavid du Colombier n += mtrrprint(p, ep - p);
908e4ac449cSDavid du Colombier buf[n] = '\0';
909e4ac449cSDavid du Colombier
910e4ac449cSDavid du Colombier n = readstr(offset, a, nn, buf);
911e4ac449cSDavid du Colombier free(buf);
912e4ac449cSDavid du Colombier return n;
9139a747e4fSDavid du Colombier }
914fb7f0c93SDavid du Colombier
915fb7f0c93SDavid du Colombier enum
916fb7f0c93SDavid du Colombier {
917fb7f0c93SDavid du Colombier CMpge,
918fb7f0c93SDavid du Colombier CMcoherence,
919fb7f0c93SDavid du Colombier CMi8253set,
92026d1d1dfSDavid du Colombier CMcache,
921fb7f0c93SDavid du Colombier };
922fb7f0c93SDavid du Colombier
923fb7f0c93SDavid du Colombier static Cmdtab archctlmsg[] =
924fb7f0c93SDavid du Colombier {
925fb7f0c93SDavid du Colombier CMpge, "pge", 2,
926fb7f0c93SDavid du Colombier CMcoherence, "coherence", 2,
927fb7f0c93SDavid du Colombier CMi8253set, "i8253set", 2,
92826d1d1dfSDavid du Colombier CMcache, "cache", 4,
929fb7f0c93SDavid du Colombier };
9309a747e4fSDavid du Colombier
9319a747e4fSDavid du Colombier static long
archctlwrite(Chan *,void * a,long n,vlong)932fb7f0c93SDavid du Colombier archctlwrite(Chan*, void *a, long n, vlong)
9339a747e4fSDavid du Colombier {
934e4ac449cSDavid du Colombier uvlong base, size;
935fb7f0c93SDavid du Colombier Cmdbuf *cb;
936fb7f0c93SDavid du Colombier Cmdtab *ct;
93726d1d1dfSDavid du Colombier char *ep;
938fb7f0c93SDavid du Colombier
939fb7f0c93SDavid du Colombier cb = parsecmd(a, n);
940fb7f0c93SDavid du Colombier if(waserror()){
941fb7f0c93SDavid du Colombier free(cb);
942fb7f0c93SDavid du Colombier nexterror();
943fb7f0c93SDavid du Colombier }
944fb7f0c93SDavid du Colombier ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
945fb7f0c93SDavid du Colombier switch(ct->index){
946fb7f0c93SDavid du Colombier case CMpge:
947fb7f0c93SDavid du Colombier if(!m->havepge)
948fb7f0c93SDavid du Colombier error("processor does not support pge");
949fb7f0c93SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
950fb7f0c93SDavid du Colombier putcr4(getcr4() | 0x80);
951fb7f0c93SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
952fb7f0c93SDavid du Colombier putcr4(getcr4() & ~0x80);
953fb7f0c93SDavid du Colombier else
954fb7f0c93SDavid du Colombier cmderror(cb, "invalid pge ctl");
955fb7f0c93SDavid du Colombier break;
956fb7f0c93SDavid du Colombier case CMcoherence:
957ed250ae1SDavid du Colombier if(strcmp(cb->f[1], "mb386") == 0)
958ed250ae1SDavid du Colombier coherence = mb386;
959ed250ae1SDavid du Colombier else if(strcmp(cb->f[1], "mb586") == 0){
960ed250ae1SDavid du Colombier if(X86FAMILY(m->cpuidax) < 5)
961ed250ae1SDavid du Colombier error("invalid coherence ctl on this cpu family");
962ed250ae1SDavid du Colombier coherence = mb586;
963b1707c5dSDavid du Colombier }else if(strcmp(cb->f[1], "mfence") == 0){
964b1707c5dSDavid du Colombier if((m->cpuiddx & Sse2) == 0)
965b1707c5dSDavid du Colombier error("invalid coherence ctl on this cpu family");
966b1707c5dSDavid du Colombier coherence = mfence;
967b1707c5dSDavid du Colombier }else if(strcmp(cb->f[1], "nop") == 0){
968fb7f0c93SDavid du Colombier /* only safe on vmware */
969fb7f0c93SDavid du Colombier if(conf.nmach > 1)
970fb7f0c93SDavid du Colombier error("cannot disable coherence on a multiprocessor");
971fb7f0c93SDavid du Colombier coherence = nop;
972fb7f0c93SDavid du Colombier }else
973fb7f0c93SDavid du Colombier cmderror(cb, "invalid coherence ctl");
974fb7f0c93SDavid du Colombier break;
975fb7f0c93SDavid du Colombier case CMi8253set:
976fb7f0c93SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
977ed250ae1SDavid du Colombier doi8253set = 1;
978dc5a79c1SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0){
979ed250ae1SDavid du Colombier doi8253set = 0;
980dc5a79c1SDavid du Colombier (*arch->timerset)(0);
981dc5a79c1SDavid du Colombier }else
982fb7f0c93SDavid du Colombier cmderror(cb, "invalid i2853set ctl");
983fb7f0c93SDavid du Colombier break;
98426d1d1dfSDavid du Colombier case CMcache:
985e4ac449cSDavid du Colombier base = strtoull(cb->f[1], &ep, 0);
98626d1d1dfSDavid du Colombier if(*ep)
98726d1d1dfSDavid du Colombier error("cache: parse error: base not a number?");
988e4ac449cSDavid du Colombier size = strtoull(cb->f[2], &ep, 0);
98926d1d1dfSDavid du Colombier if(*ep)
99026d1d1dfSDavid du Colombier error("cache: parse error: size not a number?");
99126d1d1dfSDavid du Colombier mtrr(base, size, cb->f[3]);
99226d1d1dfSDavid du Colombier break;
993fb7f0c93SDavid du Colombier }
994fb7f0c93SDavid du Colombier free(cb);
995fb7f0c93SDavid du Colombier poperror();
9969a747e4fSDavid du Colombier return n;
9979a747e4fSDavid du Colombier }
9989a747e4fSDavid du Colombier
9997dd7cddfSDavid du Colombier void
archinit(void)10007dd7cddfSDavid du Colombier archinit(void)
10017dd7cddfSDavid du Colombier {
10027dd7cddfSDavid du Colombier PCArch **p;
10037dd7cddfSDavid du Colombier
10047dd7cddfSDavid du Colombier arch = 0;
10057dd7cddfSDavid du Colombier for(p = knownarch; *p; p++){
10067dd7cddfSDavid du Colombier if((*p)->ident && (*p)->ident() == 0){
10077dd7cddfSDavid du Colombier arch = *p;
10087dd7cddfSDavid du Colombier break;
10097dd7cddfSDavid du Colombier }
10107dd7cddfSDavid du Colombier }
10117dd7cddfSDavid du Colombier if(arch == 0)
10127dd7cddfSDavid du Colombier arch = &archgeneric;
10137dd7cddfSDavid du Colombier else{
10147dd7cddfSDavid du Colombier if(arch->id == 0)
10157dd7cddfSDavid du Colombier arch->id = archgeneric.id;
10167dd7cddfSDavid du Colombier if(arch->reset == 0)
10177dd7cddfSDavid du Colombier arch->reset = archgeneric.reset;
10187dd7cddfSDavid du Colombier if(arch->serialpower == 0)
10197dd7cddfSDavid du Colombier arch->serialpower = archgeneric.serialpower;
10207dd7cddfSDavid du Colombier if(arch->modempower == 0)
10217dd7cddfSDavid du Colombier arch->modempower = archgeneric.modempower;
10227dd7cddfSDavid du Colombier if(arch->intrinit == 0)
10237dd7cddfSDavid du Colombier arch->intrinit = archgeneric.intrinit;
10247dd7cddfSDavid du Colombier if(arch->intrenable == 0)
10257dd7cddfSDavid du Colombier arch->intrenable = archgeneric.intrenable;
10267dd7cddfSDavid du Colombier }
10277dd7cddfSDavid du Colombier
10287dd7cddfSDavid du Colombier /*
10297dd7cddfSDavid du Colombier * Decide whether to use copy-on-reference (386 and mp).
10309a747e4fSDavid du Colombier * We get another chance to set it in mpinit() for a
10319a747e4fSDavid du Colombier * multiprocessor.
10327dd7cddfSDavid du Colombier */
10339a747e4fSDavid du Colombier if(X86FAMILY(m->cpuidax) == 3)
10347dd7cddfSDavid du Colombier conf.copymode = 1;
10357dd7cddfSDavid du Colombier
10363c2ddefeSDavid du Colombier if(X86FAMILY(m->cpuidax) >= 4)
10373c2ddefeSDavid du Colombier cmpswap = cmpswap486;
10383c2ddefeSDavid du Colombier
1039ed250ae1SDavid du Colombier if(X86FAMILY(m->cpuidax) >= 5)
1040ed250ae1SDavid du Colombier coherence = mb586;
1041ed250ae1SDavid du Colombier
1042b1707c5dSDavid du Colombier if(m->cpuiddx & Sse2)
1043b1707c5dSDavid du Colombier coherence = mfence;
1044b1707c5dSDavid du Colombier
104580ee5cbfSDavid du Colombier addarchfile("cputype", 0444, cputyperead, nil);
1046fb7f0c93SDavid du Colombier addarchfile("archctl", 0664, archctlread, archctlwrite);
10477dd7cddfSDavid du Colombier }
10487dd7cddfSDavid du Colombier
1049abfa367dSDavid du Colombier void
archrevert(void)1050abfa367dSDavid du Colombier archrevert(void)
1051abfa367dSDavid du Colombier {
1052abfa367dSDavid du Colombier arch = &archgeneric;
1053abfa367dSDavid du Colombier }
1054abfa367dSDavid du Colombier
10557dd7cddfSDavid du Colombier /*
10569a747e4fSDavid du Colombier * call either the pcmcia or pccard device setup
10577dd7cddfSDavid du Colombier */
10589a747e4fSDavid du Colombier int
pcmspecial(char * idstr,ISAConf * isa)10599a747e4fSDavid du Colombier pcmspecial(char *idstr, ISAConf *isa)
10607dd7cddfSDavid du Colombier {
10619a747e4fSDavid du Colombier return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
10627dd7cddfSDavid du Colombier }
10637dd7cddfSDavid du Colombier
10649a747e4fSDavid du Colombier /*
10659a747e4fSDavid du Colombier * call either the pcmcia or pccard device teardown
10669a747e4fSDavid du Colombier */
10679a747e4fSDavid du Colombier void
pcmspecialclose(int a)10689a747e4fSDavid du Colombier pcmspecialclose(int a)
10699a747e4fSDavid du Colombier {
10709a747e4fSDavid du Colombier if (_pcmspecialclose != nil)
10719a747e4fSDavid du Colombier _pcmspecialclose(a);
10729a747e4fSDavid du Colombier }
10739a747e4fSDavid du Colombier
10749a747e4fSDavid du Colombier /*
10759a747e4fSDavid du Colombier * return value and speed of timer set in arch->clockenable
10769a747e4fSDavid du Colombier */
10779a747e4fSDavid du Colombier uvlong
fastticks(uvlong * hz)10787dd7cddfSDavid du Colombier fastticks(uvlong *hz)
10797dd7cddfSDavid du Colombier {
10807dd7cddfSDavid du Colombier return (*arch->fastclock)(hz);
10817dd7cddfSDavid du Colombier }
10829a747e4fSDavid du Colombier
1083208510e1SDavid du Colombier ulong
s(void)1084208510e1SDavid du Colombier µs(void)
1085208510e1SDavid du Colombier {
1086208510e1SDavid du Colombier return fastticks2us((*arch->fastclock)(nil));
1087208510e1SDavid du Colombier }
1088208510e1SDavid du Colombier
10899a747e4fSDavid du Colombier /*
10909a747e4fSDavid du Colombier * set next timer interrupt
10919a747e4fSDavid du Colombier */
10929a747e4fSDavid du Colombier void
timerset(Tval x)1093ea58ad6fSDavid du Colombier timerset(Tval x)
10949a747e4fSDavid du Colombier {
1095ed250ae1SDavid du Colombier if(doi8253set)
10969a747e4fSDavid du Colombier (*arch->timerset)(x);
10979a747e4fSDavid du Colombier }
1098