raise_mmu_exception() was an empty stub and get_physical_address() always returned full RWX permissions. Fill in the MPU:
- Walk 16 DPR ranges against DPRE/DPWE bitmasks for R/W - Walk 16 CPR ranges against CPXE bitmasks for X - Block instruction fetch from peripheral space (segments E/F) - Dispatch TRAPC_PROT / TRAPC_SYSBUS in raise_mmu_exception() Also fix a missing else in FIELD_SETTER_WITH_FEATURE and stop force-adding PAGE_EXEC in tricore_cpu_tlb_fill(). Signed-off-by: Parthiban Nallathambi <[email protected]> --- target/tricore/helper.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 237 insertions(+), 5 deletions(-) diff --git a/target/tricore/helper.c b/target/tricore/helper.c index ce1693622b..e50ea09a92 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -27,6 +27,9 @@ #include "qemu/qemu-print.h" enum { + TLBRET_MPX = -12, + TLBRET_MPW = -11, + TLBRET_MPR = -10, TLBRET_DIRTY = -4, TLBRET_INVALID = -3, TLBRET_NOMATCH = -2, @@ -34,6 +37,207 @@ enum { TLBRET_MATCH = 0 }; +static uint32_t tricore_mpu_dpr_lower(CPUTriCoreState *env, int idx) +{ + switch (idx) { + case 0: return env->DPR0_0L; + case 1: return env->DPR0_1L; + case 2: return env->DPR0_2L; + case 3: return env->DPR0_3L; + case 4: return env->DPR1_0L; + case 5: return env->DPR1_1L; + case 6: return env->DPR1_2L; + case 7: return env->DPR1_3L; + case 8: return env->DPR2_0L; + case 9: return env->DPR2_1L; + case 10: return env->DPR2_2L; + case 11: return env->DPR2_3L; + case 12: return env->DPR3_0L; + case 13: return env->DPR3_1L; + case 14: return env->DPR3_2L; + case 15: return env->DPR3_3L; + default: return 0; + } +} + +static uint32_t tricore_mpu_dpr_upper(CPUTriCoreState *env, int idx) +{ + switch (idx) { + case 0: return env->DPR0_0U; + case 1: return env->DPR0_1U; + case 2: return env->DPR0_2U; + case 3: return env->DPR0_3U; + case 4: return env->DPR1_0U; + case 5: return env->DPR1_1U; + case 6: return env->DPR1_2U; + case 7: return env->DPR1_3U; + case 8: return env->DPR2_0U; + case 9: return env->DPR2_1U; + case 10: return env->DPR2_2U; + case 11: return env->DPR2_3U; + case 12: return env->DPR3_0U; + case 13: return env->DPR3_1U; + case 14: return env->DPR3_2U; + case 15: return env->DPR3_3U; + default: return 0; + } +} + +static uint32_t tricore_mpu_cpr_lower(CPUTriCoreState *env, int idx) +{ + switch (idx) { + case 0: return env->CPR0_0L; + case 1: return env->CPR0_1L; + case 2: return env->CPR0_2L; + case 3: return env->CPR0_3L; + case 4: return env->CPR1_0L; + case 5: return env->CPR1_1L; + case 6: return env->CPR1_2L; + case 7: return env->CPR1_3L; + case 8: return env->CPR2_0L; + case 9: return env->CPR2_1L; + case 10: return env->CPR2_2L; + case 11: return env->CPR2_3L; + case 12: return env->CPR3_0L; + case 13: return env->CPR3_1L; + case 14: return env->CPR3_2L; + case 15: return env->CPR3_3L; + default: return 0; + } +} + +static uint32_t tricore_mpu_cpr_upper(CPUTriCoreState *env, int idx) +{ + switch (idx) { + case 0: return env->CPR0_0U; + case 1: return env->CPR0_1U; + case 2: return env->CPR0_2U; + case 3: return env->CPR0_3U; + case 4: return env->CPR1_0U; + case 5: return env->CPR1_1U; + case 6: return env->CPR1_2U; + case 7: return env->CPR1_3U; + case 8: return env->CPR2_0U; + case 9: return env->CPR2_1U; + case 10: return env->CPR2_2U; + case 11: return env->CPR2_3U; + case 12: return env->CPR3_0U; + case 13: return env->CPR3_1U; + case 14: return env->CPR3_2U; + case 15: return env->CPR3_3U; + default: return 0; + } +} + +static uint32_t tricore_mpu_dpre(CPUTriCoreState *env, int prs) +{ + switch (prs) { + case 0: return env->DPRE_0; + case 1: return env->DPRE_1; + case 2: return env->DPRE_2; + case 3: return env->DPRE_3; + default: return 0; + } +} + +static uint32_t tricore_mpu_dpwe(CPUTriCoreState *env, int prs) +{ + switch (prs) { + case 0: return env->DPWE_0; + case 1: return env->DPWE_1; + case 2: return env->DPWE_2; + case 3: return env->DPWE_3; + default: return 0; + } +} + +static uint32_t tricore_mpu_cpxe(CPUTriCoreState *env, int prs) +{ + switch (prs) { + case 0: return env->CPXE_0; + case 1: return env->CPXE_1; + case 2: return env->CPXE_2; + case 3: return env->CPXE_3; + default: return 0; + } +} + +static bool tricore_mpu_enabled(CPUTriCoreState *env) +{ + /* + * The MPU is enabled when SYSCON.MPEN (bit 1) is set. + * As a pragmatic fallback, also treat the MPU as enabled + * when any enable bitmap is nonzero, since some RTOS ports + * program the range/enable registers without setting SYSCON.MPEN. + */ + if (env->SYSCON & MASK_SYSCON_PRO_TEN) { + return true; + } + return (env->DPRE_0 | env->DPRE_1 | env->DPRE_2 | env->DPRE_3 | + env->DPWE_0 | env->DPWE_1 | env->DPWE_2 | env->DPWE_3 | + env->CPXE_0 | env->CPXE_1 | env->CPXE_2 | env->CPXE_3) != 0; +} + +static int tricore_mpu_check(CPUTriCoreState *env, vaddr address, + MMUAccessType access_type, int *prot) +{ + int prs = (env->PSW & MASK_PSW_PRS) >> 12; + uint32_t dpre = tricore_mpu_dpre(env, prs); + uint32_t dpwe = tricore_mpu_dpwe(env, prs); + uint32_t cpxe = tricore_mpu_cpxe(env, prs); + int i; + + *prot = 0; + + /* Walk the 16 data protection ranges */ + for (i = 0; i < 16; i++) { + uint32_t lower = tricore_mpu_dpr_lower(env, i); + uint32_t upper = tricore_mpu_dpr_upper(env, i); + + if (address >= lower && address < upper) { + if (dpre & (1u << i)) { + *prot |= PAGE_READ; + } + if (dpwe & (1u << i)) { + *prot |= PAGE_WRITE; + } + } + } + + /* Walk the 16 code protection ranges */ + for (i = 0; i < 16; i++) { + uint32_t lower = tricore_mpu_cpr_lower(env, i); + uint32_t upper = tricore_mpu_cpr_upper(env, i); + + if (address >= lower && address < upper) { + if (cpxe & (1u << i)) { + *prot |= PAGE_EXEC; + } + } + } + + /* Check the requested access against accumulated permissions */ + switch (access_type) { + case MMU_DATA_LOAD: + if (!(*prot & PAGE_READ)) { + return TLBRET_MPR; + } + break; + case MMU_DATA_STORE: + if (!(*prot & PAGE_WRITE)) { + return TLBRET_MPW; + } + break; + case MMU_INST_FETCH: + if (!(*prot & PAGE_EXEC)) { + return TLBRET_MPX; + } + break; + } + + return TLBRET_MATCH; +} + static int get_physical_address(CPUTriCoreState *env, hwaddr *physical, int *prot, vaddr address, MMUAccessType access_type, int mmu_idx) @@ -43,6 +247,19 @@ static int get_physical_address(CPUTriCoreState *env, hwaddr *physical, *physical = address & 0xFFFFFFFF; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + /* + * Block instruction fetch from peripheral space + * (segments 0xE and 0xF). + */ + if (access_type == MMU_INST_FETCH && + (address & 0xE0000000) == 0xE0000000) { + return TLBRET_MPX; + } + + if (tricore_mpu_enabled(env)) { + ret = tricore_mpu_check(env, address, access_type, prot); + } + return ret; } @@ -60,10 +277,25 @@ hwaddr tricore_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr) return phys_addr; } -/* TODO: Add exception support */ static void raise_mmu_exception(CPUTriCoreState *env, vaddr address, int rw, int tlb_error) { + CPUState *cs = env_cpu(env); + + switch (tlb_error) { + case TLBRET_MPR: + cs->exception_index = TRAPC_PROT; + break; + case TLBRET_MPW: + cs->exception_index = TRAPC_PROT; + break; + case TLBRET_MPX: + cs->exception_index = TRAPC_PROT; + break; + default: + cs->exception_index = TRAPC_SYSBUS; + break; + } } bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -73,9 +305,8 @@ bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPUTriCoreState *env = cpu_env(cs); hwaddr physical; int prot; - int ret = 0; + int ret; - rw &= 1; ret = get_physical_address(env, &physical, &prot, address, rw, mmu_idx); @@ -85,7 +316,7 @@ bool tricore_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TLBRET_MATCH) { tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); return true; } else { @@ -170,8 +401,9 @@ void NAME(CPUTriCoreState *env, uint32_t val) \ { \ if (tricore_has_feature(env, TRICORE_FEATURE_##FEATURE)) { \ env->REG = FIELD_DP32(env->REG, REG, FIELD ## _ ## FEATURE, val); \ + } else { \ + env->REG = FIELD_DP32(env->REG, REG, FIELD ## _13, val); \ } \ - env->REG = FIELD_DP32(env->REG, REG, FIELD ## _13, val); \ } #define FIELD_SETTER(NAME, REG, FIELD) \ -- 2.47.3
