123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- /*
- * 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;
- 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;
- 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;
- }
|