kolibrios-fun/contrib/other/uarm/MMU.c

455 lines
9.9 KiB
C
Raw Normal View History

#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
}