]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mtrr.c
kernel: cleanup the software mouse cursor mess
[plan9front.git] / sys / src / 9 / pc / mtrr.c
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
39 enum {
40         CR4PageGlobalEnable     = 1 << 7,
41         CR0CacheDisable         = 1 << 30,
42 };
43
44 enum {
45         Uncacheable     = 0,
46         Writecomb       = 1,
47         Unknown1        = 2,
48         Unknown2        = 3,
49         Writethru       = 4,
50         Writeprot       = 5,
51         Writeback       = 6,
52 };
53
54 enum {
55         Capvcnt = 0xff,         /* mask: # of variable-range MTRRs we have */
56         Capwc   = 1<<8,         /* flag: have write combining? */
57         Capfix  = 1<<10,        /* flag: have fixed MTRRs? */
58         Deftype = 0xff,         /* default MTRR type */
59         Deffixena = 1<<10,      /* fixed-range MTRR enable */
60         Defena  = 1<<11,        /* MTRR enable */
61 };
62
63 typedef struct Mtrreg Mtrreg;
64 typedef struct Mtrrop Mtrrop;
65
66 struct Mtrreg {
67         vlong   base;
68         vlong   mask;
69 };
70
71 static char *types[] = {
72 [Uncacheable]   "uc",
73 [Writecomb]     "wc",
74 [Unknown1]      "uk1",
75 [Unknown2]      "uk2",
76 [Writethru]     "wt",
77 [Writeprot]     "wp",
78 [Writeback]     "wb",
79                 nil
80 };
81
82 static int dosync;
83 static Mtrreg mtrreg[Nmtrr];
84
85 static char *
86 type2str(int type)
87 {
88         if(type < 0 || type >= nelem(types))
89                 return nil;
90         return types[type];
91 }
92
93 static int
94 str2type(char *str)
95 {
96         char **p;
97
98         for(p = types; *p != nil; p++)
99                 if (strcmp(str, *p) == 0)
100                         return p - types;
101         return -1;
102 }
103
104 static uvlong
105 physmask(void)
106 {
107         ulong regs[4];
108         static vlong mask = -1;
109
110         if (mask != -1)
111                 return mask;
112         cpuid(Exthighfunc, regs);
113         if(regs[0] >= Extaddrsz) {                      /* ax */
114                 cpuid(Extaddrsz, regs);
115                 mask = (1LL << (regs[0] & 0xFF)) - 1;   /* ax */
116         }
117         return mask;
118 }
119
120 static int
121 ispow2(uvlong ul)
122 {
123         return (ul & (ul - 1)) == 0;
124 }
125
126 /* true if mtrr is valid */
127 static int
128 mtrrdec(Mtrreg *mtrr, uvlong *ptr, uvlong *size, int *type)
129 {
130         *ptr =  mtrr->base & ~(BY2PG-1);
131         *type = mtrr->base & 0xff;
132         *size = (physmask() ^ (mtrr->mask & ~(BY2PG-1))) + 1;
133         return (mtrr->mask >> 11) & 1;
134 }
135
136 static void
137 mtrrenc(Mtrreg *mtrr, uvlong ptr, uvlong size, int type, int ok)
138 {
139         mtrr->base = ptr | (type & 0xff);
140         mtrr->mask = (physmask() & ~(size - 1)) | (ok? 1<<11: 0);
141 }
142
143 /*
144  * i is the index of the MTRR, and is multiplied by 2 because
145  * mask and base offsets are interleaved.
146  */
147 static void
148 mtrrget(Mtrreg *mtrr, uint i)
149 {
150         rdmsr(MTRRPhysBase0 + 2*i, &mtrr->base);
151         rdmsr(MTRRPhysMask0 + 2*i, &mtrr->mask);
152 }
153
154 static void
155 mtrrput(Mtrreg *mtrr, uint i)
156 {
157         wrmsr(MTRRPhysBase0 + 2*i, mtrr->base);
158         wrmsr(MTRRPhysMask0 + 2*i, mtrr->mask);
159 }
160
161 static int
162 mtrrvcnt(void)
163 {
164         vlong cap;
165         int vcnt;
166
167         rdmsr(MTRRCap, &cap);
168         vcnt = cap & Capvcnt;
169         if(vcnt > Nmtrr)
170                 vcnt = Nmtrr;
171         return vcnt;
172 }
173
174 static int
175 mtrrgetall(void)
176 {
177         int i, vcnt;
178
179         vcnt = mtrrvcnt();
180         for(i = 0; i < vcnt; i++)
181                 mtrrget(&mtrreg[i], i);
182         return vcnt;
183 }
184
185 static void
186 mtrrputall(void)
187 {
188         int s, i, vcnt;
189         ulong cr0, cr4;
190         vlong def;
191
192         s = splhi();
193
194         cr4 = getcr4();
195         putcr4(cr4 & ~CR4PageGlobalEnable);
196         cr0 = getcr0();
197         wbinvd();
198         putcr0(cr0 | CR0CacheDisable);
199         wbinvd();
200         rdmsr(MTRRDefaultType, &def);
201         wrmsr(MTRRDefaultType, def & ~(vlong)Defena);
202
203         vcnt = mtrrvcnt();
204         for(i=0; i<vcnt; i++)
205                 mtrrput(&mtrreg[i], i);
206
207         wbinvd();
208         wrmsr(MTRRDefaultType, def);
209         putcr0(cr0);
210         putcr4(cr4);
211
212         splx(s);
213 }
214
215 void
216 mtrrclock(void)                         /* called from clock interrupt */
217 {
218         static Ref bar1, bar2;
219         int s;
220
221         if(dosync == 0)
222                 return;
223
224         s = splhi();
225
226         /*
227          * wait for all CPUs to sync here, so that the MTRR setup gets
228          * done at roughly the same time on all processors.
229          */
230         incref(&bar1);
231         while(bar1.ref < conf.nmach)
232                 microdelay(10);
233
234         mtrrputall();
235
236         /*
237          * wait for all CPUs to sync up again, so that we don't continue
238          * executing while the MTRRs are still being set up.
239          */
240         incref(&bar2);
241         while(bar2.ref < conf.nmach)
242                 microdelay(10);
243         decref(&bar1);
244         while(bar1.ref > 0)
245                 microdelay(10);
246         decref(&bar2);
247
248         dosync = 0;
249         splx(s);
250 }
251
252 static char*
253 mtrr0(uvlong base, uvlong size, char *tstr)
254 {
255         int i, vcnt, slot, type, mtype, mok;
256         vlong def, cap;
257         uvlong mp, msize;
258
259         if(!(m->cpuiddx & Mtrr))
260                 return "mtrrs not supported";
261         if(base & (BY2PG-1) || size & (BY2PG-1) || size == 0)
262                 return "mtrr base or size not 4k aligned or zero size";
263         if(!ispow2(size))
264                 return "mtrr size not power of 2";
265         if(base & (size - 1))
266                 return "mtrr base not naturally aligned";
267
268         if((type = str2type(tstr)) == -1)
269                 return "mtrr bad type";
270
271         rdmsr(MTRRCap, &cap);
272         rdmsr(MTRRDefaultType, &def);
273
274         switch(type){
275         default:
276                 return "mtrr unknown type";
277         case Writecomb:
278                 if(!(cap & Capwc))
279                         return "mtrr type wc (write combining) unsupported";
280                 /* fallthrough */
281         case Uncacheable:
282         case Writethru:
283         case Writeprot:
284         case Writeback:
285                 break;
286         }
287
288         vcnt = mtrrgetall();
289
290         slot = -1;
291         for(i = 0; i < vcnt; i++){
292                 mok = mtrrdec(&mtrreg[i], &mp, &msize, &mtype);
293                 if(slot == -1 && (!mok || mtype == (def & Deftype)))
294                         slot = i;       /* good, but look further for exact match */
295                 if(mok && mp == base && msize == size){
296                         slot = i;
297                         break;
298                 }
299         }
300         if(slot == -1)
301                 return "no free mtrr slots";
302
303         mtrrenc(&mtrreg[slot], base, size, type, 1);
304
305         coherence();
306
307         dosync = 1;
308         mtrrclock();
309
310         return nil;
311 }
312
313 char*
314 mtrr(uvlong base, uvlong size, char *tstr)
315 {
316         static QLock mtrrlk;
317         char *err;
318
319         qlock(&mtrrlk);
320         err = mtrr0(base, size, tstr);
321         qunlock(&mtrrlk);
322
323         return err;
324 }
325
326 int
327 mtrrprint(char *buf, long bufsize)
328 {
329         int i, n, vcnt, type;
330         uvlong base, size;
331         Mtrreg mtrr;
332         vlong def;
333
334         if(!(m->cpuiddx & Mtrr))
335                 return 0;
336         rdmsr(MTRRDefaultType, &def);
337         n = snprint(buf, bufsize, "cache default %s\n",
338                 type2str(def & Deftype));
339         vcnt = mtrrvcnt();
340         for(i = 0; i < vcnt; i++){
341                 mtrrget(&mtrr, i);
342                 if (mtrrdec(&mtrr, &base, &size, &type))
343                         n += snprint(buf+n, bufsize-n,
344                                 "cache 0x%llux %llud %s\n",
345                                 base, size, type2str(type));
346         }
347         return n;
348 }
349
350 void
351 mtrrsync(void)
352 {
353         static vlong cap0, def0;
354         vlong cap, def;
355
356         rdmsr(MTRRCap, &cap);
357         rdmsr(MTRRDefaultType, &def);
358
359         if(m->machno == 0){
360                 cap0 = cap;
361                 def0 = def;
362                 mtrrgetall();
363                 return;
364         }
365
366         if(cap0 != cap)
367                 print("mtrrcap%d: %lluX %lluX\n",
368                         m->machno, cap0, cap);
369         if(def0 != def)
370                 print("mtrrdef%d: %lluX %lluX\n",
371                         m->machno, def0, def);
372         mtrrputall();
373 }