forked from KolibriOS/kolibrios
455 lines
9.9 KiB
C
455 lines
9.9 KiB
C
|
#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
|
||
|
}
|