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