]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc64/mmu.c
pat write combinding support for 386 kernel, honor cpuid bits
[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 < TSTKTOP) || (va >= KMAP && va < KMAP+KMAPSIZE));
225
226                 p = mmualloc();
227                 p->index = index;
228                 p->level = level;
229                 if(va < TSTKTOP){
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                         table = KADDR(PPN(pte));
275                 } else {
276                         if(!create)
277                                 return 0;
278                         table = mmucreate(table, va, i, x);
279                 }
280                 x = PTLX(va, i);
281         }
282         return &table[x];
283 }
284
285 static int
286 ptecount(uintptr va, int level)
287 {
288         return (1<<PTSHIFT) - (va & PGLSZ(level+1)-1) / PGLSZ(level);
289 }
290
291 void
292 pmap(uintptr *pml4, uintptr pa, uintptr va, vlong size)
293 {
294         uintptr *pte, *ptee, flags;
295         int z, l;
296
297         if(size <= 0 || va < VMAP)
298                 panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
299         flags = pa;
300         pa = PPN(pa);
301         flags -= pa;
302         if(va >= KZERO)
303                 flags |= PTEGLOBAL;
304         while(size > 0){
305                 if(size >= PGLSZ(1) && (va % PGLSZ(1)) == 0)
306                         flags |= PTESIZE;
307                 l = (flags & PTESIZE) != 0;
308                 z = PGLSZ(l);
309                 pte = mmuwalk(pml4, va, l, 1);
310                 if(pte == 0){
311                         pte = mmuwalk(pml4, va, ++l, 0);
312                         if(pte && (*pte & PTESIZE)){
313                                 flags |= PTESIZE;
314                                 z = va & (PGLSZ(l)-1);
315                                 va -= z;
316                                 pa -= z;
317                                 size += z;
318                                 continue;
319                         }
320                         panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
321                 }
322                 ptee = pte + ptecount(va, l);
323                 while(size > 0 && pte < ptee){
324                         *pte++ = pa | flags;
325                         pa += z;
326                         va += z;
327                         size -= z;
328                 }
329         }
330 }
331
332 static void
333 mmuzap(void)
334 {
335         uintptr *pte;
336         u64int w;
337         int i, x;
338
339         pte = m->pml4;
340         pte[PTLX(KMAP, 3)] = 0;
341
342         /* common case */
343         pte[PTLX(UTZERO, 3)] = 0;
344         pte[PTLX(TSTKTOP, 3)] = 0;
345         m->mmumap[PTLX(UTZERO, 3)/MAPBITS] &= ~(1ull<<(PTLX(UTZERO, 3)%MAPBITS));
346         m->mmumap[PTLX(TSTKTOP, 3)/MAPBITS] &= ~(1ull<<(PTLX(TSTKTOP, 3)%MAPBITS));
347
348         for(i = 0; i < nelem(m->mmumap); pte += MAPBITS, i++){
349                 if((w = m->mmumap[i]) == 0)
350                         continue;
351                 m->mmumap[i] = 0;
352                 for(x = 0; w != 0; w >>= 1, x++){
353                         if(w & 1)
354                                 pte[x] = 0;
355                 }
356         }
357 }
358
359 static void
360 mmufree(Proc *proc)
361 {
362         MMU *p;
363
364         p = proc->mmutail;
365         if(p == nil)
366                 return;
367         if(m->mmucount+proc->mmucount < 256){
368                 p->next = m->mmufree;
369                 m->mmufree = proc->mmuhead;
370                 m->mmucount += proc->mmucount;
371         } else {
372                 lock(&mmupool);
373                 p->next = mmupool.free;
374                 mmupool.free = proc->mmuhead;
375                 mmupool.nfree += proc->mmucount;
376                 unlock(&mmupool);
377         }
378         proc->mmuhead = proc->mmutail = nil;
379         proc->mmucount = 0;
380 }
381
382 void
383 flushmmu(void)
384 {
385         int x;
386
387         x = splhi();
388         up->newtlb = 1;
389         mmuswitch(up);
390         splx(x);
391 }
392
393 void
394 mmuswitch(Proc *proc)
395 {
396         MMU *p;
397
398         mmuzap();
399         if(proc->newtlb){
400                 mmufree(proc);
401                 proc->newtlb = 0;
402         }
403         if((p = proc->kmaphead) != nil)
404                 m->pml4[PTLX(KMAP, 3)] = PADDR(p->page) | PTEWRITE|PTEVALID;
405         for(p = proc->mmuhead; p != nil && p->level == PML4E; p = p->next){
406                 m->mmumap[p->index/MAPBITS] |= 1ull<<(p->index%MAPBITS);
407                 m->pml4[p->index] = PADDR(p->page) | PTEUSER|PTEWRITE|PTEVALID;
408         }
409         taskswitch((uintptr)proc->kstack+KSTACK);
410 }
411
412 void
413 mmurelease(Proc *proc)
414 {
415         MMU *p;
416
417         mmuzap();
418         if((p = proc->kmaptail) != nil){
419                 if((p->next = proc->mmuhead) == nil)
420                         proc->mmutail = p;
421                 proc->mmuhead = proc->kmaphead;
422                 proc->mmucount += proc->kmapcount;
423
424                 proc->kmaphead = proc->kmaptail = nil;
425                 proc->kmapcount = proc->kmapindex = 0;
426         }
427         mmufree(proc);
428         taskswitch((uintptr)m+MACHSIZE);
429 }
430
431 void
432 putmmu(uintptr va, uintptr pa, Page *)
433 {
434         uintptr *pte, old;
435         int x;
436
437         x = splhi();
438         pte = mmuwalk(m->pml4, va, 0, 1);
439         if(pte == 0)
440                 panic("putmmu: bug: va=%#p pa=%#p", va, pa);
441         old = *pte;
442         *pte = pa | PTEVALID|PTEUSER;
443         splx(x);
444         if(old & PTEVALID)
445                 invlpg(va);
446 }
447
448 /*
449  * Double-check the user MMU.
450  * Error checking only.
451  */
452 void
453 checkmmu(uintptr va, uintptr pa)
454 {
455         uintptr *pte;
456
457         pte = mmuwalk(m->pml4, va, 0, 0);
458         if(pte != 0 && (*pte & PTEVALID) != 0 && PPN(*pte) != pa)
459                 print("%ld %s: va=%#p pa=%#p pte=%#p\n",
460                         up->pid, up->text, va, pa, *pte);
461 }
462
463 uintptr
464 cankaddr(uintptr pa)
465 {
466         if(pa >= -KZERO)
467                 return 0;
468         return -KZERO - pa;
469 }
470
471 void
472 countpagerefs(ulong *ref, int print)
473 {
474         USED(ref, print);
475 }
476
477 KMap*
478 kmap(Page *page)
479 {
480         uintptr *pte, pa, va;
481         int x;
482
483         pa = page->pa;
484         if(cankaddr(pa) != 0)
485                 return (KMap*)KADDR(pa);
486
487         x = splhi();
488         va = KMAP + ((uintptr)up->kmapindex << PGSHIFT);
489         pte = mmuwalk(m->pml4, va, 0, 1);
490         if(pte == 0 || *pte & PTEVALID)
491                 panic("kmap: pa=%#p va=%#p", pa, va);
492         *pte = pa | PTEWRITE|PTEVALID;
493         up->kmapindex = (up->kmapindex + 1) % (1<<PTSHIFT);
494         if(up->kmapindex == 0)
495                 mmuflushtlb();
496         splx(x);
497         return (KMap*)va;
498 }
499
500 void
501 kunmap(KMap *k)
502 {
503         uintptr *pte, va;
504         int x;
505
506         va = (uintptr)k;
507         if(va >= KZERO)
508                 return;
509
510         x = splhi();
511         pte = mmuwalk(m->pml4, va, 0, 0);
512         if(pte == 0 || (*pte & PTEVALID) == 0)
513                 panic("kunmap: va=%#p", va);
514         *pte = 0;
515         splx(x);
516 }
517
518 /*
519  * Add a device mapping to the vmap range.
520  * note that the VMAP and KZERO PDPs are shared
521  * between processors (see mpstartap) so no
522  * synchronization is being done.
523  */
524 void*
525 vmap(uintptr pa, int size)
526 {
527         uintptr va;
528         int o;
529
530         if(pa+size > VMAPSIZE)
531                 return 0;
532         va = pa+VMAP;
533         /*
534          * might be asking for less than a page.
535          */
536         o = pa & (BY2PG-1);
537         pa -= o;
538         va -= o;
539         size += o;
540         pmap(m->pml4, pa | PTEUNCACHED|PTEWRITE|PTEVALID, va, size);
541         return (void*)(va+o);
542 }
543
544 void
545 vunmap(void *v, int)
546 {
547         paddr(v);       /* will panic on error */
548 }
549
550 /*
551  * mark pages as write combining (used for framebuffer)
552  */
553 void
554 patwc(void *a, int n)
555 {
556         uintptr *pte, mask, attr, va;
557         int z, l;
558         vlong v;
559
560         /* check if pat is usable */
561         if((MACHP(0)->cpuiddx & Pat) == 0
562         || rdmsr(0x277, &v) == -1
563         || ((v >> PATWC*8) & 7) != 1)
564                 return;
565
566         /* set the bits for all pages in range */
567         for(va = (uintptr)a; n > 0; n -= z, va += z){
568                 l = 0;
569                 pte = mmuwalk(m->pml4, va, l, 0);
570                 if(pte == 0)
571                         pte = mmuwalk(m->pml4, va, ++l, 0);
572                 if(pte == 0 || (*pte & PTEVALID) == 0)
573                         panic("patwc: va=%#p", va);
574                 z = PGLSZ(l);
575                 z -= va & (z-1);
576                 mask = l == 0 ? 3<<3 | 1<<7 : 3<<3 | 1<<12;
577                 attr = (((PATWC&3)<<3) | ((PATWC&4)<<5) | ((PATWC&4)<<10));
578                 *pte = (*pte & ~mask) | (attr & mask);
579         }
580 }