xref: /plan9-contrib/sys/src/9/pc/mtrr.c (revision 61d44851dbae9c6db4696bac4b180d884ecea735)
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