]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc64/mmu.c
pc, pc64: clear debug watchpoint registers on exec and exit
[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 static void kernelro(void);
77
78 void
79 mmuinit(void)
80 {
81         uintptr x;
82         vlong v;
83         int i;
84
85         /* zap double map done by l.s */ 
86         m->pml4[512] = 0;
87         m->pml4[0] = 0;
88
89         if(m->machno == 0)
90                 kernelro();
91
92         m->tss = mallocz(sizeof(Tss), 1);
93         if(m->tss == nil)
94                 panic("mmuinit: no memory for Tss");
95         m->tss->iomap = 0xDFFF;
96         for(i=0; i<14; i+=2){
97                 x = (uintptr)m + MACHSIZE;
98                 m->tss->ist[i] = x;
99                 m->tss->ist[i+1] = x>>32;
100         }
101
102         /*
103          * We used to keep the GDT in the Mach structure, but it
104          * turns out that that slows down access to the rest of the
105          * page.  Since the Mach structure is accessed quite often,
106          * it pays off anywhere from a factor of 1.25 to 2 on real
107          * hardware to separate them (the AMDs are more sensitive
108          * than Intels in this regard).  Under VMware it pays off
109          * a factor of about 10 to 100.
110          */
111         memmove(m->gdt, gdt, sizeof gdt);
112
113         x = (uintptr)m->tss;
114         m->gdt[TSSSEG+0].d0 = (x<<16)|(sizeof(Tss)-1);
115         m->gdt[TSSSEG+0].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
116         m->gdt[TSSSEG+1].d0 = x>>32;
117         m->gdt[TSSSEG+1].d1 = 0;
118
119         loadptr(sizeof(gdt)-1, (uintptr)m->gdt, lgdt);
120         loadptr(sizeof(Segdesc)*512-1, (uintptr)IDTADDR, lidt);
121         taskswitch((uintptr)m + MACHSIZE);
122         ltr(TSSSEL);
123
124         wrmsr(FSbase, 0ull);
125         wrmsr(GSbase, (uvlong)&machp[m->machno]);
126         wrmsr(KernelGSbase, 0ull);
127
128         /* enable syscall extension */
129         rdmsr(Efer, &v);
130         v |= 1ull;
131         wrmsr(Efer, v);
132
133         wrmsr(Star, ((uvlong)UE32SEL << 48) | ((uvlong)KESEL << 32));
134         wrmsr(Lstar, (uvlong)syscallentry);
135         wrmsr(Sfmask, 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 {
253                 page = rampage();
254         }
255         memset(page, 0, PTSZ);
256         table[index] = PADDR(page) | flags;
257         return page;
258 }
259
260 uintptr*
261 mmuwalk(uintptr* table, uintptr va, int level, int create)
262 {
263         uintptr pte;
264         int i, x;
265
266         x = PTLX(va, 3);
267         for(i = 2; i >= level; i--){
268                 pte = table[x];
269                 if(pte & PTEVALID){
270                         if(pte & PTESIZE)
271                                 return 0;
272                         pte = PPN(pte);
273                         if(pte >= (uintptr)-KZERO)
274                                 table = (void*)(pte + VMAP);
275                         else
276                                 table = (void*)(pte + KZERO);
277                 } else {
278                         if(!create)
279                                 return 0;
280                         table = mmucreate(table, va, i, x);
281                 }
282                 x = PTLX(va, i);
283         }
284         return &table[x];
285 }
286
287 static int
288 ptecount(uintptr va, int level)
289 {
290         return (1<<PTSHIFT) - (va & PGLSZ(level+1)-1) / PGLSZ(level);
291 }
292
293 static void
294 ptesplit(uintptr* table, uintptr va)
295 {
296         uintptr *pte, pa, off;
297
298         pte = mmuwalk(table, va, 1, 0);
299         if(pte == nil || (*pte & PTESIZE) == 0 || (va & PGLSZ(1)-1) == 0)
300                 return;
301         table = rampage();
302         if(table == nil)
303                 panic("ptesplit: out of memory\n");
304         va &= -PGLSZ(1);
305         pa = *pte & ~PTESIZE;
306         for(off = 0; off < PGLSZ(1); off += PGLSZ(0))
307                 table[PTLX(va + off, 0)] = pa + off;
308         *pte = PADDR(table) | PTEVALID|PTEWRITE;
309         invlpg(va);
310 }
311
312 /*
313  * map kernel text segment readonly
314  * and everything else no-execute.
315  */
316 static void
317 kernelro(void)
318 {
319         uintptr *pte, psz, va;
320
321         ptesplit(m->pml4, APBOOTSTRAP);
322         ptesplit(m->pml4, KTZERO);
323         ptesplit(m->pml4, (uintptr)etext-1);
324
325         for(va = KZERO; va != 0; va += psz){
326                 psz = PGLSZ(0);
327                 pte = mmuwalk(m->pml4, va, 0, 0);
328                 if(pte == nil){
329                         if(va & PGLSZ(1)-1)
330                                 continue;
331                         pte = mmuwalk(m->pml4, va, 1, 0);
332                         if(pte == nil)
333                                 continue;
334                         psz = PGLSZ(1);
335                 }
336                 if((*pte & PTEVALID) == 0)
337                         continue;
338                 if(va >= KTZERO && va < (uintptr)etext)
339                         *pte &= ~PTEWRITE;
340                 else if(va != (APBOOTSTRAP & -BY2PG))
341                         *pte |= PTENOEXEC;
342                 invlpg(va);
343         }
344 }
345
346 void
347 pmap(uintptr *pml4, uintptr pa, uintptr va, vlong size)
348 {
349         uintptr *pte, *ptee, flags;
350         int z, l;
351
352         if(size <= 0 || va < VMAP)
353                 panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
354         flags = pa;
355         pa = PPN(pa);
356         flags -= pa;
357         if(va >= KZERO)
358                 flags |= PTEGLOBAL;
359         while(size > 0){
360                 if(size >= PGLSZ(1) && (va % PGLSZ(1)) == 0)
361                         flags |= PTESIZE;
362                 l = (flags & PTESIZE) != 0;
363                 z = PGLSZ(l);
364                 pte = mmuwalk(pml4, va, l, 1);
365                 if(pte == 0){
366                         pte = mmuwalk(pml4, va, ++l, 0);
367                         if(pte && (*pte & PTESIZE)){
368                                 flags |= PTESIZE;
369                                 z = va & (PGLSZ(l)-1);
370                                 va -= z;
371                                 pa -= z;
372                                 size += z;
373                                 continue;
374                         }
375                         panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
376                 }
377                 ptee = pte + ptecount(va, l);
378                 while(size > 0 && pte < ptee){
379                         *pte++ = pa | flags;
380                         pa += z;
381                         va += z;
382                         size -= z;
383                 }
384         }
385 }
386
387 static void
388 mmuzap(void)
389 {
390         uintptr *pte;
391         u64int w;
392         int i, x;
393
394         pte = m->pml4;
395         pte[PTLX(KMAP, 3)] = 0;
396
397         /* common case */
398         pte[PTLX(UTZERO, 3)] = 0;
399         pte[PTLX(USTKTOP-1, 3)] = 0;
400         m->mmumap[PTLX(UTZERO, 3)/MAPBITS] &= ~(1ull<<(PTLX(UTZERO, 3)%MAPBITS));
401         m->mmumap[PTLX(USTKTOP-1, 3)/MAPBITS] &= ~(1ull<<(PTLX(USTKTOP-1, 3)%MAPBITS));
402
403         for(i = 0; i < nelem(m->mmumap); pte += MAPBITS, i++){
404                 if((w = m->mmumap[i]) == 0)
405                         continue;
406                 m->mmumap[i] = 0;
407                 for(x = 0; w != 0; w >>= 1, x++){
408                         if(w & 1)
409                                 pte[x] = 0;
410                 }
411         }
412 }
413
414 static void
415 mmufree(Proc *proc)
416 {
417         MMU *p;
418
419         p = proc->mmutail;
420         if(p == nil)
421                 return;
422         if(m->mmucount+proc->mmucount < 256){
423                 p->next = m->mmufree;
424                 m->mmufree = proc->mmuhead;
425                 m->mmucount += proc->mmucount;
426         } else {
427                 lock(&mmupool);
428                 p->next = mmupool.free;
429                 mmupool.free = proc->mmuhead;
430                 mmupool.nfree += proc->mmucount;
431                 unlock(&mmupool);
432         }
433         proc->mmuhead = proc->mmutail = nil;
434         proc->mmucount = 0;
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 mmuswitch(Proc *proc)
450 {
451         MMU *p;
452
453         mmuzap();
454         if(proc->newtlb){
455                 mmufree(proc);
456                 proc->newtlb = 0;
457         }
458         if((p = proc->kmaphead) != nil)
459                 m->pml4[PTLX(KMAP, 3)] = PADDR(p->page) | PTEWRITE|PTEVALID;
460         for(p = proc->mmuhead; p != nil && p->level == PML4E; p = p->next){
461                 m->mmumap[p->index/MAPBITS] |= 1ull<<(p->index%MAPBITS);
462                 m->pml4[p->index] = PADDR(p->page) | PTEUSER|PTEWRITE|PTEVALID;
463         }
464         taskswitch((uintptr)proc->kstack+KSTACK);
465 }
466
467 void
468 mmurelease(Proc *proc)
469 {
470         MMU *p;
471
472         mmuzap();
473         if((p = proc->kmaptail) != nil){
474                 if((p->next = proc->mmuhead) == nil)
475                         proc->mmutail = p;
476                 proc->mmuhead = proc->kmaphead;
477                 proc->mmucount += proc->kmapcount;
478
479                 proc->kmaphead = proc->kmaptail = nil;
480                 proc->kmapcount = proc->kmapindex = 0;
481         }
482         mmufree(proc);
483         taskswitch((uintptr)m+MACHSIZE);
484 }
485
486 void
487 putmmu(uintptr va, uintptr pa, Page *)
488 {
489         uintptr *pte, old;
490         int x;
491
492         x = splhi();
493         pte = mmuwalk(m->pml4, va, 0, 1);
494         if(pte == 0)
495                 panic("putmmu: bug: va=%#p pa=%#p", va, pa);
496         old = *pte;
497         *pte = pa | PTEUSER;
498         splx(x);
499         if(old & PTEVALID)
500                 invlpg(va);
501 }
502
503 /*
504  * Double-check the user MMU.
505  * Error checking only.
506  */
507 void
508 checkmmu(uintptr va, uintptr pa)
509 {
510         uintptr *pte;
511
512         pte = mmuwalk(m->pml4, va, 0, 0);
513         if(pte != 0 && (*pte & PTEVALID) != 0 && PPN(*pte) != pa)
514                 print("%ld %s: va=%#p pa=%#p pte=%#p\n",
515                         up->pid, up->text, va, pa, *pte);
516 }
517
518 uintptr
519 cankaddr(uintptr pa)
520 {
521         if(pa >= -KZERO)
522                 return 0;
523         return -KZERO - pa;
524 }
525
526 KMap*
527 kmap(Page *page)
528 {
529         uintptr *pte, pa, va;
530         int x;
531
532         pa = page->pa;
533         if(cankaddr(pa) != 0)
534                 return (KMap*)KADDR(pa);
535
536         x = splhi();
537         va = KMAP + (((uintptr)up->kmapindex++ << PGSHIFT) & (KMAPSIZE-1));
538         pte = mmuwalk(m->pml4, va, 0, 1);
539         if(pte == 0 || (*pte & PTEVALID) != 0)
540                 panic("kmap: pa=%#p va=%#p", pa, va);
541         *pte = pa | PTEWRITE|PTENOEXEC|PTEVALID;
542         splx(x);
543         invlpg(va);
544         return (KMap*)va;
545 }
546
547 void
548 kunmap(KMap *k)
549 {
550         uintptr *pte, va;
551         int x;
552
553         va = (uintptr)k;
554         if(va >= KZERO)
555                 return;
556
557         x = splhi();
558         pte = mmuwalk(m->pml4, va, 0, 0);
559         if(pte == 0 || (*pte & PTEVALID) == 0)
560                 panic("kunmap: va=%#p", va);
561         *pte = 0;
562         splx(x);
563 }
564
565 /*
566  * Add a device mapping to the vmap range.
567  * note that the VMAP and KZERO PDPs are shared
568  * between processors (see mpstartap) so no
569  * synchronization is being done.
570  */
571 void*
572 vmap(uintptr pa, int size)
573 {
574         uintptr va;
575         int o;
576
577         if(pa+size > VMAPSIZE)
578                 return 0;
579         va = pa+VMAP;
580         /*
581          * might be asking for less than a page.
582          */
583         o = pa & (BY2PG-1);
584         pa -= o;
585         va -= o;
586         size += o;
587         pmap(m->pml4, pa | PTEUNCACHED|PTEWRITE|PTENOEXEC|PTEVALID, va, size);
588         return (void*)(va+o);
589 }
590
591 void
592 vunmap(void *v, int)
593 {
594         paddr(v);       /* will panic on error */
595 }
596
597 /*
598  * mark pages as write combining (used for framebuffer)
599  */
600 void
601 patwc(void *a, int n)
602 {
603         uintptr *pte, mask, attr, va;
604         int z, l;
605         vlong v;
606
607         /* check if pat is usable */
608         if((MACHP(0)->cpuiddx & Pat) == 0
609         || rdmsr(0x277, &v) == -1
610         || ((v >> PATWC*8) & 7) != 1)
611                 return;
612
613         /* set the bits for all pages in range */
614         for(va = (uintptr)a; n > 0; n -= z, va += z){
615                 l = 0;
616                 pte = mmuwalk(m->pml4, va, l, 0);
617                 if(pte == 0)
618                         pte = mmuwalk(m->pml4, va, ++l, 0);
619                 if(pte == 0 || (*pte & PTEVALID) == 0)
620                         panic("patwc: va=%#p", va);
621                 z = PGLSZ(l);
622                 z -= va & (z-1);
623                 mask = l == 0 ? 3<<3 | 1<<7 : 3<<3 | 1<<12;
624                 attr = (((PATWC&3)<<3) | ((PATWC&4)<<5) | ((PATWC&4)<<10));
625                 *pte = (*pte & ~mask) | (attr & mask);
626         }
627 }
628
629 /*
630  * The palloc.pages array and mmupool can be a large chunk
631  * out of the 2GB window above KZERO, so we allocate from
632  * upages and map in the VMAP window before pageinit()
633  */
634 void
635 preallocpages(void)
636 {
637         Pallocmem *pm;
638         uintptr va, base, top;
639         vlong tsize, psize;
640         ulong np, nt;
641         int i;
642
643         np = 0;
644         for(i=0; i<nelem(palloc.mem); i++){
645                 pm = &palloc.mem[i];
646                 np += pm->npage;
647         }
648         nt = np / 50;   /* 2% for mmupool */
649         np -= nt;
650
651         nt = (uvlong)nt*BY2PG / (sizeof(MMU)+PTSZ);
652         tsize = (uvlong)nt * (sizeof(MMU)+PTSZ);
653
654         psize = (uvlong)np * BY2PG;
655         psize += sizeof(Page) + BY2PG;
656         psize = (psize / (sizeof(Page)+BY2PG)) * sizeof(Page);
657
658         psize += tsize;
659         psize = ROUND(psize, PGLSZ(1));
660
661         for(i=0; i<nelem(palloc.mem); i++){
662                 pm = &palloc.mem[i];
663                 base = ROUND(pm->base, PGLSZ(1));
664                 top = pm->base + (uvlong)pm->npage * BY2PG;
665                 if((base + psize) <= VMAPSIZE && (vlong)(top - base) >= psize){
666                         pm->base = base + psize;
667                         pm->npage = (top - pm->base)/BY2PG;
668
669                         va = base + VMAP;
670                         pmap(m->pml4, base | PTEGLOBAL|PTEWRITE|PTENOEXEC|PTEVALID, va, psize);
671
672                         palloc.pages = (void*)(va + tsize);
673
674                         mmupool.nfree = mmupool.nalloc = nt;
675                         mmupool.free = (void*)(va + (uvlong)nt*PTSZ);
676                         for(i=0; i<nt; i++){
677                                 mmupool.free[i].page = (uintptr*)va;
678                                 mmupool.free[i].next = &mmupool.free[i+1];
679                                 va += PTSZ;
680                         }
681                         mmupool.free[i-1].next = nil;
682
683                         break;
684                 }
685         }
686 }