]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/mmu.c
bcm64: map framebuffer write-through to allow unaligned access
[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, attr;
128
129         if(va == 0)
130                 return 0;
131
132         attr = va & PTEMA(7);
133         va &= -PGLSZ(1);
134         off = pa % PGLSZ(1);
135         a = va + off;
136         pe = (pa + size + (PGLSZ(1)-1)) & -PGLSZ(1);
137         while(pa < pe){
138                 ((uintptr*)L1)[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | PTEWRITE | PTEAF
139                         | PTEKERNEL | PTESH(SHARE_OUTER) | attr;
140                 pa += PGLSZ(1);
141                 va += PGLSZ(1);
142         }
143         flushtlb();
144         return a;
145 }
146
147 static uintptr*
148 mmuwalk(uintptr va, int level)
149 {
150         uintptr *table, pte;
151         Page *pg;
152         int i, x;
153
154         x = PTLX(va, PTLEVELS-1);
155         table = &m->mmul1[L1TABLEX(va, PTLEVELS-1)];
156         for(i = PTLEVELS-2; i >= level; i--){
157                 pte = table[x];
158                 if(pte & PTEVALID) {
159                         if(pte & (0xFFFFULL<<48))
160                                 iprint("strange pte %#p va %#p\n", pte, va);
161                         pte &= ~(0xFFFFULL<<48 | BY2PG-1);
162                         table = KADDR(pte);
163                 } else {
164                         if(i < 2){
165                                 pg = up->mmufree;
166                                 if(pg == nil)
167                                         return nil;
168                                 up->mmufree = pg->next;
169                                 switch(i){
170                                 case 0:
171                                         pg->va = va & -PGLSZ(1);
172                                         if((pg->next = up->mmul1) == nil)
173                                                 up->mmul1tail = pg;
174                                         up->mmul1 = pg;
175                                         break;
176                                 case 1:
177                                         pg->va = va & -PGLSZ(2);
178                                         if((pg->next = up->mmul2) == nil)
179                                                 up->mmul2tail = pg;
180                                         up->mmul2 = pg;
181                                         break;
182                                 }
183                                 memset(KADDR(pg->pa), 0, BY2PG);
184                                 coherence();
185                                 table[x] = pg->pa | PTEVALID | PTETABLE;
186                                 table = KADDR(pg->pa);
187                         } else {
188                                 table[x] = PADDR(&m->mmul1[L1TABLEX(va, 2)]) | PTEVALID | PTETABLE;
189                                 table = &m->mmul1[L1TABLEX(va, 2)];
190                         }
191                 }
192                 x = PTLX(va, (uintptr)i);
193         }
194         return &table[x];
195 }
196
197 static Proc *asidlist[256];
198
199 static int
200 allocasid(Proc *p)
201 {
202         static Lock lk;
203         Proc *x;
204         int a;
205
206         lock(&lk);
207         a = p->asid;
208         if(a < 0)
209                 a = -a;
210         if(a == 0)
211                 a = p->pid;
212         for(;; a++){
213                 a %= nelem(asidlist);
214                 if(a == 0)
215                         continue;       // reserved
216                 x = asidlist[a];
217                 if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
218                         break;
219         }
220         p->asid = a;
221         asidlist[a] = p;
222         unlock(&lk);
223
224         return x != p;
225 }
226
227 static void
228 freeasid(Proc *p)
229 {
230         int a;
231
232         a = p->asid;
233         if(a < 0)
234                 a = -a;
235         if(a > 0 && asidlist[a] == p)
236                 asidlist[a] = nil;
237         p->asid = 0;
238 }
239
240 void
241 putasid(Proc *p)
242 {
243         /*
244          * Prevent the following scenario:
245          *      pX sleeps on cpuA, leaving its page tables in mmul1
246          *      pX wakes up on cpuB, and exits, freeing its page tables
247          *  pY on cpuB allocates a freed page table page and overwrites with data
248          *  cpuA takes an interrupt, and is now running with bad page tables
249          * In theory this shouldn't hurt because only user address space tables
250          * are affected, and mmuswitch will clear mmul1 before a user process is
251          * dispatched.  But empirically it correlates with weird problems, eg
252          * resetting of the core clock at 0x4000001C which confuses local timers.
253          */
254         if(conf.nmach > 1)
255                 mmuswitch(nil);
256
257         if(p->asid > 0)
258                 p->asid = -p->asid;
259 }
260
261 void
262 putmmu(uintptr va, uintptr pa, Page *pg)
263 {
264         uintptr *pte, old;
265         int s;
266
267 // iprint("cpu%d: putmmu va %#p asid %d proc %lud %s\n", m->machno, va, up->asid, up->pid, up->text);
268         s = splhi();
269         while((pte = mmuwalk(va, 0)) == nil){
270                 spllo();
271                 assert(up->mmufree == nil);
272                 up->mmufree = newpage(0, nil, 0);
273                 splhi();
274         }
275         old = *pte;
276         *pte = 0;
277         if((old & PTEVALID) != 0)
278                 flushasidvall((uvlong)up->asid<<48 | va>>12);
279         else
280                 flushasidva((uvlong)up->asid<<48 | va>>12);
281         *pte = pa | PTEPAGE | PTEUSER | PTENG | PTEAF | PTESH(SHARE_INNER);
282         if(pg->txtflush & (1UL<<m->machno)){
283                 /* pio() sets PG_TXTFLUSH whenever a text pg has been written */
284                 cachedwbinvse((void*)KADDR(pg->pa), BY2PG);
285                 cacheiinvse((void*)va, BY2PG);
286                 pg->txtflush &= ~(1UL<<m->machno);
287         }
288         splx(s);
289 }
290
291 static void
292 mmufree(Proc *p)
293 {
294         freeasid(p);
295
296         if(p->mmul1 == nil){
297                 assert(p->mmul2 == nil);
298                 return;
299         }
300         p->mmul1tail->next = p->mmufree;
301         p->mmufree = p->mmul1;
302         p->mmul1 = p->mmul1tail = nil;
303
304         if(PTLEVELS > 2){
305                 p->mmul2tail->next = p->mmufree;
306                 p->mmufree = p->mmul2;
307                 p->mmul2 = p->mmul2tail = nil;
308         }
309 }
310
311 void
312 mmuswitch(Proc *p)
313 {
314         uintptr va;
315         Page *t;
316
317         for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
318                 m->mmul1[PTL1X(va, PTLEVELS-1)] = 0;
319
320         if(p == nil){
321                 setttbr(PADDR(&m->mmul1[L1TABLEX(0, PTLEVELS-1)]));
322                 return;
323         }
324
325         if(p->newtlb){
326                 mmufree(p);
327                 p->newtlb = 0;
328         }
329
330         if(PTLEVELS == 2){
331                 for(t = p->mmul1; t != nil; t = t->next){
332                         va = t->va;
333                         m->mmul1[PTL1X(va, 1)] = t->pa | PTEVALID | PTETABLE;
334                 }
335         } else {
336                 for(t = p->mmul2; t != nil; t = t->next){
337                         va = t->va;
338                         m->mmul1[PTL1X(va, 2)] = t->pa | PTEVALID | PTETABLE;
339                         if(PTLEVELS > 3)
340                                 m->mmul1[PTL1X(va, 3)] = PADDR(&m->mmul1[L1TABLEX(va, 2)]) |
341                                         PTEVALID | PTETABLE;
342                 }
343         }
344
345         if(allocasid(p))
346                 flushasid((uvlong)p->asid<<48);
347
348 // iprint("cpu%d: mmuswitch asid %d proc %lud %s\n", m->machno, p->asid, p->pid, p->text);
349         setttbr((uvlong)p->asid<<48 | PADDR(&m->mmul1[L1TABLEX(0, PTLEVELS-1)]));
350 }
351
352 void
353 mmurelease(Proc *p)
354 {
355         Page *t;
356
357         mmuswitch(nil);
358         mmufree(p);
359
360         if((t = p->mmufree) != nil){
361                 do {
362                         p->mmufree = t->next;
363                         if(--t->ref != 0)
364                                 panic("mmurelease: bad page ref");
365                         pagechainhead(t);
366                 } while((t = p->mmufree) != nil);
367                 pagechaindone();
368         }
369 }
370
371 void
372 flushmmu(void)
373 {
374         int x;
375
376         x = splhi();
377         up->newtlb = 1;
378         mmuswitch(up);
379         splx(x);
380 }
381
382 void
383 checkmmu(uintptr, uintptr)
384 {
385 }