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