M platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c => platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c +31 -1
  
@@ 17,6 17,9 @@ static flash_sector_t first_sector = UINT16_MAX;
 static flash_sector_t sector_count = UINT16_MAX;
 static BaseFlash *    flash;
 
+static volatile bool is_issuing_read    = false;
+static volatile bool ecc_error_occurred = false;
+
 // "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't.
 static inline uint32_t detect_flash_size(void) {
 #if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
@@ 131,11 134,38 @@ bool backing_store_lock(void) {
     return true;
 }
 
+static backing_store_int_t backing_store_safe_read_from_location(backing_store_int_t *loc) {
+    backing_store_int_t value;
+    is_issuing_read    = true;
+    ecc_error_occurred = false;
+    value              = ~(*loc);
+    is_issuing_read    = false;
+    return value;
+}
+
 bool backing_store_read(uint32_t address, backing_store_int_t *value) {
     uint32_t             offset = (base_offset + address);
     backing_store_int_t *loc    = (backing_store_int_t *)flashGetOffsetAddress(flash, offset);
-    *value                      = ~(*loc);
+    backing_store_int_t  tmp    = backing_store_safe_read_from_location(loc);
+
+    if (ecc_error_occurred) {
+        bs_dprintf("Failed to read from backing store, ECC error detected\n");
+        ecc_error_occurred = false;
+        *value             = 0;
+        return false;
+    }
+
+    *value = tmp;
+
     bs_dprintf("Read  ");
     wl_dump(offset, value, sizeof(backing_store_int_t));
     return true;
 }
+
+bool backing_store_allow_ecc_errors(void) {
+    return is_issuing_read;
+}
+
+void backing_store_signal_ecc_error(void) {
+    ecc_error_occurred = true;
+}
 
A platforms/chibios/interrupt_handlers.c => platforms/chibios/interrupt_handlers.c +45 -0
  
@@ 0,0 1,45 @@
+// Copyright 2023 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+///////////////////////////////////////////////////////////////////////////////
+// BEGIN: STM32 EFL Wear-leveling ECC fault handling
+//
+// Some STM32s have ECC checks for all flash memory access. Whenever there's an
+// ECC failure, the MCU raises the NMI interrupt. Whenever we receive such an
+// interrupt whilst reading the wear-leveling EEPROM area, we gracefully cater
+// for it, signalling the wear-leveling code that a failure has occurred.
+///////////////////////////////////////////////////////////////////////////////
+
+#include <ch.h>
+#include <chcore.h>
+
+#ifdef WEAR_LEVELING_EMBEDDED_FLASH
+#    ifdef QMK_MCU_SERIES_STM32L4XX
+#        define ECC_ERRORS_TRIGGER_NMI_INTERRUPT
+#        define ECC_CHECK_REGISTER FLASH->ECCR
+#        define ECC_CHECK_FLAG FLASH_ECCR_ECCD
+#    endif // QMK_MCU_SERIES_STM32L4XX
+#endif     // WEAR_LEVELING_EMBEDDED_FLASH
+
+#ifdef ECC_ERRORS_TRIGGER_NMI_INTERRUPT
+
+extern bool backing_store_allow_ecc_errors(void);
+extern void backing_store_signal_ecc_error(void);
+
+void NMI_Handler(void) {
+    if ((ECC_CHECK_REGISTER) & (ECC_CHECK_FLAG)) {
+        if (backing_store_allow_ecc_errors()) {
+            (ECC_CHECK_REGISTER) = (ECC_CHECK_FLAG);
+            backing_store_signal_ecc_error();
+            return;
+        }
+    }
+
+    chSysHalt("NMI");
+}
+
+#endif // ECC_ERRORS_TRIGGER_NMI_INTERRUPT
+
+///////////////////////////////////////////////////////////////////////////////
+// END: STM32 EFL Wear-leveling ECC fault handling
+///////////////////////////////////////////////////////////////////////////////
 
M platforms/chibios/platform.mk => platforms/chibios/platform.mk +2 -1
  
@@ 277,7 277,8 @@ PLATFORM_SRC = \
         $(CHIBIOS)/os/various/syscalls.c \
         $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \
         $(PLATFORM_COMMON_DIR)/wait.c \
-        $(PLATFORM_COMMON_DIR)/synchronization_util.c
+        $(PLATFORM_COMMON_DIR)/synchronization_util.c \
+        $(PLATFORM_COMMON_DIR)/interrupt_handlers.c
 
 # Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise.
 QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)