<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%);">+                                  &reg8, 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, &reg.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, &reg, 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>