/* * memory-type region registers. * * due to the possibility of extended addresses (for PAE) * as large as 36 bits coming from the e820 memory map and the like, * we'll use vlongs to hold addresses and lengths, even though we don't * implement PAE in Plan 9. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" enum { /* * MTRR Physical base/mask are indexed by * MTRRPhys{Base|Mask}N = MTRRPhys{Base|Mask}0 + 2*N */ MTRRPhysBase0 = 0x200, MTRRPhysMask0 = 0x201, MTRRDefaultType = 0x2FF, MTRRCap = 0xFE, Nmtrr = 8, /* cpuid extended function codes */ Exthighfunc = 1ul << 31, Extprocsigamd, Extprocname0, Extprocname1, Extprocname2, Exttlbl1, Extl2, Extapm, Extaddrsz, Paerange = 1LL << 36, }; enum { CR4PageGlobalEnable = 1 << 7, CR0CacheDisable = 1 << 30, }; enum { Uncacheable = 0, Writecomb = 1, Unknown1 = 2, Unknown2 = 3, Writethru = 4, Writeprot = 5, Writeback = 6, }; enum { Capvcnt = 0xff, /* mask: # of variable-range MTRRs we have */ Capwc = 1<<8, /* flag: have write combining? */ Capfix = 1<<10, /* flag: have fixed MTRRs? */ Deftype = 0xff, /* default MTRR type */ Deffixena = 1<<10, /* fixed-range MTRR enable */ Defena = 1<<11, /* MTRR enable */ }; typedef struct Mtrreg Mtrreg; typedef struct Mtrrop Mtrrop; struct Mtrreg { vlong base; vlong mask; }; struct Mtrrop { Mtrreg *reg; int slot; }; static char *types[] = { [Uncacheable] "uc", [Writecomb] "wc", [Unknown1] "uk1", [Unknown2] "uk2", [Writethru] "wt", [Writeprot] "wp", [Writeback] "wb", nil }; static Mtrrop *postedop; static Rendez oprend; 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) == 0) return p - types; return -1; } static uvlong physmask(void) { ulong regs[4]; static vlong mask = -1; if (mask != -1) return mask; cpuid(Exthighfunc, regs); if(regs[0] >= Extaddrsz) { /* ax */ cpuid(Extaddrsz, regs); mask = (1LL << (regs[0] & 0xFF)) - 1; /* ax */ } mask &= Paerange - 1; /* x86 sanity */ return mask; } /* limit physical addresses to 36 bits on the x86 */ static void sanity(Mtrreg *mtrr) { mtrr->base &= Paerange - 1; mtrr->mask &= Paerange - 1; } static int ispow2(uvlong ul) { return (ul & (ul - 1)) == 0; } /* true if mtrr is valid */ static int mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type) { sanity(mtrr); *ptr = mtrr->base & ~(BY2PG-1); *type = mtrr->base & 0xff; *size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1; return (mtrr->mask >> 11) & 1; } static void mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok) { mtrr->base = ptr | (type & 0xff); mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0); sanity(mtrr); } /* * i is the index of the MTRR, and is multiplied by 2 because * mask and base offsets are interleaved. */ static void mtrrget(Mtrreg *mtrr, uint i) { if (i >= Nmtrr) error("mtrr index out of range"); rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base); rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask); sanity(mtrr); } static void mtrrput(Mtrreg *mtrr, uint i) { if (i >= Nmtrr) error("mtrr index out of range"); sanity(mtrr); wrmsr(MTRRPhysBase0 + 2*i, mtrr->base); wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask); } static void mtrrop(Mtrrop **op) { int s; ulong cr0, cr4; vlong def; static long bar1, bar2; s = splhi(); /* avoid race with mtrrclock */ /* * wait for all CPUs to sync here, so that the MTRR setup gets * done at roughly the same time on all processors. */ _xinc(&bar1); while(bar1 < conf.nmach) microdelay(10); cr4 = getcr4(); putcr4(cr4 & ~CR4PageGlobalEnable); cr0 = getcr0(); wbinvd(); putcr0(cr0 | CR0CacheDisable); wbinvd(); rdmsr(MTRRDefaultType, &def); wrmsr(MTRRDefaultType, def & ~(vlong)Defena); mtrrput((*op)->reg, (*op)->slot); wbinvd(); wrmsr(MTRRDefaultType, def); putcr0(cr0); putcr4(cr4); /* * wait for all CPUs to sync up again, so that we don't continue * executing while the MTRRs are still being set up. */ _xinc(&bar2); while(bar2 < conf.nmach) microdelay(10); *op = nil; _xdec(&bar1); while(bar1 > 0) microdelay(10); _xdec(&bar2); wakeup(&oprend); splx(s); } void mtrrclock(void) /* called from clock interrupt */ { if(postedop != nil) mtrrop(&postedop); } /* if there's an operation still pending, keep sleeping */ static int opavail(void *) { return postedop == nil; } int mtrr(uvlong base, uvlong size, char *tstr) { int i, vcnt, slot, type, mtype, mok; vlong def, cap; uvlong mp, msize; Mtrreg entry, mtrr; Mtrrop op; static int tickreg; static QLock mtrrlk; if(!(m->cpuiddx & Mtrr)) error("mtrrs not supported"); if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0) error("mtrr base or size not 4k aligned or zero size"); if(base + size >= Paerange) error("mtrr range exceeds 36 bits"); if(!ispow2(size)) error("mtrr size not power of 2"); if(base & (size - 1)) error("mtrr base not naturally aligned"); if((type = str2type(tstr)) == -1) error("mtrr bad type"); rdmsr(MTRRCap, &cap); rdmsr(MTRRDefaultType, &def); switch(type){ default: error("mtrr unknown type"); break; case Writecomb: if(!(cap & Capwc)) error("mtrr type wc (write combining) unsupported"); /* fallthrough */ case Uncacheable: case Writethru: case Writeprot: case Writeback: break; } qlock(&mtrrlk); slot = -1; vcnt = cap & Capvcnt; if(vcnt > Nmtrr) vcnt = Nmtrr; for(i = 0; i < vcnt; i++){ mtrrget(&mtrr, i); mok = mtrrdec(&mtrr, &mp, &msize, &mtype); /* reuse any entry for addresses above 4GB */ if(!mok || mp == base && msize == size || mp >= (1LL<<32)){ slot = i; break; } } if(slot == -1) error("no free mtrr slots"); while(postedop != nil) sleep(&oprend, opavail, 0); mtrrenc(&entry, base, size, type, 1); op.reg = &entry; op.slot = slot; postedop = &op; mtrrop(&postedop); qunlock(&mtrrlk); return 0; } int mtrrprint(char *buf, long bufsize) { int i, vcnt, type; long n; uvlong base, size; vlong cap, def; Mtrreg mtrr; n = 0; if(!(m->cpuiddx & Mtrr)) return 0; rdmsr(MTRRCap, &cap); rdmsr(MTRRDefaultType, &def); n += snprint(buf+n, bufsize-n, "cache default %s\n", type2str(def & Deftype)); vcnt = cap & Capvcnt; if(vcnt > Nmtrr) vcnt = Nmtrr; for(i = 0; i < vcnt; i++){ mtrrget(&mtrr, i); if (mtrrdec(&mtrr, &base, &size, &type)) n += snprint(buf+n, bufsize-n, "cache 0x%llux %llud %s\n", base, size, type2str(type)); } return n; }