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