[coreboot-gerrit] Change in coreboot[master]: [WIP]src/drivers/spi: Read Winbond's flash protection bits

Patrick Rudolph (Code Review) gerrit at coreboot.org
Fri Mar 9 14:25:08 CET 2018


Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/25082


Change subject: [WIP]src/drivers/spi: Read Winbond's flash protection bits
......................................................................

[WIP]src/drivers/spi: Read Winbond's flash protection bits

Extend the generic flash interface to probe for write protected regions.
Add Winbond custom code to return flash protection.

As next step the interface will be extended to lock specific regions and
enable software flash lock down to prevent modifying the RO region until
next power off/reboot.

Change-Id: I933a8abdc28174ec32acf323c102d606b58c1ea5
Signed-off-by: Patrick Rudolph <patrick.rudolph at 9elements.com>
---
M src/drivers/spi/spi_flash.c
M src/drivers/spi/spi_flash_internal.h
M src/drivers/spi/winbond.c
M src/include/spi_flash.h
4 files changed, 157 insertions(+), 3 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/82/25082/1

diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 93335de..2ba07c0 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -61,15 +61,23 @@
 	return ret;
 }
 
-int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len)
+int spi_flash_cmd_ext(const struct spi_slave *spi, u8 *cmd, size_t cmd_len,
+		      void *response, size_t len)
 {
-	int ret = do_spi_flash_cmd(spi, &cmd, sizeof(cmd), response, len);
+	int ret = do_spi_flash_cmd(spi, cmd, cmd_len, response, len);
 	if (ret)
-		printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n", cmd, ret);
+		printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n",
+		       cmd[0], ret);
 
 	return ret;
 }
 
+int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response,
+		  size_t len)
+{
+	return spi_flash_cmd_ext(spi, &cmd, sizeof(cmd), response, len);
+}
+
 static int spi_flash_cmd_read(const struct spi_slave *spi, const u8 *cmd,
 			      size_t cmd_len, void *data, size_t data_len)
 {
@@ -422,6 +430,25 @@
 	return -1;
 }
 
+int spi_flash_is_write_protected(const struct spi_flash *flash,
+				 const struct region *region)
+{
+	struct region flash_region = { 0 };
+
+	if (!flash)
+		return -1;
+
+	flash_region.size = flash->size;
+
+	if (!region_is_subregion(&flash_region, region))
+		return -1;
+
+	if (flash->ops->read_write_protection)
+		return 0;
+
+	return flash->ops->read_write_protection(flash, region);
+}
+
 static uint32_t volatile_group_count CAR_GLOBAL;
 
 int spi_flash_volatile_group_begin(const struct spi_flash *flash)
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h
index b42df59..ec72639 100644
--- a/src/drivers/spi/spi_flash_internal.h
+++ b/src/drivers/spi/spi_flash_internal.h
@@ -34,6 +34,10 @@
 /* Send a single-byte command to the device and read the response */
 int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len);
 
+/* Send multi-byte command to the device and read the response */
+int spi_flash_cmd_ext(const struct spi_slave *spi, u8 *cmd, size_t cmd_len,
+		      void *response, size_t len);
+
 int spi_flash_cmd_read_fast(const struct spi_flash *flash, u32 offset,
 		size_t len, void *data);
 
diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c
index 2fc8209..b7e83fe 100644
--- a/src/drivers/spi/winbond.c
+++ b/src/drivers/spi/winbond.c
@@ -17,6 +17,10 @@
 #define CMD_W25_WRDI		0x04	/* Write Disable */
 #define CMD_W25_RDSR		0x05	/* Read Status Register */
 #define CMD_W25_WRSR		0x01	/* Write Status Register */
+#define CMD_W25_RDSR2		0x35	/* Read Status2 Register */
+#define CMD_W25_WRSR2		0x31	/* Write Status2 Register */
+#define CMD_W25_RDSR3		0x15	/* Read Status3 Register */
+#define CMD_W25_WRSR3		0x11	/* Write Status3 Register */
 #define CMD_W25_READ		0x03	/* Read Data Bytes */
 #define CMD_W25_FAST_READ	0x0b	/* Read Data Bytes at Higher Speed */
 #define CMD_W25_PP		0x02	/* Page Program */
@@ -25,6 +29,7 @@
 #define CMD_W25_CE		0xc7	/* Chip Erase */
 #define CMD_W25_DP		0xb9	/* Deep Power-down */
 #define CMD_W25_RES		0xab	/* Release from DP, and Read Signature */
+#define CMD_W25_B_LOCK_STATUS	0x3d	/* Read Block/Sector Lock bit */
 
 struct winbond_spi_flash_params {
 	uint16_t	id;
@@ -184,6 +189,105 @@
 	return ret;
 }
 
