Hi, Jonas, On 01/30/2019 12:07 AM, Jonas Bonn wrote: > Setting the BPNV bit defaults the block protection bits BP0-2 to 1 at > reset. This means that all the flash sectors are protected until they > are explicity unlocked by the user. > > Together with protection of the status register via the SRWD bit and the > WP# signal, this allows a flash device to be effectively protected from > erasure by unauthorized actors. > > The patch has been tested with a Cypress s25fl512s and works as > advertised. The concern that I have with this, though, is that the BPNV > bit is "write once": if somebody inadvertently sets this feature on the > device, it is irrevocable! Whether or not this actually belongs in the > mainline kernel is therefore up for debate... My opinion is that OTP bits should be set at manufacturer sites or in a client's controlled environment. I wouldn't add this in kernel. But maybe Marek or Boris think otherwise? Cheers, ta > > Signed-off-by: Jonas Bonn <jonas@xxxxxxxxxxx> > CC: Marek Vasut <marek.vasut@xxxxxxxxx> > CC: David Woodhouse <dwmw2@xxxxxxxxxxxxx> > CC: Brian Norris <computersforpeace@xxxxxxxxx> > CC: Boris Brezillon <bbrezillon@xxxxxxxxxx> > CC: Richard Weinberger <richard@xxxxxx> > CC: linux-mtd@xxxxxxxxxxxxxxxxxxx > --- > drivers/mtd/mtdchar.c | 6 ++ > drivers/mtd/mtdcore.c | 8 +++ > drivers/mtd/spi-nor/spi-nor.c | 102 +++++++++++++++++++++++----------- > include/linux/mtd/mtd.h | 2 + > include/linux/mtd/spi-nor.h | 1 + > include/uapi/mtd/mtd-abi.h | 1 + > 6 files changed, 88 insertions(+), 32 deletions(-) > > diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c > index affb0ac39928..31dd308d34b1 100644 > --- a/drivers/mtd/mtdchar.c > +++ b/drivers/mtd/mtdchar.c > @@ -803,6 +803,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) > break; > } > > + case MEMSETDEFAULTLOCKED: > + { > + ret = mtd_set_default_locked(mtd); > + break; > + } > + > case MEMLOCK: > { > struct erase_info_user einfo; > diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c > index 254c3b413213..3cae22f161cb 100644 > --- a/drivers/mtd/mtdcore.c > +++ b/drivers/mtd/mtdcore.c > @@ -1759,6 +1759,14 @@ int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) > } > EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg); > > +int mtd_set_default_locked(struct mtd_info *mtd) > +{ > + if (!mtd->_set_default_locked) > + return -EOPNOTSUPP; > + return mtd->_set_default_locked(mtd); > +} > +EXPORT_SYMBOL_GPL(mtd_set_default_locked); > + > /* Chip-supported device locking */ > int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) > { > diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c > index d6840c2b15dd..4f9b82ff87ce 100644 > --- a/drivers/mtd/spi-nor/spi-nor.c > +++ b/drivers/mtd/spi-nor/spi-nor.c > @@ -250,7 +250,7 @@ struct flash_info { > u16 page_size; > u16 addr_width; > > - u16 flags; > + u32 flags; > #define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */ > #define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ > #define SST_WRITE BIT(2) /* use SST byte programming */ > @@ -279,6 +279,10 @@ struct flash_info { > #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ > #define USE_CLSR BIT(14) /* use CLSR command */ > #define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */ > +#define SPI_NOR_HAS_BPNV BIT(16) /* Block protection bits can be set > + * non-volatile, meaning all blocks > + * are protected by default at reset > + */ > > /* Part specific fixup hooks. */ > const struct spi_nor_fixups *fixups; > @@ -1324,6 +1328,36 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) > return write_sr_and_check(nor, status_new, mask); > } > > +/* > + * Write status Register and configuration register with 2 bytes > + * The first byte will be written to the status register, while the > + * second byte will be written to the configuration register. > + * Return negative if error occurred. > + */ > +static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) > +{ > + int ret; > + > + write_enable(nor); > + > + ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); > + if (ret < 0) { > + dev_err(nor->dev, > + "error while writing configuration register\n"); > + return -EINVAL; > + } > + > + ret = spi_nor_wait_till_ready(nor); > + if (ret) { > + dev_err(nor->dev, > + "timeout while writing configuration register\n"); > + return ret; > + } > + > + return 0; > +} > + > + > /* > * Check if a region of the flash is (completely) locked. See stm_lock() for > * more info. > @@ -1342,6 +1376,35 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) > return stm_is_locked_sr(nor, ofs, len, status); > } > > +static int spi_nor_set_default_locked(struct mtd_info *mtd) > +{ > + struct spi_nor *nor = mtd_to_spi_nor(mtd); > + struct device *dev = nor->dev; > + u8 sr_cr[2]; > + int ret; > + > + ret = read_cr(nor); > + if (ret < 0) { > + dev_err(dev, "error while reading configuration register\n"); > + return -EINVAL; > + } > + > + if (ret & CR_BPNV) > + return 0; > + > + sr_cr[1] = ret | CR_BPNV; > + > + /* Keep the current value of the Status Register. */ > + ret = read_sr(nor); > + if (ret < 0) { > + dev_err(dev, "error while reading status register\n"); > + return -EINVAL; > + } > + sr_cr[0] = ret; > + > + return write_sr_cr(nor, sr_cr); > +} > + > static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) > { > struct spi_nor *nor = mtd_to_spi_nor(mtd); > @@ -1387,35 +1450,6 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) > return ret; > } > > -/* > - * Write status Register and configuration register with 2 bytes > - * The first byte will be written to the status register, while the > - * second byte will be written to the configuration register. > - * Return negative if error occurred. > - */ > -static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) > -{ > - int ret; > - > - write_enable(nor); > - > - ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); > - if (ret < 0) { > - dev_err(nor->dev, > - "error while writing configuration register\n"); > - return -EINVAL; > - } > - > - ret = spi_nor_wait_till_ready(nor); > - if (ret) { > - dev_err(nor->dev, > - "timeout while writing configuration register\n"); > - return ret; > - } > - > - return 0; > -} > - > /** > * macronix_quad_enable() - set QE bit in Status Register. > * @nor: pointer to a 'struct spi_nor' > @@ -1886,8 +1920,8 @@ static const struct flash_info spi_nor_ids[] = { > { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, > { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) }, > { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, > - { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | USE_CLSR) }, > - { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | USE_CLSR) }, > + { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_BPNV | USE_CLSR) }, > + { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_BPNV | USE_CLSR) }, > { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, > { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, > { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, > @@ -4066,6 +4100,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, > mtd->_is_locked = spi_nor_is_locked; > } > > + if (info->flags & SPI_NOR_HAS_BPNV) { > + mtd->_set_default_locked = spi_nor_set_default_locked; > + } > + > /* sst nor chips use AAI word program */ > if (info->flags & SST_WRITE) > mtd->_write = sst_write; > diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h > index b78e2c1b27b1..a07468b6677d 100644 > --- a/include/linux/mtd/mtd.h > +++ b/include/linux/mtd/mtd.h > @@ -346,6 +346,7 @@ struct mtd_info { > int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs, > unsigned long count, loff_t to, size_t *retlen); > void (*_sync) (struct mtd_info *mtd); > + int (*_set_default_locked) (struct mtd_info *mtd); > int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); > int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); > int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len); > @@ -535,6 +536,7 @@ static inline void mtd_sync(struct mtd_info *mtd) > int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); > int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); > int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); > +int mtd_set_default_locked(struct mtd_info *mtd); > int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs); > int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs); > int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index 2353af8bac99..c7cc84a5c8f6 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -144,6 +144,7 @@ > #define FSR_PT_ERR BIT(1) /* Protection error bit */ > > /* Configuration Register bits. */ > +#define CR_BPNV BIT(3) /* Block protection non-volatile */ > #define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ > > /* Status Register 2 bits. */ > diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h > index aff5b5e59845..27b752796efa 100644 > --- a/include/uapi/mtd/mtd-abi.h > +++ b/include/uapi/mtd/mtd-abi.h > @@ -204,6 +204,7 @@ struct otp_info { > * without OOB, e.g., NOR flash. > */ > #define MEMWRITE _IOWR('M', 24, struct mtd_write_req) > +#define MEMSETDEFAULTLOCKED _IO('M', 25) > > /* > * Obsolete legacy interface. Keep it in order not to break userspace > ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/