diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2013-01-26 17:33:56 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2013-01-26 17:33:56 +0100 |
commit | bc610a1b1c32f6e2e9b034217bb3ce9a7defa739 (patch) | |
tree | 98fa1e680867eaf19c1be5e818a570713fa7a286 /sys/src/9/bcm/mmu.c | |
parent | ea108c8ca6e726ac008f75775ab83775ec233171 (diff) |
add raspberry pi kernel (from sources)
Diffstat (limited to 'sys/src/9/bcm/mmu.c')
-rw-r--r-- | sys/src/9/bcm/mmu.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/sys/src/9/bcm/mmu.c b/sys/src/9/bcm/mmu.c new file mode 100644 index 000000000..8eba6e1d3 --- /dev/null +++ b/sys/src/9/bcm/mmu.c @@ -0,0 +1,319 @@ +#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) + +enum { + L1lo = UZERO/MiB, /* L1X(UZERO)? */ + L1hi = (USTKTOP+MiB-1)/MiB, /* L1X(USTKTOP+MiB-1)? */ +}; + +void +mmuinit(void) +{ + PTE *l1, *l2; + uintptr pa, va; + + l1 = (PTE*)PADDR(L1); + l2 = (PTE*)PADDR(L2); + + /* + * map all of ram at KZERO + */ + va = KZERO; + for(pa = PHYSDRAM; pa < PHYSDRAM+DRAMSIZE; pa += MiB){ + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|Cached|Buffered; + va += MiB; + } + + /* + * identity map first MB of ram so mmu can be enabled + */ + l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|Cached|Buffered; + + /* + * map i/o registers + */ + va = VIRTIO; + for(pa = PHYSIO; pa < PHYSIO+IOSIZE; pa += MiB){ + l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section; + va += MiB; + } + + /* + * double map exception vectors at top of virtual memory + */ + va = HVECTORS; + l1[L1X(va)] = (uintptr)l2|Dom0|Coarse; + l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small; +} + +void +mmuinit1(void) +{ + PTE *l1; + + l1 = (PTE*)L1; + m->mmul1 = l1; + + /* + * undo identity map of first MB of ram + */ + l1[L1X(PHYSDRAM)] = 0; + cachedwbse(&l1[L1X(PHYSDRAM)], sizeof(PTE)); + 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(UINT2PTR(page->va), 0, BY2PG); + l1[page->daddr] = Fault; + l2 = &page->next; + } + *l2 = proc->mmul2cache; + proc->mmul2cache = proc->mmul2; + proc->mmul2 = nil; +} + +static void +mmul1empty(void) +{ +#ifdef notdef +/* there's a bug in here */ + PTE *l1; + + /* clean out any user mappings still in l1 */ + if(m->mmul1lo > L1lo){ + if(m->mmul1lo == 1) + m->mmul1[L1lo] = Fault; + else + memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE)); + m->mmul1lo = L1lo; + } + if(m->mmul1hi < L1hi){ + l1 = &m->mmul1[m->mmul1hi]; + if((L1hi - m->mmul1hi) == 1) + *l1 = Fault; + else + memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE)); + m->mmul1hi = L1hi; + } +#else + memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE)); +#endif /* notdef */ +} + +void +mmuswitch(Proc* proc) +{ + int x; + PTE *l1; + Page *page; + + /* do kprocs get here and if so, do they need to? */ + if(m->mmupid == proc->pid && !proc->newtlb) + return; + m->mmupid = proc->pid; + + /* write back dirty and invalidate l1 caches */ + cacheuwbinv(); + + if(proc->newtlb){ + mmul2empty(proc, 1); + proc->newtlb = 0; + } + + mmul1empty(); + + /* move in new map */ + l1 = m->mmul1; + for(page = proc->mmul2; page != nil; page = page->next){ + x = page->daddr; + l1[x] = PPN(page->pa)|Dom0|Coarse; + /* know here that L1lo < x < L1hi */ + if(x+1 - m->mmul1lo < m->mmul1hi - x) + m->mmul1lo = x+1; + else + m->mmul1hi = x; + } + + /* make sure map is in memory */ + /* could be smarter about how much? */ + cachedwbse(&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) +{ + Page *page, *next; + + /* write back dirty and invalidate l1 caches */ + cacheuwbinv(); + + mmul2empty(proc, 0); + for(page = proc->mmul2cache; page != nil; page = next){ + next = page->next; + if(--page->ref) + panic("mmurelease: page->ref %d", page->ref); + pagechainhead(page); + } + if(proc->mmul2cache && palloc.r.p) + wakeup(&palloc.r); + proc->mmul2cache = nil; + + mmul1empty(); + + /* make sure map is in memory */ + /* could be smarter about how much? */ + cachedwbse(&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; + Page *pg; + PTE *l1, *pte; + + x = L1X(va); + l1 = &m->mmul1[x]; + if(*l1 == Fault){ + /* wasteful - l2 pages only have 256 entries - fix */ + if(up->mmul2cache == nil){ + /* auxpg since we don't need much? memset if so */ + pg = newpage(1, 0, 0); + pg->va = VA(kmap(pg)); + } + else{ + pg = up->mmul2cache; + up->mmul2cache = pg->next; + memset(UINT2PTR(pg->va), 0, BY2PG); + } + pg->daddr = x; + pg->next = up->mmul2; + up->mmul2 = pg; + + /* force l2 page to memory */ + cachedwbse((void *)pg->va, BY2PG); + + *l1 = PPN(pg->pa)|Dom0|Coarse; + cachedwbse(l1, sizeof *l1); + + if(x >= m->mmul1lo && x < m->mmul1hi){ + if(x+1 - m->mmul1lo < m->mmul1hi - x) + m->mmul1lo = x+1; + else + m->mmul1hi = x; + } + } + pte = UINT2PTR(KADDR(PPN(*l1))); + + /* protection bits are + * PTERONLY|PTEVALID; + * PTEWRITE|PTEVALID; + * PTEWRITE|PTEUNCACHED|PTEVALID; + */ + x = Small; + if(!(pa & PTEUNCACHED)) + x |= Cached|Buffered; + if(pa & PTEWRITE) + x |= L2AP(Urw); + else + x |= L2AP(Uro); + pte[L2X(va)] = PPN(pa)|x; + cachedwbse(&pte[L2X(va)], sizeof pte[0]); + + /* clear out the current entry */ + mmuinvalidateaddr(PPN(va)); + + /* write back dirty entries - we need this because the pio() in + * fault.c is writing via a different virt addr and won't clean + * its changes out of the dcache. Page coloring doesn't work + * on this mmu because the virtual cache is set associative + * rather than direct mapped. + */ + cachedwbinv(); + if(page->cachectl[0] == PG_TXTFLUSH){ + /* pio() sets PG_TXTFLUSH whenever a text pg has been written */ + cacheiinv(); + page->cachectl[0] = PG_NOFLUSH; + } + checkmmu(va, PPN(pa)); +} + +/* + * 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 + memsize) /* assumes PHYSDRAM is 0 */ + return PHYSDRAM + memsize - 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; + mmuinvalidateaddr(va+n); + } + cachedwbse(pte0, pte - pte0); + return va + o; +} + + +void +checkmmu(uintptr va, uintptr pa) +{ + USED(va); + USED(pa); +} + |