+/*
+ * Only available on some devices.
+ * Read Block Protection Bits of each block.
+ *
+ * Returns:
+ * -1    on error
+ *  1    if region is covered by write protection
+ *  0    if a port of region isn't covered by write protection
+ */
+static int winbond_read_b_lock_protection(const struct spi_flash *flash,
+					  const struct region *region)
+{
+	int ret, i;
+	u8 cmd[5], reg8;
+
+	cmd[0] = CMD_W25_B_LOCK_STATUS;
+	cmd[4] = 0x00;
+
+	for (i = 0; i < region->size; i += 64 * KiB) {
+		cmd[1] = ((region->offset + i) >> 16) & 0xff;
+		cmd[2] = ((region->offset + i) >> 8) & 0xff;
+		cmd[3] = (region->offset + i) & 0xff;
+
+		ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd),
+					 &reg8, sizeof(reg8));
+		if (ret)
+			return ret;
+		if (!(reg8 & 1))
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Available on all devices.
+ * Read block protect bits from Status2 Reg.
+ *
+ * Returns:
+ * -1    on error
+ *  1    if region is covered by write protection
+ *  0    if a port of region isn't covered by write protection
+ */
+static int winbond_read_b_protect_protection(const struct spi_flash *flash,
+					     const struct region *region)
+{
+	int ret, block;
+	struct region wp_region;
+	union {
+		u8 u;
+		struct {
+			u8 busy : 1;
+			u8 wel  : 1;
+			u8 bp   : 3;
+			u8 tb   : 1;
+			u8 sec  : 1;
+			u8 srp0 : 1;
+		} s;
+	} reg;
+
+	ret = spi_flash_cmd(&flash->spi, flash->status_cmd, &reg.u,
+			    sizeof(reg.u));
+	if (ret)
+		return ret;
+
+	if (!reg.s.bp)
+		return 0;
+
+	/*
+	 * Get minimal block size:
+	 * SEC = 1: sector of 4KB
+	 * SEC = 0: 1/64th of device, but at least one block
+	 * FIXME: Don't assume block/sector size
+	 */
+	block = reg.s.sec ? 4 : max(64, flash->size / (64 * KiB));
+	wp_region.size = min(flash->size, (1 << reg.s.bp) * block * KiB);
+	wp_region.offset = reg.s.tb ? 0 : (flash->size - wp_region.size);
+
+	return region_is_subregion(&wp_region, region);
+}
+
+
+static int winbond_read_write_protection(const struct spi_flash *flash,
+					 const struct region *region)
+{
+	int ret;
+	u8 reg;
+
+	/* FIXME: Detect support for Status Reg3 / Block Lock bits */
+	ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR3, &reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (reg & 4)
+		return winbond_read_b_lock_protection(flash, region);
+	else
+		return winbond_read_b_protect_protection(flash, region);
+}
+
 static const struct spi_flash_ops spi_flash_ops = {
 	.write = winbond_write,
 	.erase = spi_flash_cmd_erase,
@@ -193,6 +297,7 @@
 #else
 	.read = spi_flash_cmd_read_fast,
 #endif
+	.read_write_protection = winbond_read_write_protection,
 };
 
 int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,
diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h
index 3a6df9a..1ca0731 100644
--- a/src/include/spi_flash.h
+++ b/src/include/spi_flash.h
@@ -40,6 +40,9 @@
 			const void *buf);
 	int (*erase)(const struct spi_flash *flash, u32 offset, size_t len);
 	int (*status)(const struct spi_flash *flash, u8 *reg);
+	int (*read_write_protection)(const struct spi_flash *flash,
+				     const struct region *region);
+
 };
 
 struct spi_flash {
@@ -93,6 +96,21 @@
 		    const void *buf);
 int spi_flash_erase(const struct spi_flash *flash, u32 offset, size_t len);
 int spi_flash_status(const struct spi_flash *flash, u8 *reg);
+
+/*
+ * Return the vendor dependent SPI flash write protection state.
+ * @param flash : A SPI flash device
+ * @param region: A subregion of the device's region
+ *
+ * Returns:
+ *  -1   on error
+ *   0   if the device doesn't support block protection
+ *   0   if the device doesn't enable block protection
+ *   0   if given range isn't covered by block protection
+ *   1   if given range is covered by block protection
+ */
+int spi_flash_is_write_protected(const struct spi_flash *flash,
+				 const struct region *region);
 /*
  * Some SPI controllers require exclusive access to SPI flash when volatile
  * operations like erase or write are being performed. In such cases,

-- 
To view, visit https://review.coreboot.org/25082
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I933a8abdc28174ec32acf323c102d606b58c1ea5
Gerrit-Change-Number: 25082
Gerrit-PatchSet: 1
Gerrit-Owner: Patrick Rudolph <patrick.rudolph at 9elements.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180309/092058c3/attachment-0001.html>


More information about the coreboot-gerrit mailing list