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