Qmsr,
Qbase,
- Qmax = 16,
+ Qmax = 32,
};
enum {
CR4Osfxsr = 1 << 9,
+ CR4Oxmmex = 1 << 10,
};
enum { /* cpuid standard function codes */
Procsig,
Proctlbcache,
Procserial,
+
+ Highextfunc = 0x80000000,
+ Procextfeat,
};
typedef long Rdwrfn(Chan*, void*, long, vlong);
"iob", { Qiob, 0 }, 0, 0660,
"iow", { Qiow, 0 }, 0, 0660,
"iol", { Qiol, 0 }, 0, 0660,
- "msr", { Qmsr, 0}, 0, 0660,
+ "msr", { Qmsr, 0 }, 0, 0660,
};
Lock archwlock; /* the lock is only for changing archdir */
int narchdir = Qbase;
int (*_pcmspecial)(char*, ISAConf*);
void (*_pcmspecialclose)(int);
-static int doi8253set = 1;
-
/*
* Add a file to the #P listing. Once added, you can't delete it.
* You can't add a file with the same name as one already there,
lock(&archwlock);
if(narchdir >= Qmax){
unlock(&archwlock);
+ print("addarchfile: out of entries for %s\n", name);
return nil;
}
if(port < 0){
/* find a free port above 0x400 and below 0x1000 */
port = 0x400;
- for(l = &iomap.m; *l; l = &(*l)->next){
- m = *l;
+ for(l = &iomap.m; (m = *l) != nil; l = &m->next){
if (m->start < 0x400) continue;
i = m->start - port;
if(i > size)
else
port = m->end;
}
- if(*l == nil){
+ if(m == nil){
unlock(&iomap);
return -1;
}
return -1;
}
/* see if the space clashes with previously allocated ports */
- for(l = &iomap.m; *l; l = &(*l)->next){
- m = *l;
+ for(l = &iomap.m; (m = *l) != nil; l = &m->next){
if(m->end <= port)
continue;
if(m->reserved && m->start == port && m->end >= port + size) {
IOMap *m, **l;
lock(&iomap);
- for(l = &iomap.m; *l; l = &(*l)->next){
- if((*l)->start == port){
- m = *l;
+ for(l = &iomap.m; (m = *l) != nil; l = &m->next){
+ if(m->start == port){
*l = m->next;
m->next = iomap.free;
iomap.free = m;
break;
}
- if((*l)->start > port)
+ if(m->start > port)
break;
}
archdir[0].qid.vers++;
{
IOMap *m;
- for(m = iomap.m; m; m = m->next){
+ for(m = iomap.m; m != nil; m = m->next){
if(start >= m->start && start < m->end
|| start <= m->start && end > m->start)
return 0;
}
static void
-checkport(int start, int end)
+checkport(uint start, uint end)
{
+ if(end < start || end > 0x10000)
+ error(Ebadarg);
+
/* standard vga regs are OK */
if(start >= 0x2b0 && end <= 0x2df+1)
return;
{
}
-enum
-{
- Linelen= 31,
-};
-
static long
archread(Chan *c, void *a, long n, vlong offset)
{
- char *buf, *p;
- int port;
+ char buf[32], *p;
+ uint port, end;
ushort *sp;
ulong *lp;
vlong *vp;
IOMap *m;
Rdwrfn *fn;
+ int i;
+ port = offset;
+ end = port+n;
switch((ulong)c->qid.path){
-
case Qdir:
return devdirread(c, a, n, archdir, narchdir, devgen);
case Qiob:
- port = offset;
- checkport(offset, offset+n);
- for(p = a; port < offset+n; port++)
+ checkport(port, end);
+ for(p = a; port < end; port++)
*p++ = inb(port);
return n;
case Qiow:
if(n & 1)
error(Ebadarg);
- checkport(offset, offset+n);
- sp = a;
- for(port = offset; port < offset+n; port += 2)
+ checkport(port, end);
+ for(sp = a; port < end; port += 2)
*sp++ = ins(port);
return n;
case Qiol:
if(n & 3)
error(Ebadarg);
- checkport(offset, offset+n);
- lp = a;
- for(port = offset; port < offset+n; port += 4)
+ checkport(port, end);
+ for(lp = a; port < end; port += 4)
*lp++ = inl(port);
return n;
case Qmsr:
if(n & 7)
error(Ebadarg);
- vp = a;
- for(port = offset; port < offset+n; port += 8)
+ if((uint)n/8 > -port)
+ error(Ebadarg);
+ end = port+(n/8);
+ for(vp = a; port != end; port++)
if(rdmsr(port, vp++) < 0)
error(Ebadarg);
return n;
case Qioalloc:
- break;
+ lock(&iomap);
+ i = 0;
+ for(m = iomap.m; m != nil; m = m->next){
+ i = snprint(buf, sizeof(buf), "%8lux %8lux %-12.12s\n",
+ m->start, m->end-1, m->tag);
+ offset -= i;
+ if(offset < 0)
+ break;
+ }
+ unlock(&iomap);
+ if(offset >= 0)
+ return 0;
+ if(n > -offset)
+ n = -offset;
+ offset += i;
+ memmove(a, buf+offset, n);
+ return n;
default:
if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
return fn(c, a, n, offset);
error(Eperm);
- break;
+ return 0;
}
-
- if((buf = malloc(n)) == nil)
- error(Enomem);
- p = buf;
- n = n/Linelen;
- offset = offset/Linelen;
-
- lock(&iomap);
- for(m = iomap.m; n > 0 && m != nil; m = m->next){
- if(offset-- > 0)
- continue;
- sprint(p, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
- p += Linelen;
- n--;
- }
- unlock(&iomap);
-
- n = p - buf;
- memmove(a, buf, n);
- free(buf);
-
- return n;
}
static long
archwrite(Chan *c, void *a, long n, vlong offset)
{
+ uint port, end;
char *p;
- int port;
ushort *sp;
ulong *lp;
vlong *vp;
Rdwrfn *fn;
+ port = offset;
+ end = port+n;
switch((ulong)c->qid.path){
-
case Qiob:
- p = a;
- checkport(offset, offset+n);
- for(port = offset; port < offset+n; port++)
+ checkport(port, end);
+ for(p = a; port < end; port++)
outb(port, *p++);
return n;
case Qiow:
if(n & 1)
error(Ebadarg);
- checkport(offset, offset+n);
- sp = a;
- for(port = offset; port < offset+n; port += 2)
+ checkport(port, end);
+ for(sp = a; port < end; port += 2)
outs(port, *sp++);
return n;
case Qiol:
if(n & 3)
error(Ebadarg);
- checkport(offset, offset+n);
- lp = a;
- for(port = offset; port < offset+n; port += 4)
+ checkport(port, end);
+ for(lp = a; port < end; port += 4)
outl(port, *lp++);
return n;
case Qmsr:
if(n & 7)
error(Ebadarg);
- vp = a;
- for(port = offset; port < offset+n; port += 8)
+ if((uint)n/8 > -port)
+ error(Ebadarg);
+ end = port+(n/8);
+ for(vp = a; port != end; port++)
if(wrmsr(port, *vp++) < 0)
error(Ebadarg);
return n;
default:
- if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+ if(c->qid.path < narchdir && (fn = writefn[c->qid.path]) != nil)
return fn(c, a, n, offset);
error(Eperm);
break;
{
}
-static void
+void
archreset(void)
{
i8042reset();
outb(0xcf9, 0x02);
outb(0xcf9, 0x06);
+ print("can't reset\n");
for(;;)
idle();
}
{ 6, -1, 11, "AMD-Athlon", },/* guesswork */
{ 0xF, -1, 11, "AMD-K8", }, /* guesswork */
{ 0x1F, -1, 11, "AMD-K10", }, /* guesswork */
+ { 23, 1, 13, "AMD Ryzen" },
{ -1, -1, 11, "unknown", }, /* total default */
};
void
cpuidprint(void)
{
- int i;
- char buf[128];
-
- i = sprint(buf, "cpu%d: %dMHz ", m->machno, m->cpumhz);
- if(m->cpuidid[0])
- i += sprint(buf+i, "%12.12s ", m->cpuidid);
- seprint(buf+i, buf + sizeof buf - 1,
- "%s (cpuid: AX 0x%4.4uX CX 0x%4.4uX DX 0x%4.4uX)\n",
- m->cpuidtype, m->cpuidax, m->cpuidcx, m->cpuiddx);
- print(buf);
+ print("cpu%d: %dMHz %s %s (AX %8.8uX CX %8.8uX DX %8.8uX)\n",
+ m->machno, m->cpumhz, m->cpuidid, m->cpuidtype,
+ m->cpuidax, m->cpuidcx, m->cpuiddx);
}
/*
char *p;
int family, model, nomce;
X86type *t, *tab;
- ulong cr4;
+ uintptr cr4;
ulong regs[4];
vlong mca, mct;
m->cpuidax = regs[0];
m->cpuidcx = regs[2];
m->cpuiddx = regs[3];
+
+ m->cpuidfamily = m->cpuidax >> 8 & 0xf;
+ m->cpuidmodel = m->cpuidax >> 4 & 0xf;
+ m->cpuidstepping = m->cpuidax & 0xf;
+ switch(m->cpuidfamily){
+ case 15:
+ m->cpuidfamily += m->cpuidax >> 20 & 0xff;
+ m->cpuidmodel += m->cpuidax >> 16 & 0xf;
+ break;
+ case 6:
+ m->cpuidmodel += m->cpuidax >> 16 & 0xf;
+ break;
+ }
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0 ||
strncmp(m->cpuidid, "Geode by NSC", 12) == 0)
else
tab = x86intel;
- family = X86FAMILY(m->cpuidax);
- model = X86MODEL(m->cpuidax);
+ family = m->cpuidfamily;
+ model = m->cpuidmodel;
for(t=tab; t->name; t++)
if((t->family == family && t->model == model)
|| (t->family == family && t->model == -1)
/*
* if there is one, set tsc to a known value
*/
- if((m->cpuiddx & Tsc) != 0 && getconf("*notsc") == nil){
+ if(m->cpuiddx & Tsc){
m->havetsc = 1;
cycles = _cycles;
if(m->cpuiddx & Cpumsr)
* If machine check was enabled clear out any lingering status.
*/
if(m->cpuiddx & (Pge|Mce|Pse)){
- cr4 = 0;
+ cr4 = getcr4();
if(m->cpuiddx & Pse)
cr4 |= 0x10; /* page size extensions */
if(p = getconf("*nomce"))
nomce = strtoul(p, 0, 0);
else
nomce = 0;
- if((m->cpuiddx & Mce) && !nomce){
- cr4 |= 0x40; /* machine check enable */
- if(family == 5){
+ if((m->cpuiddx & Mce) != 0 && !nomce){
+ if((m->cpuiddx & Mca) != 0){
+ vlong cap;
+ int bank;
+
+ cap = 0;
+ rdmsr(0x179, &cap);
+
+ if(cap & 0x100)
+ wrmsr(0x17B, ~0ULL); /* enable all mca features */
+
+ bank = cap & 0xFF;
+ if(bank > 64)
+ bank = 64;
+
+ /* init MCi .. MC1 (except MC0) */
+ while(--bank > 0){
+ wrmsr(0x400 + bank*4, ~0ULL);
+ wrmsr(0x401 + bank*4, 0);
+ }
+
+ if(family != 6 || model >= 0x1A)
+ wrmsr(0x400, ~0ULL);
+
+ wrmsr(0x401, 0);
+ }
+ else if(family == 5){
rdmsr(0x00, &mca);
rdmsr(0x01, &mct);
}
+ cr4 |= 0x40; /* machine check enable */
}
/*
}
putcr4(cr4);
- if(m->cpuiddx & Mce)
+
+ if((m->cpuiddx & (Mca|Mce)) == Mce)
rdmsr(0x01, &mct);
}
- if(m->cpuiddx & Fxsr){ /* have sse fp? */
+ if(m->cpuiddx & Mtrr)
+ mtrrsync();
+
+ if((m->cpuiddx & (Sse|Fxsr)) == (Sse|Fxsr)){ /* have sse fp? */
fpsave = fpssesave;
fprestore = fpsserestore;
- putcr4(getcr4() | CR4Osfxsr);
+ putcr4(getcr4() | CR4Osfxsr|CR4Oxmmex);
} else {
fpsave = fpx87save;
fprestore = fpx87restore;
}
+ if(strcmp(m->cpuidid, "GenuineIntel") == 0 && (m->cpuidcx & Rdrnd) != 0)
+ hwrandbuf = rdrandbuf;
+ else
+ hwrandbuf = nil;
+
+ if(sizeof(uintptr) == 8) {
+ /* 8-byte watchpoints are supported in Long Mode */
+ m->havewatchpt8 = 1;
+
+ /* check and enable NX bit */
+ cpuid(Highextfunc, regs);
+ if(regs[0] >= Procextfeat){
+ cpuid(Procextfeat, regs);
+ if((regs[3] & (1<<20)) != 0){
+ vlong efer;
+
+ /* enable no-execute feature */
+ if(rdmsr(Efer, &efer) != -1){
+ efer |= 1ull<<11;
+ if(wrmsr(Efer, efer) != -1)
+ m->havenx = 1;
+ }
+ }
+ }
+ } else if(strcmp(m->cpuidid, "GenuineIntel") == 0){
+ /* some random CPUs that support 8-byte watchpoints */
+ if(family == 15 && (model == 3 || model == 4 || model == 6)
+ || family == 6 && (model == 15 || model == 23 || model == 28))
+ m->havewatchpt8 = 1;
+ /* Intel SDM claims amd64 support implies 8-byte watchpoint support */
+ cpuid(Highextfunc, regs);
+ if(regs[0] >= Procextfeat){
+ cpuid(Procextfeat, regs);
+ if((regs[3] & 1<<29) != 0)
+ m->havewatchpt8 = 1;
+ }
+ }
+
cputype = t;
return t->family;
}
p = seprint(p, ep, "cmpswap486\n");
else
p = seprint(p, ep, "0x%p\n", cmpswap);
- p = seprint(p, ep, "i8253set %s\n", doi8253set ? "on" : "off");
+ p = seprint(p, ep, "arch %s\n", arch->id);
n = p - buf;
n += mtrrprint(p, ep - p);
buf[n] = '\0';
{
CMpge,
CMcoherence,
- CMi8253set,
CMcache,
};
{
CMpge, "pge", 2,
CMcoherence, "coherence", 2,
- CMi8253set, "i8253set", 2,
- CMcache, "cache", 4,
+ CMcache, "cache", 4,
};
static long
if(strcmp(cb->f[1], "mb386") == 0)
coherence = mb386;
else if(strcmp(cb->f[1], "mb586") == 0){
- if(X86FAMILY(m->cpuidax) < 5)
+ if(m->cpuidfamily < 5)
error("invalid coherence ctl on this cpu family");
coherence = mb586;
}else if(strcmp(cb->f[1], "mfence") == 0){
}else
cmderror(cb, "invalid coherence ctl");
break;
- case CMi8253set:
- if(strcmp(cb->f[1], "on") == 0)
- doi8253set = 1;
- else if(strcmp(cb->f[1], "off") == 0){
- doi8253set = 0;
- (*arch->timerset)(0);
- }else
- cmderror(cb, "invalid i2853set ctl");
- break;
case CMcache:
base = strtoull(cb->f[1], &ep, 0);
if(*ep)
size = strtoull(cb->f[2], &ep, 0);
if(*ep)
error("cache: parse error: size not a number?");
- mtrr(base, size, cb->f[3]);
+ ep = mtrr(base, size, cb->f[3]);
+ if(ep != nil)
+ error(ep);
break;
}
free(cb);
static long
rmemrw(int isr, void *a, long n, vlong off)
{
+ uintptr addr = off;
+
if(off < 0 || n < 0)
error("bad offset/count");
if(isr){
- if(off >= MB)
+ if(addr >= MB)
return 0;
- if(off+n >= MB)
- n = MB - off;
- memmove(a, KADDR((ulong)off), n);
+ if(addr+n > MB)
+ n = MB - addr;
+ memmove(a, KADDR(addr), n);
}else{
- /* allow vga framebuf's access */
- if(off >= MB || off+n > MB ||
- (off < 0xA0000 || off+n > 0xB0000+0x10000))
+ /* allow vga framebuf's write access */
+ if(addr >= MB || addr+n > MB ||
+ (addr < 0xA0000 || addr+n > 0xB0000+0x10000))
error("bad offset/count in write");
- memmove(KADDR((ulong)off), a, n);
+ memmove(KADDR(addr), a, n);
}
return n;
}
{
PCArch **p;
- arch = 0;
- for(p = knownarch; *p; p++){
- if((*p)->ident && (*p)->ident() == 0){
+ arch = &archgeneric;
+ for(p = knownarch; *p != nil; p++){
+ if((*p)->ident != nil && (*p)->ident() == 0){
arch = *p;
break;
}
}
- if(arch == 0)
- arch = &archgeneric;
- else{
- if(arch->id == 0)
+ if(arch != &archgeneric){
+ if(arch->id == nil)
arch->id = archgeneric.id;
- if(arch->reset == 0)
+ if(arch->reset == nil)
arch->reset = archgeneric.reset;
- if(arch->serialpower == 0)
+ if(arch->serialpower == nil)
arch->serialpower = archgeneric.serialpower;
- if(arch->modempower == 0)
+ if(arch->modempower == nil)
arch->modempower = archgeneric.modempower;
- if(arch->intrinit == 0)
+ if(arch->intrinit == nil)
arch->intrinit = archgeneric.intrinit;
- if(arch->intrenable == 0)
+ if(arch->intrenable == nil)
arch->intrenable = archgeneric.intrenable;
}
* We get another chance to set it in mpinit() for a
* multiprocessor.
*/
- if(X86FAMILY(m->cpuidax) == 3)
+ if(m->cpuidfamily == 3)
conf.copymode = 1;
- if(X86FAMILY(m->cpuidax) >= 4)
+ if(m->cpuidfamily >= 4)
cmpswap = cmpswap486;
- if(X86FAMILY(m->cpuidax) >= 5)
+ if(m->cpuidfamily >= 5)
coherence = mb586;
if(m->cpuiddx & Sse2)
void
timerset(Tval x)
{
- if(doi8253set)
- (*arch->timerset)(x);
+ (*arch->timerset)(x);
+}
+
+/*
+ * put the processor in the halt state if we've no processes to run.
+ * an interrupt will get us going again.
+ *
+ * halting in an smp system can result in a startup latency for
+ * processes that become ready.
+ * if idle_spin is zero, we care more about saving energy
+ * than reducing this latency.
+ *
+ * the performance loss with idle_spin == 0 seems to be slight
+ * and it reduces lock contention (thus system time and real time)
+ * on many-core systems with large values of NPROC.
+ */
+void
+idlehands(void)
+{
+ extern int nrdy, idle_spin;
+
+ if(conf.nmach == 1)
+ halt();
+ else if(m->cpuidcx & Monitor)
+ mwait(&nrdy);
+ else if(idle_spin == 0)
+ halt();
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ char cc[32], *p, *x;
+ int i;
+
+ snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+ p = getconf(cc);
+ if(p == nil)
+ return 0;
+
+ x = nil;
+ kstrdup(&x, p);
+ p = x;
+
+ isa->type = "";
+ isa->nopt = tokenize(p, isa->opt, NISAOPT);
+ for(i = 0; i < isa->nopt; i++){
+ p = isa->opt[i];
+ if(cistrncmp(p, "type=", 5) == 0)
+ isa->type = p + 5;
+ else if(cistrncmp(p, "port=", 5) == 0)
+ isa->port = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "irq=", 4) == 0)
+ isa->irq = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "dma=", 4) == 0)
+ isa->dma = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "mem=", 4) == 0)
+ isa->mem = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "size=", 5) == 0)
+ isa->size = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "freq=", 5) == 0)
+ isa->freq = strtoul(p+5, &p, 0);
+ }
+ return 1;
+}
+
+void
+dumpmcregs(void)
+{
+ vlong v, w;
+ int bank;
+
+ if((m->cpuiddx & (Mce|Cpumsr)) != (Mce|Cpumsr))
+ return;
+ if((m->cpuiddx & Mca) == 0){
+ rdmsr(0x00, &v);
+ rdmsr(0x01, &w);
+ iprint("MCA %8.8llux MCT %8.8llux\n", v, w);
+ return;
+ }
+ rdmsr(0x179, &v);
+ rdmsr(0x17A, &w);
+ iprint("MCG CAP %.16llux STATUS %.16llux\n", v, w);
+
+ bank = v & 0xFF;
+ if(bank > 64)
+ bank = 64;
+ while(--bank >= 0){
+ rdmsr(0x401 + bank*4, &v);
+ if((v & (1ull << 63)) == 0)
+ continue;
+ iprint("MC%d STATUS %.16llux", bank, v);
+ if(v & (1ull << 58)){
+ rdmsr(0x402 + bank*4, &w);
+ iprint(" ADDR %.16llux", w);
+ }
+ if(v & (1ull << 59)){
+ rdmsr(0x403 + bank*4, &w);
+ iprint(" MISC %.16llux", w);
+ }
+ iprint("\n");
+ }
+}
+
+void
+setupwatchpts(Proc *pr, Watchpt *wp, int nwp)
+{
+ int i;
+ u8int cfg;
+ Watchpt *p;
+
+ if(nwp > 4)
+ error("there are four watchpoints.");
+ if(nwp == 0){
+ memset(pr->dr, 0, sizeof(pr->dr));
+ return;
+ }
+ for(p = wp; p < wp + nwp; p++){
+ switch(p->type){
+ case WATCHRD|WATCHWR: case WATCHWR:
+ break;
+ case WATCHEX:
+ if(p->len != 1)
+ error("length must be 1 on breakpoints");
+ break;
+ default:
+ error("type must be rw-, -w- or --x");
+ }
+ switch(p->len){
+ case 1: case 2: case 4:
+ break;
+ case 8:
+ if(m->havewatchpt8) break;
+ default:
+ error(m->havewatchpt8 ? "length must be 1,2,4,8" : "length must be 1,2,4");
+ }
+ if((p->addr & p->len - 1) != 0)
+ error("address must be aligned according to length");
+ }
+
+ memset(pr->dr, 0, sizeof(pr->dr));
+ pr->dr[6] = 0xffff8ff0;
+ for(i = 0; i < nwp; i++){
+ pr->dr[i] = wp[i].addr;
+ switch(wp[i].type){
+ case WATCHRD|WATCHWR: cfg = 3; break;
+ case WATCHWR: cfg = 1; break;
+ case WATCHEX: cfg = 0; break;
+ default: continue;
+ }
+ switch(wp[i].len){
+ case 1: break;
+ case 2: cfg |= 4; break;
+ case 4: cfg |= 12; break;
+ case 8: cfg |= 8; break;
+ default: continue;
+ }
+ pr->dr[7] |= cfg << 16 + 4 * i;
+ pr->dr[7] |= 1 << 2 * i + 1;
+ }
}