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 /* true if mtrr is valid */
135 static int
mtrrdec(Mtrreg * mtrr,uvlong * ptr,uvlong * size,int * type)136 mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type)
137 {
138 sanity(mtrr);
139 *ptr = mtrr->base & ~(BY2PG-1);
140 *type = mtrr->base & 0xff;
141 *size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
142 return (mtrr->mask >> 11) & 1;
143 }
144
145 static void
mtrrenc(Mtrreg * mtrr,uvlong ptr,uvlong size,int type,int ok)146 mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok)
147 {
148 mtrr->base = ptr | (type & 0xff);
149 mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
150 sanity(mtrr);
151 }
152
153 /*
154 * i is the index of the MTRR, and is multiplied by 2 because
155 * mask and base offsets are interleaved.
156 */
157 static void
mtrrget(Mtrreg * mtrr,uint i)158 mtrrget(Mtrreg *mtrr, uint i)
159 {
160 if (i >= Nmtrr)
161 error("mtrr index out of range");
162 rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
163 rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
164 sanity(mtrr);
165 }
166
167 static void
mtrrput(Mtrreg * mtrr,uint i)168 mtrrput(Mtrreg *mtrr, uint i)
169 {
170 if (i >= Nmtrr)
171 error("mtrr index out of range");
172 sanity(mtrr);
173 wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
174 wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
175 }
176
177 static void
mtrrop(Mtrrop ** op)178 mtrrop(Mtrrop **op)
179 {
180 int s;
181 ulong cr0, cr4;
182 vlong def;
183 static long bar1, bar2;
184
185 s = splhi(); /* avoid race with mtrrclock */
186
187 /*
188 * wait for all CPUs to sync here, so that the MTRR setup gets
189 * done at roughly the same time on all processors.
190 */
191 ainc(&bar1);
192 while(bar1 < conf.nmach)
193 microdelay(10);
194
195 cr4 = getcr4();
196 putcr4(cr4 & ~CR4PageGlobalEnable);
197 cr0 = getcr0();
198 wbinvd();
199 putcr0(cr0 | CR0CacheDisable);
200 wbinvd();
201 rdmsr(MTRRDefaultType, &def);
202 wrmsr(MTRRDefaultType, def & ~(vlong)Defena);
203
204 mtrrput((*op)->reg, (*op)->slot);
205
206 wbinvd();
207 wrmsr(MTRRDefaultType, def);
208 putcr0(cr0);
209 putcr4(cr4);
210
211 /*
212 * wait for all CPUs to sync up again, so that we don't continue
213 * executing while the MTRRs are still being set up.
214 */
215 ainc(&bar2);
216 while(bar2 < conf.nmach)
217 microdelay(10);
218 *op = nil;
219 adec(&bar1);
220 while(bar1 > 0)
221 microdelay(10);
222 adec(&bar2);
223 wakeup(&oprend);
224 splx(s);
225 }
226
227 void
mtrrclock(void)228 mtrrclock(void) /* called from clock interrupt */
229 {
230 if(postedop != nil)
231 mtrrop(&postedop);
232 }
233
234 /* if there's an operation still pending, keep sleeping */
235 static int
opavail(void *)236 opavail(void *)
237 {
238 return postedop == nil;
239 }
240
241 int
mtrr(uvlong base,uvlong size,char * tstr)242 mtrr(uvlong base, uvlong size, char *tstr)
243 {
244 int i, vcnt, slot, type, mtype, mok;
245 vlong def, cap;
246 uvlong mp, msize;
247 Mtrreg entry, mtrr;
248 Mtrrop op;
249 static int tickreg;
250 static QLock mtrrlk;
251
252 if(!(m->cpuiddx & Mtrr))
253 error("mtrrs not supported");
254 if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
255 error("mtrr base or size not 4k aligned or zero size");
256 if(base + size >= Paerange)
257 error("mtrr range exceeds 36 bits");
258 if(!ispow2(size))
259 error("mtrr size not power of 2");
260 if(base & (size - 1))
261 error("mtrr base not naturally aligned");
262
263 if((type = str2type(tstr)) == -1)
264 error("mtrr bad type");
265
266 rdmsr(MTRRCap, &cap);
267 rdmsr(MTRRDefaultType, &def);
268
269 switch(type){
270 default:
271 error("mtrr unknown type");
272 break;
273 case Writecomb:
274 if(!(cap & Capwc))
275 error("mtrr type wc (write combining) unsupported");
276 /* fallthrough */
277 case Uncacheable:
278 case Writethru:
279 case Writeprot:
280 case Writeback:
281 break;
282 }
283
284 qlock(&mtrrlk);
285 slot = -1;
286 vcnt = cap & Capvcnt;
287 for(i = 0; i < vcnt && i < Nmtrr; i++){
288 mtrrget(&mtrr, i);
289 mok = mtrrdec(&mtrr, &mp, &msize, &mtype);
290 /* reuse any entry for addresses above 4GB */
291 if(!mok || mp == base && msize == size || mp >= (1LL<<32)){
292 slot = i;
293 break;
294 }
295 }
296 if(slot == -1)
297 error("no free mtrr slots");
298
299 while(postedop != nil)
300 sleep(&oprend, opavail, 0);
301 mtrrenc(&entry, base, size, type, 1);
302 op.reg = &entry;
303 op.slot = slot;
304 postedop = &op;
305 mtrrop(&postedop);
306 qunlock(&mtrrlk);
307 return 0;
308 }
309
310 int
mtrrprint(char * buf,long bufsize)311 mtrrprint(char *buf, long bufsize)
312 {
313 int i, vcnt, type;
314 long n;
315 uvlong base, size;
316 vlong cap, def;
317 Mtrreg mtrr;
318
319 n = 0;
320 if(!(m->cpuiddx & Mtrr))
321 return 0;
322 rdmsr(MTRRCap, &cap);
323 rdmsr(MTRRDefaultType, &def);
324 n += snprint(buf+n, bufsize-n, "cache default %s\n",
325 type2str(def & Deftype));
326 vcnt = cap & Capvcnt;
327 for(i = 0; i < vcnt && i < Nmtrr; i++){
328 mtrrget(&mtrr, i);
329 if (mtrrdec(&mtrr, &base, &size, &type))
330 n += snprint(buf+n, bufsize-n,
331 "cache 0x%llux %llud %s\n",
332 base, size, type2str(type));
333 }
334 return n;
335 }
336