[PATCH] spi-nor: Verify written data in paranoid mode
From: Bence Csókás
Date: Tue Apr 15 2025 - 14:05:14 EST
From: Csókás, Bence <csokas.bence@xxxxxxxxx>
Add MTD_SPI_NOR_PARANOID config option for verifying all written data to
prevent silent bit errors to be undetected, at the cost of halving SPI
bandwidth.
Co-developed-by: Szentendrei, Tamás <szentendrei.tamas@xxxxxxxxx>
Signed-off-by: Szentendrei, Tamás <szentendrei.tamas@xxxxxxxxx>
Signed-off-by: Csókás, Bence <csokas.bence@xxxxxxxxx>
---
drivers/mtd/spi-nor/Kconfig | 10 ++++++++++
drivers/mtd/spi-nor/core.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 24cd25de2b8b..425ea9a22424 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -68,6 +68,16 @@ config MTD_SPI_NOR_SWP_KEEP
endchoice
+config MTD_SPI_NOR_PARANOID
+ bool "Read back written data (paranoid mode)"
+ help
+ This option makes the SPI NOR core read back all data on a write
+ and report an error if it doesn't match the written data. This can
+ safeguard against silent bit errors resulting from a faulty Flash,
+ controller oddities, bus noise etc.
+
+ If you are unsure, select 'n'.
+
source "drivers/mtd/spi-nor/controllers/Kconfig"
endif # MTD_SPI_NOR
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ac4b960101cc..ca05a6ec8afe 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2063,6 +2063,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ u_char *verify_buf = NULL;
size_t i;
ssize_t ret;
u32 page_size = nor->params->page_size;
@@ -2073,6 +2074,14 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
+#if IS_ENABLED(CONFIG_MTD_SPI_NOR_PARANOID)
+ verify_buf = devm_kmalloc(nor->dev, page_size, GFP_KERNEL);
+ if (!verify_buf) {
+ ret = -ENOMEM;
+ goto write_err;
+ }
+#endif
+
for (i = 0; i < len; ) {
ssize_t written;
loff_t addr = to + i;
@@ -2099,11 +2108,35 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
+
+#if IS_ENABLED(CONFIG_MTD_SPI_NOR_PARANOID)
+ /* read back to make sure it's correct */
+ ret = spi_nor_read_data(nor, addr, written, verify_buf);
+ if (ret < 0)
+ goto write_err;
+ if (ret != written) {
+ /* We shouldn't see short reads */
+ dev_err(nor->dev, "Verify failed, written %zd but only read %zd",
+ written, ret);
+ ret = -EIO;
+ goto write_err;
+ }
+
+ if (memcmp(verify_buf, buf + i, written)) {
+ dev_err(nor->dev, "Verify failed, compare mismatch!");
+ ret = -EIO;
+ goto write_err;
+ }
+#endif
+
+ ret = 0;
+
*retlen += written;
i += written;
}
write_err:
+ devm_kfree(nor->dev, verify_buf);
spi_nor_unlock_and_unprep_pe(nor, to, len);
return ret;
base-commit: 834a4a689699090a406d1662b03affa8b155d025
--
2.49.0