]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mtrr.c
merge
[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         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 *
91 type2str(int type)
92 {
93         if(type < 0 || type >= nelem(types))
94                 return nil;
95         return types[type];
96 }
97
98 static int
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
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
128 sanity(Mtrreg *mtrr)
129 {
130         mtrr->base &= Paerange - 1;
131         mtrr->mask &= Paerange - 1;
132 }
133
134 static int
135 ispow2(uvlong ul)
136 {
137         return (ul & (ul - 1)) == 0;
138 }
139
140 /* true if mtrr is valid */
141 static int
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
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
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
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
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
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
242 opavail(void *)
243 {
244         return postedop == nil;
245 }
246
247 int
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++){
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
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++){
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 }