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