]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc64/mmu.c
pc64: use unsigned long for kmapindex and mmu counts avoiding signed integer division
[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 /* level */
35 enum {
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 void
74 mmuinit(void)
75 {
76         uintptr x;
77         vlong v;
78         int i;
79
80         /* zap double map done by l.s */ 
81         m->pml4[512] = 0;
82         m->pml4[0] = 0;
83
84         m->tss = mallocz(sizeof(Tss), 1);
85         if(m->tss == nil)
86                 panic("mmuinit: no memory for Tss");
87         m->tss->iomap = 0xDFFF;
88         for(i=0; i<14; i+=2){
89                 x = (uintptr)m + MACHSIZE;
90                 m->tss->ist[i] = x;
91                 m->tss->ist[i+1] = x>>32;
92         }
93
94         /*
95          * We used to keep the GDT in the Mach structure, but it
96          * turns out that that slows down access to the rest of the
97          * page.  Since the Mach structure is accessed quite often,
98          * it pays off anywhere from a factor of 1.25 to 2 on real
99          * hardware to separate them (the AMDs are more sensitive
100          * than Intels in this regard).  Under VMware it pays off
101          * a factor of about 10 to 100.
102          */
103         memmove(m->gdt, gdt, sizeof gdt);
104
105         x = (uintptr)m->tss;
106         m->gdt[TSSSEG+0].d0 = (x<<16)|(sizeof(Tss)-1);
107         m->gdt[TSSSEG+0].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
108         m->gdt[TSSSEG+1].d0 = x>>32;
109         m->gdt[TSSSEG+1].d1 = 0;
110
111         loadptr(sizeof(gdt)-1, (uintptr)m->gdt, lgdt);
112         loadptr(sizeof(Segdesc)*512-1, (uintptr)IDTADDR, lidt);
113         taskswitch((uintptr)m + MACHSIZE);
114         ltr(TSSSEL);
115
116         wrmsr(0xc0000100, 0ull);        /* 64 bit fsbase */
117         wrmsr(0xc0000101, (uvlong)&machp[m->machno]);   /* 64 bit gsbase */
118         wrmsr(0xc0000102, 0ull);        /* kernel gs base */
119
120         /* enable syscall extension */
121         rdmsr(0xc0000080, &v);
122         v |= 1ull;
123         wrmsr(0xc0000080, v);
124
125         /* IA32_STAR */
126         wrmsr(0xc0000081, ((uvlong)UE32SEL << 48) | ((uvlong)KESEL << 32));
127
128         /* IA32_LSTAR */
129         wrmsr(0xc0000082, (uvlong)syscallentry);
130
131         /* SYSCALL flags mask */
132         wrmsr(0xc0000084, 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 < TSTKTOP) || (va >= KMAP && va < KMAP+KMAPSIZE));
214
215                 p = mmualloc();
216                 p->index = index;
217                 p->level = level;
218                 if(va < TSTKTOP){
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 if(conf.mem[0].npage != 0) {
242                 page = mallocalign(PTSZ, BY2PG, 0, 0);
243         } else {
244                 page = rampage();
245         }
246         memset(page, 0, PTSZ);
247         table[index] = PADDR(page) | flags;
248         return page;
249 }
250
251 uintptr*
252 mmuwalk(uintptr* table, uintptr va, int level, int create)
253 {
254         uintptr pte;
255         int i, x;
256
257         x = PTLX(va, 3);
258         for(i = 2; i >= level; i--){
259                 pte = table[x];
260                 if(pte & PTEVALID){
261                         if(pte & PTESIZE)
262                                 return 0;
263                         table = KADDR(PPN(pte));
264                 } else {
265                         if(!create)
266                                 return 0;
267                         table = mmucreate(table, va, i, x);
268                 }
269                 x = PTLX(va, i);
270         }
271         return &table[x];
272 }
273
274 static int
275 ptecount(uintptr va, int level)
276 {
277         return (1<<PTSHIFT) - (va & PGLSZ(level+1)-1) / PGLSZ(level);
278 }
279
280 void
281 pmap(uintptr *pml4, uintptr pa, uintptr va, vlong size)
282 {
283         uintptr *pte, *ptee, flags;
284         int z, l;
285
286         if(size <= 0 || va < VMAP)
287                 panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
288         flags = pa;
289         pa = PPN(pa);
290         flags -= pa;
291         if(va >= KZERO)
292                 flags |= PTEGLOBAL;
293         while(size > 0){
294                 if(size >= PGLSZ(1) && (va % PGLSZ(1)) == 0)
295                         flags |= PTESIZE;
296                 l = (flags & PTESIZE) != 0;
297                 z = PGLSZ(l);
298                 pte = mmuwalk(pml4, va, l, 1);
299                 if(pte == 0){
300                         pte = mmuwalk(pml4, va, ++l, 0);
301                         if(pte && (*pte & PTESIZE)){
302                                 flags |= PTESIZE;
303                                 z = va & (PGLSZ(l)-1);
304                                 va -= z;
305                                 pa -= z;
306                                 size += z;
307                                 continue;
308                         }
309                         panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
310                 }
311                 ptee = pte + ptecount(va, l);
312                 while(size > 0 && pte < ptee){
313                         *pte++ = pa | flags;
314                         pa += z;
315                         va += z;
316                         size -= z;
317                 }
318         }
319 }
320
321 static void
322 mmuzap(void)
323 {
324         uintptr *pte;
325         u64int w;
326         int i, x;
327
328         pte = m->pml4;
329         pte[PTLX(KMAP, 3)] = 0;
330
331         /* common case */
332         pte[PTLX(UTZERO, 3)] = 0;
333         pte[PTLX(TSTKTOP, 3)] = 0;
334         m->mmumap[PTLX(UTZERO, 3)/MAPBITS] &= ~(1ull<<(PTLX(UTZERO, 3)%MAPBITS));
335         m->mmumap[PTLX(TSTKTOP, 3)/MAPBITS] &= ~(1ull<<(PTLX(TSTKTOP, 3)%MAPBITS));
336
337         for(i = 0; i < nelem(m->mmumap); pte += MAPBITS, i++){
338                 if((w = m->mmumap[i]) == 0)
339                         continue;
340                 m->mmumap[i] = 0;
341                 for(x = 0; w != 0; w >>= 1, x++){
342                         if(w & 1)
343                                 pte[x] = 0;
344                 }
345         }
346 }
347
348 static void
349 mmufree(Proc *proc)
350 {
351         MMU *p;
352
353         p = proc->mmutail;
354         if(p == nil)
355                 return;
356         if(m->mmucount+proc->mmucount < 256){
357                 p->next = m->mmufree;
358                 m->mmufree = proc->mmuhead;
359                 m->mmucount += proc->mmucount;
360         } else {
361                 lock(&mmupool);
362                 p->next = mmupool.free;
363                 mmupool.free = proc->mmuhead;
364                 mmupool.nfree += proc->mmucount;
365                 unlock(&mmupool);
366         }
367         proc->mmuhead = proc->mmutail = nil;
368         proc->mmucount = 0;
369 }
370
371 void
372 flushmmu(void)
373 {
374         int x;
375
376         x = splhi();
377         up->newtlb = 1;
378         mmuswitch(up);
379         splx(x);
380 }
381
382 void
383 mmuswitch(Proc *proc)
384 {
385         MMU *p;
386
387         mmuzap();
388         if(proc->newtlb){
389                 mmufree(proc);
390                 proc->newtlb = 0;
391         }
392         if((p = proc->kmaphead) != nil)
393                 m->pml4[PTLX(KMAP, 3)] = PADDR(p->page) | PTEWRITE|PTEVALID;
394         for(p = proc->mmuhead; p != nil && p->level == PML4E; p = p->next){
395                 m->mmumap[p->index/MAPBITS] |= 1ull<<(p->index%MAPBITS);
396                 m->pml4[p->index] = PADDR(p->page) | PTEUSER|PTEWRITE|PTEVALID;
397         }
398         taskswitch((uintptr)proc->kstack+KSTACK);
399 }
400
401 void
402 mmurelease(Proc *proc)
403 {
404         MMU *p;
405
406         mmuzap();
407         if((p = proc->kmaptail) != nil){
408                 if((p->next = proc->mmuhead) == nil)
409                         proc->mmutail = p;
410                 proc->mmuhead = proc->kmaphead;
411                 proc->mmucount += proc->kmapcount;
412
413                 proc->kmaphead = proc->kmaptail = nil;
414                 proc->kmapcount = proc->kmapindex = 0;
415         }
416         mmufree(proc);
417         taskswitch((uintptr)m+MACHSIZE);
418 }
419
420 void
421 putmmu(uintptr va, uintptr pa, Page *)
422 {
423         uintptr *pte, old;
424         int x;
425
426         x = splhi();
427         pte = mmuwalk(m->pml4, va, 0, 1);
428         if(pte == 0)
429                 panic("putmmu: bug: va=%#p pa=%#p", va, pa);
430         old = *pte;
431         *pte = pa | PTEVALID|PTEUSER;
432         splx(x);
433         if(old & PTEVALID)
434                 invlpg(va);
435 }
436
437 /*
438  * Double-check the user MMU.
439  * Error checking only.
440  */
441 void
442 checkmmu(uintptr va, uintptr pa)
443 {
444         uintptr *pte;
445
446         pte = mmuwalk(m->pml4, va, 0, 0);
447         if(pte != 0 && (*pte & PTEVALID) != 0 && PPN(*pte) != pa)
448                 print("%ld %s: va=%#p pa=%#p pte=%#p\n",
449                         up->pid, up->text, va, pa, *pte);
450 }
451
452 uintptr
453 cankaddr(uintptr pa)
454 {
455         if(pa >= -KZERO)
456                 return 0;
457         return -KZERO - pa;
458 }
459
460 void
461 countpagerefs(ulong *ref, int print)
462 {
463         USED(ref, print);
464 }
465
466 KMap*
467 kmap(Page *page)
468 {
469         uintptr *pte, pa, va;
470         int x;
471
472         pa = page->pa;
473         if(cankaddr(pa) != 0)
474                 return (KMap*)KADDR(pa);
475
476         x = splhi();
477         va = KMAP + ((uintptr)up->kmapindex << PGSHIFT);
478         pte = mmuwalk(m->pml4, va, 0, 1);
479         if(pte == 0 || *pte & PTEVALID)
480                 panic("kmap: pa=%#p va=%#p", pa, va);
481         *pte = pa | PTEWRITE|PTEVALID;
482         up->kmapindex = (up->kmapindex + 1) % (1<<PTSHIFT);
483         if(up->kmapindex == 0)
484                 mmuflushtlb();
485         splx(x);
486         return (KMap*)va;
487 }
488
489 void
490 kunmap(KMap *k)
491 {
492         uintptr *pte, va;
493         int x;
494
495         va = (uintptr)k;
496         if(va >= KZERO)
497                 return;
498
499         x = splhi();
500         pte = mmuwalk(m->pml4, va, 0, 0);
501         if(pte == 0 || (*pte & PTEVALID) == 0)
502                 panic("kunmap: va=%#p", va);
503         *pte = 0;
504         splx(x);
505 }
506
507 /*
508  * Add a device mapping to the vmap range.
509  */
510 void*
511 vmap(uintptr pa, int size)
512 {
513         uintptr va;
514         int o;
515
516         va = pa+VMAP;
517         /*
518          * might be asking for less than a page.
519          */
520         o = pa & (BY2PG-1);
521         pa -= o;
522         va -= o;
523         size += o;
524         pmap(m->pml4, pa | PTEUNCACHED|PTEWRITE|PTEVALID, va, size);
525         return (void*)(va+o);
526 }
527
528 void
529 vunmap(void *v, int)
530 {
531         paddr(v);       /* will panic on error */
532 }
533
534 /*
535  * vmapsync() is currently unused as the VMAP and KZERO PDPs
536  * are shared between processors. (see mpstartap)
537  */
538 int
539 vmapsync(uintptr va)
540 {
541         uintptr *pte1, *pte2;
542         int level;
543
544         if(va < VMAP || m->machno == 0)
545                 return 0;
546
547         for(level=0; level<2; level++){
548                 pte1 = mmuwalk(MACHP(0)->pml4, va, level, 0);
549                 if(pte1 && *pte1 & PTEVALID){
550                         pte2 = mmuwalk(m->pml4, va, level, 1);
551                         if(pte2 == 0)
552                                 break;
553                         if(pte1 != pte2)
554                                 *pte2 = *pte1;
555                         return 1;
556                 }
557         }
558         return 0;
559 }