]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mmu.c
pckernel: use constants instead of hardcoding cpuid bits in various places
[plan9front.git] / sys / src / 9 / pc / mmu.c
1 /*
2  * Memory mappings.  Life was easier when 2G of memory was enough.
3  *
4  * The kernel memory starts at KZERO, with the text loaded at KZERO+1M
5  * (9load sits under 1M during the load).  The memory from KZERO to the
6  * top of memory is mapped 1-1 with physical memory, starting at physical
7  * address 0.  All kernel memory and data structures (i.e., the entries stored
8  * into conf.mem) must sit in this physical range: if KZERO is at 0xF0000000,
9  * then the kernel can only have 256MB of memory for itself.
10  * 
11  * The 256M below KZERO comprises three parts.  The lowest 4M is the
12  * virtual page table, a virtual address representation of the current 
13  * page table tree.  The second 4M is used for temporary per-process
14  * mappings managed by kmap and kunmap.  The remaining 248M is used
15  * for global (shared by all procs and all processors) device memory
16  * mappings and managed by vmap and vunmap.  The total amount (256M)
17  * could probably be reduced somewhat if desired.  The largest device
18  * mapping is that of the video card, and even though modern video cards
19  * have embarrassing amounts of memory, the video drivers only use one
20  * frame buffer worth (at most 16M).  Each is described in more detail below.
21  *
22  * The VPT is a 4M frame constructed by inserting the pdb into itself.
23  * This short-circuits one level of the page tables, with the result that 
24  * the contents of second-level page tables can be accessed at VPT.  
25  * We use the VPT to edit the page tables (see mmu) after inserting them
26  * into the page directory.  It is a convenient mechanism for mapping what
27  * might be otherwise-inaccessible pages.  The idea was borrowed from
28  * the Exokernel.
29  *
30  * The VPT doesn't solve all our problems, because we still need to 
31  * prepare page directories before we can install them.  For that, we
32  * use tmpmap/tmpunmap, which map a single page at TMPADDR.
33  */
34
35 #include        "u.h"
36 #include        "../port/lib.h"
37 #include        "mem.h"
38 #include        "dat.h"
39 #include        "fns.h"
40 #include        "io.h"
41
42 /*
43  * Simple segment descriptors with no translation.
44  */
45 #define DATASEGM(p)     { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW }
46 #define EXECSEGM(p)     { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
47 #define EXEC16SEGM(p)   { 0xFFFF, SEGG|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR }
48 #define TSSSEGM(b,p)    { ((b)<<16)|sizeof(Tss),\
49                           ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP }
50
51 Segdesc gdt[NGDT] =
52 {
53 [NULLSEG]       { 0, 0},                /* null descriptor */
54 [KDSEG]         DATASEGM(0),            /* kernel data/stack */
55 [KESEG]         EXECSEGM(0),            /* kernel code */
56 [UDSEG]         DATASEGM(3),            /* user data/stack */
57 [UESEG]         EXECSEGM(3),            /* user code */
58 [TSSSEG]        TSSSEGM(0,0),           /* tss segment */
59 [KESEG16]               EXEC16SEGM(0),  /* kernel code 16-bit */
60 };
61
62 static int didmmuinit;
63 static void taskswitch(ulong, ulong);
64 static void memglobal(void);
65
66 #define vpt ((ulong*)VPT)
67 #define VPTX(va)                (((ulong)(va))>>12)
68 #define vpd (vpt+VPTX(VPT))
69
70 void
71 mmuinit0(void)
72 {
73         memmove(m->gdt, gdt, sizeof gdt);
74 }
75
76 void
77 mmuinit(void)
78 {
79         ulong x, *p;
80         ushort ptr[3];
81
82         didmmuinit = 1;
83
84         if(0) print("vpt=%#.8ux vpd=%#p kmap=%#.8ux\n",
85                 VPT, vpd, KMAP);
86
87         memglobal();
88         m->pdb[PDX(VPT)] = PADDR(m->pdb)|PTEWRITE|PTEVALID;
89         
90         m->tss = mallocz(sizeof(Tss), 1);
91         if(m->tss == nil)
92                 panic("mmuinit: no memory for Tss");
93         m->tss->iomap = 0xDFFF<<16;
94
95         /*
96          * We used to keep the GDT in the Mach structure, but it
97          * turns out that that slows down access to the rest of the
98          * page.  Since the Mach structure is accessed quite often,
99          * it pays off anywhere from a factor of 1.25 to 2 on real
100          * hardware to separate them (the AMDs are more sensitive
101          * than Intels in this regard).  Under VMware it pays off
102          * a factor of about 10 to 100.
103          */
104         memmove(m->gdt, gdt, sizeof gdt);
105         x = (ulong)m->tss;
106         m->gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss);
107         m->gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP;
108
109         ptr[0] = sizeof(gdt)-1;
110         x = (ulong)m->gdt;
111         ptr[1] = x & 0xFFFF;
112         ptr[2] = (x>>16) & 0xFFFF;
113         lgdt(ptr);
114
115         ptr[0] = sizeof(Segdesc)*256-1;
116         x = IDTADDR;
117         ptr[1] = x & 0xFFFF;
118         ptr[2] = (x>>16) & 0xFFFF;
119         lidt(ptr);
120
121         /* make kernel text unwritable */
122         for(x = KTZERO; x < (ulong)etext; x += BY2PG){
123                 p = mmuwalk(m->pdb, x, 2, 0);
124                 if(p == nil)
125                         panic("mmuinit");
126                 *p &= ~PTEWRITE;
127         }
128
129         taskswitch(PADDR(m->pdb),  (ulong)m + BY2PG);
130         ltr(TSSSEL);
131 }
132
133 /* 
134  * On processors that support it, we set the PTEGLOBAL bit in
135  * page table and page directory entries that map kernel memory.
136  * Doing this tells the processor not to bother flushing them
137  * from the TLB when doing the TLB flush associated with a 
138  * context switch (write to CR3).  Since kernel memory mappings
139  * are never removed, this is safe.  (If we ever remove kernel memory
140  * mappings, we can do a full flush by turning off the PGE bit in CR4,
141  * writing to CR3, and then turning the PGE bit back on.) 
142  *
143  * See also mmukmap below.
144  * 
145  * Processor support for the PTEGLOBAL bit is enabled in devarch.c.
146  */
147 static void
148 memglobal(void)
149 {
150         int i, j;
151         ulong *pde, *pte;
152
153         /* only need to do this once, on bootstrap processor */
154         if(m->machno != 0)
155                 return;
156
157         if(!m->havepge)
158                 return;
159
160         pde = m->pdb;
161         for(i=PDX(KZERO); i<1024; i++){
162                 if(pde[i] & PTEVALID){
163                         pde[i] |= PTEGLOBAL;
164                         if(!(pde[i] & PTESIZE)){
165                                 pte = KADDR(pde[i]&~(BY2PG-1));
166                                 for(j=0; j<1024; j++)
167                                         if(pte[j] & PTEVALID)
168                                                 pte[j] |= PTEGLOBAL;
169                         }
170                 }
171         }                       
172 }
173
174 /*
175  * Flush all the user-space and device-mapping mmu info
176  * for this process, because something has been deleted.
177  * It will be paged back in on demand.
178  */
179 void
180 flushmmu(void)
181 {
182         int s;
183
184         s = splhi();
185         up->newtlb = 1;
186         mmuswitch(up);
187         splx(s);
188 }
189
190 /*
191  * Flush a single page mapping from the tlb.
192  */
193 void
194 flushpg(ulong va)
195 {
196         if(X86FAMILY(m->cpuidax) >= 4)
197                 invlpg(va);
198         else
199                 putcr3(getcr3());
200 }
201         
202 /*
203  * Allocate a new page for a page directory. 
204  * We keep a small cache of pre-initialized
205  * page directories in each mach.
206  */
207 static Page*
208 mmupdballoc(void)
209 {
210         int s;
211         Page *page;
212         ulong *pdb;
213
214         s = splhi();
215         m->pdballoc++;
216         if(m->pdbpool == 0){
217                 spllo();
218                 page = newpage(0, 0, 0);
219                 page->va = (ulong)vpd;
220                 splhi();
221                 pdb = tmpmap(page);
222                 memmove(pdb, m->pdb, BY2PG);
223                 pdb[PDX(VPT)] = page->pa|PTEWRITE|PTEVALID;     /* set up VPT */
224                 tmpunmap(pdb);
225         }else{
226                 page = m->pdbpool;
227                 m->pdbpool = page->next;
228                 m->pdbcnt--;
229         }
230         splx(s);
231         return page;
232 }
233
234 static void
235 mmupdbfree(Proc *proc, Page *p)
236 {
237         if(islo())
238                 panic("mmupdbfree: islo");
239         m->pdbfree++;
240         if(m->pdbcnt >= 10){
241                 p->next = proc->mmufree;
242                 proc->mmufree = p;
243         }else{
244                 p->next = m->pdbpool;
245                 m->pdbpool = p;
246                 m->pdbcnt++;
247         }
248 }
249
250 /*
251  * A user-space memory segment has been deleted, or the
252  * process is exiting.  Clear all the pde entries for user-space
253  * memory mappings and device mappings.  Any entries that
254  * are needed will be paged back in as necessary.
255  */
256 static void
257 mmuptefree(Proc* proc)
258 {
259         int s;
260         ulong *pdb;
261         Page **last, *page;
262
263         if(proc->mmupdb == nil || proc->mmuused == nil)
264                 return;
265         s = splhi();
266         pdb = tmpmap(proc->mmupdb);
267         last = &proc->mmuused;
268         for(page = *last; page; page = page->next){
269                 pdb[page->daddr] = 0;
270                 last = &page->next;
271         }
272         tmpunmap(pdb);
273         splx(s);
274         *last = proc->mmufree;
275         proc->mmufree = proc->mmuused;
276         proc->mmuused = 0;
277 }
278
279 static void
280 taskswitch(ulong pdb, ulong stack)
281 {
282         Tss *tss;
283
284         tss = m->tss;
285         tss->ss0 = KDSEL;
286         tss->esp0 = stack;
287         tss->ss1 = KDSEL;
288         tss->esp1 = stack;
289         tss->ss2 = KDSEL;
290         tss->esp2 = stack;
291         putcr3(pdb);
292 }
293
294 void
295 mmuswitch(Proc* proc)
296 {
297         ulong *pdb;
298         ulong x;
299         int n;
300
301         if(proc->newtlb){
302                 mmuptefree(proc);
303                 proc->newtlb = 0;
304         }
305
306         if(proc->mmupdb){
307                 pdb = tmpmap(proc->mmupdb);
308                 pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
309                 tmpunmap(pdb);
310                 taskswitch(proc->mmupdb->pa, (ulong)(proc->kstack+KSTACK));
311         }else
312                 taskswitch(PADDR(m->pdb), (ulong)(proc->kstack+KSTACK));
313
314         memmove(&m->gdt[PROCSEG0], proc->gdt, sizeof(proc->gdt));
315         if((x = (ulong)proc->ldt) && (n = proc->nldt) > 0){
316                 m->gdt[LDTSEG].d0 = (x<<16)|((n * sizeof(Segdesc)) - 1);
317                 m->gdt[LDTSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGLDT|SEGPL(0)|SEGP;
318                 lldt(LDTSEL);
319         } else
320                 lldt(NULLSEL);
321 }
322
323 /*
324  * Release any pages allocated for a page directory base or page-tables
325  * for this process:
326  *   switch to the prototype pdb for this processor (m->pdb);
327  *   call mmuptefree() to place all pages used for page-tables (proc->mmuused)
328  *   onto the process' free list (proc->mmufree). This has the side-effect of
329  *   cleaning any user entries in the pdb (proc->mmupdb);
330  *   if there's a pdb put it in the cache of pre-initialised pdb's
331  *   for this processor (m->pdbpool) or on the process' free list;
332  *   finally, place any pages freed back into the free pool (palloc).
333  * This routine is only called from schedinit() with palloc locked.
334  */
335 void
336 mmurelease(Proc* proc)
337 {
338         Page *page, *next;
339         ulong *pdb;
340
341         if(islo())
342                 panic("mmurelease: islo");
343         taskswitch(PADDR(m->pdb), (ulong)m + BY2PG);
344         if(proc->kmaptable){
345                 if(proc->mmupdb == nil)
346                         panic("mmurelease: no mmupdb");
347                 if(--proc->kmaptable->ref)
348                         panic("mmurelease: kmap ref %d", proc->kmaptable->ref);
349                 if(proc->nkmap)
350                         panic("mmurelease: nkmap %d", proc->nkmap);
351                 /*
352                  * remove kmaptable from pdb before putting pdb up for reuse.
353                  */
354                 pdb = tmpmap(proc->mmupdb);
355                 if(PPN(pdb[PDX(KMAP)]) != proc->kmaptable->pa)
356                         panic("mmurelease: bad kmap pde %#.8lux kmap %#.8lux",
357                                 pdb[PDX(KMAP)], proc->kmaptable->pa);
358                 pdb[PDX(KMAP)] = 0;
359                 tmpunmap(pdb);
360                 /*
361                  * move kmaptable to free list.
362                  */
363                 pagechainhead(proc->kmaptable);
364                 proc->kmaptable = 0;
365         }
366         if(proc->mmupdb){
367                 mmuptefree(proc);
368                 mmupdbfree(proc, proc->mmupdb);
369                 proc->mmupdb = 0;
370         }
371         for(page = proc->mmufree; page; page = next){
372                 next = page->next;
373                 if(--page->ref)
374                         panic("mmurelease: page->ref %d", page->ref);
375                 pagechainhead(page);
376         }
377         if(proc->mmufree && palloc.r.p)
378                 wakeup(&palloc.r);
379         proc->mmufree = 0;
380         if(proc->ldt){
381                 free(proc->ldt);
382                 proc->ldt = nil;
383                 proc->nldt = 0;
384         }
385 }
386
387 /*
388  * Allocate and install pdb for the current process.
389  */
390 static void
391 upallocpdb(void)
392 {
393         int s;
394         ulong *pdb;
395         Page *page;
396         
397         if(up->mmupdb != nil)
398                 return;
399         page = mmupdballoc();
400         s = splhi();
401         if(up->mmupdb != nil){
402                 /*
403                  * Perhaps we got an interrupt while
404                  * mmupdballoc was sleeping and that
405                  * interrupt allocated an mmupdb?
406                  * Seems unlikely.
407                  */
408                 mmupdbfree(up, page);
409                 splx(s);
410                 return;
411         }
412         pdb = tmpmap(page);
413         pdb[PDX(MACHADDR)] = m->pdb[PDX(MACHADDR)];
414         tmpunmap(pdb);
415         up->mmupdb = page;
416         putcr3(up->mmupdb->pa);
417         splx(s);
418 }
419
420 /*
421  * Update the mmu in response to a user fault.  pa may have PTEWRITE set.
422  */
423 void
424 putmmu(ulong va, ulong pa, Page*)
425 {
426         int old, s;
427         Page *page;
428
429         if(up->mmupdb == nil)
430                 upallocpdb();
431
432         /*
433          * We should be able to get through this with interrupts
434          * turned on (if we get interrupted we'll just pick up 
435          * where we left off) but we get many faults accessing
436          * vpt[] near the end of this function, and they always happen
437          * after the process has been switched out and then 
438          * switched back, usually many times in a row (perhaps
439          * it cannot switch back successfully for some reason).
440          * 
441          * In any event, I'm tired of searching for this bug.  
442          * Turn off interrupts during putmmu even though
443          * we shouldn't need to.                - rsc
444          */
445         
446         s = splhi();
447         if(!(vpd[PDX(va)]&PTEVALID)){
448                 if(up->mmufree == 0){
449                         spllo();
450                         page = newpage(0, 0, 0);
451                         splhi();
452                 }
453                 else{
454                         page = up->mmufree;
455                         up->mmufree = page->next;
456                 }
457                 vpd[PDX(va)] = PPN(page->pa)|PTEUSER|PTEWRITE|PTEVALID;
458                 /* page is now mapped into the VPT - clear it */
459                 memset((void*)(VPT+PDX(va)*BY2PG), 0, BY2PG);
460                 page->daddr = PDX(va);
461                 page->next = up->mmuused;
462                 up->mmuused = page;
463         }
464         old = vpt[VPTX(va)];
465         vpt[VPTX(va)] = pa|PTEUSER|PTEVALID;
466         if(old&PTEVALID)
467                 flushpg(va);
468         if(getcr3() != up->mmupdb->pa)
469                 print("bad cr3 %#.8lux %#.8lux\n", getcr3(), up->mmupdb->pa);
470         splx(s);
471 }
472
473 /*
474  * Double-check the user MMU.
475  * Error checking only.
476  */
477 void
478 checkmmu(ulong va, ulong pa)
479 {
480         if(up->mmupdb == 0)
481                 return;
482         if(!(vpd[PDX(va)]&PTEVALID) || !(vpt[VPTX(va)]&PTEVALID))
483                 return;
484         if(PPN(vpt[VPTX(va)]) != pa)
485                 print("%ld %s: va=%#08lux pa=%#08lux pte=%#08lux\n",
486                         up->pid, up->text,
487                         va, pa, vpt[VPTX(va)]);
488 }
489
490 /*
491  * Walk the page-table pointed to by pdb and return a pointer
492  * to the entry for virtual address va at the requested level.
493  * If the entry is invalid and create isn't requested then bail
494  * out early. Otherwise, for the 2nd level walk, allocate a new
495  * page-table page and register it in the 1st level.  This is used
496  * only to edit kernel mappings, which use pages from kernel memory,
497  * so it's okay to use KADDR to look at the tables.
498  */
499 ulong*
500 mmuwalk(ulong* pdb, ulong va, int level, int create)
501 {
502         ulong *table;
503         void *map;
504
505         table = &pdb[PDX(va)];
506         if(!(*table & PTEVALID) && create == 0)
507                 return 0;
508
509         switch(level){
510
511         default:
512                 return 0;
513
514         case 1:
515                 return table;
516
517         case 2:
518                 if(*table & PTESIZE)
519                         panic("mmuwalk2: va %luX entry %luX", va, *table);
520                 if(!(*table & PTEVALID)){
521                         /*
522                          * Have to call low-level allocator from
523                          * memory.c if we haven't set up the xalloc
524                          * tables yet.
525                          */
526                         if(didmmuinit)
527                                 map = xspanalloc(BY2PG, BY2PG, 0);
528                         else
529                                 map = rampage();
530                         if(map == nil)
531                                 panic("mmuwalk xspanalloc failed");
532                         *table = PADDR(map)|PTEWRITE|PTEVALID;
533                 }
534                 table = KADDR(PPN(*table));
535                 return &table[PTX(va)];
536         }
537 }
538
539 /*
540  * Device mappings are shared by all procs and processors and
541  * live in the virtual range VMAP to VMAP+VMAPSIZE.  The master
542  * copy of the mappings is stored in mach0->pdb, and they are
543  * paged in from there as necessary by vmapsync during faults.
544  */
545
546 static Lock vmaplock;
547
548 static int findhole(ulong *a, int n, int count);
549 static ulong vmapalloc(ulong size);
550 static void pdbunmap(ulong*, ulong, int);
551
552 /*
553  * Add a device mapping to the vmap range.
554  */
555 void*
556 vmap(ulong pa, int size)
557 {
558         int osize;
559         ulong o, va;
560         
561         /*
562          * might be asking for less than a page.
563          */
564         osize = size;
565         o = pa & (BY2PG-1);
566         pa -= o;
567         size += o;
568
569         size = ROUND(size, BY2PG);
570         if(pa == 0){
571                 print("vmap pa=0 pc=%#p\n", getcallerpc(&pa));
572                 return nil;
573         }
574         ilock(&vmaplock);
575         if((va = vmapalloc(size)) == 0 
576         || pdbmap(MACHP(0)->pdb, pa|PTEUNCACHED|PTEWRITE, va, size) < 0){
577                 iunlock(&vmaplock);
578                 return 0;
579         }
580         iunlock(&vmaplock);
581         /* avoid trap on local processor
582         for(i=0; i<size; i+=4*MB)
583                 vmapsync(va+i);
584         */
585         USED(osize);
586 //      print("  vmap %#.8lux %d => %#.8lux\n", pa+o, osize, va+o);
587         return (void*)(va + o);
588 }
589
590 static int
591 findhole(ulong *a, int n, int count)
592 {
593         int have, i;
594         
595         have = 0;
596         for(i=0; i<n; i++){
597                 if(a[i] == 0)
598                         have++;
599                 else
600                         have = 0;
601                 if(have >= count)
602                         return i+1 - have;
603         }
604         return -1;
605 }
606
607 /*
608  * Look for free space in the vmap.
609  */
610 static ulong
611 vmapalloc(ulong size)
612 {
613         int i, n, o;
614         ulong *vpdb;
615         int vpdbsize;
616         
617         vpdb = &MACHP(0)->pdb[PDX(VMAP)];
618         vpdbsize = VMAPSIZE/(4*MB);
619
620         if(size >= 4*MB){
621                 n = (size+4*MB-1) / (4*MB);
622                 if((o = findhole(vpdb, vpdbsize, n)) != -1)
623                         return VMAP + o*4*MB;
624                 return 0;
625         }
626         n = (size+BY2PG-1) / BY2PG;
627         for(i=0; i<vpdbsize; i++)
628                 if((vpdb[i]&PTEVALID) && !(vpdb[i]&PTESIZE))
629                         if((o = findhole(KADDR(PPN(vpdb[i])), WD2PG, n)) != -1)
630                                 return VMAP + i*4*MB + o*BY2PG;
631         if((o = findhole(vpdb, vpdbsize, 1)) != -1)
632                 return VMAP + o*4*MB;
633                 
634         /*
635          * could span page directory entries, but not worth the trouble.
636          * not going to be very much contention.
637          */
638         return 0;
639 }
640
641 /*
642  * Remove a device mapping from the vmap range.
643  * Since pdbunmap does not remove page tables, just entries,
644  * the call need not be interlocked with vmap.
645  */
646 void
647 vunmap(void *v, int size)
648 {
649         int i;
650         ulong va, o;
651         Mach *nm;
652         Proc *p;
653         
654         /*
655          * might not be aligned
656          */
657         va = (ulong)v;
658         o = va&(BY2PG-1);
659         va -= o;
660         size += o;
661         size = ROUND(size, BY2PG);
662         
663         if(size < 0 || va < VMAP || va+size > VMAP+VMAPSIZE)
664                 panic("vunmap va=%#.8lux size=%#x pc=%#.8lux",
665                         va, size, getcallerpc(&v));
666
667         pdbunmap(MACHP(0)->pdb, va, size);
668         
669         /*
670          * Flush mapping from all the tlbs and copied pdbs.
671          * This can be (and is) slow, since it is called only rarely.
672          * It is possible for vunmap to be called with up == nil,
673          * e.g. from the reset/init driver routines during system
674          * boot. In that case it suffices to flush the MACH(0) TLB
675          * and return.
676          */
677         if(!active.thunderbirdsarego){
678                 putcr3(PADDR(MACHP(0)->pdb));
679                 return;
680         }
681         for(i=0; i<conf.nproc; i++){
682                 p = proctab(i);
683                 if(p->state == Dead)
684                         continue;
685                 if(p != up)
686                         p->newtlb = 1;
687         }
688         for(i=0; i<conf.nmach; i++){
689                 nm = MACHP(i);
690                 if(nm != m)
691                         nm->flushmmu = 1;
692         }
693         flushmmu();
694         for(i=0; i<conf.nmach; i++){
695                 nm = MACHP(i);
696                 if(nm != m)
697                         while((active.machs&(1<<nm->machno)) && nm->flushmmu)
698                                 ;
699         }
700 }
701
702 /*
703  * Add kernel mappings for pa -> va for a section of size bytes.
704  */
705 int
706 pdbmap(ulong *pdb, ulong pa, ulong va, int size)
707 {
708         int pse;
709         ulong pgsz, *pte, *table;
710         ulong flag, off;
711         
712         flag = pa&0xFFF;
713         pa &= ~0xFFF;
714
715         if((MACHP(0)->cpuiddx & Pse) && (getcr4() & 0x10))
716                 pse = 1;
717         else
718                 pse = 0;
719
720         for(off=0; off<size; off+=pgsz){
721                 table = &pdb[PDX(va+off)];
722                 if((*table&PTEVALID) && (*table&PTESIZE))
723                         panic("vmap: va=%#.8lux pa=%#.8lux pde=%#.8lux",
724                                 va+off, pa+off, *table);
725
726                 /*
727                  * Check if it can be mapped using a 4MB page:
728                  * va, pa aligned and size >= 4MB and processor can do it.
729                  */
730                 if(pse && (pa+off)%(4*MB) == 0 && (va+off)%(4*MB) == 0 && (size-off) >= 4*MB){
731                         *table = (pa+off)|flag|PTESIZE|PTEVALID;
732                         pgsz = 4*MB;
733                 }else{
734                         pte = mmuwalk(pdb, va+off, 2, 1);
735                         if(*pte&PTEVALID)
736                                 panic("vmap: va=%#.8lux pa=%#.8lux pte=%#.8lux",
737                                         va+off, pa+off, *pte);
738                         *pte = (pa+off)|flag|PTEVALID;
739                         pgsz = BY2PG;
740                 }
741         }
742         return 0;
743 }
744
745 /*
746  * Remove mappings.  Must already exist, for sanity.
747  * Only used for kernel mappings, so okay to use KADDR.
748  */
749 static void
750 pdbunmap(ulong *pdb, ulong va, int size)
751 {
752         ulong vae;
753         ulong *table;
754         
755         vae = va+size;
756         while(va < vae){
757                 table = &pdb[PDX(va)];
758                 if(!(*table & PTEVALID))
759                         panic("vunmap: not mapped");
760                 if(*table & PTESIZE){
761                         if(va & 4*MB-1)
762                                 panic("vunmap: misaligned: %#p", va);
763                         *table = 0;
764                         va += 4*MB;
765                         continue;
766                 }
767                 table = KADDR(PPN(*table));
768                 if(!(table[PTX(va)] & PTEVALID))
769                         panic("vunmap: not mapped");
770                 table[PTX(va)] = 0;
771                 va += BY2PG;
772         }
773 }
774
775 /*
776  * Handle a fault by bringing vmap up to date.
777  * Only copy pdb entries and they never go away,
778  * so no locking needed.
779  */
780 int
781 vmapsync(ulong va)
782 {
783         ulong entry, *table;
784
785         if(va < VMAP || va >= VMAP+VMAPSIZE)
786                 return 0;
787
788         entry = MACHP(0)->pdb[PDX(va)];
789         if(!(entry&PTEVALID))
790                 return 0;
791         if(!(entry&PTESIZE)){
792                 /* make sure entry will help the fault */
793                 table = KADDR(PPN(entry));
794                 if(!(table[PTX(va)]&PTEVALID))
795                         return 0;
796         }
797         vpd[PDX(va)] = entry;
798         /*
799          * TLB doesn't cache negative results, so no flush needed.
800          */
801         return 1;
802 }
803
804
805 /*
806  * KMap is used to map individual pages into virtual memory.
807  * It is rare to have more than a few KMaps at a time (in the 
808  * absence of interrupts, only two at a time are ever used,
809  * but interrupts can stack).  The mappings are local to a process,
810  * so we can use the same range of virtual address space for
811  * all processes without any coordination.
812  */
813 #define kpt (vpt+VPTX(KMAP))
814 #define NKPT (KMAPSIZE/BY2PG)
815
816 KMap*
817 kmap(Page *page)
818 {
819         int i, o, s;
820
821         if(up == nil)
822                 panic("kmap: up=0 pc=%#.8lux", getcallerpc(&page));
823         if(up->mmupdb == nil)
824                 upallocpdb();
825         if(up->nkmap < 0)
826                 panic("kmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
827         
828         /*
829          * Splhi shouldn't be necessary here, but paranoia reigns.
830          * See comment in putmmu above.
831          */
832         s = splhi();
833         up->nkmap++;
834         if(!(vpd[PDX(KMAP)]&PTEVALID)){
835                 /* allocate page directory */
836                 if(KMAPSIZE > BY2XPG)
837                         panic("bad kmapsize");
838                 if(up->kmaptable != nil)
839                         panic("kmaptable");
840                 spllo();
841                 up->kmaptable = newpage(0, 0, 0);
842                 splhi();
843                 vpd[PDX(KMAP)] = up->kmaptable->pa|PTEWRITE|PTEVALID;
844                 flushpg((ulong)kpt);
845                 memset(kpt, 0, BY2PG);
846                 kpt[0] = page->pa|PTEWRITE|PTEVALID;
847                 up->lastkmap = 0;
848                 splx(s);
849                 return (KMap*)KMAP;
850         }
851         if(up->kmaptable == nil)
852                 panic("no kmaptable");
853         o = up->lastkmap+1;
854         for(i=0; i<NKPT; i++){
855                 if(kpt[(i+o)%NKPT] == 0){
856                         o = (i+o)%NKPT;
857                         kpt[o] = page->pa|PTEWRITE|PTEVALID;
858                         up->lastkmap = o;
859                         splx(s);
860                         return (KMap*)(KMAP+o*BY2PG);
861                 }
862         }
863         panic("out of kmap");
864         return nil;
865 }
866
867 void
868 kunmap(KMap *k)
869 {
870         ulong va;
871
872         va = (ulong)k;
873         if(up->mmupdb == nil || !(vpd[PDX(KMAP)]&PTEVALID))
874                 panic("kunmap: no kmaps");
875         if(va < KMAP || va >= KMAP+KMAPSIZE)
876                 panic("kunmap: bad address %#.8lux pc=%#p", va, getcallerpc(&k));
877         if(!(vpt[VPTX(va)]&PTEVALID))
878                 panic("kunmap: not mapped %#.8lux pc=%#p", va, getcallerpc(&k));
879         up->nkmap--;
880         if(up->nkmap < 0)
881                 panic("kunmap %lud %s: nkmap=%d", up->pid, up->text, up->nkmap);
882         vpt[VPTX(va)] = 0;
883         flushpg(va);
884 }
885
886 /*
887  * Temporary one-page mapping used to edit page directories.
888  *
889  * The fasttmp #define controls whether the code optimizes
890  * the case where the page is already mapped in the physical
891  * memory window.  
892  */
893 #define fasttmp 1
894
895 void*
896 tmpmap(Page *p)
897 {
898         ulong i;
899         ulong *entry;
900         
901         if(islo())
902                 panic("tmpaddr: islo");
903
904         if(fasttmp && p->pa < -KZERO)
905                 return KADDR(p->pa);
906
907         /*
908          * PDX(TMPADDR) == PDX(MACHADDR), so this
909          * entry is private to the processor and shared 
910          * between up->mmupdb (if any) and m->pdb.
911          */
912         entry = &vpt[VPTX(TMPADDR)];
913         if(!(*entry&PTEVALID)){
914                 for(i=KZERO; i<=CPU0MACH; i+=BY2PG)
915                         print("%#p: *%#p=%#p (vpt=%#p index=%#p)\n", i, &vpt[VPTX(i)], vpt[VPTX(i)], vpt, VPTX(i));
916                 panic("tmpmap: no entry");
917         }
918         if(PPN(*entry) != PPN(TMPADDR-KZERO))
919                 panic("tmpmap: already mapped entry=%#.8lux", *entry);
920         *entry = p->pa|PTEWRITE|PTEVALID;
921         flushpg(TMPADDR);
922         return (void*)TMPADDR;
923 }
924
925 void
926 tmpunmap(void *v)
927 {
928         ulong *entry;
929         
930         if(islo())
931                 panic("tmpaddr: islo");
932         if(fasttmp && (ulong)v >= KZERO && v != (void*)TMPADDR)
933                 return;
934         if(v != (void*)TMPADDR)
935                 panic("tmpunmap: bad address");
936         entry = &vpt[VPTX(TMPADDR)];
937         if(!(*entry&PTEVALID) || PPN(*entry) == PPN(PADDR(TMPADDR)))
938                 panic("tmpmap: not mapped entry=%#.8lux", *entry);
939         *entry = PPN(TMPADDR-KZERO)|PTEWRITE|PTEVALID;
940         flushpg(TMPADDR);
941 }
942
943 /*
944  * These could go back to being macros once the kernel is debugged,
945  * but the extra checking is nice to have.
946  */
947 void*
948 kaddr(ulong pa)
949 {
950         if(pa > (ulong)-KZERO)
951                 panic("kaddr: pa=%#.8lux", pa);
952         return (void*)(pa+KZERO);
953 }
954
955 ulong
956 paddr(void *v)
957 {
958         ulong va;
959         
960         va = (ulong)v;
961         if(va < KZERO)
962                 panic("paddr: va=%#.8lux pc=%#p", va, getcallerpc(&v));
963         return va-KZERO;
964 }
965
966 /*
967  * More debugging.
968  */
969 void
970 countpagerefs(ulong *ref, int print)
971 {
972         int i, n;
973         Mach *mm;
974         Page *pg;
975         Proc *p;
976         
977         n = 0;
978         for(i=0; i<conf.nproc; i++){
979                 p = proctab(i);
980                 if(p->mmupdb){
981                         if(print){
982                                 if(ref[pagenumber(p->mmupdb)])
983                                         iprint("page %#.8lux is proc %d (pid %lud) pdb\n",
984                                                 p->mmupdb->pa, i, p->pid);
985                                 continue;
986                         }
987                         if(ref[pagenumber(p->mmupdb)]++ == 0)
988                                 n++;
989                         else
990                                 iprint("page %#.8lux is proc %d (pid %lud) pdb but has other refs!\n",
991                                         p->mmupdb->pa, i, p->pid);
992                 }
993                 if(p->kmaptable){
994                         if(print){
995                                 if(ref[pagenumber(p->kmaptable)])
996                                         iprint("page %#.8lux is proc %d (pid %lud) kmaptable\n",
997                                                 p->kmaptable->pa, i, p->pid);
998                                 continue;
999                         }
1000                         if(ref[pagenumber(p->kmaptable)]++ == 0)
1001                                 n++;
1002                         else
1003                                 iprint("page %#.8lux is proc %d (pid %lud) kmaptable but has other refs!\n",
1004                                         p->kmaptable->pa, i, p->pid);
1005                 }
1006                 for(pg=p->mmuused; pg; pg=pg->next){
1007                         if(print){
1008                                 if(ref[pagenumber(pg)])
1009                                         iprint("page %#.8lux is on proc %d (pid %lud) mmuused\n",
1010                                                 pg->pa, i, p->pid);
1011                                 continue;
1012                         }
1013                         if(ref[pagenumber(pg)]++ == 0)
1014                                 n++;
1015                         else
1016                                 iprint("page %#.8lux is on proc %d (pid %lud) mmuused but has other refs!\n",
1017                                         pg->pa, i, p->pid);
1018                 }
1019                 for(pg=p->mmufree; pg; pg=pg->next){
1020                         if(print){
1021                                 if(ref[pagenumber(pg)])
1022                                         iprint("page %#.8lux is on proc %d (pid %lud) mmufree\n",
1023                                                 pg->pa, i, p->pid);
1024                                 continue;
1025                         }
1026                         if(ref[pagenumber(pg)]++ == 0)
1027                                 n++;
1028                         else
1029                                 iprint("page %#.8lux is on proc %d (pid %lud) mmufree but has other refs!\n",
1030                                         pg->pa, i, p->pid);
1031                 }
1032         }
1033         if(!print)
1034                 iprint("%d pages in proc mmu\n", n);
1035         n = 0;
1036         for(i=0; i<conf.nmach; i++){
1037                 mm = MACHP(i);
1038                 for(pg=mm->pdbpool; pg; pg=pg->next){
1039                         if(print){
1040                                 if(ref[pagenumber(pg)])
1041                                         iprint("page %#.8lux is in cpu%d pdbpool\n",
1042                                                 pg->pa, i);
1043                                 continue;
1044                         }
1045                         if(ref[pagenumber(pg)]++ == 0)
1046                                 n++;
1047                         else
1048                                 iprint("page %#.8lux is in cpu%d pdbpool but has other refs!\n",
1049                                         pg->pa, i);
1050                 }
1051         }
1052         if(!print){
1053                 iprint("%d pages in mach pdbpools\n", n);
1054                 for(i=0; i<conf.nmach; i++)
1055                         iprint("cpu%d: %d pdballoc, %d pdbfree\n",
1056                                 i, MACHP(i)->pdballoc, MACHP(i)->pdbfree);
1057         }
1058 }
1059
1060 void
1061 checkfault(ulong, ulong)
1062 {
1063 }
1064
1065 /*
1066  * Return the number of bytes that can be accessed via KADDR(pa).
1067  * If pa is not a valid argument to KADDR, return 0.
1068  */
1069 ulong
1070 cankaddr(ulong pa)
1071 {
1072         if(pa >= -KZERO)
1073                 return 0;
1074         return -KZERO - pa;
1075 }
1076