[PATCH 03/16] target/arm: opt-in align-down for a misaligned PMSAv7 MPU RBAR
Kyle Fox <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
A PMSAv7 MPU region base that is not aligned to its region size is UNPREDICTABLE per the architecture, and QEMU disables such a region. Some PMSAv7 implementations instead ignore the sub-size low bits of RBAR.ADDR and match against the aligned-down base. The NXP i.MX 95 RT core (Cortex-M7) SDK firmware relies on this for a peripheral region (DRBAR 0x4c800000 programmed with a 512MB size, intended as 0x40000000). Add an opt-in "pmsav7-rbar-align-down" property that aligns such a base down instead of dropping the region. It defaults to false, so existing behaviour is unchanged, and is only registered on PMSAv7 CPUs (v7-M and v7-R, i.e. v7 without v8), where get_phys_addr_pmsav7() runs. Signed-off-by: Kyle Fox <[email protected]> --- target/arm/cpu.c | 19 +++++++++++++++++++ target/arm/cpu.h | 6 ++++++ target/arm/ptw.c | 28 +++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 77aa78f00e2..22e84c1ced2 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1349,6 +1349,16 @@ static const Property arm_cpu_pmsav7_dregion_property = DEFINE_PROP_UNSIGNED_NODEFAULT("pmsav7-dregion", ARMCPU, pmsav7_dregion, qdev_prop_uint32, uint32_t); + +/* + * Opt-in: treat a PMSAv7 MPU region base that is not aligned to its region + * size as aligned-down instead of UNPREDICTABLE (see get_phys_addr_pmsav7()). + * Only registered on PMSAv7 (v7-M / v7-R) CPUs, and only enabled by an + * integrator whose firmware relies on it (e.g. the i.MX 95 Cortex-M7). + */ +static const Property arm_cpu_pmsav7_rbar_align_down_property = + DEFINE_PROP_BOOL("pmsav7-rbar-align-down", ARMCPU, + pmsav7_rbar_align_down, false); #endif static bool arm_get_pmu(Object *obj, Error **errp) @@ -1659,6 +1669,15 @@ static void arm_cpu_post_init(Object *obj) if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_pmsav7_dregion_property); + /* + * get_phys_addr_pmsav7() only runs on PMSAv7 CPUs, i.e. v7 + * without v8 (a v8 M/R core uses the PMSAv8 MPU, which has no + * such alignment rule), so only offer the property there. + */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) { + qdev_property_add_static(DEVICE(obj), + &arm_cpu_pmsav7_rbar_align_down_property); + } } } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c0492c8dfbc..03ead3cda15 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1145,6 +1145,12 @@ struct ArchCPU { * architecture version. */ bool cfgend; + /* + * If set, a PMSAv7 MPU region base that is not aligned to its region + * size is treated as aligned-down (matching Cortex-M silicon such as the + * i.MX 95 M-cores) instead of the architectural UNPREDICTABLE drop. + */ + bool pmsav7_rbar_align_down; QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; QLIST_HEAD(, ARMELChangeHook) el_change_hooks; diff --git a/target/arm/ptw.c b/target/arm/ptw.c index a29de0385f4..51805c05226 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -2757,11 +2757,29 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, rmask = (1ull << rsize) - 1; if (base & rmask) { - qemu_log_mask(LOG_GUEST_ERROR, - "DRBAR[%d]: 0x%" PRIx32 " misaligned " - "to DRSR region size, mask = 0x%" PRIx32 "\n", - n, base, rmask); - continue; + if (cpu->pmsav7_rbar_align_down) { + /* + * Opt-in behaviour for Cortex-M silicon such as the + * i.MX 95 M-cores: a region base not aligned to its size + * is treated as aligned-down (RBAR.ADDR is only + * [31:log2(size)]), matching that hardware, whose SM/RT + * firmware programs e.g. DRBAR 0x4c800000 with a 512MB + * size intended as 0x40000000. Off by default because the + * architecture calls a misaligned base UNPREDICTABLE. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "DRBAR[%d]: 0x%" PRIx32 " not aligned to " + "DRSR region size (mask 0x%" PRIx32 "); " + "aligning down to 0x%" PRIx32 "\n", + n, base, rmask, base & ~rmask); + base &= ~rmask; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "DRBAR[%d]: 0x%" PRIx32 " misaligned " + "to DRSR region size, mask = 0x%" PRIx32 "\n", + n, base, rmask); + continue; + } } if (address < base || address > base + rmask) { -- 2.34.1