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