ref: a15863b8ae23a26a2f825daf6107ab92e53c2669
dir: /sys/src/9/bcm/mmu.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "arm.h" #define FEXT(d, o, w) (((d)>>(o)) & ((1<<(w))-1)) #define L1X(va) FEXT((va), 20, 12) #define L2X(va) FEXT((va), 12, 8) #define L1ptedramattrs soc.l1ptedramattrs #define L2ptedramattrs soc.l2ptedramattrs #define PTEDRAM (PHYSDRAM|Dom0|L1AP(Krw)|Section|L1ptedramattrs) enum { L1lo = UZERO/MiB, /* L1X(UZERO)? */ L1hi = (USTKTOP+MiB-1)/MiB, /* L1X(USTKTOP+MiB-1)? */ L2size = 256*sizeof(PTE), }; /* * Set up initial PTEs for cpu0 (called with mmu off) */ void mmuinit(void *a) { PTE *l1, *l2; uintptr pa, va; l1 = (PTE*)a; l2 = (PTE*)PADDR(L2); /* * map all of ram at KZERO */ va = KZERO; for(pa = PHYSDRAM; pa < PHYSDRAM+soc.dramsize; pa += MiB){ l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1ptedramattrs; va += MiB; } /* * identity map first MB of ram so mmu can be enabled */ l1[L1X(PHYSDRAM)] = PTEDRAM; /* * map i/o registers */ va = soc.virtio; for(pa = soc.physio; pa < soc.physio+soc.iosize; pa += MiB){ l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; va += MiB; } pa = soc.armlocal; if(pa) l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|L1noexec; /* * double map exception vectors near top of virtual memory */ va = HVECTORS; l1[L1X(va)] = (uintptr)l2|Dom0|Coarse; l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small|L2ptedramattrs; } /* * enable/disable identity map of first MB of ram */ void mmuinit1(int on) { PTE *l1; l1 = m->mmul1; l1[L1X(PHYSDRAM)] = on? PTEDRAM: Fault; cachedwbtlb(&l1[L1X(PHYSDRAM)], sizeof(PTE)); mmuinvalidateaddr(PHYSDRAM); mmuinvalidate(); } static void mmul2empty(Proc* proc, int clear) { PTE *l1; Page **l2, *page; l1 = m->mmul1; l2 = &proc->mmul2; for(page = *l2; page != nil; page = page->next){ if(clear) memset((void*)page->va, 0, L2size); l1[page->daddr] = Fault; l2 = &page->next; } coherence(); *l2 = proc->mmul2cache; proc->mmul2cache = proc->mmul2; proc->mmul2 = nil; } static void mmul1empty(void) { PTE *l1; /* clean out any user mappings still in l1 */ if(m->mmul1lo > 0){ if(m->mmul1lo == 1) m->mmul1[L1lo] = Fault; else memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE)); m->mmul1lo = 0; } if(m->mmul1hi > 0){ l1 = &m->mmul1[L1hi - m->mmul1hi]; if(m->mmul1hi == 1) *l1 = Fault; else memset(l1, 0, m->mmul1hi*sizeof(PTE)); m->mmul1hi = 0; } } void mmuswitch(Proc* proc) { int x; PTE *l1; Page *page; if(proc != nil && proc->newtlb){ mmul2empty(proc, 1); proc->newtlb = 0; } mmul1empty(); /* move in new map */ l1 = m->mmul1; if(proc != nil) for(page = proc->mmul2; page != nil; page = page->next){ x = page->daddr; l1[x] = PPN(page->pa)|Dom0|Coarse; if(x >= L1lo + m->mmul1lo && x < L1hi - m->mmul1hi){ if(x+1 - L1lo < L1hi - x) m->mmul1lo = x+1 - L1lo; else m->mmul1hi = L1hi - x; } } /* make sure map is in memory */ /* could be smarter about how much? */ cachedwbtlb(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); /* lose any possible stale tlb entries */ mmuinvalidate(); } void flushmmu(void) { int s; s = splhi(); up->newtlb = 1; mmuswitch(up); splx(s); } void mmurelease(Proc* proc) { mmul2empty(proc, 0); freepages(proc->mmul2cache, nil, 0); proc->mmul2cache = nil; mmul1empty(); /* make sure map is in memory */ /* could be smarter about how much? */ cachedwbtlb(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE)); /* lose any possible stale tlb entries */ mmuinvalidate(); } void putmmu(uintptr va, uintptr pa, Page* page) { int x, s; Page *pg; PTE *l1, *pte; /* * disable interrupts to prevent flushmmu (called from hzclock) * from clearing page tables while we are setting them */ s = splhi(); x = L1X(va); l1 = &m->mmul1[x]; if(*l1 == Fault){ /* l2 pages only have 256 entries - wastes 3K per 1M of address space */ if(up->mmul2cache == nil){ spllo(); pg = newpage(1, 0, 0); splhi(); /* if newpage slept, we might be on a different cpu */ l1 = &m->mmul1[x]; pg->va = VA(kmap(pg)); }else{ pg = up->mmul2cache; up->mmul2cache = pg->next; } pg->daddr = x; pg->next = up->mmul2; up->mmul2 = pg; /* force l2 page to memory (armv6) */ cachedwbtlb((void *)pg->va, L2size); *l1 = PPN(pg->pa)|Dom0|Coarse; cachedwbtlb(l1, sizeof *l1); if(x >= L1lo + m->mmul1lo && x < L1hi - m->mmul1hi){ if(x+1 - L1lo < L1hi - x) m->mmul1lo = x+1 - L1lo; else m->mmul1hi = L1hi - x; } } pte = KADDR(PPN(*l1)); /* protection bits are * PTERONLY|PTEVALID; * PTEWRITE|PTEVALID; * PTEWRITE|PTEUNCACHED|PTEVALID; */ x = Small; if(!(pa & PTEUNCACHED)) x |= L2ptedramattrs; if(pa & PTEWRITE) x |= L2AP(Urw); else x |= L2AP(Uro); if(pa & PTENOEXEC) x |= L2noexec; pte[L2X(va)] = PPN(pa)|x; cachedwbtlb(&pte[L2X(va)], sizeof(PTE)); /* clear out the current entry */ mmuinvalidateaddr(PPN(va)); if(needtxtflush(page)){ cachedwbse((void*)(page->pa|KZERO), BY2PG); cacheiinvse((void*)page->va, BY2PG); donetxtflush(page); } //checkmmu(va, PPN(pa)); splx(s); } void* mmuuncache(void* v, usize size) { int x; PTE *pte; uintptr va; /* * Simple helper for ucalloc(). * Uncache a Section, must already be * valid in the MMU. */ va = (uintptr)v; assert(!(va & (1*MiB-1)) && size == 1*MiB); x = L1X(va); pte = &m->mmul1[x]; if((*pte & (Fine|Section|Coarse)) != Section) return nil; *pte &= ~L1ptedramattrs; mmuinvalidateaddr(va); cachedwbinvse(pte, 4); return v; } /* * Return the number of bytes that can be accessed via KADDR(pa). * If pa is not a valid argument to KADDR, return 0. */ uintptr cankaddr(uintptr pa) { if(pa < PHYSDRAM+soc.dramsize) return ((uintptr)PHYSDRAM+soc.dramsize) - pa; return 0; } uintptr mmukmap(uintptr va, uintptr pa, usize size) { int o; usize n; PTE *pte, *pte0; assert((va & (MiB-1)) == 0); o = pa & (MiB-1); pa -= o; size += o; pte = pte0 = &m->mmul1[L1X(va)]; for(n = 0; n < size; n += MiB) if(*pte++ != Fault) return 0; pte = pte0; for(n = 0; n < size; n += MiB){ *pte++ = (pa+n)|Dom0|L1AP(Krw)|Section|L1noexec; mmuinvalidateaddr(va+n); } cachedwbtlb(pte0, (uintptr)pte - (uintptr)pte0); return va + o; } void checkmmu(uintptr va, uintptr pa) { int x; PTE *l1, *pte; x = L1X(va); l1 = &m->mmul1[x]; if(*l1 == Fault){ iprint("checkmmu cpu%d va=%lux l1 %p=%ux\n", m->machno, va, l1, *l1); return; } pte = KADDR(PPN(*l1)); pte += L2X(va); if(pa == ~0 || (pa != 0 && PPN(*pte) != pa)) iprint("checkmmu va=%lux pa=%lux l1 %p=%ux pte %p=%ux\n", va, pa, l1, *l1, pte, *pte); } void kunmap(KMap *k) { USED(k); coherence(); }