]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc64/mmu.c
bcm, bcm64: add support for device tree parameter passing
[plan9front.git] / sys / src / 9 / pc64 / 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        "io.h"
7
8 /*
9  * Simple segment descriptors with no translation.
10  */
11 #define EXECSEGM(p)     { 0, SEGL|SEGP|SEGPL(p)|SEGEXEC }
12 #define DATASEGM(p)     { 0, SEGB|SEGG|SEGP|SEGPL(p)|SEGDATA|SEGW }
13 #define EXEC32SEGM(p)   { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
14 #define DATA32SEGM(p)   { 0xFFFF, SEGB|SEGG|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
15
16 Segdesc gdt[NGDT] =
17 {
18 [NULLSEG]       { 0, 0},                /* null descriptor */
19 [KESEG]         EXECSEGM(0),            /* kernel code */
20 [KDSEG]         DATASEGM(0),            /* kernel data */
21 [UE32SEG]       EXEC32SEGM(3),          /* user code 32 bit*/
22 [UDSEG]         DATA32SEGM(3),          /* user data/stack */
23 [UESEG]         EXECSEGM(3),            /* user code */
24 };
25
26 static struct {
27         Lock;
28         MMU     *free;
29
30         ulong   nalloc;
31         ulong   nfree;
32 } mmupool;
33
34 enum {
35         /* level */
36         PML4E   = 2,
37         PDPE    = 1,
38         PDE     = 0,
39
40         MAPBITS = 8*sizeof(m->mmumap[0]),
41
42         /* PAT entry used for write combining */
43         PATWC   = 7,
44 };
45
46 static void
47 loadptr(u16int lim, uintptr off, void (*load)(void*))
48 {
49         u64int b[2], *o;
50         u16int *s;
51
52         o = &b[1];
53         s = ((u16int*)o)-1;
54
55         *s = lim;
56         *o = off;
57
58         (*load)(s);
59 }
60
61 static void
62 taskswitch(uintptr stack)
63 {
64         Tss *tss;
65
66         tss = m->tss;
67         tss->rsp0[0] = (u32int)stack;
68         tss->rsp0[1] = stack >> 32;
69         tss->rsp1[0] = (u32int)stack;
70         tss->rsp1[1] = stack >> 32;
71         tss->rsp2[0] = (u32int)stack;
72         tss->rsp2[1] = stack >> 32;
73         mmuflushtlb();
74 }
75
76 void
77 mmuinit(void)
78 {
79         uintptr x;
80         vlong v;
81         int i;
82
83         /* zap double map done by l.s */ 
84         m->pml4[512] = 0;
85         m->pml4[0] = 0;
86
87         m->tss = mallocz(sizeof(Tss), 1);
88         if(m->tss == nil)
89                 panic("mmuinit: no memory for Tss");
90         m->tss->iomap = 0xDFFF;
91         for(i=0; i<14; i+=2){
92                 x = (uintptr)m + MACHSIZE;
93                 m->tss->ist[i] = x;
94                 m->tss->ist[i+1] = x>>32;
95         }
96
97         /*
98          * We used to keep the GDT in the Mach structure, but it
99          * turns out that that slows down access to the rest of the
100          * page.  Since the Mach structure is accessed quite often,
101          * it pays off anywhere from a factor of 1.25 to 2 on real
102          * hardware to separate them (the AMDs are more sensitive
103          * than Intels in this regard).  Under VMware it pays off
104          * a factor of about 10 to 100.
105          */
106         memmove(m->gdt, gdt, sizeof gdt);
107
108         x = (uintptr)m->tss;
109         m->gdt[TSSSEG+0].d0 = (x<<16)|(sizeof(Tss)-1);
110         m->gdt[TSSSEG+0].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
111         m->gdt[TSSSEG+1].d0 = x>>32;
112         m->gdt[TSSSEG+1].d1 = 0;
113
114         loadptr(sizeof(gdt)-1, (uintptr)m->gdt, lgdt);
115         loadptr(sizeof(Segdesc)*512-1, (uintptr)IDTADDR, lidt);
116         taskswitch((uintptr)m + MACHSIZE);
117         ltr(TSSSEL);
118
119         wrmsr(0xc0000100, 0ull);        /* 64 bit fsbase */
120         wrmsr(0xc0000101, (uvlong)&machp[m->machno]);   /* 64 bit gsbase */
121         wrmsr(0xc0000102, 0ull);        /* kernel gs base */
122
123         /* enable syscall extension */
124         rdmsr(0xc0000080, &v);
125         v |= 1ull;
126         wrmsr(0xc0000080, v);
127
128         /* IA32_STAR */
129         wrmsr(0xc0000081, ((uvlong)UE32SEL << 48) | ((uvlong)KESEL << 32));
130
131         /* IA32_LSTAR */
132         wrmsr(0xc0000082, (uvlong)syscallentry);
133
134         /* SYSCALL flags mask */
135         wrmsr(0xc0000084, 0x200);
136
137         /* IA32_PAT write combining */
138         if((MACHP(0)->cpuiddx & Pat) != 0
139         && rdmsr(0x277, &v) != -1){
140                 v &= ~(255LL<<(PATWC*8));
141                 v |= 1LL<<(PATWC*8);    /* WC */
142                 wrmsr(0x277, v);
143         }
144 }
145
146 /*
147  * These could go back to being macros once the kernel is debugged,
148  * but the extra checking is nice to have.
149  */
150 void*
151 kaddr(uintptr pa)
152 {
153         if(pa >= (uintptr)-KZERO)
154                 panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
155         return (void*)(pa+KZERO);
156 }
157
158 uintptr
159 paddr(void *v)
160 {
161         uintptr va;
162         
163         va = (uintptr)v;
164         if(va >= KZERO)
165                 return va-KZERO;
166         if(va >= VMAP)
167                 return va-VMAP;
168         panic("paddr: va=%#p pc=%#p", va, getcallerpc(&v));
169         return 0;
170 }
171
172 static MMU*
173 mmualloc(void)
174 {
175         MMU *p;
176         int i, n;
177
178         p = m->mmufree;
179         if(p != nil){
180                 m->mmufree = p->next;
181                 m->mmucount--;
182         } else {
183                 lock(&mmupool);
184                 p = mmupool.free;
185                 if(p != nil){
186                         mmupool.free = p->next;
187                         mmupool.nfree--;
188                 } else {
189                         unlock(&mmupool);
190
191                         n = 256;
192                         p = malloc(n * sizeof(MMU));
193                         if(p == nil)
194                                 panic("mmualloc: out of memory for MMU");
195                         p->page = mallocalign(n * PTSZ, BY2PG, 0, 0);
196                         if(p->page == nil)
197                                 panic("mmualloc: out of memory for MMU pages");
198                         for(i=1; i<n; i++){
199                                 p[i].page = p[i-1].page + (1<<PTSHIFT);
200                                 p[i-1].next = &p[i];
201                         }
202
203                         lock(&mmupool);
204                         p[n-1].next = mmupool.free;
205                         mmupool.free = p->next;
206                         mmupool.nalloc += n;
207                         mmupool.nfree += n-1;
208                 }
209                 unlock(&mmupool);
210         }
211         p->next = nil;
212         return p;
213 }
214
215 static uintptr*
216 mmucreate(uintptr *table, uintptr va, int level, int index)
217 {
218         uintptr *page, flags;
219         MMU *p;
220         
221         flags = PTEWRITE|PTEVALID;
222         if(va < VMAP){
223                 assert(up != nil);
224                 assert((va < USTKTOP) || (va >= KMAP && va < KMAP+KMAPSIZE));
225
226                 p = mmualloc();
227                 p->index = index;
228                 p->level = level;
229                 if(va < USTKTOP){
230                         flags |= PTEUSER;
231                         if(level == PML4E){
232                                 if((p->next = up->mmuhead) == nil)
233                                         up->mmutail = p;
234                                 up->mmuhead = p;
235                                 m->mmumap[index/MAPBITS] |= 1ull<<(index%MAPBITS);
236                         } else {
237                                 up->mmutail->next = p;
238                                 up->mmutail = p;
239                         }
240                         up->mmucount++;
241                 } else {
242                         if(level == PML4E){
243                                 up->kmaptail = p;
244                                 up->kmaphead = p;
245                         } else {
246                                 up->kmaptail->next = p;
247                                 up->kmaptail = p;
248                         }
249                         up->kmapcount++;
250                 }
251                 page = p->page;
252         } else if(conf.mem[0].npage != 0) {
253                 page = mallocalign(PTSZ, BY2PG, 0, 0);
254         } else {
255                 page = rampage();
256         }
257         memset(page, 0, PTSZ);
258         table[index] = PADDR(page) | flags;
259         return page;
260 }
261
262 uintptr*
263 mmuwalk(uintptr* table, uintptr va, int level, int create)
264 {
265         uintptr pte;
266         int i, x;
267
268         x = PTLX(va, 3);
269         for(i = 2; i >= level; i--){
270                 pte = table[x];
271                 if(pte & PTEVALID){
272                         if(pte & PTESIZE)
273                                 return 0;
274                         pte = PPN(pte);
275                         if(pte >= (uintptr)-KZERO)
276                                 table = (void*)(pte + VMAP);
277                         else
278                                 table = (void*)(pte + KZERO);
279                 } else {
280                         if(!create)
281                                 return 0;
282                         table = mmucreate(table, va, i, x);
283                 }
284                 x = PTLX(va, i);
285         }
286         return &table[x];
287 }
288
289 static int
290 ptecount(uintptr va, int level)
291 {
292         return (1<<PTSHIFT) - (va & PGLSZ(level+1)-1) / PGLSZ(level);
293 }
294
295 void
296 pmap(uintptr *pml4, uintptr pa, uintptr va, vlong size)
297 {
298         uintptr *pte, *ptee, flags;
299         int z, l;
300
301         if(size <= 0 || va < VMAP)
302                 panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
303         flags = pa;
304         pa = PPN(pa);
305         flags -= pa;
306         if(va >= KZERO)
307                 flags |= PTEGLOBAL;
308         while(size > 0){
309                 if(size >= PGLSZ(1) && (va % PGLSZ(1)) == 0)
310                         flags |= PTESIZE;
311                 l = (flags & PTESIZE) != 0;
312                 z = PGLSZ(l);
313                 pte = mmuwalk(pml4, va, l, 1);
314                 if(pte == 0){
315                         pte = mmuwalk(pml4, va, ++l, 0);
316                         if(pte && (*pte & PTESIZE)){
317                                 flags |= PTESIZE;
318                                 z = va & (PGLSZ(l)-1);
319                                 va -= z;
320                                 pa -= z;
321                                 size += z;
322                                 continue;
323                         }
324                         panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
325                 }
326                 ptee = pte + ptecount(va, l);
327                 while(size > 0 && pte < ptee){
328                         *pte++ = pa | flags;
329                         pa += z;
330                         va += z;
331                         size -= z;
332                 }
333         }
334 }
335
336 static void
337 mmuzap(void)
338 {
339         uintptr *pte;
340         u64int w;
341         int i, x;
342
343         pte = m->pml4;
344         pte[PTLX(KMAP, 3)] = 0;
345
346         /* common case */
347         pte[PTLX(UTZERO, 3)] = 0;
348         pte[PTLX(USTKTOP-1, 3)] = 0;
349         m->mmumap[PTLX(UTZERO, 3)/MAPBITS] &= ~(1ull<<(PTLX(UTZERO, 3)%MAPBITS));
350         m->mmumap[PTLX(USTKTOP-1, 3)/MAPBITS] &= ~(1ull<<(PTLX(USTKTOP-1, 3)%MAPBITS));
351
352         for(i = 0; i < nelem(m->mmumap); pte += MAPBITS, i++){
353                 if((w = m->mmumap[i]) == 0)
354                         continue;
355                 m->mmumap[i] = 0;
356                 for(x = 0; w != 0; w >>= 1, x++){
357                         if(w & 1)
358                                 pte[x] = 0;
359                 }
360         }
361 }
362
363 static void
364 mmufree(Proc *proc)
365 {
366         MMU *p;
367
368         p = proc->mmutail;
369         if(p == nil)
370                 return;
371         if(m->mmucount+proc->mmucount < 256){
372                 p->next = m->mmufree;
373                 m->mmufree = proc->mmuhead;
374                 m->mmucount += proc->mmucount;
375         } else {
376                 lock(&mmupool);
377                 p->next = mmupool.free;
378                 mmupool.free = proc->mmuhead;
379                 mmupool.nfree += proc->mmucount;
380                 unlock(&mmupool);
381         }
382         proc->mmuhead = proc->mmutail = nil;
383         proc->mmucount = 0;
384 }
385
386 void
387 flushmmu(void)
388 {
389         int x;
390
391         x = splhi();
392         up->newtlb = 1;
393         mmuswitch(up);
394         splx(x);
395 }
396
397 void
398 mmuswitch(Proc *proc)
399 {
400         MMU *p;
401
402         mmuzap();
403         if(proc->newtlb){
404                 mmufree(proc);
405                 proc->newtlb = 0;
406         }
407         if((p = proc->kmaphead) != nil)
408                 m->pml4[PTLX(KMAP, 3)] = PADDR(p->page) | PTEWRITE|PTEVALID;
409         for(p = proc->mmuhead; p != nil && p->level == PML4E; p = p->next){
410                 m->mmumap[p->index/MAPBITS] |= 1ull<<(p->index%MAPBITS);
411                 m->pml4[p->index] = PADDR(p->page) | PTEUSER|PTEWRITE|PTEVALID;
412         }
413         taskswitch((uintptr)proc->kstack+KSTACK);
414 }
415
416 void
417 mmurelease(Proc *proc)
418 {
419         MMU *p;
420
421         mmuzap();
422         if((p = proc->kmaptail) != nil){
423                 if((p->next = proc->mmuhead) == nil)
424                         proc->mmutail = p;
425                 proc->mmuhead = proc->kmaphead;
426                 proc->mmucount += proc->kmapcount;
427
428                 proc->kmaphead = proc->kmaptail = nil;
429                 proc->kmapcount = proc->kmapindex = 0;
430         }
431         mmufree(proc);
432         taskswitch((uintptr)m+MACHSIZE);
433 }
434
435 void
436 putmmu(uintptr va, uintptr pa, Page *)
437 {
438         uintptr *pte, old;
439         int x;
440
441         x = splhi();
442         pte = mmuwalk(m->pml4, va, 0, 1);
443         if(pte == 0)
444                 panic("putmmu: bug: va=%#p pa=%#p", va, pa);
445         old = *pte;
446         *pte = pa | PTEVALID|PTEUSER;
447         splx(x);
448         if(old & PTEVALID)
449                 invlpg(va);
450 }
451
452 /*
453  * Double-check the user MMU.
454  * Error checking only.
455  */
456 void
457 checkmmu(uintptr va, uintptr pa)
458 {
459         uintptr *pte;
460
461         pte = mmuwalk(m->pml4, va, 0, 0);
462         if(pte != 0 && (*pte & PTEVALID) != 0 && PPN(*pte) != pa)
463                 print("%ld %s: va=%#p pa=%#p pte=%#p\n",
464                         up->pid, up->text, va, pa, *pte);
465 }
466
467 uintptr
468 cankaddr(uintptr pa)
469 {
470         if(pa >= -KZERO)
471                 return 0;
472         return -KZERO - pa;
473 }
474
475 KMap*
476 kmap(Page *page)
477 {
478         uintptr *pte, pa, va;
479         int x;
480
481         pa = page->pa;
482         if(cankaddr(pa) != 0)
483                 return (KMap*)KADDR(pa);
484
485         x = splhi();
486         va = KMAP + (((uintptr)up->kmapindex++ << PGSHIFT) & (KMAPSIZE-1));
487         pte = mmuwalk(m->pml4, va, 0, 1);
488         if(pte == 0 || (*pte & PTEVALID) != 0)
489                 panic("kmap: pa=%#p va=%#p", pa, va);
490         *pte = pa | PTEWRITE|PTEVALID;
491         splx(x);
492         invlpg(va);
493         return (KMap*)va;
494 }
495
496 void
497 kunmap(KMap *k)
498 {
499         uintptr *pte, va;
500         int x;
501
502         va = (uintptr)k;
503         if(va >= KZERO)
504                 return;
505
506         x = splhi();
507         pte = mmuwalk(m->pml4, va, 0, 0);
508         if(pte == 0 || (*pte & PTEVALID) == 0)
509                 panic("kunmap: va=%#p", va);
510         *pte = 0;
511         splx(x);
512 }
513
514 /*
515  * Add a device mapping to the vmap range.
516  * note that the VMAP and KZERO PDPs are shared
517  * between processors (see mpstartap) so no
518  * synchronization is being done.
519  */
520 void*
521 vmap(uintptr pa, int size)
522 {
523         uintptr va;
524         int o;
525
526         if(pa+size > VMAPSIZE)
527                 return 0;
528         va = pa+VMAP;
529         /*
530          * might be asking for less than a page.
531          */
532         o = pa & (BY2PG-1);
533         pa -= o;
534         va -= o;
535         size += o;
536         pmap(m->pml4, pa | PTEUNCACHED|PTEWRITE|PTEVALID, va, size);
537         return (void*)(va+o);
538 }
539
540 void
541 vunmap(void *v, int)
542 {
543         paddr(v);       /* will panic on error */
544 }
545
546 /*
547  * mark pages as write combining (used for framebuffer)
548  */
549 void
550 patwc(void *a, int n)
551 {
552         uintptr *pte, mask, attr, va;
553         int z, l;
554         vlong v;
555
556         /* check if pat is usable */
557         if((MACHP(0)->cpuiddx & Pat) == 0
558         || rdmsr(0x277, &v) == -1
559         || ((v >> PATWC*8) & 7) != 1)
560                 return;
561
562         /* set the bits for all pages in range */
563         for(va = (uintptr)a; n > 0; n -= z, va += z){
564                 l = 0;
565                 pte = mmuwalk(m->pml4, va, l, 0);
566                 if(pte == 0)
567                         pte = mmuwalk(m->pml4, va, ++l, 0);
568                 if(pte == 0 || (*pte & PTEVALID) == 0)
569                         panic("patwc: va=%#p", va);
570                 z = PGLSZ(l);
571                 z -= va & (z-1);
572                 mask = l == 0 ? 3<<3 | 1<<7 : 3<<3 | 1<<12;
573                 attr = (((PATWC&3)<<3) | ((PATWC&4)<<5) | ((PATWC&4)<<10));
574                 *pte = (*pte & ~mask) | (attr & mask);
575         }
576 }
577
578 /*
579  * The palloc.pages array and mmupool can be a large chunk
580  * out of the 2GB window above KZERO, so we allocate from
581  * upages and map in the VMAP window before pageinit()
582  */
583 void
584 preallocpages(void)
585 {
586         Pallocmem *pm;
587         uintptr va, base, top;
588         vlong tsize, psize;
589         ulong np, nt;
590         int i;
591
592         np = 0;
593         for(i=0; i<nelem(palloc.mem); i++){
594                 pm = &palloc.mem[i];
595                 np += pm->npage;
596         }
597         nt = np / 50;   /* 2% for mmupool */
598         np -= nt;
599
600         nt = (uvlong)nt*BY2PG / (sizeof(MMU)+PTSZ);
601         tsize = (uvlong)nt * (sizeof(MMU)+PTSZ);
602
603         psize = (uvlong)np * BY2PG;
604         psize += sizeof(Page) + BY2PG;
605         psize = (psize / (sizeof(Page)+BY2PG)) * sizeof(Page);
606
607         psize += tsize;
608         psize = ROUND(psize, PGLSZ(1));
609
610         for(i=0; i<nelem(palloc.mem); i++){
611                 pm = &palloc.mem[i];
612                 base = ROUND(pm->base, PGLSZ(1));
613                 top = pm->base + (uvlong)pm->npage * BY2PG;
614                 if((base + psize) <= VMAPSIZE && (vlong)(top - base) >= psize){
615                         pm->base = base + psize;
616                         pm->npage = (top - pm->base)/BY2PG;
617
618                         va = base + VMAP;
619                         pmap(m->pml4, base | PTEGLOBAL|PTEWRITE|PTEVALID, va, psize);
620
621                         palloc.pages = (void*)(va + tsize);
622
623                         mmupool.nfree = mmupool.nalloc = nt;
624                         mmupool.free = (void*)(va + (uvlong)nt*PTSZ);
625                         for(i=0; i<nt; i++){
626                                 mmupool.free[i].page = (uintptr*)va;
627                                 mmupool.free[i].next = &mmupool.free[i+1];
628                                 va += PTSZ;
629                         }
630                         mmupool.free[i-1].next = nil;
631
632                         break;
633                 }
634         }
635 }