xref: /plan9/sys/src/9/pc/mtrr.c (revision b4124be8ab537717d181aafbde6d3e49218b08ef)
1 /*
2  * memory-type region registers.
3  *
4  * due to the possibility of extended addresses (for PAE)
5  * as large as 36 bits coming from the e820 memory map and the like,
6  * we'll use vlongs to hold addresses and lengths, even though we don't
7  * implement PAE in Plan 9.
8  */
9 #include "u.h"
10 #include "../port/lib.h"
11 #include "mem.h"
12 #include "dat.h"
13 #include "fns.h"
14 #include "io.h"
15 
16 enum {
17 	/*
18 	 * MTRR Physical base/mask are indexed by
19 	 *	MTRRPhys{Base|Mask}N = MTRRPhys{Base|Mask}0 + 2*N
20 	 */
21 	MTRRPhysBase0 = 0x200,
22 	MTRRPhysMask0 = 0x201,
23 	MTRRDefaultType = 0x2FF,
24 	MTRRCap = 0xFE,
25 	Nmtrr = 8,
26 
27 	/* cpuid extended function codes */
28 	Exthighfunc = 1ul << 31,
29 	Extprocsigamd,
30 	Extprocname0,
31 	Extprocname1,
32 	Extprocname2,
33 	Exttlbl1,
34 	Extl2,
35 	Extapm,
36 	Extaddrsz,
37 
38 	Paerange = 1LL << 36,
39 };
40 
41 enum {
42 	CR4PageGlobalEnable	= 1 << 7,
43 	CR0CacheDisable		= 1 << 30,
44 };
45 
46 enum {
47 	Uncacheable	= 0,
48 	Writecomb	= 1,
49 	Unknown1	= 2,
50 	Unknown2	= 3,
51 	Writethru	= 4,
52 	Writeprot	= 5,
53 	Writeback	= 6,
54 };
55 
56 enum {
57 	Capvcnt = 0xff,		/* mask: # of variable-range MTRRs we have */
58 	Capwc	= 1<<8,		/* flag: have write combining? */
59 	Capfix	= 1<<10,	/* flag: have fixed MTRRs? */
60 	Deftype = 0xff,		/* default MTRR type */
61 	Deffixena = 1<<10,	/* fixed-range MTRR enable */
62 	Defena	= 1<<11,	/* MTRR enable */
63 };
64 
65 typedef struct Mtrreg Mtrreg;
66 typedef struct Mtrrop Mtrrop;
67 
68 struct Mtrreg {
69 	vlong	base;
70 	vlong	mask;
71 };
72 struct Mtrrop {
73 	Mtrreg	*reg;
74 	int	slot;
75 };
76 
77 static char *types[] = {
78 [Uncacheable]	"uc",
79 [Writecomb]	"wc",
80 [Unknown1]	"uk1",
81 [Unknown2]	"uk2",
82 [Writethru]	"wt",
83 [Writeprot]	"wp",
84 [Writeback]	"wb",
85 		nil
86 };
87 static Mtrrop *postedop;
88 static Rendez oprend;
89 
90 static char *
type2str(int type)91 type2str(int type)
92 {
93 	if(type < 0 || type >= nelem(types))
94 		return nil;
95 	return types[type];
96 }
97 
98 static int
str2type(char * str)99 str2type(char *str)
100 {
101 	char **p;
102 
103 	for(p = types; *p != nil; p++)
104 		if (strcmp(str, *p) == 0)
105 			return p - types;
106 	return -1;
107 }
108 
109 static uvlong
physmask(void)110 physmask(void)
111 {
112 	ulong regs[4];
113 	static vlong mask = -1;
114 
115 	if (mask != -1)
116 		return mask;
117 	cpuid(Exthighfunc, regs);
118 	if(regs[0] >= Extaddrsz) {			/* ax */
119 		cpuid(Extaddrsz, regs);
120 		mask = (1LL << (regs[0] & 0xFF)) - 1;	/* ax */
121 	}
122 	mask &= Paerange - 1;				/* x86 sanity */
123 	return mask;
124 }
125 
126 /* limit physical addresses to 36 bits on the x86 */
127 static void
sanity(Mtrreg * mtrr)128 sanity(Mtrreg *mtrr)
129 {
130 	mtrr->base &= Paerange - 1;
131 	mtrr->mask &= Paerange - 1;
132 }
133 
134 static int
ispow2(uvlong ul)135 ispow2(uvlong ul)
136 {
137 	return (ul & (ul - 1)) == 0;
138 }
139 
140 /* true if mtrr is valid */
141 static int
mtrrdec(Mtrreg * mtrr,uvlong * ptr,uvlong * size,int * type)142 mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type)
143 {
144 	sanity(mtrr);
145 	*ptr =  mtrr->base & ~(BY2PG-1);
146 	*type = mtrr->base & 0xff;
147 	*size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
148 	return (mtrr->mask >> 11) & 1;
149 }
150 
151 static void
mtrrenc(Mtrreg * mtrr,uvlong ptr,uvlong size,int type,int ok)152 mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok)
153 {
154 	mtrr->base = ptr | (type & 0xff);
155 	mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
156 	sanity(mtrr);
157 }
158 
159 /*
160  * i is the index of the MTRR, and is multiplied by 2 because
161  * mask and base offsets are interleaved.
162  */
163 static void
mtrrget(Mtrreg * mtrr,uint i)164 mtrrget(Mtrreg *mtrr, uint i)
165 {
166 	if (i >= Nmtrr)
167 		error("mtrr index out of range");
168 	rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
169 	rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
170 	sanity(mtrr);
171 }
172 
173 static void
mtrrput(Mtrreg * mtrr,uint i)174 mtrrput(Mtrreg *mtrr, uint i)
175 {
176 	if (i >= Nmtrr)
177 		error("mtrr index out of range");
178 	sanity(mtrr);
179 	wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
180 	wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
181 }
182 
183 static void
mtrrop(Mtrrop ** op)184 mtrrop(Mtrrop **op)
185 {
186 	int s;
187 	ulong cr0, cr4;
188 	vlong def;
189 	static long bar1, bar2;
190 
191 	s = splhi();		/* avoid race with mtrrclock */
192 
193 	/*
194 	 * wait for all CPUs to sync here, so that the MTRR setup gets
195 	 * done at roughly the same time on all processors.
196 	 */
197 	_xinc(&bar1);
198 	while(bar1 < conf.nmach)
199 		microdelay(10);
200 
201 	cr4 = getcr4();
202 	putcr4(cr4 & ~CR4PageGlobalEnable);
203 	cr0 = getcr0();
204 	wbinvd();
205 	putcr0(cr0 | CR0CacheDisable);
206 	wbinvd();
207 	rdmsr(MTRRDefaultType, &def);
208 	wrmsr(MTRRDefaultType, def & ~(vlong)Defena);
209 
210 	mtrrput((*op)->reg, (*op)->slot);
211 
212 	wbinvd();
213 	wrmsr(MTRRDefaultType, def);
214 	putcr0(cr0);
215 	putcr4(cr4);
216 
217 	/*
218 	 * wait for all CPUs to sync up again, so that we don't continue
219 	 * executing while the MTRRs are still being set up.
220 	 */
221 	_xinc(&bar2);
222 	while(bar2 < conf.nmach)
223 		microdelay(10);
224 	*op = nil;
225 	_xdec(&bar1);
226 	while(bar1 > 0)
227 		microdelay(10);
228 	_xdec(&bar2);
229 	wakeup(&oprend);
230 	splx(s);
231 }
232 
233 void
mtrrclock(void)234 mtrrclock(void)				/* called from clock interrupt */
235 {
236 	if(postedop != nil)
237 		mtrrop(&postedop);
238 }
239 
240 /* if there's an operation still pending, keep sleeping */
241 static int
opavail(void *)242 opavail(void *)
243 {
244 	return postedop == nil;
245 }
246 
247 int
mtrr(uvlong base,uvlong size,char * tstr)248 mtrr(uvlong base, uvlong size, char *tstr)
249 {
250 	int i, vcnt, slot, type, mtype, mok;
251 	vlong def, cap;
252 	uvlong mp, msize;
253 	Mtrreg entry, mtrr;
254 	Mtrrop op;
255 	static int tickreg;
256 	static QLock mtrrlk;
257 
258 	if(!(m->cpuiddx & Mtrr))
259 		error("mtrrs not supported");
260 	if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
261 		error("mtrr base or size not 4k aligned or zero size");
262 	if(base + size >= Paerange)
263 		error("mtrr range exceeds 36 bits");
264 	if(!ispow2(size))
265 		error("mtrr size not power of 2");
266 	if(base & (size - 1))
267 		error("mtrr base not naturally aligned");
268 
269 	if((type = str2type(tstr)) == -1)
270 		error("mtrr bad type");
271 
272 	rdmsr(MTRRCap, &cap);
273 	rdmsr(MTRRDefaultType, &def);
274 
275 	switch(type){
276 	default:
277 		error("mtrr unknown type");
278 		break;
279 	case Writecomb:
280 		if(!(cap & Capwc))
281 			error("mtrr type wc (write combining) unsupported");
282 		/* fallthrough */
283 	case Uncacheable:
284 	case Writethru:
285 	case Writeprot:
286 	case Writeback:
287 		break;
288 	}
289 
290 	qlock(&mtrrlk);
291 	slot = -1;
292 	vcnt = cap & Capvcnt;
293 	for(i = 0; i < vcnt && i < Nmtrr; i++){
294 		mtrrget(&mtrr, i);
295 		mok = mtrrdec(&mtrr, &mp, &msize, &mtype);
296 		/* reuse any entry for addresses above 4GB */
297 		if(!mok || mp == base && msize == size || mp >= (1LL<<32)){
298 			slot = i;
299 			break;
300 		}
301 	}
302 	if(slot == -1)
303 		error("no free mtrr slots");
304 
305 	while(postedop != nil)
306 		sleep(&oprend, opavail, 0);
307 	mtrrenc(&entry, base, size, type, 1);
308 	op.reg = &entry;
309 	op.slot = slot;
310 	postedop = &op;
311 	mtrrop(&postedop);
312 	qunlock(&mtrrlk);
313 	return 0;
314 }
315 
316 int
mtrrprint(char * buf,long bufsize)317 mtrrprint(char *buf, long bufsize)
318 {
319 	int i, vcnt, type;
320 	long n;
321 	uvlong base, size;
322 	vlong cap, def;
323 	Mtrreg mtrr;
324 
325 	n = 0;
326 	if(!(m->cpuiddx & Mtrr))
327 		return 0;
328 	rdmsr(MTRRCap, &cap);
329 	rdmsr(MTRRDefaultType, &def);
330 	n += snprint(buf+n, bufsize-n, "cache default %s\n",
331 		type2str(def & Deftype));
332 	vcnt = cap & Capvcnt;
333 	for(i = 0; i < vcnt && i < Nmtrr; i++){
334 		mtrrget(&mtrr, i);
335 		if (mtrrdec(&mtrr, &base, &size, &type))
336 			n += snprint(buf+n, bufsize-n,
337 				"cache 0x%llux %llud %s\n",
338 				base, size, type2str(type));
339 	}
340 	return n;
341 }
342