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