]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/mmu.c
bcm64: switch to 64k page size
[plan9front.git] / sys / src / 9 / bcm64 / mmu.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "sysreg.h"
7
8 void
9 mmu0init(uintptr *l1)
10 {
11         uintptr va, pa, pe, attr;
12
13         /* KZERO */
14         attr = PTEWRITE | PTEAF | PTEKERNEL | PTESH(SHARE_INNER);
15         pe = PHYSDRAM + soc.dramsize;
16         if(pe > (uintptr)-KZERO)
17                 pe = (uintptr)-KZERO;
18         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
19                 if(pe - pa < PGLSZ(1)){
20                         l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
21                         l1[PTL1X(pa, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
22                         for(; pa < pe; pa += PGLSZ(0), va += PGLSZ(0))
23                                 l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
24                         break;
25                 }
26                 l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
27                 l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | attr;
28         }
29         pe = (uintptr)-KZERO;   /* populate top levels for mmukmap() */
30         if(PTLEVELS > 2)
31         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2)){
32                 l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
33                 l1[PTL1X(pa, 2)] = (uintptr)&l1[L1TABLEX(pa, 1)] | PTEVALID | PTETABLE;
34         }
35         if(PTLEVELS > 3)
36         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3)){
37                 l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
38                 l1[PTL1X(pa, 3)] = (uintptr)&l1[L1TABLEX(pa, 2)] | PTEVALID | PTETABLE;
39         }
40
41         /* VIRTIO */
42         attr = PTEWRITE | PTEAF | PTEKERNEL | PTESH(SHARE_OUTER) | PTEDEVICE;
43         pe = soc.physio + IOSIZE;
44         for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
45                 if(pe - pa < PGLSZ(1)){
46                         l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
47                         for(; pa < pe; pa += PGLSZ(0), va += PGLSZ(0))
48                                 l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
49                         break;
50                 }
51                 l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
52         }
53         if(PTLEVELS > 2)
54         for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
55                 l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
56         if(PTLEVELS > 3)
57         for(pa = soc.physio, va = VIRTIO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
58                 l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
59
60         /* ARMLOCAL */
61         pe = soc.armlocal + MB;
62         for(pa = soc.armlocal, va = ARMLOCAL; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
63                 l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
64         if(PTLEVELS > 2)
65         for(pa = soc.armlocal, va = ARMLOCAL; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
66                 l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
67         if(PTLEVELS > 3)
68         for(pa = soc.armlocal, va = ARMLOCAL; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
69                 l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
70 }
71
72 void
73 mmu0clear(uintptr *l1)
74 {
75         uintptr va, pa, pe;
76
77         pe = PHYSDRAM + soc.dramsize;
78         if(pe > (uintptr)-KZERO)
79                 pe = (uintptr)-KZERO;
80         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
81                 if(PTL1X(pa, 1) != PTL1X(va, 1))
82                         l1[PTL1X(pa, 1)] = 0;
83         if(PTLEVELS > 2)
84         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
85                 if(PTL1X(pa, 2) != PTL1X(va, 2))
86                         l1[PTL1X(pa, 2)] = 0;
87         if(PTLEVELS > 3)
88         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
89                 if(PTL1X(pa, 3) != PTL1X(va, 3))
90                         l1[PTL1X(pa, 3)] = 0;
91 }
92
93 void
94 mmuidmap(uintptr *l1)
95 {
96         uintptr va, pa, pe;
97
98         mmuswitch(nil);
99         flushtlb();
100
101         pe = PHYSDRAM + soc.dramsize;
102         if(pe > (uintptr)-KZERO)
103                 pe = (uintptr)-KZERO;
104         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
105                 l1[PTL1X(pa, 1)] = l1[PTL1X(va, 1)];
106         if(PTLEVELS > 2)
107         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
108                 l1[PTL1X(pa, 2)] = l1[PTL1X(va, 2)];
109         if(PTLEVELS > 3)
110         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
111                 l1[PTL1X(pa, 3)] = l1[PTL1X(va, 3)];
112         setttbr(PADDR(&l1[L1TABLEX(0, PTLEVELS-1)]));
113 }
114
115 void
116 mmu1init(void)
117 {
118         m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0);
119         if(m->mmutop == nil)
120                 panic("mmu1init: no memory for mmutop");
121         memset(m->mmutop, 0, L1TOPSIZE);
122         mmuswitch(nil);
123 }
124
125 uintptr
126 paddr(void *va)
127 {
128         if((uintptr)va >= KZERO)
129                 return (uintptr)va-KZERO;
130         panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
131         return 0;
132 }
133
134 uintptr
135 cankaddr(uintptr pa)
136 {
137         if(pa < (uintptr)-KZERO)
138                 return -KZERO - pa;
139         return 0;
140 }
141
142 void*
143 kaddr(uintptr pa)
144 {
145         if(pa < (uintptr)-KZERO)
146                 return (void*)(pa + KZERO);
147         panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
148         return nil;
149 }
150
151 void
152 kmapinval(void)
153 {
154 }
155
156 KMap*
157 kmap(Page *p)
158 {
159         return kaddr(p->pa);
160 }
161
162 void
163 kunmap(KMap*)
164 {
165 }
166
167 uintptr
168 mmukmap(uintptr va, uintptr pa, usize size)
169 {
170         uintptr a, pe, off, attr;
171
172         if(va == 0)
173                 return 0;
174
175         attr = va & PTEMA(7);
176         va &= -PGLSZ(1);
177         off = pa % PGLSZ(1);
178         a = va + off;
179         pe = (pa + size + (PGLSZ(1)-1)) & -PGLSZ(1);
180         while(pa < pe){
181                 ((uintptr*)L1)[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
182                         | PTEKERNEL | PTESH(SHARE_OUTER) | attr;
183                 pa += PGLSZ(1);
184                 va += PGLSZ(1);
185         }
186         flushtlb();
187         return a;
188 }
189
190 static uintptr*
191 mmuwalk(uintptr va, int level)
192 {
193         uintptr *table, pte;
194         Page *pg;
195         int i, x;
196
197         x = PTLX(va, PTLEVELS-1);
198         table = m->mmutop;
199         for(i = PTLEVELS-2; i >= level; i--){
200                 pte = table[x];
201                 if(pte & PTEVALID) {
202                         if(pte & (0xFFFFULL<<48))
203                                 iprint("strange pte %#p va %#p\n", pte, va);
204                         pte &= ~(0xFFFFULL<<48 | BY2PG-1);
205                         table = KADDR(pte);
206                 } else {
207                         pg = up->mmufree;
208                         if(pg == nil)
209                                 return nil;
210                         up->mmufree = pg->next;
211                         pg->va = va & -PGLSZ(i+1);
212                         if((pg->next = up->mmuhead[i+1]) == nil)
213                                 up->mmutail[i+1] = pg;
214                         up->mmuhead[i+1] = pg;
215                         memset(KADDR(pg->pa), 0, BY2PG);
216                         coherence();
217                         table[x] = pg->pa | PTEVALID | PTETABLE;
218                         table = KADDR(pg->pa);
219                 }
220                 x = PTLX(va, (uintptr)i);
221         }
222         return &table[x];
223 }
224
225 static Proc *asidlist[256];
226
227 static int
228 allocasid(Proc *p)
229 {
230         static Lock lk;
231         Proc *x;
232         int a;
233
234         lock(&lk);
235         a = p->asid;
236         if(a < 0)
237                 a = -a;
238         if(a == 0)
239                 a = p->pid;
240         for(;; a++){
241                 a %= nelem(asidlist);
242                 if(a == 0)
243                         continue;       // reserved
244                 x = asidlist[a];
245                 if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
246                         break;
247         }
248         p->asid = a;
249         asidlist[a] = p;
250         unlock(&lk);
251
252         return x != p;
253 }
254
255 static void
256 freeasid(Proc *p)
257 {
258         int a;
259
260         a = p->asid;
261         if(a < 0)
262                 a = -a;
263         if(a > 0 && asidlist[a] == p)
264                 asidlist[a] = nil;
265         p->asid = 0;
266 }
267
268 void
269 putasid(Proc *p)
270 {
271         /*
272          * Prevent the following scenario:
273          *      pX sleeps on cpuA, leaving its page tables in mmutop
274          *      pX wakes up on cpuB, and exits, freeing its page tables
275          *  pY on cpuB allocates a freed page table page and overwrites with data
276          *  cpuA takes an interrupt, and is now running with bad page tables
277          * In theory this shouldn't hurt because only user address space tables
278          * are affected, and mmuswitch will clear mmutop before a user process is
279          * dispatched.  But empirically it correlates with weird problems, eg
280          * resetting of the core clock at 0x4000001C which confuses local timers.
281          */
282         if(conf.nmach > 1)
283                 mmuswitch(nil);
284
285         if(p->asid > 0)
286                 p->asid = -p->asid;
287 }
288
289 void
290 putmmu(uintptr va, uintptr pa, Page *pg)
291 {
292         uintptr *pte, old;
293         int s;
294
295         s = splhi();
296         while((pte = mmuwalk(va, 0)) == nil){
297                 spllo();
298                 up->mmufree = newpage(0, nil, 0);
299                 splhi();
300         }
301         old = *pte;
302         *pte = 0;
303         if((old & PTEVALID) != 0)
304                 flushasidvall((uvlong)up->asid<<48 | va>>12);
305         else
306                 flushasidva((uvlong)up->asid<<48 | va>>12);
307         *pte = pa | PTEPAGE | PTEUSER | PTENG | PTEAF | PTESH(SHARE_INNER);
308         if(pg->txtflush & (1UL<<m->machno)){
309                 /* pio() sets PG_TXTFLUSH whenever a text pg has been written */
310                 cachedwbinvse((void*)KADDR(pg->pa), BY2PG);
311                 cacheiinvse((void*)va, BY2PG);
312                 pg->txtflush &= ~(1UL<<m->machno);
313         }
314         splx(s);
315 }
316
317 static void
318 mmufree(Proc *p)
319 {
320         int i;
321
322         freeasid(p);
323
324         for(i=1; i<PTLEVELS; i++){
325                 if(p->mmuhead[i] == nil)
326                         break;
327                 p->mmutail[i]->next = p->mmufree;
328                 p->mmufree = p->mmuhead[i];
329                 p->mmuhead[i] = p->mmutail[i] = nil;
330         }
331 }
332
333 void
334 mmuswitch(Proc *p)
335 {
336         uintptr va;
337         Page *t;
338
339         for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
340                 m->mmutop[PTLX(va, PTLEVELS-1)] = 0;
341
342         if(p == nil){
343                 setttbr(PADDR(m->mmutop));
344                 return;
345         }
346
347         if(p->newtlb){
348                 mmufree(p);
349                 p->newtlb = 0;
350         }
351
352         for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){
353                 va = t->va;
354                 m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID | PTETABLE;
355         }
356
357         if(allocasid(p))
358                 flushasid((uvlong)p->asid<<48);
359
360         setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop));
361 }
362
363 void
364 mmurelease(Proc *p)
365 {
366         Page *t;
367
368         mmuswitch(nil);
369         mmufree(p);
370
371         if((t = p->mmufree) != nil){
372                 do {
373                         p->mmufree = t->next;
374                         if(--t->ref != 0)
375                                 panic("mmurelease: bad page ref");
376                         pagechainhead(t);
377                 } while((t = p->mmufree) != nil);
378                 pagechaindone();
379         }
380 }
381
382 void
383 flushmmu(void)
384 {
385         int x;
386
387         x = splhi();
388         up->newtlb = 1;
389         mmuswitch(up);
390         splx(x);
391 }
392
393 void
394 checkmmu(uintptr, uintptr)
395 {
396 }