<p>Patrick Rudolph has uploaded this change for <strong>review</strong>.</p><p><a href="https://review.coreboot.org/25082">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">[WIP]src/drivers/spi: Read Winbond's flash protection bits<br><br>Extend the generic flash interface to probe for write protected regions.<br>Add Winbond custom code to return flash protection.<br><br>As next step the interface will be extended to lock specific regions and<br>enable software flash lock down to prevent modifying the RO region until<br>next power off/reboot.<br><br>Change-Id: I933a8abdc28174ec32acf323c102d606b58c1ea5<br>Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com><br>---<br>M src/drivers/spi/spi_flash.c<br>M src/drivers/spi/spi_flash_internal.h<br>M src/drivers/spi/winbond.c<br>M src/include/spi_flash.h<br>4 files changed, 157 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://review.coreboot.org:29418/coreboot refs/changes/82/25082/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c</span><br><span>index 93335de..2ba07c0 100644</span><br><span>--- a/src/drivers/spi/spi_flash.c</span><br><span>+++ b/src/drivers/spi/spi_flash.c</span><br><span>@@ -61,15 +61,23 @@</span><br><span> return ret;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_cmd_ext(const struct spi_slave *spi, u8 *cmd, size_t cmd_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *response, size_t len)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- int ret = do_spi_flash_cmd(spi, &cmd, sizeof(cmd), response, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret = do_spi_flash_cmd(spi, cmd, cmd_len, response, len);</span><br><span> if (ret)</span><br><span style="color: hsl(0, 100%, 40%);">- printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n", cmd, ret);</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[0], ret);</span><br><span> </span><br><span> return ret;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response,</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return spi_flash_cmd_ext(spi, &cmd, sizeof(cmd), response, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int spi_flash_cmd_read(const struct spi_slave *spi, const u8 *cmd,</span><br><span> size_t cmd_len, void *data, size_t data_len)</span><br><span> {</span><br><span>@@ -422,6 +430,25 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_is_write_protected(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct region flash_region = { 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!flash)</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ flash_region.size = flash->size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!region_is_subregion(&flash_region, region))</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (flash->ops->read_write_protection)</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return flash->ops->read_write_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static uint32_t volatile_group_count CAR_GLOBAL;</span><br><span> </span><br><span> int spi_flash_volatile_group_begin(const struct spi_flash *flash)</span><br><span>diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h</span><br><span>index b42df59..ec72639 100644</span><br><span>--- a/src/drivers/spi/spi_flash_internal.h</span><br><span>+++ b/src/drivers/spi/spi_flash_internal.h</span><br><span>@@ -34,6 +34,10 @@</span><br><span> /* Send a single-byte command to the device and read the response */</span><br><span> int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Send multi-byte command to the device and read the response */</span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_cmd_ext(const struct spi_slave *spi, u8 *cmd, size_t cmd_len,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *response, size_t len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int spi_flash_cmd_read_fast(const struct spi_flash *flash, u32 offset,</span><br><span> size_t len, void *data);</span><br><span> </span><br><span>diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c</span><br><span>index 2fc8209..b7e83fe 100644</span><br><span>--- a/src/drivers/spi/winbond.c</span><br><span>+++ b/src/drivers/spi/winbond.c</span><br><span>@@ -17,6 +17,10 @@</span><br><span> #define CMD_W25_WRDI 0x04 /* Write Disable */</span><br><span> #define CMD_W25_RDSR 0x05 /* Read Status Register */</span><br><span> #define CMD_W25_WRSR 0x01 /* Write Status Register */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_RDSR2 0x35 /* Read Status2 Register */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_WRSR2 0x31 /* Write Status2 Register */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_RDSR3 0x15 /* Read Status3 Register */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_WRSR3 0x11 /* Write Status3 Register */</span><br><span> #define CMD_W25_READ 0x03 /* Read Data Bytes */</span><br><span> #define CMD_W25_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */</span><br><span> #define CMD_W25_PP 0x02 /* Page Program */</span><br><span>@@ -25,6 +29,7 @@</span><br><span> #define CMD_W25_CE 0xc7 /* Chip Erase */</span><br><span> #define CMD_W25_DP 0xb9 /* Deep Power-down */</span><br><span> #define CMD_W25_RES 0xab /* Release from DP, and Read Signature */</span><br><span style="color: hsl(120, 100%, 40%);">+#define CMD_W25_B_LOCK_STATUS 0x3d /* Read Block/Sector Lock bit */</span><br><span> </span><br><span> struct winbond_spi_flash_params {</span><br><span> uint16_t id;</span><br><span>@@ -184,6 +189,105 @@</span><br><span> return ret;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Only available on some devices.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Read Block Protection Bits of each block.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ * -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ * 1 if region is covered by write protection</span><br><span style="color: hsl(120, 100%, 40%);">+ * 0 if a port of region isn't covered by write protection</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int winbond_read_b_lock_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret, i;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 cmd[5], reg8;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[0] = CMD_W25_B_LOCK_STATUS;</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[4] = 0x00;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < region->size; i += 64 * KiB) {</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[1] = ((region->offset + i) >> 16) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[2] = ((region->offset + i) >> 8) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ cmd[3] = (region->offset + i) & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd),</span><br><span style="color: hsl(120, 100%, 40%);">+ ®8, sizeof(reg8));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(reg8 & 1))</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Available on all devices.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Read block protect bits from Status2 Reg.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ * -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ * 1 if region is covered by write protection</span><br><span style="color: hsl(120, 100%, 40%);">+ * 0 if a port of region isn't covered by write protection</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int winbond_read_b_protect_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret, block;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct region wp_region;</span><br><span style="color: hsl(120, 100%, 40%);">+ union {</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 u;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct {</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 busy : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 wel : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 bp : 3;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 tb : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 sec : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 srp0 : 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } s;</span><br><span style="color: hsl(120, 100%, 40%);">+ } reg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = spi_flash_cmd(&flash->spi, flash->status_cmd, ®.u,</span><br><span style="color: hsl(120, 100%, 40%);">+ sizeof(reg.u));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!reg.s.bp)</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Get minimal block size:</span><br><span style="color: hsl(120, 100%, 40%);">+ * SEC = 1: sector of 4KB</span><br><span style="color: hsl(120, 100%, 40%);">+ * SEC = 0: 1/64th of device, but at least one block</span><br><span style="color: hsl(120, 100%, 40%);">+ * FIXME: Don't assume block/sector size</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ block = reg.s.sec ? 4 : max(64, flash->size / (64 * KiB));</span><br><span style="color: hsl(120, 100%, 40%);">+ wp_region.size = min(flash->size, (1 << reg.s.bp) * block * KiB);</span><br><span style="color: hsl(120, 100%, 40%);">+ wp_region.offset = reg.s.tb ? 0 : (flash->size - wp_region.size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return region_is_subregion(&wp_region, region);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int winbond_read_write_protection(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 reg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* FIXME: Detect support for Status Reg3 / Block Lock bits */</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = spi_flash_cmd(&flash->spi, CMD_W25_RDSR3, ®, sizeof(reg));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ret)</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (reg & 4)</span><br><span style="color: hsl(120, 100%, 40%);">+ return winbond_read_b_lock_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ return winbond_read_b_protect_protection(flash, region);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static const struct spi_flash_ops spi_flash_ops = {</span><br><span> .write = winbond_write,</span><br><span> .erase = spi_flash_cmd_erase,</span><br><span>@@ -193,6 +297,7 @@</span><br><span> #else</span><br><span> .read = spi_flash_cmd_read_fast,</span><br><span> #endif</span><br><span style="color: hsl(120, 100%, 40%);">+ .read_write_protection = winbond_read_write_protection,</span><br><span> };</span><br><span> </span><br><span> int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,</span><br><span>diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h</span><br><span>index 3a6df9a..1ca0731 100644</span><br><span>--- a/src/include/spi_flash.h</span><br><span>+++ b/src/include/spi_flash.h</span><br><span>@@ -40,6 +40,9 @@</span><br><span> const void *buf);</span><br><span> int (*erase)(const struct spi_flash *flash, u32 offset, size_t len);</span><br><span> int (*status)(const struct spi_flash *flash, u8 *reg);</span><br><span style="color: hsl(120, 100%, 40%);">+ int (*read_write_protection)(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> };</span><br><span> </span><br><span> struct spi_flash {</span><br><span>@@ -93,6 +96,21 @@</span><br><span> const void *buf);</span><br><span> int spi_flash_erase(const struct spi_flash *flash, u32 offset, size_t len);</span><br><span> int spi_flash_status(const struct spi_flash *flash, u8 *reg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return the vendor dependent SPI flash write protection state.</span><br><span style="color: hsl(120, 100%, 40%);">+ * @param flash : A SPI flash device</span><br><span style="color: hsl(120, 100%, 40%);">+ * @param region: A subregion of the device's region</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+ * -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ * 0 if the device doesn't support block protection</span><br><span style="color: hsl(120, 100%, 40%);">+ * 0 if the device doesn't enable block protection</span><br><span style="color: hsl(120, 100%, 40%);">+ * 0 if given range isn't covered by block protection</span><br><span style="color: hsl(120, 100%, 40%);">+ * 1 if given range is covered by block protection</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int spi_flash_is_write_protected(const struct spi_flash *flash,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct region *region);</span><br><span> /*</span><br><span> * Some SPI controllers require exclusive access to SPI flash when volatile</span><br><span> * operations like erase or write are being performed. In such cases,</span><br><span></span><br></pre><p>To view, visit <a href="https://review.coreboot.org/25082">change 25082</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://review.coreboot.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://review.coreboot.org/25082"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: coreboot </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I933a8abdc28174ec32acf323c102d606b58c1ea5 </div>
<div style="display:none"> Gerrit-Change-Number: 25082 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Patrick Rudolph <patrick.rudolph@9elements.com> </div>