From d5657967530ab6ac31776acc6582eda516bac5d9 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 19 Jun 2019 15:25:33 +0200 Subject: [PATCH 01/10] mtd_spi_nor: check WEL flag for write and erase --- drivers/Kconfig | 1 + drivers/include/mtd_spi_nor.h | 20 +++++- drivers/mtd_spi_nor/Kconfig | 22 +++++++ drivers/mtd_spi_nor/mtd_spi_nor.c | 79 +++++++++++++++++++++-- drivers/mtd_spi_nor/mtd_spi_nor_configs.c | 2 + 5 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 drivers/mtd_spi_nor/Kconfig diff --git a/drivers/Kconfig b/drivers/Kconfig index a2f3ee786768..facbb5d74db5 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -56,6 +56,7 @@ endmenu # Sensor Device Drivers menu "Storage Device Drivers" rsource "mtd_sdcard/Kconfig" +rsource "mtd_spi_nor/Kconfig" endmenu # Storage Device Drivers endmenu # Drivers diff --git a/drivers/include/mtd_spi_nor.h b/drivers/include/mtd_spi_nor.h index 3852946e0715..0624a6ac1052 100644 --- a/drivers/include/mtd_spi_nor.h +++ b/drivers/include/mtd_spi_nor.h @@ -36,6 +36,24 @@ extern "C" { #endif +#define STATUS_WIP 0x01u +#define STATUS_WEL 0x02u +#define STATUS_BP0 0x04u +#define STATUS_BP1 0x08u +#define STATUS_BP2 0x10u +#define STATUS_BP3 0x20u +#define STATUS_QE 0x40u +#define STATUS_SRWD 0x80u + +#define SECURITY_SOTP 0x01u +#define SECURITY_LDSO 0x02u +#define SECURITY_PSB 0x04u +#define SECURITY_ESB 0x08u +#define SECURITY_XXXXX 0x10u +#define SECURITY_PFAIL 0x20u +#define SECURITY_EFAIL 0x40u +#define SECURITY_WPSEL 0x80u + /** * @brief SPI NOR flash opcode table */ @@ -53,7 +71,7 @@ typedef struct { uint8_t chip_erase; /**< Chip erase */ uint8_t sleep; /**< Deep power down */ uint8_t wake; /**< Release from deep power down */ - /* TODO: enter 4 byte address mode for large memories */ + uint8_t rdscur; /**< Read security register */ } mtd_spi_nor_opcode_t; /** diff --git a/drivers/mtd_spi_nor/Kconfig b/drivers/mtd_spi_nor/Kconfig new file mode 100644 index 000000000000..02fc754077ee --- /dev/null +++ b/drivers/mtd_spi_nor/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2020 Continental +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# +menuconfig KCONFIG_USEMODULE_MTD_SPI_NOR + bool "Configure MTD_SPI_NOR driver" + depends on USEMODULE_MTD_SPI_NOR + help + Configure the MODULE_MTD_SPI_NOR driver using Kconfig. + +if KCONFIG_USEMODULE_MTD_SPI_NOR + +config MTD_SPI_NOR_RDSCUR + bool "Enable rdscur command usage" + help + Enable this if your SPI flash supports RDSCUR (Read Security register) + command (Macronix flashes do). In that case a rdscur command is issued + after a program or an erase command to check if it succeed. + +endif # KCONFIG_USEMODULE_MTD_SPI_NOR diff --git a/drivers/mtd_spi_nor/mtd_spi_nor.c b/drivers/mtd_spi_nor/mtd_spi_nor.c index 869f22871028..832b5a05fa90 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor.c @@ -205,7 +205,8 @@ static void mtd_spi_cmd_read(const mtd_spi_nor_t *dev, uint8_t opcode, void *des * @param[out] src write buffer * @param[in] count number of bytes to write after the opcode has been sent */ -static void __attribute__((unused)) mtd_spi_cmd_write(const mtd_spi_nor_t *dev, uint8_t opcode, const void *src, uint32_t count) +static void __attribute__((unused)) mtd_spi_cmd_write(const mtd_spi_nor_t *dev, uint8_t opcode, + const void *src, uint32_t count) { TRACE("mtd_spi_cmd_write: %p, %02x, %p, %" PRIu32 "\n", (void *)dev, (unsigned int)opcode, src, count); @@ -353,6 +354,34 @@ static void delay_us(unsigned us) #endif } +static inline void wait_for_write_enable_cleared(const mtd_spi_nor_t *dev) +{ + do { + uint8_t status; + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); + + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WEL\n", (unsigned int)status); + if ((status & STATUS_WEL) == 0) { + break; + } + thread_yield(); + } while (1); +} + +static inline void wait_for_write_enable_set(const mtd_spi_nor_t *dev) +{ + do { + uint8_t status; + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); + + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting WEL\n", (unsigned int)status); + if (status & STATUS_WEL) { + break; + } + thread_yield(); + } while (1); +} + static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us) { unsigned i = 0, j = 0; @@ -369,8 +398,8 @@ static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us uint8_t status; mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); - TRACE("mtd_spi_nor: wait device status = 0x%02x\n", (unsigned int)status); - if ((status & 1) == 0) { /* TODO magic number */ + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WIP\n", (unsigned int)status); + if ((status & STATUS_WIP) == 0) { break; } i++; @@ -487,7 +516,8 @@ static int mtd_spi_nor_init(mtd_dev_t *mtd) mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd; DEBUG("mtd_spi_nor_init: -> spi: %lx, cs: %lx, opcodes: %p\n", - (unsigned long)_get_spi(dev), (unsigned long)dev->params->cs, (void *)dev->params->opcode); + (unsigned long)_get_spi(dev), (unsigned long)dev->params->cs, + (void *)dev->params->opcode); /* CS, WP, Hold */ _init_pins(dev); @@ -506,7 +536,8 @@ static int mtd_spi_nor_init(mtd_dev_t *mtd) return -EIO; } DEBUG("mtd_spi_nor_init: Found chip with ID: (%d, 0x%02x, 0x%02x, 0x%02x)\n", - dev->jedec_id.bank, dev->jedec_id.manuf, dev->jedec_id.device[0], dev->jedec_id.device[1]); + dev->jedec_id.bank, dev->jedec_id.manuf, + dev->jedec_id.device[0], dev->jedec_id.device[1]); /* derive density from JEDEC ID */ if (mtd->sector_count == 0) { @@ -614,6 +645,7 @@ static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page uint32_t remaining = mtd->page_size - offset; size = MIN(remaining, size); + int ret = size; uint32_t addr = page * mtd->page_size + offset; @@ -622,19 +654,36 @@ static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page /* write enable */ mtd_spi_cmd(dev, dev->params->opcode->wren); + /* Wait for WEL to be set */ + wait_for_write_enable_set(dev); + /* Page program */ mtd_spi_cmd_addr_write(dev, dev->params->opcode->page_program, addr, src, size); /* waiting for the command to complete before returning */ wait_for_write_complete(dev, 0); + /* Wait for WEL to be cleared */ + wait_for_write_enable_cleared(dev); + + if (IS_ACTIVE(CONFIG_MTD_SPI_NOR_RDSCUR)) { + /* Read security register */ + uint8_t rdscur; + mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &rdscur, sizeof(rdscur)); + if (rdscur & SECURITY_PFAIL) { + DEBUG("mtd_spi_nor_write: ERR: page program failed!\n"); + ret = -EIO; + } + } + mtd_spi_release(dev); - return size; + return ret; } static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) { + int ret = 0; DEBUG("mtd_spi_nor_erase: %p, 0x%" PRIx32 ", 0x%" PRIx32 "\n", (void *)mtd, addr, size); mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd; @@ -664,6 +713,9 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) /* write enable */ mtd_spi_cmd(dev, dev->params->opcode->wren); + /* Wait for WEL to be set */ + wait_for_write_enable_set(dev); + if (size == total_size) { mtd_spi_cmd(dev, dev->params->opcode->chip_erase); size -= total_size; @@ -703,10 +755,23 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) /* waiting for the command to complete before continuing */ wait_for_write_complete(dev, us); + + /* Wait for WEL to be cleared */ + wait_for_write_enable_cleared(dev); + + if (IS_ACTIVE(CONFIG_MTD_SPI_NOR_RDSCUR)) { + /* Read security register */ + uint8_t rdscur; + mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &rdscur, sizeof(rdscur)); + if (rdscur & SECURITY_EFAIL) { + DEBUG("mtd_spi_nor_erase: ERR: erase failed!\n"); + ret = -EIO; + } + } } mtd_spi_release(dev); - return 0; + return ret; } const mtd_desc_t mtd_spi_nor_driver = { diff --git a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c index 65d674b0226d..60e5b13e619b 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c @@ -40,6 +40,7 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, + .rdscur = 0x2b, }; const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { @@ -56,6 +57,7 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, + .rdscur = 0x2b, }; /** @} */ From 61a03bb54aa06bee7e2542998fc1d0cddd1844cd Mon Sep 17 00:00:00 2001 From: Sylvain FABRE Date: Wed, 8 Jul 2020 18:03:27 +0200 Subject: [PATCH 02/10] littlefs: fix return code in case of corruption --- pkg/littlefs/fs/littlefs_fs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/littlefs/fs/littlefs_fs.c b/pkg/littlefs/fs/littlefs_fs.c index 6f23c9067651..fcf31d1ca099 100644 --- a/pkg/littlefs/fs/littlefs_fs.c +++ b/pkg/littlefs/fs/littlefs_fs.c @@ -84,7 +84,11 @@ static int _dev_write(const struct lfs_config *c, lfs_block_t block, (void *)c, block, off, buffer, size); uint32_t page = (fs->base_addr + block) * fs->sectors_per_block * mtd->pages_per_sector; - return mtd_write_page_raw(mtd, buffer, page, off, size); + int ret = mtd_write_page_raw(mtd, buffer, page, off, size); + if (ret == -EIO) { + ret = LFS_ERR_CORRUPT; + } + return ret; } static int _dev_erase(const struct lfs_config *c, lfs_block_t block) @@ -95,7 +99,11 @@ static int _dev_erase(const struct lfs_config *c, lfs_block_t block) DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block); uint32_t sector = (fs->base_addr + block) * fs->sectors_per_block; - return mtd_erase_sector(mtd, sector, fs->sectors_per_block); + int ret = mtd_erase_sector(mtd, sector, fs->sectors_per_block); + if (ret == -EIO) { + ret = LFS_ERR_CORRUPT; + } + return ret; } static int _dev_sync(const struct lfs_config *c) From 216a370e9a6df4a5034e01931374fe1a14d86028 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Mon, 31 Aug 2020 16:06:17 +0200 Subject: [PATCH 03/10] littlefs2: fix return code in case of corruption --- pkg/littlefs2/fs/littlefs2_fs.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pkg/littlefs2/fs/littlefs2_fs.c b/pkg/littlefs2/fs/littlefs2_fs.c index d2968aa6f655..44685704fada 100644 --- a/pkg/littlefs2/fs/littlefs2_fs.c +++ b/pkg/littlefs2/fs/littlefs2_fs.c @@ -84,7 +84,11 @@ static int _dev_write(const struct lfs_config *c, lfs_block_t block, (void *)c, block, off, buffer, size); uint32_t page = (fs->base_addr + block) * fs->sectors_per_block * mtd->pages_per_sector; - return mtd_write_page_raw(mtd, buffer, page, off, size); + int ret = mtd_write_page_raw(mtd, buffer, page, off, size); + if (ret == -EIO) { + ret = LFS_ERR_CORRUPT; + } + return ret; } static int _dev_erase(const struct lfs_config *c, lfs_block_t block) @@ -95,7 +99,12 @@ static int _dev_erase(const struct lfs_config *c, lfs_block_t block) DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block); uint32_t sector = (fs->base_addr + block) * fs->sectors_per_block; - return mtd_erase_sector(mtd, sector, fs->sectors_per_block);} + int ret = mtd_erase_sector(mtd, sector, fs->sectors_per_block); + if (ret == -EIO) { + ret = LFS_ERR_CORRUPT; + } + return ret; +} static int _dev_sync(const struct lfs_config *c) { From e435a37024177abc2e61a47d9a104f42eba7adf0 Mon Sep 17 00:00:00 2001 From: crasbe Date: Wed, 17 Apr 2024 16:26:45 +0200 Subject: [PATCH 04/10] drivers/mtd_spi_nor: removed Kconfig & added MX/ISSI security features --- drivers/Kconfig | 1 - drivers/Makefile.dep | 4 + drivers/include/mtd_spi_nor.h | 59 +++-- drivers/mtd_spi_nor/Kconfig | 22 -- drivers/mtd_spi_nor/Makefile.include | 6 + .../mtd_spi_nor/include/mtd_spi_nor_defines.h | 230 ++++++++++++++++++ drivers/mtd_spi_nor/mtd_spi_nor.c | 102 +++++--- drivers/mtd_spi_nor/mtd_spi_nor_configs.c | 12 + 8 files changed, 359 insertions(+), 77 deletions(-) delete mode 100644 drivers/mtd_spi_nor/Kconfig create mode 100644 drivers/mtd_spi_nor/Makefile.include create mode 100644 drivers/mtd_spi_nor/include/mtd_spi_nor_defines.h diff --git a/drivers/Kconfig b/drivers/Kconfig index facbb5d74db5..a2f3ee786768 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -56,7 +56,6 @@ endmenu # Sensor Device Drivers menu "Storage Device Drivers" rsource "mtd_sdcard/Kconfig" -rsource "mtd_spi_nor/Kconfig" endmenu # Storage Device Drivers endmenu # Drivers diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index f86459b0c230..402f02d1084d 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -120,6 +120,10 @@ ifneq (,$(filter mtd_%,$(USEMODULE))) USEMODULE += mtd endif +ifneq (,$(filter mtd_spi_nor_%,$(USEMODULE))) + USEMODULE += mtd_spi_nor +endif + # nrfmin is a concrete module but comes from cpu/nrf5x_common. Due to limitations # in the dependency resolution mechanism it's not possible to move its # dependency resolution at cpu level. diff --git a/drivers/include/mtd_spi_nor.h b/drivers/include/mtd_spi_nor.h index 0624a6ac1052..28926e948d9e 100644 --- a/drivers/include/mtd_spi_nor.h +++ b/drivers/include/mtd_spi_nor.h @@ -12,8 +12,33 @@ * @ingroup drivers_storage * @brief Driver for serial NOR flash memory technology devices attached via SPI * - * @{ * + * @section mtd_spi_nor_overview Overview + * Various manufacturers such as ISSI or Macronix offer small SPI NOR Flash which usually + * come in 8-pin packages and are used for persistent data storage. + * This driver adds support for these devices with support for RIOT's MTD subsystem. + * + * @section mtd_spi_nor_usage Usage + * + * The basic functions of all flash devices can be used by just including the + * mtd_spi_nor module. + * + * USEMODULE += mtd_spi_nor + * + * For ISSI and Macronix devices, some security features are provided + * to check if program or erase operations were successful. + * These features can be activated with the corresponding pseudomodules in the Makefile + * of your board or project: + * + * USEMODULE += mtd_spi_nor_mx_security + * or + * USEMODULE += mtd_spi_nor_issi_security + * + * \n + * Some examples of how to work with the MTD SPI NOR driver can be found in the test for the + * driver or in some board definitions such as for the nRF52840DK. + * + * @{ * @file * @brief Interface definition for the serial flash memory driver * @@ -30,30 +55,13 @@ #include "periph/spi.h" #include "periph/gpio.h" #include "mtd.h" +#include "kernel_defines.h" #ifdef __cplusplus extern "C" { #endif -#define STATUS_WIP 0x01u -#define STATUS_WEL 0x02u -#define STATUS_BP0 0x04u -#define STATUS_BP1 0x08u -#define STATUS_BP2 0x10u -#define STATUS_BP3 0x20u -#define STATUS_QE 0x40u -#define STATUS_SRWD 0x80u - -#define SECURITY_SOTP 0x01u -#define SECURITY_LDSO 0x02u -#define SECURITY_PSB 0x04u -#define SECURITY_ESB 0x08u -#define SECURITY_XXXXX 0x10u -#define SECURITY_PFAIL 0x20u -#define SECURITY_EFAIL 0x40u -#define SECURITY_WPSEL 0x80u - /** * @brief SPI NOR flash opcode table */ @@ -71,7 +79,13 @@ typedef struct { uint8_t chip_erase; /**< Chip erase */ uint8_t sleep; /**< Deep power down */ uint8_t wake; /**< Release from deep power down */ +#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) uint8_t rdscur; /**< Read security register */ +#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) + uint8_t rderp; /**< Read extended read parameters register */ + uint8_t clerp; /**< Clear extended read parameters register */ + uint8_t wrdi; /**< Write disable */ +#endif } mtd_spi_nor_opcode_t; /** @@ -188,7 +202,12 @@ extern const mtd_desc_t mtd_spi_nor_driver; * The numbers were taken from Micron M25P16, but the same opcodes can * be found in Macronix MX25L25735E, and multiple other data sheets for * different devices, as well as in the Linux kernel, so they seem quite - * sensible for default values. */ + * sensible for default values. + * + * To enable manufacturer specific security functions, the according + * pseudomodules have to be used which will activate the corresponding + * opcodes. + */ extern const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default; /** diff --git a/drivers/mtd_spi_nor/Kconfig b/drivers/mtd_spi_nor/Kconfig deleted file mode 100644 index 02fc754077ee..000000000000 --- a/drivers/mtd_spi_nor/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2020 Continental -# -# This file is subject to the terms and conditions of the GNU Lesser -# General Public License v2.1. See the file LICENSE in the top level -# directory for more details. -# -menuconfig KCONFIG_USEMODULE_MTD_SPI_NOR - bool "Configure MTD_SPI_NOR driver" - depends on USEMODULE_MTD_SPI_NOR - help - Configure the MODULE_MTD_SPI_NOR driver using Kconfig. - -if KCONFIG_USEMODULE_MTD_SPI_NOR - -config MTD_SPI_NOR_RDSCUR - bool "Enable rdscur command usage" - help - Enable this if your SPI flash supports RDSCUR (Read Security register) - command (Macronix flashes do). In that case a rdscur command is issued - after a program or an erase command to check if it succeed. - -endif # KCONFIG_USEMODULE_MTD_SPI_NOR diff --git a/drivers/mtd_spi_nor/Makefile.include b/drivers/mtd_spi_nor/Makefile.include new file mode 100644 index 000000000000..be745a3df86c --- /dev/null +++ b/drivers/mtd_spi_nor/Makefile.include @@ -0,0 +1,6 @@ +# include variants of MTD SPI NOR drivers as pseudo modules +PSEUDOMODULES += mtd_spi_nor_mx_security +PSEUDOMODULES += mtd_spi_nor_issi_security + +USEMODULE_INCLUDES_mtd_spi_nor := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_mtd_spi_nor) diff --git a/drivers/mtd_spi_nor/include/mtd_spi_nor_defines.h b/drivers/mtd_spi_nor/include/mtd_spi_nor_defines.h new file mode 100644 index 000000000000..16df103134f9 --- /dev/null +++ b/drivers/mtd_spi_nor/include/mtd_spi_nor_defines.h @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 Technische Universität Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_mtd_spi_nor + * @{ + * + * @file + * @brief Definitions for the MTD SPI NOR Flash driver + * + * @author Christopher Büchse + * + */ + +#ifndef MTD_SPI_NOR_DEFINES_H +#define MTD_SPI_NOR_DEFINES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Common Status Bits from the Status Register of SPI NOR Flashs + * @{ + */ +/** + * @brief Write In Progress Flag (R) + * + * 0 - Device is ready + * 1 - Write cycle in progress and device is busy + */ +#define SPI_NOR_STATUS_WIP 0x01u + +/** + * @brief Write Enable Latch Flag (R/W) + * + * 0 - Device is not write enabled + * 1 - Device is write enabled + */ +#define SPI_NOR_STATUS_WEL 0x02u + +/** + * @brief Block Protection Bit 0 Flag (R/W) + * + * 0 - Specific blocks are not write-protected + * 1 - Specific blocks are write-protected + */ +#define SPI_NOR_STATUS_BP0 0x04u + +/** + * @brief Block Protection Bit 1 Flag (R/W) + * + * 0 - Specific blocks are not write-protected + * 1 - Specific blocks are write-protected + */ +#define SPI_NOR_STATUS_BP1 0x08u + +/** + * @brief Block Protection Bit 2 Flag (R/W) + * + * 0 - Specific blocks are not write-protected + * 1 - Specific blocks are write-protected + */ +#define SPI_NOR_STATUS_BP2 0x10u + +/** + * @brief Block Protection Bit 3 Flag (R/W) + * + * 0 - Specific blocks are not write-protected + * 1 - Specific blocks are write-protected + */ +#define SPI_NOR_STATUS_BP3 0x20u + +/** + * @brief Quad Enable Flag (R/W) + * + * 0 - Quad output function disabled + * 1 - Quad output function enabled + */ +#define SPI_NOR_STATUS_QE 0x40u + +/** + * @brief Status Register Write Disable Flag (R/W) + * + * 0 - Status Register is not write protected + * 1 - Status Register is write protected + */ +#define SPI_NOR_STATUS_SRWD 0x80u + +/** @} */ + +/** + * @name Macronix Style Security Register Bits + * @note These flags were taken from the MX25L51245G datasheet, but probably apply + * to other devices from Macronix as well. + * @{ + */ +/** + * @brief Secured OTP Flag + * + * 0 - OTP area not factory locked + * 1 - OTP area factory locked + */ +#define MX_SECURITY_SOTP 0x01u + +/** + * @brief Lock-down Secured OTP Flag + * + * 0 - OTP area not (user) locked + * 1 - OTP area locked (can not be programmed/erased) + */ +#define MX_SECURITY_LDSO 0x02u + +/** + * @brief Program Suspend Flag + * + * 0 - Program is not suspended + * 1 - Program suspended + */ +#define MX_SECURITY_PSB 0x04u + +/** + * @brief Erase Suspend Flag + * + * 0 - Erase is not suspended + * 1 - Erase is suspended + */ +#define MX_SECURITY_ESB 0x08u + +/** + * @brief Reserved + */ +#define MX_SECURITY_XXXXX 0x10u + +/** + * @brief Program Fail Flag + * + * 0 - Program Operation succeeded + * 1 - Program Operation failed or region is protected + */ +#define MX_SECURITY_PFAIL 0x20u + +/** + * @brief Erase Fail Flag + * + * 0 - Erase Operation succeeded + * 1 - Erase Operation failed or region is protected + */ +#define MX_SECURITY_EFAIL 0x40u + +/** + * @brief Write Protection Selection Flag + * + * 0 - Normal Write Protect mode + * 1 - Advanced Sector Protection mode + */ +#define MX_SECURITY_WPSEL 0x80u +/** @} */ + +/** + * @name ISSI Style Security Register Bits from Extended Read Register (ERP) + * @note These flags were taken from the IS25LE01G datasheet, but probably + * apply to other devices from ISSI as well. + * @{ + */ + +/** + * @brief Reserved + */ +#define IS_ERP_XXXXX 0x01u + +/** + * @brief Protection Error Flag (R) + * + * 0 - No protection error + * 1 - Protection Error occurred in program or erase + */ +#define IS_ERP_PROT_E 0x02u + +/** + * @brief Program Error Flag (R) + * + * 0 - Program Operation succeeded + * 1 - Program Operation failed or region is protected + */ +#define IS_ERP_P_ERR 0x04u + +/** + * @brief Erase Error Flag (R) + * + * 0 - Erase Operation succeeded + * 1 - Erase Operation failed or region is protected + */ +#define IS_ERP_E_ERR 0x08u + +/** + * @brief Data Learning Pattern Flag (R/W) + * + * 0 - DLP is disabled + * 1 - DLP is enabled + */ +#define IS_ERP_DLPEN 0x10u + +/** + * @brief Output Driver Strength Bit 0 (R/W) + */ +#define IS_ERP_ODS0 0x20u + +/** + * @brief Output Driver Strength Bit 1 (R/W) + */ +#define IS_ERP_ODS1 0x40u + +/** + * @brief Output Driver Strength Bit 2 (R/W) + */ +#define IS_ERP_ODS2 0x80u +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MTD_SPI_NOR_DEFINES_H */ +/** @} */ diff --git a/drivers/mtd_spi_nor/mtd_spi_nor.c b/drivers/mtd_spi_nor/mtd_spi_nor.c index 832b5a05fa90..97090597f7f3 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor.c @@ -35,6 +35,8 @@ #include "time_units.h" #include "thread.h" +#include "include/mtd_spi_nor_defines.h" + #if IS_USED(MODULE_ZTIMER) #include "ztimer.h" #elif IS_USED(MODULE_XTIMER) @@ -64,7 +66,7 @@ #define MTD_4K (4096ul) #define MTD_4K_ADDR_MASK (0xFFF) -#define MBIT_AS_BYTES ((1024 * 1024) / 8) +#define MBIT_AS_BYTES ((1024UL * 1024UL) / 8) /** * @brief JEDEC memory manufacturer ID codes. @@ -75,8 +77,8 @@ #define JEDEC_BANK(n) ((n) << 8) typedef enum { - SPI_NOR_JEDEC_ATMEL = 0x1F | JEDEC_BANK(1), - SPI_NOR_JEDEC_MICROCHIP = 0xBF | JEDEC_BANK(1), + SPI_NOR_JEDEC_ATMEL = 0x1FU | JEDEC_BANK(1), + SPI_NOR_JEDEC_MICROCHIP = 0xBFU | JEDEC_BANK(1), } jedec_manuf_t; /** @} */ @@ -232,7 +234,7 @@ static void mtd_spi_cmd(const mtd_spi_nor_t *dev, uint8_t opcode) static bool mtd_spi_manuf_match(const mtd_jedec_id_t *id, jedec_manuf_t manuf) { - return manuf == ((id->bank << 8) | id->manuf); + return manuf == (uint32_t)((id->bank << 8) | id->manuf); } /** @@ -314,7 +316,7 @@ static uint32_t mtd_spi_nor_get_size(const mtd_jedec_id_t *id) /* ID 2 is used to encode the product version, usually 1 or 2 */ (id->device[1] & ~0x3) == 0) { /* capacity encoded as power of 32k sectors */ - return (32 * 1024) << (0x1F & id->device[0]); + return (32 * (uint32_t)1024) << (0x1F & id->device[0]); } if (mtd_spi_manuf_match(id, SPI_NOR_JEDEC_MICROCHIP)) { switch (id->device[1]) { @@ -360,8 +362,9 @@ static inline void wait_for_write_enable_cleared(const mtd_spi_nor_t *dev) uint8_t status; mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); - TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WEL\n", (unsigned int)status); - if ((status & STATUS_WEL) == 0) { + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WEL-Flag\n", + (unsigned int)status); + if ((status & SPI_NOR_STATUS_WEL) == 0) { break; } thread_yield(); @@ -374,8 +377,9 @@ static inline void wait_for_write_enable_set(const mtd_spi_nor_t *dev) uint8_t status; mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); - TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting WEL\n", (unsigned int)status); - if (status & STATUS_WEL) { + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting WEL-Flag\n", + (unsigned int)status); + if (status & SPI_NOR_STATUS_WEL) { break; } thread_yield(); @@ -398,8 +402,9 @@ static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us uint8_t status; mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); - TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WIP\n", (unsigned int)status); - if ((status & STATUS_WIP) == 0) { + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting !WIP-Flag\n", + (unsigned int)status); + if ((status & SPI_NOR_STATUS_WIP) == 0) { break; } i++; @@ -654,7 +659,7 @@ static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page /* write enable */ mtd_spi_cmd(dev, dev->params->opcode->wren); - /* Wait for WEL to be set */ + /* Wait for WEL-Flag to be set */ wait_for_write_enable_set(dev); /* Page program */ @@ -663,18 +668,32 @@ static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page /* waiting for the command to complete before returning */ wait_for_write_complete(dev, 0); - /* Wait for WEL to be cleared */ - wait_for_write_enable_cleared(dev); - - if (IS_ACTIVE(CONFIG_MTD_SPI_NOR_RDSCUR)) { - /* Read security register */ - uint8_t rdscur; - mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &rdscur, sizeof(rdscur)); - if (rdscur & SECURITY_PFAIL) { - DEBUG("mtd_spi_nor_write: ERR: page program failed!\n"); - ret = -EIO; - } +#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) + /* Read security register */ + uint8_t scur_reg; + mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &scur_reg, sizeof(scur_reg)); + if (scur_reg & MX_SECURITY_PFAIL) { + DEBUG("mtd_spi_nor_write: ERR: page program failed! scur = %02x\n", scur_reg); + ret = -EIO; } +#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) + /* Read extended read register for security flags */ + uint8_t erp_reg; + mtd_spi_cmd_read(dev, dev->params->opcode->rderp, &erp_reg, sizeof(erp_reg)); + if (erp_reg & IS_ERP_P_ERR) { + DEBUG("mtd_spi_nor_write: ERR: page program failed! erp = %02x\n", erp_reg); + ret = -EIO; + + /* clear the extended read parameters register manually */ + mtd_spi_cmd(dev, dev->params->opcode->clerp); + + /* ISSI flashs do not reset the WEL-Flag when an operation was not *completed* */ + mtd_spi_cmd(dev, dev->params->opcode->wrdi); + } +#endif + + /* Wait for WEL-Flag to be cleared */ + wait_for_write_enable_cleared(dev); mtd_spi_release(dev); @@ -713,7 +732,7 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) /* write enable */ mtd_spi_cmd(dev, dev->params->opcode->wren); - /* Wait for WEL to be set */ + /* Wait for WEL-Flag to be set */ wait_for_write_enable_set(dev); if (size == total_size) { @@ -756,18 +775,33 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) /* waiting for the command to complete before continuing */ wait_for_write_complete(dev, us); - /* Wait for WEL to be cleared */ - wait_for_write_enable_cleared(dev); +#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) + /* Read security register */ + uint8_t scur_reg; + mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &scur_reg, sizeof(scur_reg)); + if (scur_reg & MX_SECURITY_EFAIL) { + DEBUG("mtd_spi_nor_write: ERR: erase failed! scur = %02x\n", scur_reg); + ret = -EIO; + } +#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) + /* Read extended read register for security flags */ + uint8_t erp_reg; + mtd_spi_cmd_read(dev, dev->params->opcode->rderp, &erp_reg, sizeof(erp_reg)); + if (erp_reg & IS_ERP_E_ERR) { + DEBUG("mtd_spi_nor_write: ERR: erase failed! erp = %02x\n", erp_reg); + ret = -EIO; - if (IS_ACTIVE(CONFIG_MTD_SPI_NOR_RDSCUR)) { - /* Read security register */ - uint8_t rdscur; - mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &rdscur, sizeof(rdscur)); - if (rdscur & SECURITY_EFAIL) { - DEBUG("mtd_spi_nor_erase: ERR: erase failed!\n"); - ret = -EIO; - } + /* clear the extended read parameters register manually */ + mtd_spi_cmd(dev, dev->params->opcode->clerp); + + /* ISSI flashs do not reset the WEL-Flag when an operation was not *completed* */ + mtd_spi_cmd(dev, dev->params->opcode->wrdi); } +#endif + + /* Wait for WEL-Flag to be cleared */ + wait_for_write_enable_cleared(dev); + } mtd_spi_release(dev); diff --git a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c index 60e5b13e619b..3fe6f76b740e 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c @@ -40,7 +40,13 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, +#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) .rdscur = 0x2b, +#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) + .rderp = 0x81, + .clerp = 0x82, + .wrdi = 0x04, +#endif }; const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { @@ -57,7 +63,13 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, +#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) .rdscur = 0x2b, +#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) + .rderp = 0x81, + .clerp = 0x82, + .wrdi = 0x04, +#endif }; /** @} */ From 8f25a37dd23435b1334fe791ffb3bd81d541cadd Mon Sep 17 00:00:00 2001 From: crasbe Date: Wed, 24 Apr 2024 22:05:16 +0200 Subject: [PATCH 05/10] tests/mtd_spi_nor: Added test with security features --- tests/drivers/mtd_spi_nor/Makefile | 11 ++ tests/drivers/mtd_spi_nor/Makefile.ci | 3 + tests/drivers/mtd_spi_nor/README.md | 64 ++++++ tests/drivers/mtd_spi_nor/flash_dut.c | 141 ++++++++++++++ tests/drivers/mtd_spi_nor/main.c | 269 ++++++++++++++++++++++++++ 5 files changed, 488 insertions(+) create mode 100644 tests/drivers/mtd_spi_nor/Makefile create mode 100644 tests/drivers/mtd_spi_nor/Makefile.ci create mode 100644 tests/drivers/mtd_spi_nor/README.md create mode 100644 tests/drivers/mtd_spi_nor/flash_dut.c create mode 100644 tests/drivers/mtd_spi_nor/main.c diff --git a/tests/drivers/mtd_spi_nor/Makefile b/tests/drivers/mtd_spi_nor/Makefile new file mode 100644 index 000000000000..c21ee50315bb --- /dev/null +++ b/tests/drivers/mtd_spi_nor/Makefile @@ -0,0 +1,11 @@ +BOARD ?= nrf52840dk + +include ../Makefile.drivers_common + +USEMODULE += embunit +USEMODULE += mtd_spi_nor + +#CFLAGS += -DTHREAD_STACKSIZE_MAIN=2048 +#CFLAGS += -DISR_STACKSIZE=1024 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/drivers/mtd_spi_nor/Makefile.ci b/tests/drivers/mtd_spi_nor/Makefile.ci new file mode 100644 index 000000000000..72db76ccb5cc --- /dev/null +++ b/tests/drivers/mtd_spi_nor/Makefile.ci @@ -0,0 +1,3 @@ +BOARD_INSUFFICIENT_MEMORY := \ + atmega8 \ + # diff --git a/tests/drivers/mtd_spi_nor/README.md b/tests/drivers/mtd_spi_nor/README.md new file mode 100644 index 000000000000..f72d963d226e --- /dev/null +++ b/tests/drivers/mtd_spi_nor/README.md @@ -0,0 +1,64 @@ +# Test Program for the MTD SPI NOR Flash Driver + +This program performs a number of tests with the MTD SPI NOR Flash driver, +including reading, writing and erasing of the chip. + +This test will destroy the data present on the chip! + +# Usage + +## nRF52840DK +The test is designed to work with the built-in MX25F6435F flash of the +Nordic Semiconductor nRF52840DK development board. +Currently the nRF52840DK Flash definitions do not have the security options enabled. + +To run the test you simply have to compile and run the test: +``` +make flash term +``` +The tests should conclude with "OK (3 tests)". + +To enable the security features of the built-in flash, the "MTD_SPI_NOR_MX_SECURITY" +pseudomodule can be added to the make call: +``` +USEMODULE+=mtd_spi_nor_mx_security make flash term +``` + +Likewise, the tests should conclude with "OK (3 tests)". + +## nRF52840DK with different flash chips +The default SPI Device for using different Flash chips is SPI(1), which is present on +the Arduino Uno style headers. The default pin for Chip Select is P1.5. The SPI Device and +Chip Select pin can be changed with the CFLAGS FLASH_SPI_DEV and FLASH_SPI_CS: +``` +CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)'" ... +``` + +The Device under Test can be changed with CFLAGS as well: +``` +... CFLAGS+="-DTEST_IS25LE01G" ... +``` + +If the Flash supports the security features, they can be enabled with the usage of a +pseudomodule as well: +``` +... USEMODULE+=mtd_spi_nor_issi_security ... +``` + +A full test call would therefore be for example: +``` +CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)' -DTEST_IS25LE01G" \ +USEMODULE+=mtd_spi_nor_issi_security make flash term +``` + +# Tested/Supported Chips + +## ISSI + +- IS25LP128 (no security features) +- IS25LE01G + +## Macronix + +- M25F6435F (built-in on nRF52840DK) +- M25F12873F diff --git a/tests/drivers/mtd_spi_nor/flash_dut.c b/tests/drivers/mtd_spi_nor/flash_dut.c new file mode 100644 index 000000000000..95dd66eaa035 --- /dev/null +++ b/tests/drivers/mtd_spi_nor/flash_dut.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2024 Technische Universität Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @{ + * + * @file + * @brief Definitions for different Flash chips that can be tested + * + * @author Christopher Büchse + */ + +#include "timex.h" +#include "mtd_spi_nor.h" + +/* The default CS pin and SPI device is for the built-in flash of the nRF52840DK */ +#ifndef FLASH_SPI_CS +#define FLASH_SPI_CS GPIO_PIN(0, 17) +#endif + +#ifndef FLASH_SPI_DEV +#define FLASH_SPI_DEV 0 +#endif + +#ifdef TEST_IS25LP128 + +#define IS25LP128_PAGE_SIZE (256) +#define IS25LP128_PAGES_PER_SECTOR (16) +#define IS25LP128_SECTOR_COUNT (4096) +#define IS25LP128_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ + SPI_NOR_F_SECT_64K) +#define IS25LP128_SPI_CLK SPI_CLK_100KHZ +#define IS25LP128_SPI_MODE SPI_MODE_0 + +static const mtd_spi_nor_params_t _is25lp128_flash_nor_params = { + .opcode = &mtd_spi_nor_opcode_default, + .wait_chip_erase = 30LU * US_PER_SEC, + .wait_32k_erase = 100LU *US_PER_MS, + .wait_sector_erase = 70LU * US_PER_MS, + .wait_chip_wake_up = 100LU * US_PER_MS, + .clk = IS25LP128_SPI_CLK, + .flag = IS25LP128_FLAGS, + .spi = SPI_DEV(FLASH_SPI_DEV), + .mode = IS25LP128_SPI_MODE, + .cs = FLASH_SPI_CS, + .wp = GPIO_UNDEF, + .hold = GPIO_UNDEF, +}; + +static mtd_spi_nor_t _is25lp128_nor_dev = { + .base = { + .driver = &mtd_spi_nor_driver, + .page_size = IS25LP128_PAGE_SIZE, + .pages_per_sector = IS25LP128_PAGES_PER_SECTOR, + .sector_count = IS25LP128_SECTOR_COUNT, + }, + .params = &_is25lp128_flash_nor_params, +}; + +MTD_XFA_ADD(_is25lp128_nor_dev, 10); + +#elif defined TEST_IS25LE01G + +#define IS25LE01G_PAGE_SIZE (256) +#define IS25LE01G_PAGES_PER_SECTOR (16) +#define IS25LE01G_SECTOR_COUNT (32768) +#define IS25LE01G_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ + SPI_NOR_F_SECT_64K) +#define IS25LE01G_SPI_CLK SPI_CLK_10MHZ +#define IS25LE01G_SPI_MODE SPI_MODE_0 + +static const mtd_spi_nor_params_t _is25le01g_flash_nor_params = { + .opcode = &mtd_spi_nor_opcode_default, + .wait_chip_erase = 90LU * US_PER_SEC, + .wait_32k_erase = 140LU * US_PER_MS, + .wait_sector_erase = 100LU * US_PER_MS, + .wait_chip_wake_up = 35LU * US_PER_MS, + .clk = IS25LE01G_SPI_CLK, + .flag = IS25LE01G_FLAGS, + .spi = SPI_DEV(FLASH_SPI_DEV), + .mode = IS25LE01G_SPI_MODE, + .cs = FLASH_SPI_CS, + .wp = GPIO_UNDEF, + .hold = GPIO_UNDEF, +}; + +static mtd_spi_nor_t _is25le01g_nor_dev = { + .base = { + .driver = &mtd_spi_nor_driver, + .page_size = IS25LE01G_PAGE_SIZE, + .pages_per_sector = IS25LE01G_PAGES_PER_SECTOR, + .sector_count = IS25LE01G_SECTOR_COUNT, + }, + .params = &_is25le01g_flash_nor_params, +}; + +MTD_XFA_ADD(_is25le01g_nor_dev, 10); + +#elif defined TEST_MX25L12873F + +#define MX25L12873F_PAGE_SIZE (256) +#define MX25L12873F_PAGES_PER_SECTOR (16) +#define MX25L12873F_SECTOR_COUNT (4096) +#define MX25L12873F_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ + SPI_NOR_F_SECT_64K) +#define MX25L12873F_SPI_CLK SPI_CLK_10MHZ +#define MX25L12873F_SPI_MODE SPI_MODE_0 + +static const mtd_spi_nor_params_t _mx25l12873f_flash_nor_params = { + .opcode = &mtd_spi_nor_opcode_default, + .wait_chip_erase = 50LU * US_PER_SEC, + .wait_32k_erase = 150LU *US_PER_MS, + .wait_sector_erase = 40LU * US_PER_MS, + .wait_chip_wake_up = 35LU * US_PER_MS, + .clk = MX25L12873F_SPI_CLK, + .flag = MX25L12873F_FLAGS, + .spi = SPI_DEV(FLASH_SPI_DEV), + .mode = MX25L12873F_SPI_MODE, + .cs = FLASH_SPI_CS, + .wp = GPIO_UNDEF, + .hold = GPIO_UNDEF, +}; + +static mtd_spi_nor_t _mx25l12873f_nor_dev = { + .base = { + .driver = &mtd_spi_nor_driver, + .page_size = MX25L12873F_PAGE_SIZE, + .pages_per_sector = MX25L12873F_PAGES_PER_SECTOR, + .sector_count = MX25L12873F_SECTOR_COUNT, + }, + .params = &_mx25l12873f_flash_nor_params, +}; + +MTD_XFA_ADD(_mx25l12873f_nor_dev, 10); + +#endif /* TEST_MX25L12873F */ +/** @} */ diff --git a/tests/drivers/mtd_spi_nor/main.c b/tests/drivers/mtd_spi_nor/main.c new file mode 100644 index 000000000000..6e6d6ca9a011 --- /dev/null +++ b/tests/drivers/mtd_spi_nor/main.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2024 Technische Universität Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Test for the MTD SPI NOR Driver + * + * @author Christopher Büchse + */ + +#include +#include + +#include "busy_wait.h" +#include "embUnit.h" + +#include "mtd_spi_nor.h" +#include "../../drivers/mtd_spi_nor/include/mtd_spi_nor_defines.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define ENABLE_TRACE 0 +#define TRACE(...) DEBUG(__VA_ARGS__) + +#define TEST_MTD mtd_dev_get(MTD_NUMOF-1) /* Make sure to get the last device */ + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static inline spi_t _get_spi(const mtd_spi_nor_t *dev) +{ + return dev->params->spi; +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static void mtd_spi_cmd(const mtd_spi_nor_t *dev, uint8_t opcode) +{ + if (IS_ACTIVE(ENABLE_TRACE)) { + TRACE("mtd_spi_cmd: %p, %02x\n", + (void *)dev, (unsigned int)opcode); + } + spi_transfer_byte(_get_spi(dev), dev->params->cs, false, opcode); +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static void mtd_spi_cmd_read(const mtd_spi_nor_t *dev, uint8_t opcode, void *dest, uint32_t count) +{ + if (IS_ACTIVE(ENABLE_TRACE)) { + TRACE("mtd_spi_cmd_read: %p, %02x, %p, %" PRIu32 "\n", + (void *)dev, (unsigned int)opcode, dest, count); + } + spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode, NULL, dest, count); +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static void mtd_spi_cmd_write(const mtd_spi_nor_t *dev, uint8_t opcode, + const void *src, uint32_t count) +{ + if (IS_ACTIVE(ENABLE_TRACE)) { + TRACE("mtd_spi_cmd_write: %p, %02x, %p, %" PRIu32 "\n", + (void *)dev, (unsigned int)opcode, src, count); + } + spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode, + (void *)src, NULL, count); +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static inline void wait_for_write_enable_set(const mtd_spi_nor_t *dev) +{ + do { + uint8_t status; + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status)); + + if (IS_ACTIVE(ENABLE_TRACE)) { + TRACE("mtd_spi_nor: wait device status = 0x%02x, waiting WEL-Flag\n", + (unsigned int)status); + } + if (status & SPI_NOR_STATUS_WEL) { + break; + } + thread_yield(); + } while (1); +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static void mtd_spi_acquire(const mtd_spi_nor_t *dev) +{ + spi_acquire(_get_spi(dev), dev->params->cs, + dev->params->mode, dev->params->clk); +} + +/* This function was copied from drivers/mtd_spi_nor/mtd_spi_nor.c */ +static void mtd_spi_release(const mtd_spi_nor_t *dev) +{ + spi_release(_get_spi(dev)); +} + +static void setup(void) +{ +} + +static void teardown(void) +{ +} + +static void test_mtd_init(void) +{ + DEBUG("test_mtd_init: Initializing the Device\n"); + + int ret = mtd_init(TEST_MTD); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* make sure the Block Protect bits are not set before we start the tests */ + mtd_spi_nor_t *dev = (mtd_spi_nor_t *)TEST_MTD; + + uint8_t status_reg; + const uint8_t bp_flags = SPI_NOR_STATUS_BP0 | SPI_NOR_STATUS_BP1 | \ + SPI_NOR_STATUS_BP2 | SPI_NOR_STATUS_BP3; + + mtd_init(TEST_MTD); + + /* Revert everything back to the original state */ + mtd_spi_acquire(dev); + + /* check that the Status Register is writable */ + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status_reg, sizeof(status_reg)); + if (status_reg & SPI_NOR_STATUS_SRWD) { + status_reg &= !SPI_NOR_STATUS_SRWD; + } + + /* Send WREN command to write to the Status Register */ + mtd_spi_cmd(dev, dev->params->opcode->wren); + wait_for_write_enable_set(dev); + + /* Mask the status bits and Block Protection Flags */ + status_reg &= !(SPI_NOR_STATUS_WEL | SPI_NOR_STATUS_WIP); + status_reg &= !bp_flags; + + mtd_spi_cmd_write(dev, 0x01, &status_reg, sizeof(status_reg)); /* Opcode: WRSR */ + busy_wait_us(15000); /* writing the SR can take up to 15ms */ + mtd_spi_release(dev); +} + +static void test_mtd_erase(void) +{ + DEBUG("test_mtd_erase: Erasing the first sector\n"); + + const uint32_t sector_size = TEST_MTD->page_size*TEST_MTD->pages_per_sector; + uint8_t buffer[TEST_MTD->page_size]; + + int ret = mtd_erase(TEST_MTD, 0, sector_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* read back the sector and check that it is blank */ + for (uint32_t page = 0; page < TEST_MTD->pages_per_sector; page++) { + ret = mtd_read_page(TEST_MTD, buffer, page, 0, TEST_MTD->page_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + uint8_t sum = 0xFF; + for (uint32_t i = 0; i < TEST_MTD->page_size; i++) { + sum &= buffer[i]; + } + TEST_ASSERT_EQUAL_INT(0xFF, sum); + } +} + +static void test_mtd_block_protect(void) +{ + int ret; + const char test_data[16] = "BLOCK TEST"; + const uint8_t bp_flags = SPI_NOR_STATUS_BP0 | SPI_NOR_STATUS_BP1 | \ + SPI_NOR_STATUS_BP2 | SPI_NOR_STATUS_BP3; + + uint8_t status_reg; + mtd_spi_nor_t *dev = (mtd_spi_nor_t *)TEST_MTD; + + if (!(IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) || IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY))) { + DEBUG("test_mtd_block_protect: No security features enabled, skip test.\n"); + return; + } + + DEBUG("test_mtd_block_protect: Checking the Block Protect Functions\n"); + + mtd_spi_acquire(dev); + + /* check that the Status Register is writable */ + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status_reg, sizeof(status_reg)); + if (status_reg & SPI_NOR_STATUS_SRWD) { + status_reg &= !SPI_NOR_STATUS_SRWD; + } + + /* Mask the status bits */ + status_reg &= !(SPI_NOR_STATUS_WEL | SPI_NOR_STATUS_WIP); + + /* Send WREN command to write to the Status Register */ + mtd_spi_cmd(dev, dev->params->opcode->wren); + wait_for_write_enable_set(dev); + + /* read Status Register again */ + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status_reg, sizeof(status_reg)); + TEST_ASSERT_EQUAL_INT(0, status_reg & SPI_NOR_STATUS_SRWD); + TEST_ASSERT_EQUAL_INT(SPI_NOR_STATUS_WEL, status_reg & SPI_NOR_STATUS_WEL); + + /* protect all blocks */ + status_reg |= bp_flags; + mtd_spi_cmd_write(dev, 0x01, &status_reg, sizeof(status_reg)); /* Opcode WRSR */ + busy_wait_us(15000); /* writing the SR can take up to 15ms */ + + /* confirm that the Block Protection was actually set */ + mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status_reg, sizeof(status_reg)); + TEST_ASSERT_EQUAL_INT(bp_flags, status_reg & bp_flags); + + mtd_spi_release(dev); + + /* Perform a write test to check if the P_FAIL flag is correctly handled */ + ret = mtd_write_page_raw(TEST_MTD, test_data, 0x00000000, 0x00, sizeof(test_data)); + TEST_ASSERT_EQUAL_INT(-EIO, ret); + + /* Perform an erase test to check if the E_FAIL flag is correctly handled */ + ret = mtd_erase(TEST_MTD, 0x00000000, dev->base.page_size * dev->base.pages_per_sector); + TEST_ASSERT_EQUAL_INT(-EIO, ret); + + /* Revert everything back to the original state */ + mtd_spi_acquire(dev); + + /* Send WREN command to write to the Status Register */ + mtd_spi_cmd(dev, dev->params->opcode->wren); + wait_for_write_enable_set(dev); + + /* Mask the status bits and Block Protection Flags */ + status_reg &= !(SPI_NOR_STATUS_WEL | SPI_NOR_STATUS_WIP); + status_reg &= !bp_flags; + + mtd_spi_cmd_write(dev, 0x01, &status_reg, sizeof(status_reg)); /* Opcode: WRSR */ + busy_wait_us(15000); /* writing the SR can take up to 15ms */ + mtd_spi_release(dev); +} + +Test *tests_mtd_spi_nor_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_mtd_init), + new_TestFixture(test_mtd_erase), + + new_TestFixture(test_mtd_block_protect), + }; + + EMB_UNIT_TESTCALLER(mtd_mtd_spi_nor_tests, setup, teardown, fixtures); + + return (Test *)&mtd_mtd_spi_nor_tests; +} + +int main(void) +{ + DEBUG("tests/mtd_spi_nor: %u MTD devices present, selected device: %u\n", + (uint8_t)MTD_NUMOF, (uint8_t)(MTD_NUMOF-1)); + + TESTS_START(); + TESTS_RUN(tests_mtd_spi_nor_tests()); + TESTS_END(); + return 0; +} +/** @} */ From 85d4c8608b814c670ed37c31d1e5733fd98a461f Mon Sep 17 00:00:00 2001 From: crasbe Date: Tue, 2 Jul 2024 18:27:30 +0200 Subject: [PATCH 06/10] fixup! drivers/mtd_spi_nor: removed Kconfig & added MX/ISSI security features --- drivers/Makefile.dep | 4 - drivers/include/mtd_spi_nor.h | 46 +++++++---- drivers/mtd_spi_nor/Makefile.include | 6 -- drivers/mtd_spi_nor/mtd_spi_nor.c | 93 ++++++++++++----------- drivers/mtd_spi_nor/mtd_spi_nor_configs.c | 22 ++---- 5 files changed, 85 insertions(+), 86 deletions(-) delete mode 100644 drivers/mtd_spi_nor/Makefile.include diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 402f02d1084d..f86459b0c230 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -120,10 +120,6 @@ ifneq (,$(filter mtd_%,$(USEMODULE))) USEMODULE += mtd endif -ifneq (,$(filter mtd_spi_nor_%,$(USEMODULE))) - USEMODULE += mtd_spi_nor -endif - # nrfmin is a concrete module but comes from cpu/nrf5x_common. Due to limitations # in the dependency resolution mechanism it's not possible to move its # dependency resolution at cpu level. diff --git a/drivers/include/mtd_spi_nor.h b/drivers/include/mtd_spi_nor.h index 28926e948d9e..e7e730323922 100644 --- a/drivers/include/mtd_spi_nor.h +++ b/drivers/include/mtd_spi_nor.h @@ -25,14 +25,12 @@ * * USEMODULE += mtd_spi_nor * - * For ISSI and Macronix devices, some security features are provided + * For ISSI and Macronix devices, some data integrity features are provided * to check if program or erase operations were successful. - * These features can be activated with the corresponding pseudomodules in the Makefile - * of your board or project: + * These features can be activated by specifying the manufacturer specific flags in + * the mtd_spi_nor_params_t structure. * - * USEMODULE += mtd_spi_nor_mx_security - * or - * USEMODULE += mtd_spi_nor_issi_security + * TODO * * \n * Some examples of how to work with the MTD SPI NOR driver can be found in the test for the @@ -64,6 +62,9 @@ extern "C" /** * @brief SPI NOR flash opcode table + * + * Manufacturer specific opcodes have a short form of the manufacturer as a prefix, + * for example "MX" for "Macronix". */ typedef struct { uint8_t rdid; /**< Read identification (JEDEC ID) */ @@ -79,13 +80,10 @@ typedef struct { uint8_t chip_erase; /**< Chip erase */ uint8_t sleep; /**< Deep power down */ uint8_t wake; /**< Release from deep power down */ -#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) - uint8_t rdscur; /**< Read security register */ -#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) - uint8_t rderp; /**< Read extended read parameters register */ - uint8_t clerp; /**< Clear extended read parameters register */ - uint8_t wrdi; /**< Write disable */ -#endif + uint8_t mx_rdscur; /**< Read security register (Macronix specific) */ + uint8_t issi_rderp; /**< Read extended read parameters register (ISSI specific) */ + uint8_t issi_clerp; /**< Clear extended read parameters register (ISSI specific) */ + uint8_t issi_wrdi; /**< Write disable (ISSI specific) */ } mtd_spi_nor_opcode_t; /** @@ -128,6 +126,22 @@ typedef struct __attribute__((packed)) { */ #define SPI_NOR_F_SECT_64K (4) +/** + * @brief Enable data integrity checks after program/erase operations + * for devices that support it. + */ +#define SPI_NOR_F_CHECK_INTEGRETY (256) + +/** + * @brief Enable functionality that is specific for Macronix devices. + */ +#define SPI_NOR_F_MANUF_MACRONIX (0xC2) + +/** + * @brief Enable functionality that is specific for ISSI devices. + */ +#define SPI_NOR_F_MANUF_ISSI (0x9D) + /** * @brief Compile-time parameters for a serial flash device */ @@ -204,9 +218,9 @@ extern const mtd_desc_t mtd_spi_nor_driver; * different devices, as well as in the Linux kernel, so they seem quite * sensible for default values. * - * To enable manufacturer specific security functions, the according - * pseudomodules have to be used which will activate the corresponding - * opcodes. + * To enable manufacturer specific data integrity functions, the according + * flags have to be set in the mtd_spi_nor_params_t structure in the + * mtd_spi_nor_params_t.manufacturer element. */ extern const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default; diff --git a/drivers/mtd_spi_nor/Makefile.include b/drivers/mtd_spi_nor/Makefile.include deleted file mode 100644 index be745a3df86c..000000000000 --- a/drivers/mtd_spi_nor/Makefile.include +++ /dev/null @@ -1,6 +0,0 @@ -# include variants of MTD SPI NOR drivers as pseudo modules -PSEUDOMODULES += mtd_spi_nor_mx_security -PSEUDOMODULES += mtd_spi_nor_issi_security - -USEMODULE_INCLUDES_mtd_spi_nor := $(LAST_MAKEFILEDIR)/include -USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_mtd_spi_nor) diff --git a/drivers/mtd_spi_nor/mtd_spi_nor.c b/drivers/mtd_spi_nor/mtd_spi_nor.c index 97090597f7f3..9abc52447614 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor.c @@ -436,6 +436,49 @@ static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us DEBUG("\n"); } +static inline int check_data_integrity(const mtd_spi_nor_t *dev, int ret) +{ + /* skip the test if the device definitiuon does not include the integrity test */ + if (!(dev->params->flag & SPI_NOR_F_CHECK_INTEGRETY)) { + return ret; + } + + int new_ret = ret; + uint8_t buffer; + + /* read the first byte of the JEDEC ID to distinguish between manufacturers */ + mtd_spi_cmd_read(dev, dev->params->opcode->rdid, &buffer, sizeof(buffer)); + + if (buffer == SPI_NOR_F_MANUF_MACRONIX) { + /* Macronix specific: Read security register */ + mtd_spi_cmd_read(dev, dev->params->opcode->mx_rdscur, &buffer, sizeof(buffer)); + if (buffer & MX_SECURITY_PFAIL || buffer & MX_SECURITY_EFAIL) { + DEBUG("mtd_spi_nor: ERR: program/erase failed! scur = %02x\n", buffer); + + new_ret = -EIO; + } + } else if (buffer == SPI_NOR_F_MANUF_ISSI) { + /* ISSI specific: Read extended read register for data integrity flags */ + mtd_spi_cmd_read(dev, dev->params->opcode->issi_rderp, &buffer, sizeof(buffer)); + if (buffer & IS_ERP_P_ERR || buffer & IS_ERP_E_ERR) { + DEBUG("mtd_spi_nor: ERR: program/erase failed! erp = %02x\n", buffer); + + new_ret = -EIO; + + /* clear the extended read parameters register manually */ + mtd_spi_cmd(dev, dev->params->opcode->issi_clerp); + + /* ISSI flashs do not reset the WEL-Flag when an operation was not *completed* */ + mtd_spi_cmd(dev, dev->params->opcode->issi_wrdi); + } + } else { + DEBUG("mtd_spi_nor: ERR: unknown manufacturer ID = %02x, skip data integrity test\n", + buffer); + } + + return new_ret; +} + static void _init_pins(mtd_spi_nor_t *dev) { DEBUG("mtd_spi_nor_init: init pins\n"); @@ -668,29 +711,8 @@ static int mtd_spi_nor_write_page(mtd_dev_t *mtd, const void *src, uint32_t page /* waiting for the command to complete before returning */ wait_for_write_complete(dev, 0); -#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) - /* Read security register */ - uint8_t scur_reg; - mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &scur_reg, sizeof(scur_reg)); - if (scur_reg & MX_SECURITY_PFAIL) { - DEBUG("mtd_spi_nor_write: ERR: page program failed! scur = %02x\n", scur_reg); - ret = -EIO; - } -#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) - /* Read extended read register for security flags */ - uint8_t erp_reg; - mtd_spi_cmd_read(dev, dev->params->opcode->rderp, &erp_reg, sizeof(erp_reg)); - if (erp_reg & IS_ERP_P_ERR) { - DEBUG("mtd_spi_nor_write: ERR: page program failed! erp = %02x\n", erp_reg); - ret = -EIO; - - /* clear the extended read parameters register manually */ - mtd_spi_cmd(dev, dev->params->opcode->clerp); - - /* ISSI flashs do not reset the WEL-Flag when an operation was not *completed* */ - mtd_spi_cmd(dev, dev->params->opcode->wrdi); - } -#endif + /* (optionally) check if the operation was successful */ + ret = check_data_integrity(dev, ret); /* Wait for WEL-Flag to be cleared */ wait_for_write_enable_cleared(dev); @@ -775,29 +797,8 @@ static int mtd_spi_nor_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t size) /* waiting for the command to complete before continuing */ wait_for_write_complete(dev, us); -#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) - /* Read security register */ - uint8_t scur_reg; - mtd_spi_cmd_read(dev, dev->params->opcode->rdscur, &scur_reg, sizeof(scur_reg)); - if (scur_reg & MX_SECURITY_EFAIL) { - DEBUG("mtd_spi_nor_write: ERR: erase failed! scur = %02x\n", scur_reg); - ret = -EIO; - } -#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) - /* Read extended read register for security flags */ - uint8_t erp_reg; - mtd_spi_cmd_read(dev, dev->params->opcode->rderp, &erp_reg, sizeof(erp_reg)); - if (erp_reg & IS_ERP_E_ERR) { - DEBUG("mtd_spi_nor_write: ERR: erase failed! erp = %02x\n", erp_reg); - ret = -EIO; - - /* clear the extended read parameters register manually */ - mtd_spi_cmd(dev, dev->params->opcode->clerp); - - /* ISSI flashs do not reset the WEL-Flag when an operation was not *completed* */ - mtd_spi_cmd(dev, dev->params->opcode->wrdi); - } -#endif + /* (optionally) check if the operation was successful */ + ret = check_data_integrity(dev, ret); /* Wait for WEL-Flag to be cleared */ wait_for_write_enable_cleared(dev); diff --git a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c index 3fe6f76b740e..e66a1e656495 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor_configs.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor_configs.c @@ -40,13 +40,10 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, -#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) - .rdscur = 0x2b, -#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) - .rderp = 0x81, - .clerp = 0x82, - .wrdi = 0x04, -#endif + .mx_rdscur = 0x2b, + .issi_rderp = 0x81, + .issi_clerp = 0x82, + .issi_wrdi = 0x04, }; const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { @@ -63,13 +60,10 @@ const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default_4bytes = { .chip_erase = 0xc7, .sleep = 0xb9, .wake = 0xab, -#if IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) - .rdscur = 0x2b, -#elif IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY) - .rderp = 0x81, - .clerp = 0x82, - .wrdi = 0x04, -#endif + .mx_rdscur = 0x2b, + .issi_rderp = 0x81, + .issi_clerp = 0x82, + .issi_wrdi = 0x04, }; /** @} */ From 5cd68257d5011ea29a423e4eaa51f4add652acb8 Mon Sep 17 00:00:00 2001 From: crasbe Date: Tue, 2 Jul 2024 18:29:41 +0200 Subject: [PATCH 07/10] fixup! tests/mtd_spi_nor: Added test with security features --- tests/drivers/mtd_spi_nor/README.md | 22 ++++++++-------------- tests/drivers/mtd_spi_nor/flash_dut.c | 8 ++++---- tests/drivers/mtd_spi_nor/main.c | 6 +++++- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/drivers/mtd_spi_nor/README.md b/tests/drivers/mtd_spi_nor/README.md index f72d963d226e..7a59d294e578 100644 --- a/tests/drivers/mtd_spi_nor/README.md +++ b/tests/drivers/mtd_spi_nor/README.md @@ -11,27 +11,25 @@ This test will destroy the data present on the chip! The test is designed to work with the built-in MX25F6435F flash of the Nordic Semiconductor nRF52840DK development board. Currently the nRF52840DK Flash definitions do not have the security options enabled. +To enable the security features of the built-in flash, the "SPI_NOR_F_MANUF_CHECK_INTEGRITY" +flag has to be added to the NRF52840DK_NOR_FLAGS define in the +boards/nrf52840dk/include/board.h file. To run the test you simply have to compile and run the test: ``` make flash term ``` The tests should conclude with "OK (3 tests)". - -To enable the security features of the built-in flash, the "MTD_SPI_NOR_MX_SECURITY" -pseudomodule can be added to the make call: -``` -USEMODULE+=mtd_spi_nor_mx_security make flash term ``` Likewise, the tests should conclude with "OK (3 tests)". ## nRF52840DK with different flash chips -The default SPI Device for using different Flash chips is SPI(1), which is present on +The default SPI Device for using different Flash chips is SPI(0), which is present on the Arduino Uno style headers. The default pin for Chip Select is P1.5. The SPI Device and Chip Select pin can be changed with the CFLAGS FLASH_SPI_DEV and FLASH_SPI_CS: ``` -CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)'" ... +CFLAGS+="-DFLASH_SPI_DEV=0 -DFLASH_SPI_CS='GPIO_PIN(1,5)'" ... ``` The Device under Test can be changed with CFLAGS as well: @@ -39,16 +37,12 @@ The Device under Test can be changed with CFLAGS as well: ... CFLAGS+="-DTEST_IS25LE01G" ... ``` -If the Flash supports the security features, they can be enabled with the usage of a -pseudomodule as well: -``` -... USEMODULE+=mtd_spi_nor_issi_security ... -``` +If the Flash supports the security features, it is automatically enabled in the +mtd_spi_nor_params_t in the flash_dut.c file. A full test call would therefore be for example: ``` -CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)' -DTEST_IS25LE01G" \ -USEMODULE+=mtd_spi_nor_issi_security make flash term +CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)' -DTEST_IS25LE01G" make flash term ``` # Tested/Supported Chips diff --git a/tests/drivers/mtd_spi_nor/flash_dut.c b/tests/drivers/mtd_spi_nor/flash_dut.c index 95dd66eaa035..f216d770a498 100644 --- a/tests/drivers/mtd_spi_nor/flash_dut.c +++ b/tests/drivers/mtd_spi_nor/flash_dut.c @@ -19,7 +19,7 @@ /* The default CS pin and SPI device is for the built-in flash of the nRF52840DK */ #ifndef FLASH_SPI_CS -#define FLASH_SPI_CS GPIO_PIN(0, 17) +#define FLASH_SPI_CS GPIO_PIN(1, 5) #endif #ifndef FLASH_SPI_DEV @@ -33,7 +33,7 @@ #define IS25LP128_SECTOR_COUNT (4096) #define IS25LP128_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ SPI_NOR_F_SECT_64K) -#define IS25LP128_SPI_CLK SPI_CLK_100KHZ +#define IS25LP128_SPI_CLK SPI_CLK_1MHZ #define IS25LP128_SPI_MODE SPI_MODE_0 static const mtd_spi_nor_params_t _is25lp128_flash_nor_params = { @@ -69,7 +69,7 @@ MTD_XFA_ADD(_is25lp128_nor_dev, 10); #define IS25LE01G_PAGES_PER_SECTOR (16) #define IS25LE01G_SECTOR_COUNT (32768) #define IS25LE01G_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ - SPI_NOR_F_SECT_64K) + SPI_NOR_F_SECT_64K | SPI_NOR_F_CHECK_INTEGRETY) #define IS25LE01G_SPI_CLK SPI_CLK_10MHZ #define IS25LE01G_SPI_MODE SPI_MODE_0 @@ -106,7 +106,7 @@ MTD_XFA_ADD(_is25le01g_nor_dev, 10); #define MX25L12873F_PAGES_PER_SECTOR (16) #define MX25L12873F_SECTOR_COUNT (4096) #define MX25L12873F_FLAGS (SPI_NOR_F_SECT_4K | SPI_NOR_F_SECT_32K | \ - SPI_NOR_F_SECT_64K) + SPI_NOR_F_SECT_64K | SPI_NOR_F_CHECK_INTEGRETY) #define MX25L12873F_SPI_CLK SPI_CLK_10MHZ #define MX25L12873F_SPI_MODE SPI_MODE_0 diff --git a/tests/drivers/mtd_spi_nor/main.c b/tests/drivers/mtd_spi_nor/main.c index 6e6d6ca9a011..7dba368a8d78 100644 --- a/tests/drivers/mtd_spi_nor/main.c +++ b/tests/drivers/mtd_spi_nor/main.c @@ -157,6 +157,8 @@ static void test_mtd_erase(void) int ret = mtd_erase(TEST_MTD, 0, sector_size); TEST_ASSERT_EQUAL_INT(0, ret); + return; + /* read back the sector and check that it is blank */ for (uint32_t page = 0; page < TEST_MTD->pages_per_sector; page++) { ret = mtd_read_page(TEST_MTD, buffer, page, 0, TEST_MTD->page_size); @@ -180,7 +182,7 @@ static void test_mtd_block_protect(void) uint8_t status_reg; mtd_spi_nor_t *dev = (mtd_spi_nor_t *)TEST_MTD; - if (!(IS_USED(MODULE_MTD_SPI_NOR_MX_SECURITY) || IS_USED(MODULE_MTD_SPI_NOR_ISSI_SECURITY))) { + if (!(dev->params->flag & SPI_NOR_F_CHECK_INTEGRETY)) { DEBUG("test_mtd_block_protect: No security features enabled, skip test.\n"); return; } @@ -222,6 +224,8 @@ static void test_mtd_block_protect(void) ret = mtd_write_page_raw(TEST_MTD, test_data, 0x00000000, 0x00, sizeof(test_data)); TEST_ASSERT_EQUAL_INT(-EIO, ret); + busy_wait_us(1000000); + /* Perform an erase test to check if the E_FAIL flag is correctly handled */ ret = mtd_erase(TEST_MTD, 0x00000000, dev->base.page_size * dev->base.pages_per_sector); TEST_ASSERT_EQUAL_INT(-EIO, ret); From 2ab4d6663ba722edbcaf4aecb8bf9c5c6d338ae7 Mon Sep 17 00:00:00 2001 From: crasbe Date: Tue, 2 Jul 2024 18:41:59 +0200 Subject: [PATCH 08/10] fixup! fixup! tests/mtd_spi_nor: Added test with security features --- tests/drivers/mtd_spi_nor/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/drivers/mtd_spi_nor/README.md b/tests/drivers/mtd_spi_nor/README.md index 7a59d294e578..f4a463b2273e 100644 --- a/tests/drivers/mtd_spi_nor/README.md +++ b/tests/drivers/mtd_spi_nor/README.md @@ -10,8 +10,8 @@ This test will destroy the data present on the chip! ## nRF52840DK The test is designed to work with the built-in MX25F6435F flash of the Nordic Semiconductor nRF52840DK development board. -Currently the nRF52840DK Flash definitions do not have the security options enabled. -To enable the security features of the built-in flash, the "SPI_NOR_F_MANUF_CHECK_INTEGRITY" +Currently the nRF52840DK Flash definitions do not have the data integrity options enabled. +To enable these features for the built-in flash, the "SPI_NOR_F_MANUF_CHECK_INTEGRITY" flag has to be added to the NRF52840DK_NOR_FLAGS define in the boards/nrf52840dk/include/board.h file. @@ -37,7 +37,7 @@ The Device under Test can be changed with CFLAGS as well: ... CFLAGS+="-DTEST_IS25LE01G" ... ``` -If the Flash supports the security features, it is automatically enabled in the +If the Flash supports the data integrity features, it is automatically enabled in the mtd_spi_nor_params_t in the flash_dut.c file. A full test call would therefore be for example: @@ -49,7 +49,7 @@ CFLAGS+="-DFLASH_SPI_DEV=1 -DFLASH_SPI_CS='GPIO_PIN(1,5)' -DTEST_IS25LE01G" make ## ISSI -- IS25LP128 (no security features) +- IS25LP128 (no data integrity check features) - IS25LE01G ## Macronix From 56c28dfa6bb17a8ae311fcfb58665cde8cd4d68c Mon Sep 17 00:00:00 2001 From: crasbe Date: Tue, 2 Jul 2024 18:46:06 +0200 Subject: [PATCH 09/10] fixup! fixup! drivers/mtd_spi_nor: removed Kconfig & added MX/ISSI security features --- drivers/include/mtd_spi_nor.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/include/mtd_spi_nor.h b/drivers/include/mtd_spi_nor.h index e7e730323922..4bada7453aa6 100644 --- a/drivers/include/mtd_spi_nor.h +++ b/drivers/include/mtd_spi_nor.h @@ -27,10 +27,8 @@ * * For ISSI and Macronix devices, some data integrity features are provided * to check if program or erase operations were successful. - * These features can be activated by specifying the manufacturer specific flags in - * the mtd_spi_nor_params_t structure. - * - * TODO + * These features can be activated by specifying the "SPI_NOR_F_CHECK_INTEGRITY" flag in + * the "flag" parameter of the mtd_spi_nor_params_t structure. * * \n * Some examples of how to work with the MTD SPI NOR driver can be found in the test for the @@ -130,7 +128,7 @@ typedef struct __attribute__((packed)) { * @brief Enable data integrity checks after program/erase operations * for devices that support it. */ -#define SPI_NOR_F_CHECK_INTEGRETY (256) +#define SPI_NOR_F_CHECK_INTEGRETY (256) /** * @brief Enable functionality that is specific for Macronix devices. @@ -140,7 +138,7 @@ typedef struct __attribute__((packed)) { /** * @brief Enable functionality that is specific for ISSI devices. */ -#define SPI_NOR_F_MANUF_ISSI (0x9D) +#define SPI_NOR_F_MANUF_ISSI (0x9D) /** * @brief Compile-time parameters for a serial flash device @@ -217,10 +215,6 @@ extern const mtd_desc_t mtd_spi_nor_driver; * be found in Macronix MX25L25735E, and multiple other data sheets for * different devices, as well as in the Linux kernel, so they seem quite * sensible for default values. - * - * To enable manufacturer specific data integrity functions, the according - * flags have to be set in the mtd_spi_nor_params_t structure in the - * mtd_spi_nor_params_t.manufacturer element. */ extern const mtd_spi_nor_opcode_t mtd_spi_nor_opcode_default; From 41921a7bcf7c3437f64f051923c156baa6887bbd Mon Sep 17 00:00:00 2001 From: crasbe Date: Tue, 2 Jul 2024 18:52:04 +0200 Subject: [PATCH 10/10] fixup! fixup! fixup! tests/mtd_spi_nor: Added test with security features --- tests/drivers/mtd_spi_nor/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/drivers/mtd_spi_nor/main.c b/tests/drivers/mtd_spi_nor/main.c index 7dba368a8d78..522f72bef932 100644 --- a/tests/drivers/mtd_spi_nor/main.c +++ b/tests/drivers/mtd_spi_nor/main.c @@ -224,7 +224,10 @@ static void test_mtd_block_protect(void) ret = mtd_write_page_raw(TEST_MTD, test_data, 0x00000000, 0x00, sizeof(test_data)); TEST_ASSERT_EQUAL_INT(-EIO, ret); - busy_wait_us(1000000); + /* wait for one second between the tests */ + for (uint16_t ms = 1000; ms; ms--) { + busy_wait_us((unsigned int)US_PER_MS); + } /* Perform an erase test to check if the E_FAIL flag is correctly handled */ ret = mtd_erase(TEST_MTD, 0x00000000, dev->base.page_size * dev->base.pages_per_sector);