From e00f150e17f643a64040a71d5cf528333e4d59a2 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 12 Nov 2026 09:54:34 -0800 Subject: [PATCH 1/2] Guard the STM32H7S GFXMMU window with an MPU region while the D-cache is on --- docs/Targets.md | 19 +++++++++++++++++++ hal/stm32h7s.c | 31 +++++++++++++++++++++++++++++++ hal/stm32h7s.h | 16 ++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/docs/Targets.md b/docs/Targets.md index bd7e9e68e3..99f2f296dd 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -1938,6 +1938,25 @@ external memory loader all leave a working configuration behind, so a port that omits this appears to boot fine and only fails once power is actually removed. +### GFXMMU window and the D-cache + +`0x25000000`-`0x25FFFFFF` holds the GFXMMU virtual buffers. With the GFXMMU +clock off, which is the reset state, a read in that window never completes: +no bus error is returned, the core stalls and the debugger can no longer +halt it. The window is Normal memory in the default memory map, and a +Cortex-M7 with the D-cache enabled may read Normal memory speculatively, so +no code has to touch the window on purpose. `hal_init()` therefore sets one +MPU region marking it no-access before enabling the D-cache, and +`hal_prepare_boot()` clears it again. With `NO_MPU=1` the HAL also enables +the MPU around that region and disables it before the jump, so the +application starts from the reset state; with `NO_MPU=0` the region is added +to wolfBoot's own map, in a slot that map does not use. + +An application that enables the D-cache needs the same protection: either a +no-access MPU region over the window, or a no-access background region with +only the memories in use and the peripheral range opened. The background +region STM32CubeMX generates covers `0x60000000`-`0xDFFFFFFF` only. + ### Bootloader self-update is off `RAM_CODE` is 0 in the example configuration. It would compile in the diff --git a/hal/stm32h7s.c b/hal/stm32h7s.c index 48fdac6660..16b951375f 100644 --- a/hal/stm32h7s.c +++ b/hal/stm32h7s.c @@ -276,6 +276,35 @@ void RAMFUNCTION hal_cache_invalidate(void) ISB(); } +/* Make the GFXMMU virtual-buffer window inaccessible while the D-cache is + * on. The window is Normal memory in the default map, the core may read + * Normal memory speculatively once the D-cache is enabled, and with the + * GFXMMU clock off such a read stalls the bus for good: no fault, and the + * debugger loses the core. A no-access region stops the read being issued. */ +static void mpu_guard_enable(void) +{ + MPU_RNR = GFXMMU_MPU_REGION; + MPU_RBAR = GFXMMU_VBUF_BASE; + MPU_RASR = MPU_RASR_XN | MPU_RASR_SIZE_16MB | MPU_RASR_ENABLE; +#ifdef WOLFBOOT_NO_MPU + /* Nothing else has set the MPU up: keep the default map for the rest */ + MPU_CTRL = MPU_CTRL_PRIVDEFENA | MPU_CTRL_ENABLE; +#endif + DSB(); + ISB(); +} + +static void mpu_guard_disable(void) +{ +#ifdef WOLFBOOT_NO_MPU + MPU_CTRL = 0; +#endif + MPU_RNR = GFXMMU_MPU_REGION; + MPU_RASR = 0; + DSB(); + ISB(); +} + static void dcache_enable(void) { uint32_t sets, ways; @@ -884,6 +913,7 @@ void hal_init(void) icache_enable(); octospi_gpio_init(); octospi_init(); + mpu_guard_enable(); dcache_enable(); #ifdef DEBUG_UART uart_init(); @@ -905,6 +935,7 @@ void hal_prepare_boot(void) octospi_enable_mmap(); dcache_disable(); icache_disable(); + mpu_guard_disable(); } #endif /* !WOLFBOOT_UNIT_TEST_FLASH_ERASE */ diff --git a/hal/stm32h7s.h b/hal/stm32h7s.h index 610eeedcf5..26c1a608c4 100644 --- a/hal/stm32h7s.h +++ b/hal/stm32h7s.h @@ -454,6 +454,22 @@ #define CCSIDR (*(volatile uint32_t *)(0xE000ED80UL)) #define CSSELR (*(volatile uint32_t *)(0xE000ED84UL)) +#define MPU_CTRL (*(volatile uint32_t *)(0xE000ED94UL)) +#define MPU_RNR (*(volatile uint32_t *)(0xE000ED98UL)) +#define MPU_RBAR (*(volatile uint32_t *)(0xE000ED9CUL)) +#define MPU_RASR (*(volatile uint32_t *)(0xE000EDA0UL)) +#define MPU_CTRL_ENABLE (1 << 0) +#define MPU_CTRL_PRIVDEFENA (1 << 2) +#define MPU_RASR_ENABLE (1 << 0) +#define MPU_RASR_SIZE_16MB (23 << 1) +#define MPU_RASR_XN (1 << 28) + +/* GFXMMU virtual buffers 0..3. A read here never completes while the + * GFXMMU clock is off. */ +#define GFXMMU_VBUF_BASE (0x25000000UL) +/* MPU region for the guard: one mpu_init() in src/boot_arm.c leaves free. */ +#define GFXMMU_MPU_REGION 3 + #define CACHE_LINE_SIZE 32 #endif /* _STM32H7S_DEF_INCLUDED */ From 9d5437760007c9786aa5fe21b82392f20f211abc Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 12 Nov 2026 14:27:31 -0800 Subject: [PATCH 2/2] Drop the GFXMMU section from the STM32H7S target docs --- docs/Targets.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index 99f2f296dd..bd7e9e68e3 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -1938,25 +1938,6 @@ external memory loader all leave a working configuration behind, so a port that omits this appears to boot fine and only fails once power is actually removed. -### GFXMMU window and the D-cache - -`0x25000000`-`0x25FFFFFF` holds the GFXMMU virtual buffers. With the GFXMMU -clock off, which is the reset state, a read in that window never completes: -no bus error is returned, the core stalls and the debugger can no longer -halt it. The window is Normal memory in the default memory map, and a -Cortex-M7 with the D-cache enabled may read Normal memory speculatively, so -no code has to touch the window on purpose. `hal_init()` therefore sets one -MPU region marking it no-access before enabling the D-cache, and -`hal_prepare_boot()` clears it again. With `NO_MPU=1` the HAL also enables -the MPU around that region and disables it before the jump, so the -application starts from the reset state; with `NO_MPU=0` the region is added -to wolfBoot's own map, in a slot that map does not use. - -An application that enables the D-cache needs the same protection: either a -no-access MPU region over the window, or a no-access background region with -only the memories in use and the peripheral range opened. The background -region STM32CubeMX generates covers `0x60000000`-`0xDFFFFFFF` only. - ### Bootloader self-update is off `RAM_CODE` is 0 in the example configuration. It would compile in the