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