1e4ac449cSDavid du Colombier /*
2e4ac449cSDavid du Colombier * memory-type region registers.
3e4ac449cSDavid du Colombier *
4e4ac449cSDavid du Colombier * due to the possibility of extended addresses (for PAE)
5e4ac449cSDavid du Colombier * as large as 36 bits coming from the e820 memory map and the like,
6e4ac449cSDavid du Colombier * we'll use vlongs to hold addresses and lengths, even though we don't
7e4ac449cSDavid du Colombier * implement PAE in Plan 9.
8e4ac449cSDavid du Colombier */
926d1d1dfSDavid du Colombier #include "u.h"
1026d1d1dfSDavid du Colombier #include "../port/lib.h"
1126d1d1dfSDavid du Colombier #include "mem.h"
1226d1d1dfSDavid du Colombier #include "dat.h"
1326d1d1dfSDavid du Colombier #include "fns.h"
1426d1d1dfSDavid du Colombier #include "io.h"
1526d1d1dfSDavid du Colombier
1626d1d1dfSDavid du Colombier enum {
1726d1d1dfSDavid du Colombier /*
1826d1d1dfSDavid du Colombier * MTRR Physical base/mask are indexed by
19390ad7e1SDavid du Colombier * MTRRPhys{Base|Mask}N = MTRRPhys{Base|Mask}0 + 2*N
2026d1d1dfSDavid du Colombier */
2126d1d1dfSDavid du Colombier MTRRPhysBase0 = 0x200,
2226d1d1dfSDavid du Colombier MTRRPhysMask0 = 0x201,
2326d1d1dfSDavid du Colombier MTRRDefaultType = 0x2FF,
2426d1d1dfSDavid du Colombier MTRRCap = 0xFE,
25e4ac449cSDavid du Colombier Nmtrr = 8,
2626d1d1dfSDavid du Colombier
2726d1d1dfSDavid du Colombier /* cpuid extended function codes */
2826d1d1dfSDavid du Colombier Exthighfunc = 1ul << 31,
2926d1d1dfSDavid du Colombier Extprocsigamd,
3026d1d1dfSDavid du Colombier Extprocname0,
3126d1d1dfSDavid du Colombier Extprocname1,
3226d1d1dfSDavid du Colombier Extprocname2,
3326d1d1dfSDavid du Colombier Exttlbl1,
3426d1d1dfSDavid du Colombier Extl2,
3526d1d1dfSDavid du Colombier Extapm,
3626d1d1dfSDavid du Colombier Extaddrsz,
37e4ac449cSDavid du Colombier
38e4ac449cSDavid du Colombier Paerange = 1LL << 36,
3926d1d1dfSDavid du Colombier };
4026d1d1dfSDavid du Colombier
4126d1d1dfSDavid du Colombier enum {
4226d1d1dfSDavid du Colombier CR4PageGlobalEnable = 1 << 7,
4326d1d1dfSDavid du Colombier CR0CacheDisable = 1 << 30,
4426d1d1dfSDavid du Colombier };
4526d1d1dfSDavid du Colombier
4626d1d1dfSDavid du Colombier enum {
4726d1d1dfSDavid du Colombier Uncacheable = 0,
4826d1d1dfSDavid du Colombier Writecomb = 1,
4926d1d1dfSDavid du Colombier Unknown1 = 2,
5026d1d1dfSDavid du Colombier Unknown2 = 3,
5126d1d1dfSDavid du Colombier Writethru = 4,
5226d1d1dfSDavid du Colombier Writeprot = 5,
5326d1d1dfSDavid du Colombier Writeback = 6,
5426d1d1dfSDavid du Colombier };
5526d1d1dfSDavid du Colombier
5626d1d1dfSDavid du Colombier enum {
5726d1d1dfSDavid du Colombier Capvcnt = 0xff, /* mask: # of variable-range MTRRs we have */
5826d1d1dfSDavid du Colombier Capwc = 1<<8, /* flag: have write combining? */
5926d1d1dfSDavid du Colombier Capfix = 1<<10, /* flag: have fixed MTRRs? */
6026d1d1dfSDavid du Colombier Deftype = 0xff, /* default MTRR type */
6126d1d1dfSDavid du Colombier Deffixena = 1<<10, /* fixed-range MTRR enable */
6226d1d1dfSDavid du Colombier Defena = 1<<11, /* MTRR enable */
6326d1d1dfSDavid du Colombier };
6426d1d1dfSDavid du Colombier
65e4ac449cSDavid du Colombier typedef struct Mtrreg Mtrreg;
66e4ac449cSDavid du Colombier typedef struct Mtrrop Mtrrop;
67e4ac449cSDavid du Colombier
6826d1d1dfSDavid du Colombier struct Mtrreg {
6926d1d1dfSDavid du Colombier vlong base;
7026d1d1dfSDavid du Colombier vlong mask;
7126d1d1dfSDavid du Colombier };
7226d1d1dfSDavid du Colombier struct Mtrrop {
7326d1d1dfSDavid du Colombier Mtrreg *reg;
7426d1d1dfSDavid du Colombier int slot;
7526d1d1dfSDavid du Colombier };
7626d1d1dfSDavid du Colombier
7726d1d1dfSDavid du Colombier static char *types[] = {
7826d1d1dfSDavid du Colombier [Uncacheable] "uc",
7926d1d1dfSDavid du Colombier [Writecomb] "wc",
8026d1d1dfSDavid du Colombier [Unknown1] "uk1",
8126d1d1dfSDavid du Colombier [Unknown2] "uk2",
8226d1d1dfSDavid du Colombier [Writethru] "wt",
8326d1d1dfSDavid du Colombier [Writeprot] "wp",
8426d1d1dfSDavid du Colombier [Writeback] "wb",
8526d1d1dfSDavid du Colombier nil
8626d1d1dfSDavid du Colombier };
87390ad7e1SDavid du Colombier static Mtrrop *postedop;
88390ad7e1SDavid du Colombier static Rendez oprend;
8926d1d1dfSDavid du Colombier
9026d1d1dfSDavid du Colombier static char *
type2str(int type)9126d1d1dfSDavid du Colombier type2str(int type)
9226d1d1dfSDavid du Colombier {
9326d1d1dfSDavid du Colombier if(type < 0 || type >= nelem(types))
9426d1d1dfSDavid du Colombier return nil;
9526d1d1dfSDavid du Colombier return types[type];
9626d1d1dfSDavid du Colombier }
9726d1d1dfSDavid du Colombier
9826d1d1dfSDavid du Colombier static int
str2type(char * str)9926d1d1dfSDavid du Colombier str2type(char *str)
10026d1d1dfSDavid du Colombier {
10126d1d1dfSDavid du Colombier char **p;
10226d1d1dfSDavid du Colombier
10326d1d1dfSDavid du Colombier for(p = types; *p != nil; p++)
10426d1d1dfSDavid du Colombier if (strcmp(str, *p) == 0)
10526d1d1dfSDavid du Colombier return p - types;
10626d1d1dfSDavid du Colombier return -1;
10726d1d1dfSDavid du Colombier }
10826d1d1dfSDavid du Colombier
109e4ac449cSDavid du Colombier static uvlong
physmask(void)11026d1d1dfSDavid du Colombier physmask(void)
11126d1d1dfSDavid du Colombier {
11226d1d1dfSDavid du Colombier ulong regs[4];
113e4ac449cSDavid du Colombier static vlong mask = -1;
11426d1d1dfSDavid du Colombier
115e4ac449cSDavid du Colombier if (mask != -1)
116e4ac449cSDavid du Colombier return mask;
11726d1d1dfSDavid du Colombier cpuid(Exthighfunc, regs);
118e4ac449cSDavid du Colombier if(regs[0] >= Extaddrsz) { /* ax */
11926d1d1dfSDavid du Colombier cpuid(Extaddrsz, regs);
120e4ac449cSDavid du Colombier mask = (1LL << (regs[0] & 0xFF)) - 1; /* ax */
121e4ac449cSDavid du Colombier }
122390ad7e1SDavid du Colombier mask &= Paerange - 1; /* x86 sanity */
123e4ac449cSDavid du Colombier return mask;
12426d1d1dfSDavid du Colombier }
12526d1d1dfSDavid du Colombier
126390ad7e1SDavid du Colombier /* limit physical addresses to 36 bits on the x86 */
127390ad7e1SDavid du Colombier static void
sanity(Mtrreg * mtrr)128390ad7e1SDavid du Colombier sanity(Mtrreg *mtrr)
12926d1d1dfSDavid du Colombier {
130390ad7e1SDavid du Colombier mtrr->base &= Paerange - 1;
131390ad7e1SDavid du Colombier mtrr->mask &= Paerange - 1;
13226d1d1dfSDavid du Colombier }
13326d1d1dfSDavid du Colombier
13426d1d1dfSDavid du Colombier static int
ispow2(uvlong ul)135e4ac449cSDavid du Colombier ispow2(uvlong ul)
13626d1d1dfSDavid du Colombier {
13726d1d1dfSDavid du Colombier return (ul & (ul - 1)) == 0;
13826d1d1dfSDavid du Colombier }
13926d1d1dfSDavid du Colombier
140e4ac449cSDavid du Colombier /* true if mtrr is valid */
141e4ac449cSDavid du Colombier static int
mtrrdec(Mtrreg * mtrr,uvlong * ptr,uvlong * size,int * type)142e4ac449cSDavid du Colombier mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type)
14326d1d1dfSDavid du Colombier {
144390ad7e1SDavid du Colombier sanity(mtrr);
14526d1d1dfSDavid du Colombier *ptr = mtrr->base & ~(BY2PG-1);
14626d1d1dfSDavid du Colombier *type = mtrr->base & 0xff;
14726d1d1dfSDavid du Colombier *size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
148e4ac449cSDavid du Colombier return (mtrr->mask >> 11) & 1;
14926d1d1dfSDavid du Colombier }
15026d1d1dfSDavid du Colombier
15126d1d1dfSDavid du Colombier static void
mtrrenc(Mtrreg * mtrr,uvlong ptr,uvlong size,int type,int ok)152e4ac449cSDavid du Colombier mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok)
15326d1d1dfSDavid du Colombier {
15426d1d1dfSDavid du Colombier mtrr->base = ptr | (type & 0xff);
15526d1d1dfSDavid du Colombier mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
156390ad7e1SDavid du Colombier sanity(mtrr);
15726d1d1dfSDavid du Colombier }
15826d1d1dfSDavid du Colombier
15926d1d1dfSDavid du Colombier /*
16026d1d1dfSDavid du Colombier * i is the index of the MTRR, and is multiplied by 2 because
16126d1d1dfSDavid du Colombier * mask and base offsets are interleaved.
16226d1d1dfSDavid du Colombier */
16326d1d1dfSDavid du Colombier static void
mtrrget(Mtrreg * mtrr,uint i)164e4ac449cSDavid du Colombier mtrrget(Mtrreg *mtrr, uint i)
16526d1d1dfSDavid du Colombier {
166e4ac449cSDavid du Colombier if (i >= Nmtrr)
167e4ac449cSDavid du Colombier error("mtrr index out of range");
16826d1d1dfSDavid du Colombier rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
16926d1d1dfSDavid du Colombier rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
170390ad7e1SDavid du Colombier sanity(mtrr);
17126d1d1dfSDavid du Colombier }
17226d1d1dfSDavid du Colombier
17326d1d1dfSDavid du Colombier static void
mtrrput(Mtrreg * mtrr,uint i)174390ad7e1SDavid du Colombier mtrrput(Mtrreg *mtrr, uint i)
17526d1d1dfSDavid du Colombier {
176e4ac449cSDavid du Colombier if (i >= Nmtrr)
177e4ac449cSDavid du Colombier error("mtrr index out of range");
178390ad7e1SDavid du Colombier sanity(mtrr);
17926d1d1dfSDavid du Colombier wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
18026d1d1dfSDavid du Colombier wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
18126d1d1dfSDavid du Colombier }
18226d1d1dfSDavid du Colombier
18326d1d1dfSDavid du Colombier static void
mtrrop(Mtrrop ** op)18426d1d1dfSDavid du Colombier mtrrop(Mtrrop **op)
18526d1d1dfSDavid du Colombier {
18626d1d1dfSDavid du Colombier int s;
18726d1d1dfSDavid du Colombier ulong cr0, cr4;
18826d1d1dfSDavid du Colombier vlong def;
18926d1d1dfSDavid du Colombier static long bar1, bar2;
19026d1d1dfSDavid du Colombier
19126d1d1dfSDavid du Colombier s = splhi(); /* avoid race with mtrrclock */
19226d1d1dfSDavid du Colombier
19326d1d1dfSDavid du Colombier /*
19426d1d1dfSDavid du Colombier * wait for all CPUs to sync here, so that the MTRR setup gets
19526d1d1dfSDavid du Colombier * done at roughly the same time on all processors.
19626d1d1dfSDavid du Colombier */
19726d1d1dfSDavid du Colombier _xinc(&bar1);
19826d1d1dfSDavid du Colombier while(bar1 < conf.nmach)
19926d1d1dfSDavid du Colombier microdelay(10);
20026d1d1dfSDavid du Colombier
20126d1d1dfSDavid du Colombier cr4 = getcr4();
20226d1d1dfSDavid du Colombier putcr4(cr4 & ~CR4PageGlobalEnable);
20326d1d1dfSDavid du Colombier cr0 = getcr0();
20426d1d1dfSDavid du Colombier wbinvd();
20526d1d1dfSDavid du Colombier putcr0(cr0 | CR0CacheDisable);
20626d1d1dfSDavid du Colombier wbinvd();
20726d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
20826d1d1dfSDavid du Colombier wrmsr(MTRRDefaultType, def & ~(vlong)Defena);
20926d1d1dfSDavid du Colombier
21026d1d1dfSDavid du Colombier mtrrput((*op)->reg, (*op)->slot);
21126d1d1dfSDavid du Colombier
21226d1d1dfSDavid du Colombier wbinvd();
21326d1d1dfSDavid du Colombier wrmsr(MTRRDefaultType, def);
21426d1d1dfSDavid du Colombier putcr0(cr0);
21526d1d1dfSDavid du Colombier putcr4(cr4);
21626d1d1dfSDavid du Colombier
21726d1d1dfSDavid du Colombier /*
21826d1d1dfSDavid du Colombier * wait for all CPUs to sync up again, so that we don't continue
21926d1d1dfSDavid du Colombier * executing while the MTRRs are still being set up.
22026d1d1dfSDavid du Colombier */
22126d1d1dfSDavid du Colombier _xinc(&bar2);
22226d1d1dfSDavid du Colombier while(bar2 < conf.nmach)
22326d1d1dfSDavid du Colombier microdelay(10);
22426d1d1dfSDavid du Colombier *op = nil;
22526d1d1dfSDavid du Colombier _xdec(&bar1);
22626d1d1dfSDavid du Colombier while(bar1 > 0)
22726d1d1dfSDavid du Colombier microdelay(10);
22826d1d1dfSDavid du Colombier _xdec(&bar2);
229390ad7e1SDavid du Colombier wakeup(&oprend);
23026d1d1dfSDavid du Colombier splx(s);
23126d1d1dfSDavid du Colombier }
23226d1d1dfSDavid du Colombier
23326d1d1dfSDavid du Colombier void
mtrrclock(void)23426d1d1dfSDavid du Colombier mtrrclock(void) /* called from clock interrupt */
23526d1d1dfSDavid du Colombier {
23626d1d1dfSDavid du Colombier if(postedop != nil)
23726d1d1dfSDavid du Colombier mtrrop(&postedop);
23826d1d1dfSDavid du Colombier }
23926d1d1dfSDavid du Colombier
240390ad7e1SDavid du Colombier /* if there's an operation still pending, keep sleeping */
241390ad7e1SDavid du Colombier static int
opavail(void *)242390ad7e1SDavid du Colombier opavail(void *)
243390ad7e1SDavid du Colombier {
244390ad7e1SDavid du Colombier return postedop == nil;
245390ad7e1SDavid du Colombier }
246390ad7e1SDavid du Colombier
24726d1d1dfSDavid du Colombier int
mtrr(uvlong base,uvlong size,char * tstr)248e4ac449cSDavid du Colombier mtrr(uvlong base, uvlong size, char *tstr)
24926d1d1dfSDavid du Colombier {
250e4ac449cSDavid du Colombier int i, vcnt, slot, type, mtype, mok;
25126d1d1dfSDavid du Colombier vlong def, cap;
252e4ac449cSDavid du Colombier uvlong mp, msize;
253e4ac449cSDavid du Colombier Mtrreg entry, mtrr;
25426d1d1dfSDavid du Colombier Mtrrop op;
25526d1d1dfSDavid du Colombier static int tickreg;
25626d1d1dfSDavid du Colombier static QLock mtrrlk;
25726d1d1dfSDavid du Colombier
25826d1d1dfSDavid du Colombier if(!(m->cpuiddx & Mtrr))
259e4ac449cSDavid du Colombier error("mtrrs not supported");
260e4ac449cSDavid du Colombier if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
261e4ac449cSDavid du Colombier error("mtrr base or size not 4k aligned or zero size");
262e4ac449cSDavid du Colombier if(base + size >= Paerange)
263e4ac449cSDavid du Colombier error("mtrr range exceeds 36 bits");
26426d1d1dfSDavid du Colombier if(!ispow2(size))
26526d1d1dfSDavid du Colombier error("mtrr size not power of 2");
26626d1d1dfSDavid du Colombier if(base & (size - 1))
26726d1d1dfSDavid du Colombier error("mtrr base not naturally aligned");
26826d1d1dfSDavid du Colombier
26926d1d1dfSDavid du Colombier if((type = str2type(tstr)) == -1)
27026d1d1dfSDavid du Colombier error("mtrr bad type");
27126d1d1dfSDavid du Colombier
27226d1d1dfSDavid du Colombier rdmsr(MTRRCap, &cap);
27326d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
27426d1d1dfSDavid du Colombier
27526d1d1dfSDavid du Colombier switch(type){
27626d1d1dfSDavid du Colombier default:
27726d1d1dfSDavid du Colombier error("mtrr unknown type");
27826d1d1dfSDavid du Colombier break;
27926d1d1dfSDavid du Colombier case Writecomb:
28026d1d1dfSDavid du Colombier if(!(cap & Capwc))
28126d1d1dfSDavid du Colombier error("mtrr type wc (write combining) unsupported");
28226d1d1dfSDavid du Colombier /* fallthrough */
28326d1d1dfSDavid du Colombier case Uncacheable:
28426d1d1dfSDavid du Colombier case Writethru:
28526d1d1dfSDavid du Colombier case Writeprot:
28626d1d1dfSDavid du Colombier case Writeback:
28726d1d1dfSDavid du Colombier break;
28826d1d1dfSDavid du Colombier }
28926d1d1dfSDavid du Colombier
290390ad7e1SDavid du Colombier qlock(&mtrrlk);
29126d1d1dfSDavid du Colombier slot = -1;
29226d1d1dfSDavid du Colombier vcnt = cap & Capvcnt;
293*b4124be8SDavid du Colombier for(i = 0; i < vcnt && i < Nmtrr; i++){
29426d1d1dfSDavid du Colombier mtrrget(&mtrr, i);
295e4ac449cSDavid du Colombier mok = mtrrdec(&mtrr, &mp, &msize, &mtype);
296390ad7e1SDavid du Colombier /* reuse any entry for addresses above 4GB */
297390ad7e1SDavid du Colombier if(!mok || mp == base && msize == size || mp >= (1LL<<32)){
29826d1d1dfSDavid du Colombier slot = i;
29926d1d1dfSDavid du Colombier break;
30026d1d1dfSDavid du Colombier }
30126d1d1dfSDavid du Colombier }
30226d1d1dfSDavid du Colombier if(slot == -1)
30326d1d1dfSDavid du Colombier error("no free mtrr slots");
304e4ac449cSDavid du Colombier
305390ad7e1SDavid du Colombier while(postedop != nil)
306390ad7e1SDavid du Colombier sleep(&oprend, opavail, 0);
30726d1d1dfSDavid du Colombier mtrrenc(&entry, base, size, type, 1);
30826d1d1dfSDavid du Colombier op.reg = &entry;
30926d1d1dfSDavid du Colombier op.slot = slot;
31026d1d1dfSDavid du Colombier postedop = &op;
31126d1d1dfSDavid du Colombier mtrrop(&postedop);
31226d1d1dfSDavid du Colombier qunlock(&mtrrlk);
31326d1d1dfSDavid du Colombier return 0;
31426d1d1dfSDavid du Colombier }
31526d1d1dfSDavid du Colombier
31626d1d1dfSDavid du Colombier int
mtrrprint(char * buf,long bufsize)31726d1d1dfSDavid du Colombier mtrrprint(char *buf, long bufsize)
31826d1d1dfSDavid du Colombier {
319e4ac449cSDavid du Colombier int i, vcnt, type;
32026d1d1dfSDavid du Colombier long n;
321e4ac449cSDavid du Colombier uvlong base, size;
32226d1d1dfSDavid du Colombier vlong cap, def;
32326d1d1dfSDavid du Colombier Mtrreg mtrr;
32426d1d1dfSDavid du Colombier
32526d1d1dfSDavid du Colombier n = 0;
32626d1d1dfSDavid du Colombier if(!(m->cpuiddx & Mtrr))
32726d1d1dfSDavid du Colombier return 0;
32826d1d1dfSDavid du Colombier rdmsr(MTRRCap, &cap);
32926d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
33026d1d1dfSDavid du Colombier n += snprint(buf+n, bufsize-n, "cache default %s\n",
33126d1d1dfSDavid du Colombier type2str(def & Deftype));
33226d1d1dfSDavid du Colombier vcnt = cap & Capvcnt;
333*b4124be8SDavid du Colombier for(i = 0; i < vcnt && i < Nmtrr; i++){
33426d1d1dfSDavid du Colombier mtrrget(&mtrr, i);
335e4ac449cSDavid du Colombier if (mtrrdec(&mtrr, &base, &size, &type))
336e4ac449cSDavid du Colombier n += snprint(buf+n, bufsize-n,
337e4ac449cSDavid du Colombier "cache 0x%llux %llud %s\n",
33826d1d1dfSDavid du Colombier base, size, type2str(type));
33926d1d1dfSDavid du Colombier }
34026d1d1dfSDavid du Colombier return n;
34126d1d1dfSDavid du Colombier }
342