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