diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6b904e43937289..8e96e2c65c7a5f 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -spi-nor-objs := core.o sfdp.o swp.o otp.o sysfs.o +spi-nor-objs := core.o sfdp.o swp.o otp.o advprotsec.o sysfs.o spi-nor-objs += atmel.o spi-nor-objs += catalyst.o spi-nor-objs += eon.o diff --git a/drivers/mtd/spi-nor/advprotsec.c b/drivers/mtd/spi-nor/advprotsec.c new file mode 100644 index 00000000000000..4dc8e67b16ef2a --- /dev/null +++ b/drivers/mtd/spi-nor/advprotsec.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI NOR Advanced Sector Protection and Security Features + * + * Copyright (C) 2021 Micron Technology, Inc. + */ + +#include +#include + +#include "core.h" + +static int spi_nor_secure_read(struct mtd_info *mtd, size_t len, u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->secure_read(nor, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_secure_write(struct mtd_info *mtd, size_t len, u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->secure_write(nor, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_read_vlock_bits(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->read_vlock_bits(nor, addr, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_write_vlock_bits(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->write_vlock_bits(nor, addr, len, buf); + if (ret) + return ret; + + ret = spi_nor_write_disable(nor); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_read_nvlock_bits(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->read_nvlock_bits(nor, addr, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_write_nvlock_bits(struct mtd_info *mtd, u32 addr) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->write_nvlock_bits(nor, addr); + if (ret) + return ret; + + ret = spi_nor_write_disable(nor); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_erase_nvlock_bits(struct mtd_info *mtd) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->erase_nvlock_bits(nor); + if (ret) + return ret; + + ret = spi_nor_write_disable(nor); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_read_global_freeze_bits(struct mtd_info *mtd, size_t len, + u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->read_global_freeze_bits(nor, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_write_global_freeze_bits(struct mtd_info *mtd, size_t len, + u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->write_global_freeze_bits(nor, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_read_password(struct mtd_info *mtd, size_t len, u8 *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->sec_ops->read_password(nor, len, buf); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +void spi_nor_register_security_ops(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + + if (!nor->params->sec_ops) + return; + + mtd->_secure_packet_read = spi_nor_secure_read; + mtd->_secure_packet_write = spi_nor_secure_write; + mtd->_read_vlock_bits = spi_nor_read_vlock_bits; + mtd->_write_vlock_bits = spi_nor_write_vlock_bits; + mtd->_read_nvlock_bits = spi_nor_read_nvlock_bits; + mtd->_write_nvlock_bits = spi_nor_write_nvlock_bits; + mtd->_erase_nvlock_bits = spi_nor_erase_nvlock_bits; + mtd->_read_global_freeze_bits = spi_nor_read_global_freeze_bits; + mtd->_write_global_freeze_bits = spi_nor_write_global_freeze_bits; + mtd->_read_password = spi_nor_read_password; +} diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index cc08bd707378f9..864f3c7783b3b3 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3199,6 +3199,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, spi_nor_register_locking_ops(nor); + spi_nor_register_security_ops(nor); + /* Send all the required SPI flash commands to initialize device */ ret = spi_nor_init(nor); if (ret) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 88227044fc86c9..bce358c9fb948b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -360,6 +360,25 @@ struct mtd_info { int (*_get_device) (struct mtd_info *mtd); void (*_put_device) (struct mtd_info *mtd); + /* + * Security Operations + */ + int (*_secure_packet_read)(struct mtd_info *mtd, size_t len, u8 *buf); + int (*_secure_packet_write)(struct mtd_info *mtd, size_t len, u8 *buf); + int (*_read_vlock_bits)(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf); + int (*_write_vlock_bits)(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf); + int (*_read_nvlock_bits)(struct mtd_info *mtd, u32 addr, size_t len, + u8 *buf); + int (*_write_nvlock_bits)(struct mtd_info *mtd, u32 addr); + int (*_erase_nvlock_bits)(struct mtd_info *mtd); + int (*_read_global_freeze_bits)(struct mtd_info *mtd, size_t len, + u8 *buf); + int (*_write_global_freeze_bits)(struct mtd_info *mtd, size_t len, + u8 *buf); + int (*_read_password)(struct mtd_info *mtd, size_t len, u8 *buf); + /* * flag indicates a panic write, low level drivers can take appropriate * action if required to ensure writes go through