From b25e2e01255e35bcb8e88cea3d5d91fdd14df167 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 22 Dec 2021 19:07:00 +0100 Subject: [PATCH] spiFlash: enable/disable protection. now handle correctly device with protection enabled --- src/spiFlash.cpp | 212 +++++++++++++++++++++++++++++++++++++++-------- src/spiFlash.hpp | 21 ++++- 2 files changed, 194 insertions(+), 39 deletions(-) diff --git a/src/spiFlash.cpp b/src/spiFlash.cpp index ffafb93..0decf19 100644 --- a/src/spiFlash.cpp +++ b/src/spiFlash.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "progressBar.hpp" @@ -28,6 +29,8 @@ #define FLASH_WREN 0x06 /* sector (4Kb) erase */ #define FLASH_SE 0x20 +/* read configuration register */ +#define FLASH_RDCR 0x35 /* write function register (at least ISSI) */ #define FLASH_WRFR 0x42 /* read function register (at least ISSI) */ @@ -64,10 +67,12 @@ /* Global Block Protection unlock */ #define FLASH_ULBPR 0x98 -SPIFlash::SPIFlash(SPIInterface *spi, int8_t verbose): +SPIFlash::SPIFlash(SPIInterface *spi, bool unprotect, int8_t verbose): _spi(spi), _verbose(verbose), _jedec_id(0), - _flash_model(NULL) + _flash_model(NULL), _unprotect(unprotect) { + reset(); + power_up(); read_id(); } @@ -130,8 +135,8 @@ int SPIFlash::sectors_erase(int base_addr, int size) break; } - /* if block erase + addr end out of end_addr -> use sector_erase */ - if (addr + 0x10000 > end_addr) { + /* if block erase + addr end out of end_addr -> use sector_erase (4Kb) */ + if (addr + 0x10000 > end_addr && _flash_model && _flash_model->subsector_erase) { step = 0x1000; ret = sector_erase(addr); } else { @@ -236,19 +241,75 @@ int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len) { if (_jedec_id == 0) read_id(); + bool must_relock = false; // used to relock after write; - /* check Block Protect Bits */ - if (_jedec_id == 0xbf2642bf) { // microchip SST26VF032B + /* microchip SST26VF032B have global lock set + * at powerup. global unlock must be send inconditionally + * with or without block protection + */ + if (_jedec_id == 0xbf2642bf) { // microchip SST26VF032B if (!global_unlock()) return -1; - } else { - uint8_t status = read_status_reg(); - if ((status & 0x1c) !=0) { + } + /* check Block Protect Bits (hide WIP/WEN bits) */ + uint8_t status = read_status_reg() & ~0x03; + if (_verbose > 0) + display_status_reg(status); + /* if known chip */ + if (_flash_model) { + /* check if offset + len fit in flash */ + if ((unsigned int)(base_addr + len) > (_flash_model->nr_sector * 0x10000) - 1) { + printError("flash overflow"); + return -1; + } + /* compute protected area */ + int8_t tb = get_tb(); + if (tb == -1) + return -1; + std::map lock_len = bp_to_len(status, tb); + printf("%08x %08x %08x %02x\n", base_addr, + lock_len["start"], lock_len["end"], status); + + /* if some blocks are locked */ + if (lock_len["start"] != 0 || lock_len["end"] != 0) { + /* if overlap */ + if (tb == 1) { // bottom blocks are protected + // check if start is in protected blocks + if ((uint32_t)base_addr <= lock_len["end"]) + must_relock = true; + } else { // top blocks + if ((uint32_t)(base_addr + len) >= lock_len["start"]) + must_relock = true; + } + } + /* ISSI IS25LP032 seems have a bug: + * block protection is always in top mode regardless of + * the TB bit: if write is not at offset 0 -> force unlock + */ + if ((_jedec_id >> 8) == 0x9d6016 && tb == 1 && base_addr != 0) { + _unprotect = true; + must_relock = true; + } + } else { // unknown chip: basic test + printWarn("flash chip unknown: use basic protection detection"); + if ((status & 0x1c) != 0) + must_relock = true; + } + + /* if it's needs to unlock */ + if (must_relock) { + printf("unlock blocks\n"); + if (!_unprotect) { + printError("Error: block protection is set"); + printError(" can't unlock without --unprotect-flash"); + return -1; + } else { if (disable_protection() != 0) return -1; } } + /* Now we can erase sector and write new data */ ProgressBar progress("Writing", len, 50, _verbose < 0); if (sectors_erase(base_addr, len) == -1) return -1; @@ -262,6 +323,13 @@ int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len) progress.display(addr); } progress.done(); + + /* and if required: relock blocks */ + if (must_relock) { + enable_protection(status); + if (_verbose > 0) + display_status_reg(read_status_reg()); + } return 0; } @@ -376,12 +444,41 @@ void SPIFlash::display_status_reg(uint8_t reg) if (reg & _flash_model->bp_offset[i]) bp |= 1 << i; } + printf("RDSR : %02x\n", reg); printf("WIP : %d\n", reg&0x01); printf("WEL : %d\n", (reg>>1)&0x01); printf("BP : %x\n", bp); - printf("TB : %d\n", tb); - printf("SRWD : %d\n", (((reg>>7)&0x01))); + if ((_jedec_id >> 8) != 0x9d60) { + printf("TB : %d\n", tb); + } else { // ISSI IS25LP + printf("QE : %d\n", ((reg >> 6) & 0x01)); + } + printf("SRWD : %d\n", ((reg >> 7) & 0x01)); + + /* function register */ + switch (_jedec_id >> 8) { + case 0x9d60: + _spi->spi_put(FLASH_RDFR, NULL, ®, 1); + printf("\nFunction Register\n"); + printf("RDFR : %02x\n", reg); + printf("RES : %d\n", ((reg >> 0) & 0x01)); + printf("TBS : %d\n", ((reg >> 1) & 0x01)); + printf("PSUS : %d\n", ((reg >> 2) & 0x01)); + printf("ESUS : %d\n", ((reg >> 3) & 0x01)); + printf("IRL : %x\n", ((reg >> 4) & 0x0f)); + break; + case 0x010216: + _spi->spi_put(FLASH_RDCR, NULL, ®, 1); + printf("\nConfiguration Register\n"); + printf("RDCR : %02x\n", reg); + printf("FREEZE : %d\n", ((reg >> 0) & 0x01)); + printf("QUAD : %d\n", ((reg >> 1) & 0x01)); + printf("TBPARM : %d\n", ((reg >> 2) & 0x01)); + printf("BPNV : %d\n", ((reg >> 3) & 0x01)); + printf("TBPROT : %d\n", ((reg >> 5) & 0x01)); + break; + } } uint8_t SPIFlash::read_status_reg() @@ -456,8 +553,9 @@ int SPIFlash::disable_protection() if (read_status_reg() != 0) { std::cout << "disable protection failed" << std::endl; return -1; - } else - return 0; + } + + return 0; } /* write protect code to status register @@ -489,7 +587,7 @@ int SPIFlash::enable_protection(uint8_t protect_code) return 0; } -int SPIFlash::enable_protection(int length) +int SPIFlash::enable_protection(uint32_t length) { /* flash device is not listed: can't know BPx position, nor * TB offset, nor TB non-volatile vs OTP */ @@ -506,27 +604,35 @@ int SPIFlash::enable_protection(int length) * current (temporary) policy: do nothing */ if (_flash_model->tb_otp) { - uint8_t status; - /* red TB: not aloways in status register */ - switch (_flash_model->tb_register) { - case STATR: // status register - status = read_status_reg(); - break; - case FUNCR: // function register - _spi->spi_put(FLASH_RDFR, NULL, &status, 1); - break; - default: // unknown - printError("Unknown Top/Bottom register"); - return -1; - } - + uint8_t tb = get_tb(); /* check if TB is set */ - if (!(status & _flash_model->tb_otp)) { + if (tb == 0) { printError("TOP/BOTTOM bit is OTP: can't write this bit"); return -1; } } + /* spansion devices have only one instruction to write + * both status register and configuration register + * we have to write 2 bytes: + * 0: status register + * 1: configuration register + */ + if ((_jedec_id >> 8) == 0x010216) { + int ret = 0; + uint8_t status; + _spi->spi_put(FLASH_RDCR, NULL, &status, 1); + uint8_t cfg[2] = {bp, status}; + cfg[1] |= _flash_model->tb_offset; + _spi->spi_put(FLASH_WRSR, cfg, NULL, 2); + if (_spi->spi_wait(FLASH_RDSR, 0x03, 0, 1000) < 0) { + printError("Error: enable protection failed\n"); + return -1; + } + + return ret; + } + /* if TB is located in status register -> set to 1 */ if (_flash_model->tb_register == STATR) bp |= _flash_model->tb_offset; @@ -555,8 +661,9 @@ int SPIFlash::enable_protection(int length) printError("Error: enable protection failed\n"); return -1; } - _spi->spi_put(reg_rd, &val, NULL, 1); - if (reg_rd != val) { + uint8_t rd_val; + _spi->spi_put(reg_rd, NULL, &rd_val, 1); + if (rd_val != val) { printError("failed to update TB bit"); return -1; } @@ -565,12 +672,38 @@ int SPIFlash::enable_protection(int length) return ret; } -/* convert bp area (status register) to len in byte */ -uint32_t SPIFlash::bp_to_len(uint8_t bp) +/* retrieve TB (Top/Bottom) bit from register */ +int8_t SPIFlash::get_tb() { + uint8_t status; + /* read TB: not always in status register */ + switch (_flash_model->tb_register) { + case STATR: // status register + status = read_status_reg(); + break; + case FUNCR: // function register + _spi->spi_put(FLASH_RDFR, NULL, &status, 1); + break; + case CONFR: // function register + _spi->spi_put(FLASH_RDCR, NULL, &status, 1); + break; + default: // unknown + printError("Unknown Top/Bottom register"); + return -1; + } + + return (status & _flash_model->tb_offset) ? 1 : 0; +} + +/* convert bp area (status register) to len in byte */ +std::map SPIFlash::bp_to_len(uint8_t bp, uint8_t tb) +{ + std::map protect_area; + protect_area["start"] = 0; + protect_area["end"] = 0; /* 0 -> no block protected */ if (bp == 0) - return 0; + return protect_area; /* reconstruct code based on each BPx bit */ uint8_t tmp = 0; @@ -579,8 +712,17 @@ uint32_t SPIFlash::bp_to_len(uint8_t bp) tmp |= (1 << i); /* bp code is 2^(bp-1) blocks */ uint16_t nr_sectors = (1 << (tmp-1)); + printf("nr_sectors : %d\n", nr_sectors); + uint32_t len = nr_sectors * 0x10000; + if (tb == 1) { + protect_area["start"] = 0; + protect_area["end"] = len - 1; + } else { + protect_area["end"] = (_flash_model->nr_sector * 0x10000) - 1; + protect_area["start"] = (protect_area["end"] + 1) - len; + } - return nr_sectors * 0x10000; + return protect_area; } /* convert len (in byte) to bp (block protection) */ diff --git a/src/spiFlash.hpp b/src/spiFlash.hpp index 590890b..57453c1 100644 --- a/src/spiFlash.hpp +++ b/src/spiFlash.hpp @@ -6,6 +6,7 @@ #ifndef SRC_SPIFLASH_HPP_ #define SRC_SPIFLASH_HPP_ +#include #include #include "spiInterface.hpp" @@ -13,11 +14,11 @@ class SPIFlash { public: - SPIFlash(SPIInterface *spi, int8_t verbose); + SPIFlash(SPIInterface *spi, bool unprotect, int8_t verbose); /* power */ virtual void power_up(); virtual void power_down(); - void reset(); + virtual void reset(); /* protection */ int write_enable(); int write_disable(); @@ -37,7 +38,7 @@ class SPIFlash { * \param[in] length: TODO * \return -1 if write enable or enabling failed */ - int enable_protection(int len); + int enable_protection(uint32_t len); /*! * \brief unlock all sectors: specific to * Microchip SST26VF032B / SST26VF032BA @@ -94,17 +95,28 @@ class SPIFlash { uint8_t read_status_reg(); /* display/info */ void display_status_reg(uint8_t reg); + void display_status_reg() {display_status_reg(read_status_reg());} virtual void read_id(); uint16_t readNonVolatileCfgReg(); uint16_t readVolatileCfgReg(); protected: + /*! + * \brief retrieve TB (Top/Bottom) bit from one register + * (depends on flash) + * \return -1 if unknown register, 1 or 0 otherwise + */ + int8_t get_tb(); + + public: /*! * \brief convert block protect to len in byte * \param[in] bp: block protection * \return protect area in byte */ - uint32_t bp_to_len(uint8_t bp); + std::map bp_to_len(uint8_t bp, uint8_t tb); + + protected: /*! * \brief convert len (in byte) to corresponding block protect * \param[in] len: len in byte @@ -116,6 +128,7 @@ class SPIFlash { int8_t _verbose; uint32_t _jedec_id; /**< CHIP ID */ flash_t *_flash_model; /**< detect flash model */ + bool _unprotect; /**< allows to unprotect memory before write */ }; #endif // SRC_SPIFLASH_HPP_