#include "MMU.h" void mmuTlbFlush(ArmMmu* mmu){ UInt8 i, j; for(i = 0; i < MMU_TLB_BUCKET_NUM; i++){ for(j = 0; j < MMU_TLB_BUCKET_SIZE; j++) mmu->tlb[i][j].sz = 0; mmu->replPos[i] = 0; mmu->readPos[i] = 0; } } void mmuInit(ArmMmu* mmu, ArmMmuReadF readF, void* userData){ __mem_zero(mmu, sizeof(ArmMmu)); mmu->readF = readF; mmu->userData = userData; mmu->transTablPA = MMU_DISABLED_TTP; mmu->domainCfg = 0; mmuTlbFlush(mmu); } void muDeinit(_UNUSED_ ArmMmu* mmu){ //nothing here } static _INLINE_ UInt8 mmuPrvHashAddr(UInt32 addr){ //addresses are granular on 1K addr >>= 10; addr = addr ^ (addr >> 5) ^ (addr >> 10); return addr % MMU_TLB_BUCKET_NUM; } Boolean mmuTranslate(ArmMmu* mmu, UInt32 adr, Boolean priviledged, Boolean write, UInt32* paP, UInt8* fsrP){ UInt32 va, pa = 0, sz, t; UInt8 i, j, dom, ap = 0; Boolean section = false, coarse = true, pxa_tex_page = false; UInt8 bucket; //handle the 'MMU off' case if(mmu->transTablPA == MMU_DISABLED_TTP){ va = pa = 0; goto calc; } //check the TLB if(MMU_TLB_BUCKET_SIZE && MMU_TLB_BUCKET_NUM){ bucket = mmuPrvHashAddr(adr); for(j = 0, i = mmu->readPos[bucket]; j < MMU_TLB_BUCKET_SIZE; j++, i--){ if(i == 0xFF) i = MMU_TLB_BUCKET_SIZE - 1; va = mmu->tlb[bucket][i].va; sz = mmu->tlb[bucket][i].sz; if(va <= adr && va + sz > adr){ pa = mmu->tlb[bucket][i].pa; ap = mmu->tlb[bucket][i].ap; dom = mmu->tlb[bucket][i].domain; mmu->readPos[bucket] = i; goto check; } } } //read first level table if(mmu->transTablPA & 3){ *fsrP = 0x01; //alignment fault return false; } if(!mmu->readF(mmu->userData, &t, mmu->transTablPA + ((adr & 0xFFF00000) >> 18))){ *fsrP = 0x0C; //translation external abort first level return false; } dom = (t >> 5) & 0x0F; switch(t & 3){ case 0: //fault *fsrP = 0x5; //section translation fault return false; case 1: //coarse pagetable t &= 0xFFFFFC00UL; t += (adr & 0x000FF000UL) >> 10; break; case 2: //1MB section pa = t & 0xFFF00000UL; va = adr & 0xFFF00000UL; sz = 1UL << 20; ap = (t >> 10) & 3; section = true; goto translated; case 3: //fine page table coarse = false; t &= 0xFFFFF000UL; t += (adr & 0x000FFC00UL) >> 8; break; } //read second level table if(!mmu->readF(mmu->userData, &t, t)){ *fsrP = 0x0E | (dom << 4); //translation external abort second level return false; } switch(t & 3){ case 0: //fault *fsrP = 0x07 | (dom << 4); //page translation fault return false; case 1: //64K mapping pa = t & 0xFFFF0000UL; va = adr & 0xFFFF0000UL; sz = 65536UL; ap = (adr >> 14) & 3; //in "ap" store which AP we need [of the 4] break; case 2: //4K mapping (1K effective thenks to having 4 AP fields) page_size_4k: pa = t & 0xFFFFF000UL; va = adr & 0xFFFFF000UL; sz = 4096; ap = (adr >> 10) & 3; //in "ap" store which AP we need [of the 4] break; case 3: //1K mapping if(coarse){ pxa_tex_page = true; goto page_size_4k; } pa = t & 0xFFFFFC00UL; va = adr & 0xFFFFFC00UL; ap = (t >> 4) & 3; //in "ap" store the actual AP [and skip quarter-page resolution later using the goto] sz = 1024; goto translated; } //handle 4 AP sections i = (t >> 4) & 0xFF; if(pxa_tex_page || ((i & 0x0F) == (i >> 4) && (i & 0x03) == ((i >> 2) & 0x03))){ //if all domains are the same, add the whole thing ap = (t >> 4) & 3; } else{ //take the quarter that is the one we need err_str("quarter page found!\r\n"); ap = (t >> (4 + 2 * ap)) & 3; sz /= 4; pa += ((UInt32)ap) * sz; va += ((UInt32)ap) * sz; } translated: //insert tlb entry if(MMU_TLB_BUCKET_NUM && MMU_TLB_BUCKET_SIZE){ mmu->tlb[bucket][mmu->replPos[bucket]].pa = pa; mmu->tlb[bucket][mmu->replPos[bucket]].sz = sz; mmu->tlb[bucket][mmu->replPos[bucket]].va = va; mmu->tlb[bucket][mmu->replPos[bucket]].ap = ap; mmu->tlb[bucket][mmu->replPos[bucket]].domain = dom; mmu->readPos[bucket] = mmu->replPos[bucket]; if(++mmu->replPos[bucket] == MMU_TLB_BUCKET_SIZE) mmu->replPos[bucket] = 0; } check: //check domain permissions switch((mmu->domainCfg >> (dom * 2)) & 3){ case 0: //NO ACCESS: case 2: //RESERVED: unpredictable (treat as no access) *fsrP = (section ? 0x08 : 0xB) | (dom << 4); //section or page domain fault return false; case 1: //CLIENT: check permissions break; case 3: //MANAGER: allow all access goto calc; } //check permissions switch(ap){ case 0: if(write || (!mmu->R && (!priviledged || !mmu->S))) break; goto calc; case 1: if(!priviledged) break; goto calc; case 2: if(!priviledged && write) break; goto calc; case 3: //all is good, allow access! goto calc; } //perm_err: *fsrP = (section ? 0x0D : 0x0F) | (dom << 4); //section or subpage permission fault return false; calc: *paP = adr - va + pa; return true; } UInt32 mmuGetTTP(ArmMmu* mmu){ return mmu->transTablPA; } void mmuSetTTP(ArmMmu* mmu, UInt32 ttp){ UInt8 i; mmuTlbFlush(mmu); for(i = 0; i < MMU_TLB_BUCKET_NUM; i++){ mmu->replPos[i] = 0; mmu->readPos[i] = 0; } mmu->transTablPA = ttp; } void mmuSetS(ArmMmu* mmu, Boolean on){ mmu->S = on; } void mmuSetR(ArmMmu* mmu, Boolean on){ mmu->R = on; } Boolean mmuGetS(ArmMmu* mmu){ return mmu->S; } Boolean mmuGetR(ArmMmu* mmu){ return mmu->R; } UInt32 mmuGetDomainCfg(ArmMmu* mmu){ return mmu->domainCfg; } void mmuSetDomainCfg(ArmMmu* mmu, UInt32 val){ mmu->domainCfg = val; } /////////////////////////// debugging helpers /////////////////////////// UInt32 mmuDR(ArmMmu* mmu, UInt32 addr){ UInt32 t = 0; if(!mmu->readF(mmu->userData, &t, addr)) t = 0xFFFFFFF0UL; return t; } static void mmuDumpUpdate(UInt32 va, UInt32 pa, UInt32 len, UInt8 dom, UInt8 ap, Boolean c, Boolean b, Boolean valid){ UInt32 va_end;; static Boolean wasValid = false; static UInt8 wasDom = 0; static UInt8 wasAp = 0; static Boolean wasB = 0; static Boolean wasC = 0; static UInt32 startVa = 0; static UInt32 startPa = 0; static UInt32 expectPa = 0; va_end = (va || len) ? va - 1 : 0xFFFFFFFFUL; if(!wasValid && !valid) return; //no need to bother... if(valid != wasValid || dom != wasDom || ap != wasAp || c != wasC || b != wasB || expectPa != pa){ //not a continuation of what we've been at... if(wasValid){ err_str("0x"); err_hex(startVa); err_str("-0x"); err_hex(va_end); err_str(" -> 0x"); err_hex(startPa); err_str("-0x"); err_hex(startPa + (va_end - startVa)); err_str(" dom"); err_dec(wasDom); err_str(" ap"); err_dec(wasAp); err_str(" "); err_str(wasC ? "c" : " "); err_str(wasB ? "b" : " "); err_str("\r\n"); } wasValid = valid; if(valid){ //start of a new range wasDom = dom; wasAp = ap; wasC = c; wasB = b; startVa = va; startPa = pa; expectPa = pa + len; } } else{ //continuation of what we've been up to... expectPa += len; } } static void mmuDump(ArmMmu* mmu){ UInt32 i, j, t, sla, va, psz; UInt8 dom; Boolean coarse = false; for(i = 0; i < 0x1000; i++){ t = mmuDR(mmu, mmu->transTablPA + (i << 2)); va = i << 20; dom = (t >> 5) & 0x0F; switch(t & 3){ case 0: //done mmuDumpUpdate(va, 0, 1UL << 20, 0, 0, false, false, false); continue; case 1: //coarse page table coarse = true; t &= 0xFFFFFC00UL; break; case 2: //section mmuDumpUpdate(va, t & 0xFFF00000UL, 1UL << 20, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true); continue; case 3: //fine page table t &= 0xFFFFF000UL; break; } sla = t; psz = coarse ? 4096 : 1024; for(j = 0; j < ((1UL << 20) / psz); j++){ t = mmuDR(mmu, sla + (j << 2)); va = (i << 20) + (j * psz); switch(t & 3){ case 0: //invalid mmuDumpUpdate(va, 0, psz, 0, 0, false, false, false); break; case 1: //large 64k page mmuDumpUpdate(va + 0 * 16384UL, (t & 0xFFFF0000UL) + 0 * 16384UL, 16384, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 1 * 16384UL, (t & 0xFFFF0000UL) + 1 * 16384UL, 16384, dom, (t >> 6) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 2 * 16384UL, (t & 0xFFFF0000UL) + 2 * 16384UL, 16384, dom, (t >> 8) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 3 * 16384UL, (t & 0xFFFF0000UL) + 3 * 16384UL, 16384, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true); j += coarse ? 15 : 63; break; case 2: //small 4k page mmuDumpUpdate(va + 0 * 1024, (t & 0xFFFFF000UL) + 0 * 1024, 1024, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 1 * 1024, (t & 0xFFFFF000UL) + 1 * 1024, 1024, dom, (t >> 6) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 2 * 1024, (t & 0xFFFFF000UL) + 2 * 1024, 1024, dom, (t >> 8) & 3, !!(t & 8), !!(t & 4), true); mmuDumpUpdate(va + 3 * 1024, (t & 0xFFFFF000UL) + 3 * 1024, 1024, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true); if(!coarse) j += 3; break; case 3: //tiny 1k page or TEX page on pxa if(coarse){ mmuDumpUpdate(va, t & 0xFFFFF000UL, 4096, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true); } else{ mmuDumpUpdate(va, t & 0xFFFFFC00UL, 1024, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true); } break; } } } mmuDumpUpdate(0, 0, 0, 0, 0, false, false, false); //finish things off }