diff --git a/desmume/src/MMU.c b/desmume/src/MMU.c index d1a5c6818..150f4c18b 100644 --- a/desmume/src/MMU.c +++ b/desmume/src/MMU.c @@ -28,6 +28,7 @@ #include "debug.h" #include "NDSSystem.h" #include "cflash.h" +#include "cp15.h" #include "registers.h" @@ -402,7 +403,7 @@ u8 FASTCALL MMU_read8(u32 proc, u32 adr) u16 FASTCALL MMU_read16(u32 proc, u32 adr) -{ +{ if((proc == ARMCPU_ARM9) && ((adr & ~0x3FFF) == MMU.DTCMRegion)) { /* Returns data from DTCM (ARM9 only) */ @@ -2287,3 +2288,126 @@ void FASTCALL MMU_doDMA(u32 proc, u32 num) break; } } + +u8 FASTCALL MMU_read8_acl(u32 proc, u32 adr, u32 access) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + return MMU_read8(proc,adr) ; +} + +u16 FASTCALL MMU_read16_acl(u32 proc, u32 adr, u32 access) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + return MMU_read16(proc,adr) ; +} + +u32 FASTCALL MMU_read32_acl(u32 proc, u32 adr, u32 access) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + return MMU_read32(proc,adr) ; +} + +void FASTCALL MMU_write8_acl(u32 proc, u32 adr, u8 val) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + u32 access = CP15_ACCESS_WRITE ; + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + MMU_write8(proc,adr,val) ; +} + +void FASTCALL MMU_write16_acl(u32 proc, u32 adr, u16 val) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + u32 access = CP15_ACCESS_WRITE ; + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + MMU_write16(proc,adr,val) ; +} + +void FASTCALL MMU_write32_acl(u32 proc, u32 adr, u32 val) +{ + if (proc == ARMCPU_ARM9) /* on arm9 we need to check the MPU regions */ + { + u32 access = CP15_ACCESS_WRITE ; + if ((NDS_ARM9.CPSR.val & 0x1F) == 0x10) + { + /* is user mode access */ + access &= ~1 ; + } else { + /* every other mode: sys */ + access |= 1 ; + } + if (armcp15_isAccessAllowed((armcp15_t *)NDS_ARM9.coproc[15],adr,access)==FALSE) + { + execute = FALSE ; + } + } + MMU_write32(proc,adr,val) ; +} diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index 6362fe458..f6a022a82 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -106,20 +106,34 @@ void MMU_setRom(u8 * rom, u32 mask); void MMU_unsetRom(); #define MMU_readByte MMU_read8 -#define MMU_readHWord MMU_read16 +#define MMU_readHWord MMU_read16 #define MMU_readWord MMU_read32 +#define MMU_readByteACL MMU_read8_acl +#define MMU_readHWordACL MMU_read16_acl +#define MMU_readWordACL MMU_read32_acl u8 FASTCALL MMU_read8(u32 proc, u32 adr); u16 FASTCALL MMU_read16(u32 proc, u32 adr); u32 FASTCALL MMU_read32(u32 proc, u32 adr); +u8 FASTCALL MMU_read8_acl(u32 proc, u32 adr, u32 access); +u16 FASTCALL MMU_read16_acl(u32 proc, u32 adr, u32 access); +u32 FASTCALL MMU_read32_acl(u32 proc, u32 adr, u32 access); + #define MMU_writeByte MMU_write8 #define MMU_writeHWord MMU_write16 #define MMU_writeWord MMU_write32 +#define MMU_writeByteACL MMU_write8_acl +#define MMU_writeHWordACL MMU_write16_acl +#define MMU_writeWordACL MMU_write32_acl void FASTCALL MMU_write8(u32 proc, u32 adr, u8 val); void FASTCALL MMU_write16(u32 proc, u32 adr, u16 val); void FASTCALL MMU_write32(u32 proc, u32 adr, u32 val); + +void FASTCALL MMU_write8_acl(u32 proc, u32 adr, u8 val); +void FASTCALL MMU_write16_acl(u32 proc, u32 adr, u16 val); +void FASTCALL MMU_write32_acl(u32 proc, u32 adr, u32 val); void FASTCALL MMU_doDMA(u32 proc, u32 num); diff --git a/desmume/src/cp15.c b/desmume/src/cp15.c index 75dbe8dfe..22141df05 100644 --- a/desmume/src/cp15.c +++ b/desmume/src/cp15.c @@ -55,10 +55,195 @@ armcp15_t *armcp15_new(armcpu_t * c) armcp15->ITCMRegion = 0x0C; armcp15->DTCMRegion = 0x0080000A; armcp15->processID = 0; + + /* preset calculated regionmasks */ + int i ; + for (i=0;i<8;i++) { + armcp15->regionWriteMask_USR[i] = 0 ; + armcp15->regionWriteMask_SYS[i] = 0 ; + armcp15->regionReadMask_USR[i] = 0 ; + armcp15->regionReadMask_SYS[i] = 0 ; + armcp15->regionExecuteMask_USR[i] = 0 ; + armcp15->regionExecuteMask_SYS[i] = 0 ; + armcp15->regionWriteSet_USR[i] = 0 ; + armcp15->regionWriteSet_SYS[i] = 0 ; + armcp15->regionReadSet_USR[i] = 0 ; + armcp15->regionReadSet_SYS[i] = 0 ; + armcp15->regionExecuteSet_USR[i] = 0 ; + armcp15->regionExecuteSet_SYS[i] = 0 ; + } ; return armcp15; } +#define ACCESSTYPE(val,n) (((val) > (4*n)) & 0x0F) +#define SIZEIDENTIFIER(val) ((((val) >> 1) & 0x1F)) +#define SIZEBINARY(val) (1 << (SIZEIDENTIFIER(val)+1)) +#define MASKFROMREG(val) (~((SIZEBINARY(val)-1) | 0x3F)) +#define SETFROMREG(val) ((val) & MASKFROMREG(val)) +/* sets the precalculated regions to mask,set for the affected accesstypes */ +void armcp15_setSingleRegionAccess(armcp15_t *armcp15,unsigned long dAccess,unsigned long iAccess,unsigned char num, unsigned long mask,unsigned long set) { + switch (ACCESSTYPE(dAccess,num)) { + case 4: /* UNP */ + case 7: /* UNP */ + case 8: /* UNP */ + case 9: /* UNP */ + case 10: /* UNP */ + case 11: /* UNP */ + case 12: /* UNP */ + case 13: /* UNP */ + case 14: /* UNP */ + case 15: /* UNP */ + case 0: /* no access at all */ + armcp15->regionWriteMask_USR[num] = 0 ; + armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_USR[num] = 0 ; + armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionWriteMask_SYS[num] = 0 ; + armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_SYS[num] = 0 ; + armcp15->regionReadSet_SYS[num] = 0xFFFFFFFF ; + break ; + case 1: /* no access at USR, all to sys */ + armcp15->regionWriteMask_USR[num] = 0 ; + armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_USR[num] = 0 ; + armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionWriteMask_SYS[num] = mask ; + armcp15->regionWriteSet_SYS[num] = set ; + armcp15->regionReadMask_SYS[num] = mask ; + armcp15->regionReadSet_SYS[num] = set ; + break ; + case 2: /* read at USR, all to sys */ + armcp15->regionWriteMask_USR[num] = 0 ; + armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_USR[num] = mask ; + armcp15->regionReadSet_USR[num] = set ; + armcp15->regionWriteMask_SYS[num] = mask ; + armcp15->regionWriteSet_SYS[num] = set ; + armcp15->regionReadMask_SYS[num] = mask ; + armcp15->regionReadSet_SYS[num] = set ; + break ; + case 3: /* all to USR, all to sys */ + armcp15->regionWriteMask_USR[num] = mask ; + armcp15->regionWriteSet_USR[num] = set ; + armcp15->regionReadMask_USR[num] = mask ; + armcp15->regionReadSet_USR[num] = set ; + armcp15->regionWriteMask_SYS[num] = mask ; + armcp15->regionWriteSet_SYS[num] = set ; + armcp15->regionReadMask_SYS[num] = mask ; + armcp15->regionReadSet_SYS[num] = set ; + break ; + case 5: /* no access at USR, read to sys */ + armcp15->regionWriteMask_USR[num] = 0 ; + armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_USR[num] = 0 ; + armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionWriteMask_SYS[num] = 0 ; + armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_SYS[num] = mask ; + armcp15->regionReadSet_SYS[num] = set ; + break ; + case 6: /* read at USR, read to sys */ + armcp15->regionWriteMask_USR[num] = 0 ; + armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_USR[num] = mask ; + armcp15->regionReadSet_USR[num] = set ; + armcp15->regionWriteMask_SYS[num] = 0 ; + armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; + armcp15->regionReadMask_SYS[num] = mask ; + armcp15->regionReadSet_SYS[num] = set ; + break ; + } + switch (ACCESSTYPE(iAccess,num)) { + case 4: /* UNP */ + case 7: /* UNP */ + case 8: /* UNP */ + case 9: /* UNP */ + case 10: /* UNP */ + case 11: /* UNP */ + case 12: /* UNP */ + case 13: /* UNP */ + case 14: /* UNP */ + case 15: /* UNP */ + case 0: /* no access at all */ + armcp15->regionExecuteMask_USR[num] = 0 ; + armcp15->regionExecuteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionExecuteMask_SYS[num] = 0 ; + armcp15->regionExecuteSet_SYS[num] = 0xFFFFFFFF ; + break ; + case 1: + armcp15->regionExecuteMask_USR[num] = 0 ; + armcp15->regionExecuteSet_USR[num] = 0xFFFFFFFF ; + armcp15->regionExecuteMask_SYS[num] = mask ; + armcp15->regionExecuteSet_SYS[num] = set ; + break ; + case 2: + case 3: + case 6: + armcp15->regionExecuteMask_USR[num] = mask ; + armcp15->regionExecuteSet_USR[num] = set ; + armcp15->regionExecuteMask_SYS[num] = mask ; + armcp15->regionExecuteSet_SYS[num] = set ; + break ; + } +} ; + +/* precalculate region masks/sets from cp15 register */ +void armcp15_maskPrecalc(armcp15_t *armcp15) +{ + #define precalc(num) { \ + u32 mask = 0, set = 0xFFFFFFFF ; /* (x & 0) == 0xFF..FF is allways false (disabled) */ \ + if (BIT_N(armcp15->protectBaseSize##num,0)) /* if region is enabled */ \ + { /* reason for this define: naming includes var */ \ + mask = MASKFROMREG(armcp15->protectBaseSize##num) ; \ + set = SETFROMREG(armcp15->protectBaseSize##num) ; \ + if (SIZEIDENTIFIER(armcp15->protectBaseSize##num)==0x1F) \ + { /* for the 4GB region, u32 suffers wraparound */ \ + mask = 0 ; set = 0 ; /* (x & 0) == 0 is allways true (enabled) */ \ + } \ + } \ + armcp15_setSingleRegionAccess(armcp15,armcp15->DaccessPerm,armcp15->IaccessPerm,num,mask,set) ; \ + } + precalc(0) ; + precalc(1) ; + precalc(2) ; + precalc(3) ; + precalc(4) ; + precalc(5) ; + precalc(6) ; + precalc(7) ; +} + +BOOL armcp15_isAccessAllowed(armcp15_t *armcp15,u32 address,u32 access) +{ + if (!(armcp15->ctrl & 1)) return TRUE ; /* protection checking is not enabled */ + int i ; + for (i=0;i<8;i++) { + switch (access) { + case CP15_ACCESS_WRITEUSR: + if ((address & armcp15->regionWriteMask_USR[i]) == armcp15->regionWriteSet_USR[i]) return TRUE ; + break ; + case CP15_ACCESS_WRITESYS: + if ((address & armcp15->regionWriteMask_SYS[i]) == armcp15->regionWriteSet_SYS[i]) return TRUE ; + break ; + case CP15_ACCESS_READUSR: + if ((address & armcp15->regionReadMask_USR[i]) == armcp15->regionReadSet_USR[i]) return TRUE ; + break ; + case CP15_ACCESS_READSYS: + if ((address & armcp15->regionReadMask_SYS[i]) == armcp15->regionReadSet_SYS[i]) return TRUE ; + break ; + case CP15_ACCESS_EXECUSR: + if ((address & armcp15->regionExecuteMask_USR[i]) == armcp15->regionExecuteSet_USR[i]) return TRUE ; + break ; + case CP15_ACCESS_EXECSYS: + if ((address & armcp15->regionExecuteMask_SYS[i]) == armcp15->regionExecuteSet_SYS[i]) return TRUE ; + break ; + } + } + /* when protections are enabled, but no region allows access, deny access */ + return FALSE ; +} BOOL armcp15_dataProcess(armcp15_t *armcp15, u8 CRd, u8 CRn, u8 CRm, u8 opcode1, u8 opcode2) { @@ -136,7 +321,7 @@ BOOL armcp15_moveCP2ARM(armcp15_t *armcp15, u32 * R, u8 CRn, u8 CRm, u8 opcode1, { case 2 : *R = armcp15->DaccessPerm; - return TRUE; + return TRUE; case 3 : *R = armcp15->IaccessPerm; return TRUE; @@ -293,7 +478,7 @@ BOOL armcp15_moveARM2CP(armcp15_t *armcp15, u32 val, u8 CRn, u8 CRm, u8 opcode1, { case 2 : armcp15->DaccessPerm = val; - return TRUE; + return TRUE; case 3 : armcp15->IaccessPerm = val; return TRUE; @@ -309,27 +494,35 @@ BOOL armcp15_moveARM2CP(armcp15_t *armcp15, u32 val, u8 CRn, u8 CRm, u8 opcode1, { case 0 : armcp15->protectBaseSize0 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 1 : armcp15->protectBaseSize1 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 2 : armcp15->protectBaseSize2 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 3 : armcp15->protectBaseSize3 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 4 : armcp15->protectBaseSize4 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 5 : armcp15->protectBaseSize5 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 6 : armcp15->protectBaseSize6 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; case 7 : armcp15->protectBaseSize7 = val; + armcp15_maskPrecalc(armcp15) ; return TRUE; default : return FALSE; diff --git a/desmume/src/cp15.h b/desmume/src/cp15.h index 6877096dc..c00e3625d 100644 --- a/desmume/src/cp15.h +++ b/desmume/src/cp15.h @@ -53,6 +53,20 @@ typedef struct u32 RAM_TAG; u32 testState; u32 cacheDbg; + /* calculated bitmasks for the regions to decide rights uppon */ + /* calculation is done in the MCR instead of on mem access for performance */ + u32 regionWriteMask_USR[8] ; + u32 regionWriteMask_SYS[8] ; + u32 regionReadMask_USR[8] ; + u32 regionReadMask_SYS[8] ; + u32 regionExecuteMask_USR[8] ; + u32 regionExecuteMask_SYS[8] ; + u32 regionWriteSet_USR[8] ; + u32 regionWriteSet_SYS[8] ; + u32 regionReadSet_USR[8] ; + u32 regionReadSet_SYS[8] ; + u32 regionExecuteSet_USR[8] ; + u32 regionExecuteSet_SYS[8] ; armcpu_t * cpu; @@ -64,5 +78,17 @@ BOOL armcp15_load(armcp15_t *armcp15, u8 CRd, u8 adr); BOOL armcp15_store(armcp15_t *armcp15, u8 CRd, u8 adr); BOOL armcp15_moveCP2ARM(armcp15_t *armcp15, u32 * R, u8 CRn, u8 CRm, u8 opcode1, u8 opcode2); BOOL armcp15_moveARM2CP(armcp15_t *armcp15, u32 val, u8 CRn, u8 CRm, u8 opcode1, u8 opcode2); +BOOL armcp15_isAccessAllowed(armcp15_t *armcp15,u32 address,u32 access) ; + + +#define CP15_ACCESS_WRITE 0 +#define CP15_ACCESS_READ 2 +#define CP15_ACCESS_EXECUTE 4 +#define CP15_ACCESS_WRITEUSR CP15_ACCESS_WRITE +#define CP15_ACCESS_WRITESYS 1 +#define CP15_ACCESS_READUSR CP15_ACCESS_READ +#define CP15_ACCESS_READSYS 3 +#define CP15_ACCESS_EXECUSR CP15_ACCESS_EXECUTE +#define CP15_ACCESS_EXECSYS 5 #endif /* __CP15_H__*/