#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" typedef struct Mtrreg Mtrreg; typedef struct Mtrrop Mtrrop; struct Mtrreg { vlong base; vlong mask; }; struct Mtrrop { Mtrreg *reg; int slot; }; enum { Uncacheable = 0, Writecomb = 1, Unknown1 = 2, Unknown2 = 3, Writethru = 4, Writeprot = 5, Writeback = 6 }; enum { Capvcnt = 0xff, Capwc = 1<<8, Capfix = 1<<10, Deftype = 0xff, Deffixena = 1<<10, Defena = 1<<11, }; static char *types[] = { [Uncacheable] "uc", [Writecomb] "wc", [Unknown1] "uk1", [Unknown2] "uk2", [Writethru] "wt", [Writeprot] "wp", [Writeback] "wb", nil }; static char * type2str(int type) { if(type < 0 || type >= nelem(types)) return nil; return types[type]; } static int str2type(char *str) { char **p; for(p = types; *p != nil; p++) if(!strcmp(str, *p)) return p - types; return -1; } static vlong physmask(void) { ulong ax; cpuid(0x80000000, &ax, nil, nil, nil); if(ax < 0x80000008) return 0xFFFFFFFFFLL; /* cpu does not tell. default to 36bit phys */ cpuid(0x80000008, &ax, nil, nil, nil); return (1LL << (ax & 0xFF)) - 1; } static int overlap(uintptr b1, long s1, uintptr b2, long s2) { if(b1 > b2) return overlap(b2, s2, b1, s1); if(b1 + s1 > b2) return 1; return 0; } static int ctpop(vlong a) { int i; i = 0; while(a > 0){ if(a & 1) i++; a >>= 1; } return i; } static void mtrrdec(Mtrreg *mtrr, uintptr *ptr, long *size, int *type, int *valid) { if(ptr != nil) *ptr = mtrr->base & ~4095LL; if(type != nil) *type = mtrr->base & 0xff; if(size != nil) *size = (physmask() ^ (mtrr->mask & ~4095LL)) + 1; if(valid != nil) *valid = (mtrr->mask >> 11) & 1; } static void mtrrenc(Mtrreg *mtrr, uintptr ptr, long size, int type, int valid) { mtrr->base = ptr; mtrr->base |= type & 0xff; mtrr->mask = physmask() & ~(size - 1); mtrr->mask |= valid ? 1<<11 : 0; } static void mtrrget(Mtrreg *mtrr, int i) { rdmsr(0x200 + 2*i, &mtrr->base); rdmsr(0x200 + 2*i + 1, &mtrr->mask); } static void mtrrput(Mtrreg *mtrr, int i) { wrmsr(0x200 + 2*i, mtrr->base); wrmsr(0x200 + 2*i + 1, mtrr->mask); } static void mtrrop(Mtrrop **op) { vlong def; ulong cr0; ulong cr4; int pl; static long bar1, bar2; pl = splhi(); /*iprint("cpu%d enter mtrrop\n", m->machno);*/ _xinc(&bar1); while(bar1 < conf.nmach) microdelay(10); cr4 = getcr4(); putcr4(cr4 & ~(1<<7)); cr0 = getcr0(); wbinvd(); putcr0(cr0 | (1<<30)); wbinvd(); rdmsr(0x2FF, &def); wrmsr(0x2FF, def & ~(vlong)Defena); mtrrput((*op)->reg, (*op)->slot); wbinvd(); wrmsr(0x2FF, def); putcr0(cr0); putcr4(cr4); _xinc(&bar2); while(bar2 < conf.nmach) microdelay(10); *op = nil; _xdec(&bar1); while(bar1 > 0) microdelay(10); _xdec(&bar2); splx(pl); } static Mtrrop *postedop; void mtrrcheck(void) { if(postedop != nil) mtrrop(&postedop); } int mtrr(uintptr base, long size, char *tstr) { Mtrrop op; Mtrreg entry; vlong def, cap; int i, vcnt, slot, type, x; static int tickreg; static QLock mtrrlk; if((m->cpuiddx & Mtrr) == 0) error("mtrr not supported"); if((base & 4095LL) != 0 || (size & 4095LL) != 0 || size <= 0) error("mtrr base and/or size not 4k aligned or size <= 0"); if(base + size < base) error("mtrr range overlaps 4G"); if(ctpop(size) != 1) error("mtrr size not power of 2"); if((base & (size - 1)) != 0) error("mtrr base not naturally aligned"); if((type = str2type(tstr)) == -1) error("mtrr invalid type"); rdmsr(0x0FE, &cap); rdmsr(0x2FF, &def); switch(type){ default: error("mtrr unknown type"); case Writecomb: if((cap & Capwc) == 0) error("mtrr type wc (write combining) unsupported"); /* fallthrough */ case Uncacheable: case Writethru: case Writeprot: case Writeback: break; } slot = -1; vcnt = cap & Capvcnt; for(i = 0; i < vcnt; i++){ Mtrreg mtrr; uintptr mp; long msize; int mtype, mvalid; mtrrget(&mtrr, i); mtrrdec(&mtrr, &mp, &msize, &mtype, &mvalid); if(!mvalid) slot = i; if(mvalid && mp == base && msize == size){ slot = i; goto doit; } if(mvalid && overlap(mp, msize, base, size)) error("mtrr range overlaps with existing definition"); } if(slot == -1) error("no free mtrr slots"); doit: qlock(&mtrrlk); mtrrenc(&entry, base, size, type, 1); op.reg = &entry; op.slot = slot; postedop = &op; x = splhi(); /* avoid race with mtrrcheck */ mtrrop(&postedop); splx(x); qunlock(&mtrrlk); return 0; } int mtrrprint(char *buf, long bufsize) { Mtrreg mtrr; vlong cap, def; long n; int i, vcnt; n = 0; if(m->cpuiddx & Mtrr){ rdmsr(0x0FE, &cap); rdmsr(0x2FF, &def); n += snprint(buf+n, bufsize-n, "cache default %s\n", type2str(def & Deftype)); vcnt = cap & Capvcnt; for(i = 0; i < vcnt; i++){ uintptr base; long size; int type, valid; mtrrget(&mtrr, i); mtrrdec(&mtrr, &base, &size, &type, &valid); if(valid) n += snprint(buf+n, bufsize-n, "cache 0x%lux %lud %s\n", base, size, type2str(type)); } } return n; }