[PATCH v1 7/7] provide "sf protect check" command

Bernhard Kirchen schlimmchen at gmail.com
Thu Jan 28 17:18:47 CET 2021


this change exposes checking the protection mechanism of SPI NOR flash
chips to the CLI. it expands what was already there to communicate not
only the protection state of a particular region, but also whether or
not the hardware write protection mechanism of the chip is enabled.

the new command works for Micron (and compatible) SPI NOR flash chips as
well as the SST26* series of SPI NOR flash chips.

the changes were tested on proprietary boards using a M25P16 and a
SST26VF016B.

Signed-off-by: Bernhard Kirchen <bernhard.kirchen at mbconnectline.com>
---

 cmd/sf.c                       | 23 +++++++++++++++--
 drivers/mtd/spi/spi-nor-core.c | 45 +++++++++++++++++++++++++---------
 include/linux/mtd/spi-nor.h    | 17 ++++++++++++-
 include/spi_flash.h            |  8 ++++++
 4 files changed, 78 insertions(+), 15 deletions(-)

diff --git a/cmd/sf.c b/cmd/sf.c
index ecd2918cbc..664442813d 100644
--- a/cmd/sf.c
+++ b/cmd/sf.c
@@ -355,6 +355,7 @@ static int do_spi_protect(int argc, char *const argv[])
 	int ret = 0;
 	loff_t start, len;
 	bool prot = false;
+	struct lock_info info;
 
 	if (argc != 4)
 		return -1;
@@ -369,6 +370,24 @@ static int do_spi_protect(int argc, char *const argv[])
 		return 1;
 	}
 
+	if (strcmp(argv[1], "check") == 0) {
+		info.ofs = start;
+		info.len = len;
+		ret = spi_flash_is_protected(flash, &info);
+		if (ret != 0) {
+			printf("ERROR: operation failed (%d)\n", ret);
+			return 1;
+		}
+
+		printf("region [0x%06x, 0x%06x] is %sfully write-protected\n",
+		       info.ofs, (info.ofs + info.len - 1),
+		       (info.is_locked ? "" : "NOT "));
+		printf("hardware write protection is %sactive\n",
+		       (info.hw_protection ? "" : "NOT "));
+
+		return 0;
+	}
+
 	if (strcmp(argv[1], "lock") == 0)
 		prot = true;
 	else if (strcmp(argv[1], "unlock") == 0)
@@ -612,8 +631,8 @@ U_BOOT_CMD(
 	"sf update addr offset|partition len    - erase and write 'len' bytes from memory\n"
 	"                                         at 'addr' to flash at 'offset'\n"
 	"                                         or to start of mtd 'partition'\n"
-	"sf protect lock|unlock sector len      - protect/unprotect 'len' bytes starting\n"
-	"                                         at address 'sector'\n"
+	"sf protect lock|unlock|check offs len  - (un)protect or check protection of 'len'\n"
+	"                                         bytes starting at address 'offs'\n"
 #ifdef CONFIG_CMD_SF_TEST
 	"sf test offset len                     - run a very basic destructive test\n"
 	"                                         ranging 'len' bytes from 'offset'\n"
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 0b48e068be..f77f22684d 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -869,18 +869,25 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
  * Check if a region of the flash is (completely) locked. See stm_lock() for
  * more info.
  *
- * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
- * negative on errors.
+ * Updates struct lock_info in-place to reflect protection state.
  */
-static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int stm_is_locked(struct spi_nor *nor, struct lock_info *info)
 {
-	int status;
+	int status, ret;
 
 	status = read_sr(nor);
 	if (status < 0)
 		return status;
 
-	return stm_is_locked_sr(nor, ofs, len, status);
+	info->hw_protection = ((status & SR_SRWD) > 0);
+
+	ret = stm_is_locked_sr(nor, info->ofs, info->len, status);
+	if (ret < 0)
+		return ret;
+
+	info->is_locked = (ret > 0);
+
+	return 0;
 }
 #endif /* CONFIG_SPI_FLASH_STMICRO */
 
@@ -1208,18 +1215,32 @@ static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 	return sst26_lock_ctl(nor, ofs, len, SST26_CTL_LOCK);
 }
 
-static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26_is_locked(struct spi_nor *nor, struct lock_info *info)
 {
+	int ret;
+
 	/*
 	 * is_locked function is used for check before reading or erasing flash
-	 * region, so offset and length might be not 64k allighned, so adjust
-	 * them to be 64k allighned as sst26_lock_ctl works only with 64k
-	 * allighned regions.
+	 * region, so offset and length might be not 64k aligned, so adjust
+	 * them to be 64k aligned as sst26_lock_ctl works only with 64k
+	 * aligned regions.
 	 */
-	ofs -= ofs & (SZ_64K - 1);
-	len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
+	info->ofs -= info->ofs & (SZ_64K - 1);
+	info->len = info->len & (SZ_64K - 1) ? (info->len & ~(SZ_64K - 1)) + SZ_64K : info->len;
 
-	return sst26_lock_ctl(nor, ofs, len, SST26_CTL_CHECK);
+	ret = sst26_lock_ctl(nor, info->ofs, info->len, SST26_CTL_CHECK);
+	if (ret < 0)
+		return ret;
+
+	info->is_locked = (ret == 0);
+
+	ret = read_cr(nor);
+	if (ret < 0)
+		return ret;
+
+	info->hw_protection = ((ret & (CR_WPEN | CR_QUAD_EN_SPAN)) == CR_WPEN);
+
+	return 0;
 }
 
 static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index ee5a59cf0a..bfd503b1b2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -256,6 +256,21 @@ enum spi_nor_option_flags {
  */
 struct flash_info;
 
+/**
+ * struct lock_info - structure describing flash protection
+ * @ofs:            start address of region
+ * @len:            length of region
+ * @is_locked:      true if the region is write-protected (locked)
+ * @hw_protection:  true if the write-protection is backed by hardware, e.g.,
+ *                  the lock can only be removed by setting WP# pin high
+ */
+struct lock_info {
+	u32 ofs;
+	u32 len;
+	bool is_locked;
+	bool hw_protection;
+};
+
 /*
  * TODO: Remove, once all users of spi_flash interface are moved to MTD
  *
@@ -344,7 +359,7 @@ struct spi_nor {
 
 	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
-	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+	int (*flash_is_locked)(struct spi_nor *nor, struct lock_info *lock);
 	int (*quad_enable)(struct spi_nor *nor);
 
 	void *priv;
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 85cae32cc7..09a4fdad1c 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -179,4 +179,12 @@ static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len,
 		return flash->flash_unlock(flash, ofs, len);
 }
 
+static inline int spi_flash_is_protected(struct spi_flash *flash, struct lock_info *info)
+{
+	if (!flash->flash_is_locked)
+		return -EOPNOTSUPP;
+
+	return flash->flash_is_locked(flash, info);
+}
+
 #endif /* _SPI_FLASH_H_ */
-- 
2.29.2



More information about the U-Boot mailing list