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
134e4ac449cSDavid du Colombier /* true if mtrr is valid */
135e4ac449cSDavid du Colombier static int
mtrrdec(Mtrreg * mtrr,uvlong * ptr,uvlong * size,int * type)136e4ac449cSDavid du Colombier mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type)
13726d1d1dfSDavid du Colombier {
138390ad7e1SDavid du Colombier sanity(mtrr);
13926d1d1dfSDavid du Colombier *ptr = mtrr->base & ~(BY2PG-1);
14026d1d1dfSDavid du Colombier *type = mtrr->base & 0xff;
14126d1d1dfSDavid du Colombier *size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
142e4ac449cSDavid du Colombier return (mtrr->mask >> 11) & 1;
14326d1d1dfSDavid du Colombier }
14426d1d1dfSDavid du Colombier
14526d1d1dfSDavid du Colombier static void
mtrrenc(Mtrreg * mtrr,uvlong ptr,uvlong size,int type,int ok)146e4ac449cSDavid du Colombier mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok)
14726d1d1dfSDavid du Colombier {
14826d1d1dfSDavid du Colombier mtrr->base = ptr | (type & 0xff);
14926d1d1dfSDavid du Colombier mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
150390ad7e1SDavid du Colombier sanity(mtrr);
15126d1d1dfSDavid du Colombier }
15226d1d1dfSDavid du Colombier
15326d1d1dfSDavid du Colombier /*
15426d1d1dfSDavid du Colombier * i is the index of the MTRR, and is multiplied by 2 because
15526d1d1dfSDavid du Colombier * mask and base offsets are interleaved.
15626d1d1dfSDavid du Colombier */
15726d1d1dfSDavid du Colombier static void
mtrrget(Mtrreg * mtrr,uint i)158e4ac449cSDavid du Colombier mtrrget(Mtrreg *mtrr, uint i)
15926d1d1dfSDavid du Colombier {
160e4ac449cSDavid du Colombier if (i >= Nmtrr)
161e4ac449cSDavid du Colombier error("mtrr index out of range");
16226d1d1dfSDavid du Colombier rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
16326d1d1dfSDavid du Colombier rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
164390ad7e1SDavid du Colombier sanity(mtrr);
16526d1d1dfSDavid du Colombier }
16626d1d1dfSDavid du Colombier
16726d1d1dfSDavid du Colombier static void
mtrrput(Mtrreg * mtrr,uint i)168390ad7e1SDavid du Colombier mtrrput(Mtrreg *mtrr, uint i)
16926d1d1dfSDavid du Colombier {
170e4ac449cSDavid du Colombier if (i >= Nmtrr)
171e4ac449cSDavid du Colombier error("mtrr index out of range");
172390ad7e1SDavid du Colombier sanity(mtrr);
17326d1d1dfSDavid du Colombier wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
17426d1d1dfSDavid du Colombier wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
17526d1d1dfSDavid du Colombier }
17626d1d1dfSDavid du Colombier
17726d1d1dfSDavid du Colombier static void
mtrrop(Mtrrop ** op)17826d1d1dfSDavid du Colombier mtrrop(Mtrrop **op)
17926d1d1dfSDavid du Colombier {
18026d1d1dfSDavid du Colombier int s;
18126d1d1dfSDavid du Colombier ulong cr0, cr4;
18226d1d1dfSDavid du Colombier vlong def;
18326d1d1dfSDavid du Colombier static long bar1, bar2;
18426d1d1dfSDavid du Colombier
18526d1d1dfSDavid du Colombier s = splhi(); /* avoid race with mtrrclock */
18626d1d1dfSDavid du Colombier
18726d1d1dfSDavid du Colombier /*
18826d1d1dfSDavid du Colombier * wait for all CPUs to sync here, so that the MTRR setup gets
18926d1d1dfSDavid du Colombier * done at roughly the same time on all processors.
19026d1d1dfSDavid du Colombier */
191*61d44851SDavid du Colombier ainc(&bar1);
19226d1d1dfSDavid du Colombier while(bar1 < conf.nmach)
19326d1d1dfSDavid du Colombier microdelay(10);
19426d1d1dfSDavid du Colombier
19526d1d1dfSDavid du Colombier cr4 = getcr4();
19626d1d1dfSDavid du Colombier putcr4(cr4 & ~CR4PageGlobalEnable);
19726d1d1dfSDavid du Colombier cr0 = getcr0();
19826d1d1dfSDavid du Colombier wbinvd();
19926d1d1dfSDavid du Colombier putcr0(cr0 | CR0CacheDisable);
20026d1d1dfSDavid du Colombier wbinvd();
20126d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
20226d1d1dfSDavid du Colombier wrmsr(MTRRDefaultType, def & ~(vlong)Defena);
20326d1d1dfSDavid du Colombier
20426d1d1dfSDavid du Colombier mtrrput((*op)->reg, (*op)->slot);
20526d1d1dfSDavid du Colombier
20626d1d1dfSDavid du Colombier wbinvd();
20726d1d1dfSDavid du Colombier wrmsr(MTRRDefaultType, def);
20826d1d1dfSDavid du Colombier putcr0(cr0);
20926d1d1dfSDavid du Colombier putcr4(cr4);
21026d1d1dfSDavid du Colombier
21126d1d1dfSDavid du Colombier /*
21226d1d1dfSDavid du Colombier * wait for all CPUs to sync up again, so that we don't continue
21326d1d1dfSDavid du Colombier * executing while the MTRRs are still being set up.
21426d1d1dfSDavid du Colombier */
215*61d44851SDavid du Colombier ainc(&bar2);
21626d1d1dfSDavid du Colombier while(bar2 < conf.nmach)
21726d1d1dfSDavid du Colombier microdelay(10);
21826d1d1dfSDavid du Colombier *op = nil;
219*61d44851SDavid du Colombier adec(&bar1);
22026d1d1dfSDavid du Colombier while(bar1 > 0)
22126d1d1dfSDavid du Colombier microdelay(10);
222*61d44851SDavid du Colombier adec(&bar2);
223390ad7e1SDavid du Colombier wakeup(&oprend);
22426d1d1dfSDavid du Colombier splx(s);
22526d1d1dfSDavid du Colombier }
22626d1d1dfSDavid du Colombier
22726d1d1dfSDavid du Colombier void
mtrrclock(void)22826d1d1dfSDavid du Colombier mtrrclock(void) /* called from clock interrupt */
22926d1d1dfSDavid du Colombier {
23026d1d1dfSDavid du Colombier if(postedop != nil)
23126d1d1dfSDavid du Colombier mtrrop(&postedop);
23226d1d1dfSDavid du Colombier }
23326d1d1dfSDavid du Colombier
234390ad7e1SDavid du Colombier /* if there's an operation still pending, keep sleeping */
235390ad7e1SDavid du Colombier static int
opavail(void *)236390ad7e1SDavid du Colombier opavail(void *)
237390ad7e1SDavid du Colombier {
238390ad7e1SDavid du Colombier return postedop == nil;
239390ad7e1SDavid du Colombier }
240390ad7e1SDavid du Colombier
24126d1d1dfSDavid du Colombier int
mtrr(uvlong base,uvlong size,char * tstr)242e4ac449cSDavid du Colombier mtrr(uvlong base, uvlong size, char *tstr)
24326d1d1dfSDavid du Colombier {
244e4ac449cSDavid du Colombier int i, vcnt, slot, type, mtype, mok;
24526d1d1dfSDavid du Colombier vlong def, cap;
246e4ac449cSDavid du Colombier uvlong mp, msize;
247e4ac449cSDavid du Colombier Mtrreg entry, mtrr;
24826d1d1dfSDavid du Colombier Mtrrop op;
24926d1d1dfSDavid du Colombier static int tickreg;
25026d1d1dfSDavid du Colombier static QLock mtrrlk;
25126d1d1dfSDavid du Colombier
25226d1d1dfSDavid du Colombier if(!(m->cpuiddx & Mtrr))
253e4ac449cSDavid du Colombier error("mtrrs not supported");
254e4ac449cSDavid du Colombier if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
255e4ac449cSDavid du Colombier error("mtrr base or size not 4k aligned or zero size");
256e4ac449cSDavid du Colombier if(base + size >= Paerange)
257e4ac449cSDavid du Colombier error("mtrr range exceeds 36 bits");
25826d1d1dfSDavid du Colombier if(!ispow2(size))
25926d1d1dfSDavid du Colombier error("mtrr size not power of 2");
26026d1d1dfSDavid du Colombier if(base & (size - 1))
26126d1d1dfSDavid du Colombier error("mtrr base not naturally aligned");
26226d1d1dfSDavid du Colombier
26326d1d1dfSDavid du Colombier if((type = str2type(tstr)) == -1)
26426d1d1dfSDavid du Colombier error("mtrr bad type");
26526d1d1dfSDavid du Colombier
26626d1d1dfSDavid du Colombier rdmsr(MTRRCap, &cap);
26726d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
26826d1d1dfSDavid du Colombier
26926d1d1dfSDavid du Colombier switch(type){
27026d1d1dfSDavid du Colombier default:
27126d1d1dfSDavid du Colombier error("mtrr unknown type");
27226d1d1dfSDavid du Colombier break;
27326d1d1dfSDavid du Colombier case Writecomb:
27426d1d1dfSDavid du Colombier if(!(cap & Capwc))
27526d1d1dfSDavid du Colombier error("mtrr type wc (write combining) unsupported");
27626d1d1dfSDavid du Colombier /* fallthrough */
27726d1d1dfSDavid du Colombier case Uncacheable:
27826d1d1dfSDavid du Colombier case Writethru:
27926d1d1dfSDavid du Colombier case Writeprot:
28026d1d1dfSDavid du Colombier case Writeback:
28126d1d1dfSDavid du Colombier break;
28226d1d1dfSDavid du Colombier }
28326d1d1dfSDavid du Colombier
284390ad7e1SDavid du Colombier qlock(&mtrrlk);
28526d1d1dfSDavid du Colombier slot = -1;
28626d1d1dfSDavid du Colombier vcnt = cap & Capvcnt;
287b4124be8SDavid du Colombier for(i = 0; i < vcnt && i < Nmtrr; i++){
28826d1d1dfSDavid du Colombier mtrrget(&mtrr, i);
289e4ac449cSDavid du Colombier mok = mtrrdec(&mtrr, &mp, &msize, &mtype);
290390ad7e1SDavid du Colombier /* reuse any entry for addresses above 4GB */
291390ad7e1SDavid du Colombier if(!mok || mp == base && msize == size || mp >= (1LL<<32)){
29226d1d1dfSDavid du Colombier slot = i;
29326d1d1dfSDavid du Colombier break;
29426d1d1dfSDavid du Colombier }
29526d1d1dfSDavid du Colombier }
29626d1d1dfSDavid du Colombier if(slot == -1)
29726d1d1dfSDavid du Colombier error("no free mtrr slots");
298e4ac449cSDavid du Colombier
299390ad7e1SDavid du Colombier while(postedop != nil)
300390ad7e1SDavid du Colombier sleep(&oprend, opavail, 0);
30126d1d1dfSDavid du Colombier mtrrenc(&entry, base, size, type, 1);
30226d1d1dfSDavid du Colombier op.reg = &entry;
30326d1d1dfSDavid du Colombier op.slot = slot;
30426d1d1dfSDavid du Colombier postedop = &op;
30526d1d1dfSDavid du Colombier mtrrop(&postedop);
30626d1d1dfSDavid du Colombier qunlock(&mtrrlk);
30726d1d1dfSDavid du Colombier return 0;
30826d1d1dfSDavid du Colombier }
30926d1d1dfSDavid du Colombier
31026d1d1dfSDavid du Colombier int
mtrrprint(char * buf,long bufsize)31126d1d1dfSDavid du Colombier mtrrprint(char *buf, long bufsize)
31226d1d1dfSDavid du Colombier {
313e4ac449cSDavid du Colombier int i, vcnt, type;
31426d1d1dfSDavid du Colombier long n;
315e4ac449cSDavid du Colombier uvlong base, size;
31626d1d1dfSDavid du Colombier vlong cap, def;
31726d1d1dfSDavid du Colombier Mtrreg mtrr;
31826d1d1dfSDavid du Colombier
31926d1d1dfSDavid du Colombier n = 0;
32026d1d1dfSDavid du Colombier if(!(m->cpuiddx & Mtrr))
32126d1d1dfSDavid du Colombier return 0;
32226d1d1dfSDavid du Colombier rdmsr(MTRRCap, &cap);
32326d1d1dfSDavid du Colombier rdmsr(MTRRDefaultType, &def);
32426d1d1dfSDavid du Colombier n += snprint(buf+n, bufsize-n, "cache default %s\n",
32526d1d1dfSDavid du Colombier type2str(def & Deftype));
32626d1d1dfSDavid du Colombier vcnt = cap & Capvcnt;
327b4124be8SDavid du Colombier for(i = 0; i < vcnt && i < Nmtrr; i++){
32826d1d1dfSDavid du Colombier mtrrget(&mtrr, i);
329e4ac449cSDavid du Colombier if (mtrrdec(&mtrr, &base, &size, &type))
330e4ac449cSDavid du Colombier n += snprint(buf+n, bufsize-n,
331e4ac449cSDavid du Colombier "cache 0x%llux %llud %s\n",
33226d1d1dfSDavid du Colombier base, size, type2str(type));
33326d1d1dfSDavid du Colombier }
33426d1d1dfSDavid du Colombier return n;
33526d1d1dfSDavid du Colombier }
336