[PATCH] target/arm: align down misaligned PMSAv7 MPU region base instead of dropping it

Kyle Fox posted 1 patch 2 days, 17 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260605034729.2874861-1-kylefoxaustin.github@gmail.com
Maintainers: Peter Maydell <peter.maydell@linaro.org>
target/arm/ptw.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
[PATCH] target/arm: align down misaligned PMSAv7 MPU region base instead of dropping it
Posted by Kyle Fox 2 days, 17 hours ago
When a PMSAv7 (ARMv7-M) MPU region's DRBAR base is not aligned to its
DRSR region size, get_phys_addr_pmsav7() logged a guest-error and
skipped the region entirely (continue). The architecture calls a
misaligned base UNPREDICTABLE, but real Cortex-M hardware does not
disable the region: RBAR.ADDR is only bits [31:log2(size)], so the
sub-size low bits are simply ignored and the region matches against the
aligned-down base.

NXP's i.MX95 Cortex-M7 firmware (and the MCUXpresso SDK demos) rely on
this. The M7 sets up a deny-all background region (region 0, whole
address space, AP=000) and then grants the peripheral space with a
512 MiB region programmed as DRBAR=0x4c800000 - misaligned, intended as
0x40000000. QEMU dropped that region, so a privileged access to e.g.
LPUART3 at 0x42570000 fell through to the deny-all region and took a
MemManage fault (CFSR.DACCVIOL), trapping the firmware in its default
fault handler before it could print anything.

Align the base down to the region size (base &= ~rmask) to match
silicon, and keep a (now-accurate) guest-error note. This only changes
the previously-UNPREDICTABLE misaligned case; correctly-aligned regions
are unaffected.

Signed-off-by: Kyle Fox <kylefoxaustin.github@gmail.com>
---
Found while bringing up the i.MX95 Cortex-M7 in an out-of-tree machine
model: the M7's MCUXpresso-SDK firmware programs the misaligned 512 MiB
peripheral region described above. With this change the firmware reaches
its FreeRTOS/UART banner; without it the region was dropped and the first
peripheral access took a MemManage DACCVIOL.

The new branch only executes in the previously-UNPREDICTABLE misaligned
case (base & rmask != 0), so correctly-aligned MPU regions are unchanged.

Tested on master: qemu-system-arm builds clean, and the ARMv7-M / MPS2
qtests pass with no regression -- boot-serial (incl. stm32vldiscovery,
Cortex-M3), the stm32l4x5 suite (Cortex-M4: exti/gpio/rcc/syscfg/usart),
microbit, sse-timer and cmsdk-apb-watchdog.

 target/arm/ptw.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 0a5201763a..3914d05449 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -2665,11 +2665,24 @@ static bool get_phys_addr_pmsav7(CPUARMState *env,
             rmask = (1ull << rsize) - 1;
 
             if (base & rmask) {
+                /*
+                 * The region base is not aligned to the region size. The
+                 * architecture calls this UNPREDICTABLE, but real Cortex-M
+                 * hardware ignores the sub-size low bits of RBAR.ADDR (the
+                 * field is only [31:log2(size)]) and matches against the
+                 * aligned-down base rather than disabling the region. NXP's
+                 * i.MX95 M7 firmware relies on this for its peripheral
+                 * region (e.g. DRBAR 0x4c800000 with a 512MB size, intended
+                 * as 0x40000000), so align down to match silicon instead of
+                 * dropping the region (which would leave the access to fall
+                 * through to a lower-priority deny-all background region).
+                 */
                 qemu_log_mask(LOG_GUEST_ERROR,
-                              "DRBAR[%d]: 0x%" PRIx32 " misaligned "
-                              "to DRSR region size, mask = 0x%" PRIx32 "\n",
-                              n, base, rmask);
-                continue;
+                              "DRBAR[%d]: 0x%" PRIx32 " not aligned to DRSR "
+                              "region size (mask 0x%" PRIx32 "); aligning down "
+                              "to 0x%" PRIx32 " to match Cortex-M behaviour\n",
+                              n, base, rmask, base & ~rmask);
+                base &= ~rmask;
             }
 
             if (address < base || address > base + rmask) {
-- 
2.34.1