]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/mmu.c
amd64: FP: always use enough to fit AVX state and align to 64 bytes
[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 = -KZERO;
16         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
17                 l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
18                 l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | attr;
19         }
20
21         /* VIRTIO */
22         attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE;
23         pe = soc.physio + soc.iosize;
24         for(pa = soc.physio, va = soc.virtio; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
25                 if(((pa|va) & PGLSZ(1)-1) != 0){
26                         l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
27                         for(; pa < pe && ((va|pa) & PGLSZ(1)-1) != 0; pa += PGLSZ(0), va += PGLSZ(0)){
28                                 assert(l1[PTLX(va, 0)] == 0);
29                                 l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
30                         }
31                         break;
32                 }
33                 l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
34         }
35
36         /* ARMLOCAL */
37         attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE;
38         pe = soc.armlocal + MB;
39         for(pa = soc.armlocal, va = ARMLOCAL; 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         if(PTLEVELS > 2)
52         for(va = KSEG0; va != 0; va += PGLSZ(2))
53                 l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
54         if(PTLEVELS > 3)
55         for(va = KSEG0; va != 0; va += PGLSZ(3))
56                 l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
57 }
58
59 void
60 mmu0clear(uintptr *l1)
61 {
62         uintptr va, pa, pe;
63
64         pe = -KZERO;
65         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
66                 if(PTL1X(pa, 1) != PTL1X(va, 1))
67                         l1[PTL1X(pa, 1)] = 0;
68         if(PTLEVELS > 2)
69         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
70                 if(PTL1X(pa, 2) != PTL1X(va, 2))
71                         l1[PTL1X(pa, 2)] = 0;
72         if(PTLEVELS > 3)
73         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
74                 if(PTL1X(pa, 3) != PTL1X(va, 3))
75                         l1[PTL1X(pa, 3)] = 0;
76 }
77
78 void
79 mmuidmap(uintptr *l1)
80 {
81         uintptr va, pa, pe;
82
83         pe = PHYSDRAM + soc.dramsize;
84         if(pe > (uintptr)-KZERO)
85                 pe = (uintptr)-KZERO;
86         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
87                 l1[PTL1X(pa, 1)] = l1[PTL1X(va, 1)];
88         if(PTLEVELS > 2)
89         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
90                 l1[PTL1X(pa, 2)] = l1[PTL1X(va, 2)];
91         if(PTLEVELS > 3)
92         for(pa = PHYSDRAM, va = KZERO; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
93                 l1[PTL1X(pa, 3)] = l1[PTL1X(va, 3)];
94         setttbr(PADDR(&l1[L1TABLEX(0, PTLEVELS-1)]));
95         flushtlb();
96 }
97
98 void
99 mmu1init(void)
100 {
101         m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0);
102         if(m->mmutop == nil)
103                 panic("mmu1init: no memory for mmutop");
104         memset(m->mmutop, 0, L1TOPSIZE);
105         mmuswitch(nil);
106 }
107
108 /* KZERO maps the first 1GB of ram */
109 uintptr
110 paddr(void *va)
111 {
112         if((uintptr)va >= KZERO)
113                 return (uintptr)va-KZERO;
114         panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
115         return 0;
116 }
117
118 uintptr
119 cankaddr(uintptr pa)
120 {
121         if(pa < (uintptr)-KZERO)
122                 return -KZERO - pa;
123         return 0;
124 }
125
126 void*
127 kaddr(uintptr pa)
128 {
129         if(pa < (uintptr)-KZERO)
130                 return (void*)(pa + KZERO);
131         panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
132         return nil;
133 }
134
135 static void*
136 kmapaddr(uintptr pa)
137 {
138         if(pa < (uintptr)-KZERO)
139                 return (void*)(pa + KZERO);
140         if(pa >= KMAPEND-KMAP)
141                 panic("kmapaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
142         return (void*)(pa + KMAP);
143 }
144
145 KMap*
146 kmap(Page *p)
147 {
148         return kmapaddr(p->pa);
149 }
150
151 void
152 kunmap(KMap*)
153 {
154 }
155
156 void
157 kmapinval(void)
158 {
159 }
160
161 #define INITMAP (ROUND((uintptr)end + BY2PG, PGLSZ(1))-KZERO)
162
163 static void*
164 rampage(void)
165 {
166         uintptr pa;
167
168         if(conf.npage)
169                 return mallocalign(BY2PG, BY2PG, 0, 0);
170
171         pa = conf.mem[0].base;
172         assert((pa % BY2PG) == 0);
173         assert(pa < INITMAP);
174         conf.mem[0].base += BY2PG;
175         return KADDR(pa);
176 }
177
178 static void
179 l1map(uintptr va, uintptr pa, uintptr pe, uintptr attr)
180 {
181         uintptr *l1, *l0;
182
183         assert(pa < pe);
184
185         va &= -BY2PG;
186         pa &= -BY2PG;
187         pe = PGROUND(pe);
188
189         attr |= PTEKERNEL | PTEAF;
190
191         l1 = (uintptr*)L1;
192
193         while(pa < pe){
194                 if(l1[PTL1X(va, 1)] == 0 && (pe-pa) >= PGLSZ(1) && ((va|pa) & PGLSZ(1)-1) == 0){
195                         l1[PTL1X(va, 1)] = PTEVALID | PTEBLOCK | pa | attr;
196                         va += PGLSZ(1);
197                         pa += PGLSZ(1);
198                         continue;
199                 }
200                 if(l1[PTL1X(va, 1)] & PTEVALID) {
201                         assert((l1[PTL1X(va, 1)] & PTETABLE) == PTETABLE);
202                         l0 = KADDR(l1[PTL1X(va, 1)] & -PGLSZ(0));
203                 } else {
204                         l0 = rampage();
205                         memset(l0, 0, BY2PG);
206                         l1[PTL1X(va, 1)] = PTEVALID | PTETABLE | PADDR(l0);
207                 }
208                 assert(l0[PTLX(va, 0)] == 0);
209                 l0[PTLX(va, 0)] = PTEVALID | PTEPAGE | pa | attr;
210                 va += BY2PG;
211                 pa += BY2PG;
212         }
213 }
214
215 static void
216 kmapram(uintptr base, uintptr limit)
217 {
218         if(base < (uintptr)-KZERO && limit > (uintptr)-KZERO){
219                 kmapram(base, (uintptr)-KZERO);
220                 kmapram((uintptr)-KZERO, limit);
221                 return;
222         }
223         if(base < INITMAP)
224                 base = INITMAP;
225         if(base >= limit || limit <= INITMAP)
226                 return;
227
228         l1map((uintptr)kmapaddr(base), base, limit,
229                 PTEWRITE | PTEPXN | PTEUXN | PTESH(SHARE_INNER));
230 }
231
232 void
233 meminit(void)
234 {
235         uvlong memsize = 0;
236         uintptr pa, va;
237         char *p, *e;
238         int i;
239
240         if(p = getconf("*maxmem")){
241                 memsize = strtoull(p, &e, 0) - PHYSDRAM;
242                 for(i = 1; i < nelem(conf.mem); i++){
243                         if(e <= p || *e != ' ')
244                                 break;
245                         p = ++e;
246                         conf.mem[i].base = strtoull(p, &e, 0);
247                         if(e <= p || *e != ' ')
248                                 break;
249                         p = ++e;
250                         conf.mem[i].limit = strtoull(p, &e, 0);
251                 }
252         }
253
254         if (memsize < INITMAP)          /* sanity */
255                 memsize = INITMAP;
256
257         getramsize(&conf.mem[0]);
258         if(conf.mem[0].limit == 0){
259                 conf.mem[0].base = PHYSDRAM;
260                 conf.mem[0].limit = PHYSDRAM + memsize;
261         }else if(p != nil)
262                 conf.mem[0].limit = conf.mem[0].base + memsize;
263
264         /*
265          * now we know the real memory regions, unmap
266          * everything above INITMAP and map again with
267          * the proper sizes.
268          */
269         coherence();
270         for(va = INITMAP+KZERO; va != 0; va += PGLSZ(1)){
271                 pa = va-KZERO;
272                 ((uintptr*)L1)[PTL1X(pa, 1)] = 0;
273                 ((uintptr*)L1)[PTL1X(va, 1)] = 0;
274         }
275         flushtlb();
276
277         pa = PGROUND((uintptr)end)-KZERO;
278         for(i=0; i<nelem(conf.mem); i++){
279                 if(conf.mem[i].limit >= KMAPEND-KMAP)
280                         conf.mem[i].limit = KMAPEND-KMAP;
281
282                 if(conf.mem[i].limit <= conf.mem[i].base){
283                         conf.mem[i].limit = conf.mem[i].base = 0;
284                         continue;
285                 }
286
287                 if(conf.mem[i].base < PHYSDRAM + soc.dramsize
288                 && conf.mem[i].limit > PHYSDRAM + soc.dramsize)
289                         conf.mem[i].limit = PHYSDRAM + soc.dramsize;
290
291                 /* take kernel out of allocatable space */
292                 if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
293                         conf.mem[i].base = pa;
294
295                 kmapram(conf.mem[i].base, conf.mem[i].limit);
296         }
297         flushtlb();
298
299         /* rampage() is now done, count up the pages for each bank */
300         for(i=0; i<nelem(conf.mem); i++)
301                 conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
302 }
303
304 uintptr
305 mmukmap(uintptr va, uintptr pa, usize size)
306 {
307         uintptr attr, off;
308
309         if(va == 0)
310                 return 0;
311
312         off = pa & BY2PG-1;
313
314         attr = va & PTEMA(7);
315         attr |= PTEWRITE | PTEUXN | PTEPXN | PTESH(SHARE_OUTER);
316
317         va &= -BY2PG;
318         pa &= -BY2PG;
319
320         l1map(va, pa, pa + off + size, attr);
321         flushtlb();
322
323         return va + off;
324 }
325
326 void*
327 vmap(uvlong pa, int size)
328 {
329         static uintptr base = VMAP;
330         uvlong pe = pa + size;
331         uintptr va;
332
333         va = base;
334         base += PGROUND(pe) - (pa & -BY2PG);
335         
336         return (void*)mmukmap(va | PTEDEVICE, pa, size);
337 }
338
339 void
340 vunmap(void *, int)
341 {
342 }
343
344 static uintptr*
345 mmuwalk(uintptr va, int level)
346 {
347         uintptr *table, pte;
348         Page *pg;
349         int i, x;
350
351         x = PTLX(va, PTLEVELS-1);
352         table = m->mmutop;
353         for(i = PTLEVELS-2; i >= level; i--){
354                 pte = table[x];
355                 if(pte & PTEVALID) {
356                         if(pte & (0xFFFFULL<<48))
357                                 iprint("strange pte %#p va %#p\n", pte, va);
358                         pte &= ~(0xFFFFULL<<48 | BY2PG-1);
359                 } else {
360                         pg = up->mmufree;
361                         if(pg == nil)
362                                 return nil;
363                         up->mmufree = pg->next;
364                         pg->va = va & -PGLSZ(i+1);
365                         if((pg->next = up->mmuhead[i+1]) == nil)
366                                 up->mmutail[i+1] = pg;
367                         up->mmuhead[i+1] = pg;
368                         pte = pg->pa;
369                         memset(kmapaddr(pte), 0, BY2PG);
370                         coherence();
371                         table[x] = pte | PTEVALID | PTETABLE;
372                 }
373                 table = kmapaddr(pte);
374                 x = PTLX(va, (uintptr)i);
375         }
376         return &table[x];
377 }
378
379 static Proc *asidlist[256];
380
381 static int
382 allocasid(Proc *p)
383 {
384         static Lock lk;
385         Proc *x;
386         int a;
387
388         lock(&lk);
389         a = p->asid;
390         if(a < 0)
391                 a = -a;
392         if(a == 0)
393                 a = p->pid;
394         for(;; a++){
395                 a %= nelem(asidlist);
396                 if(a == 0)
397                         continue;       // reserved
398                 x = asidlist[a];
399                 if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
400                         break;
401         }
402         p->asid = a;
403         asidlist[a] = p;
404         unlock(&lk);
405
406         return x != p;
407 }
408
409 static void
410 freeasid(Proc *p)
411 {
412         int a;
413
414         a = p->asid;
415         if(a < 0)
416                 a = -a;
417         if(a > 0 && asidlist[a] == p)
418                 asidlist[a] = nil;
419         p->asid = 0;
420 }
421
422 void
423 putasid(Proc *p)
424 {
425         /*
426          * Prevent the following scenario:
427          *      pX sleeps on cpuA, leaving its page tables in mmutop
428          *      pX wakes up on cpuB, and exits, freeing its page tables
429          *  pY on cpuB allocates a freed page table page and overwrites with data
430          *  cpuA takes an interrupt, and is now running with bad page tables
431          * In theory this shouldn't hurt because only user address space tables
432          * are affected, and mmuswitch will clear mmutop before a user process is
433          * dispatched.  But empirically it correlates with weird problems, eg
434          * resetting of the core clock at 0x4000001C which confuses local timers.
435          */
436         if(conf.nmach > 1)
437                 mmuswitch(nil);
438
439         if(p->asid > 0)
440                 p->asid = -p->asid;
441 }
442
443 void
444 putmmu(uintptr va, uintptr pa, Page *pg)
445 {
446         uintptr *pte, old;
447         int s;
448
449         s = splhi();
450         while((pte = mmuwalk(va, 0)) == nil){
451                 spllo();
452                 up->mmufree = newpage(0, nil, 0);
453                 splhi();
454         }
455         old = *pte;
456         *pte = 0;
457         if((old & PTEVALID) != 0)
458                 flushasidvall((uvlong)up->asid<<48 | va>>12);
459         else
460                 flushasidva((uvlong)up->asid<<48 | va>>12);
461         *pte = pa | PTEPAGE | PTEUSER | PTEPXN | PTENG | PTEAF |
462                 (((pa & PTEMA(7)) == PTECACHED)? PTESH(SHARE_INNER): PTESH(SHARE_OUTER));
463         if(pg->txtflush & (1UL<<m->machno)){
464                 /* pio() sets PG_TXTFLUSH whenever a text pg has been written */
465                 cachedwbinvse(kmap(pg), BY2PG);
466                 cacheiinvse((void*)va, BY2PG);
467                 pg->txtflush &= ~(1UL<<m->machno);
468         }
469         splx(s);
470 }
471
472 static void
473 mmufree(Proc *p)
474 {
475         int i;
476
477         freeasid(p);
478
479         for(i=1; i<PTLEVELS; i++){
480                 if(p->mmuhead[i] == nil)
481                         break;
482                 p->mmutail[i]->next = p->mmufree;
483                 p->mmufree = p->mmuhead[i];
484                 p->mmuhead[i] = p->mmutail[i] = nil;
485         }
486 }
487
488 void
489 mmuswitch(Proc *p)
490 {
491         uintptr va;
492         Page *t;
493
494         for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
495                 m->mmutop[PTLX(va, PTLEVELS-1)] = 0;
496
497         if(p == nil){
498                 setttbr(PADDR(m->mmutop));
499                 return;
500         }
501
502         if(p->newtlb){
503                 mmufree(p);
504                 p->newtlb = 0;
505         }
506
507         if(allocasid(p))
508                 flushasid((uvlong)p->asid<<48);
509
510         setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop));
511
512         for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){
513                 va = t->va;
514                 m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID | PTETABLE;
515         }
516 }
517
518 void
519 mmurelease(Proc *p)
520 {
521         Page *t;
522
523         mmuswitch(nil);
524         mmufree(p);
525
526         if((t = p->mmufree) != nil){
527                 do {
528                         p->mmufree = t->next;
529                         if(--t->ref != 0)
530                                 panic("mmurelease: bad page ref");
531                         pagechainhead(t);
532                 } while((t = p->mmufree) != nil);
533                 pagechaindone();
534         }
535 }
536
537 void
538 flushmmu(void)
539 {
540         int x;
541
542         x = splhi();
543         up->newtlb = 1;
544         mmuswitch(up);
545         splx(x);
546 }
547
548 void
549 checkmmu(uintptr, uintptr)
550 {
551 }