diff --git a/components/bootloader_support/include/bootloader_flash.h b/components/bootloader_support/include/bootloader_flash.h index 8bf7485252..2edc46ebbf 100644 --- a/components/bootloader_support/include/bootloader_flash.h +++ b/components/bootloader_support/include/bootloader_flash.h @@ -18,6 +18,10 @@ #include "sdkconfig.h" #include "soc/soc_caps.h" +#ifdef __cplusplus +extern "C" { +#endif + #if SOC_CACHE_SUPPORT_WRAP /** * @brief Set the burst mode setting command for specified wrap mode. @@ -27,3 +31,15 @@ */ esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode); #endif + +/** + * @brief Unlock Flash write protect. + * Please do not call this function in SDK. + * + * @note This can be overridden because it's attribute weak. + */ +esp_err_t bootloader_flash_unlock(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index d83024a6fc..ed76562d71 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -31,7 +31,9 @@ # define SPIFLASH SPIMEM1 #endif -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32S2 #include "esp32s2/rom/spi_flash.h" #elif CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/spi_flash.h" @@ -39,6 +41,17 @@ #include "esp32c3/rom/spi_flash.h" #endif +#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF) +#define ISSI_ID 0x9D +#define GD_Q_ID_HIGH 0xC8 +#define GD_Q_ID_MID 0x40 +#define GD_Q_ID_LOW 0x16 + +#define ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2) +#define ESP_BOOTLOADER_SPIFLASH_QE_16B BIT9 // QE position when you write 16 bits at one time. +#define ESP_BOOTLOADER_SPIFLASH_QE_8B BIT1 // QE position when you write 8 bits(for SR2) at one time. +#define ESP_BOOTLOADER_SPIFLASH_WRITE_8B (8) +#define ESP_BOOTLOADER_SPIFLASH_WRITE_16B (16) #ifndef BOOTLOADER_BUILD /* Normal app version maps to esp_spi_flash.h operations... @@ -398,7 +411,7 @@ esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool return ESP_FAIL; } - err = spi_to_esp_err(esp_rom_spiflash_unlock()); + err = bootloader_flash_unlock(); if (err != ESP_OK) { return err; } @@ -442,10 +455,90 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size) #endif +FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip) +{ + return BYTESHIFT(chip->device_id, 2) == ISSI_ID; +} + +// For GD25Q32, GD25Q64, GD25Q127C, GD25Q128, which use single command to read/write different SR. +FORCE_INLINE_ATTR bool is_gd_q_chip(const esp_rom_spiflash_chip_t* chip) +{ + return BYTESHIFT(chip->device_id, 2) == GD_Q_ID_HIGH && BYTESHIFT(chip->device_id, 1) == GD_Q_ID_MID && BYTESHIFT(chip->device_id, 0) >= GD_Q_ID_LOW; +} + +esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void) +{ + uint16_t status = 0; // status for SR1 or SR1+SR2 if writing SR with 01H + 2Bytes. + uint16_t new_status = 0; + uint8_t status_sr2 = 0; // status_sr2 for SR2. + uint8_t new_status_sr2 = 0; + uint8_t write_sr_bit = 0; + esp_err_t err = ESP_OK; + + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + if (is_issi_chip(&g_rom_flashchip)) { + write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B; + // ISSI chips have different QE position + + status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8); + + /* Clear all bits in the mask. + (This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.) + */ + new_status = status & (~ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI); + // Skip if nothing needs to be cleared. Otherwise will waste time waiting for the flash to clear nothing. + } else if (is_gd_q_chip(&g_rom_flashchip)) { + /* The GD chips behaviour is to clear all bits in SR1 and clear bits in SR2 except QE bit. + Use 01H to write SR1 and 31H to write SR2. + */ + write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B; + + status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8); + new_status = 0; + + status_sr2 = bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8); + new_status_sr2 = status_sr2 & ESP_BOOTLOADER_SPIFLASH_QE_8B; + } else { + /* For common behaviour, like XMC chips, Use 01H+2Bytes to write both SR1 and SR2*/ + write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_16B; + status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8); + + /* Clear all bits except QE, if it is set. + (This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.) + */ + new_status = status & ESP_BOOTLOADER_SPIFLASH_QE_16B; + } + + if (status != new_status) { + /* if the status in SR not equal to the ideal status, the status need to be updated */ + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + bootloader_execute_flash_command(CMD_WREN, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + bootloader_execute_flash_command(CMD_WRSR, new_status, write_sr_bit, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + } + + if (status_sr2 != new_status_sr2) { + /* If the status in SR2 not equal to the ideal status, the status need to be updated. + It doesn't need to be updated if status in SR2 is 0. + Note: if we need to update both SR1 and SR2, the `CMD_WREN` needs to be sent again. + */ + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + bootloader_execute_flash_command(CMD_WREN, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + bootloader_execute_flash_command(CMD_WRSR2, new_status_sr2, write_sr_bit, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + } + + bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + return err; +} + #ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here extern uint8_t g_rom_spiflash_dummy_len_plus[]; #endif -uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len) +uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len) { uint32_t old_ctrl_reg = SPIFLASH.ctrl.val; #if CONFIG_IDF_TARGET_ESP32 diff --git a/components/bootloader_support/src/esp32/bootloader_esp32.c b/components/bootloader_support/src/esp32/bootloader_esp32.c index 7fcefbd3c1..5206e159b0 100644 --- a/components/bootloader_support/src/esp32/bootloader_esp32.c +++ b/components/bootloader_support/src/esp32/bootloader_esp32.c @@ -260,7 +260,7 @@ static esp_err_t bootloader_init_spi_flash(void) } #endif - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT bootloader_enable_qio_mode(); diff --git a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c index 31a9be36bc..a4f513d59e 100644 --- a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c +++ b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c @@ -212,7 +212,7 @@ static esp_err_t bootloader_init_spi_flash(void) #endif bootloader_spi_flash_resume(); - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT bootloader_enable_qio_mode(); diff --git a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c index aa089531c3..780a8bfeb4 100644 --- a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c +++ b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c @@ -206,7 +206,7 @@ static esp_err_t bootloader_init_spi_flash(void) } #endif - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT bootloader_enable_qio_mode(); diff --git a/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c b/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c index f6e0c2e853..96c62bec54 100644 --- a/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c +++ b/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c @@ -206,7 +206,7 @@ static esp_err_t bootloader_init_spi_flash(void) } #endif - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT bootloader_enable_qio_mode(); diff --git a/components/esp_rom/include/esp32/rom/spi_flash.h b/components/esp_rom/include/esp32/rom/spi_flash.h index 6d162343f3..500ba300cb 100644 --- a/components/esp_rom/include/esp32/rom/spi_flash.h +++ b/components/esp_rom/include/esp32/rom/spi_flash.h @@ -260,7 +260,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_read_status(esp_rom_spiflash_chip_t * esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status); /** - * @brief Write status to Falsh status register. + * @brief Write status to Flash status register. * Please do not call this function in SDK. * * @param esp_rom_spiflash_chip_t *spi : The information for Flash, which is exported from ld file. diff --git a/components/esp_rom/include/esp32s2/rom/opi_flash.h b/components/esp_rom/include/esp32s2/rom/opi_flash.h index c985810b67..bb209f67f0 100644 --- a/components/esp_rom/include/esp32s2/rom/opi_flash.h +++ b/components/esp_rom/include/esp32s2/rom/opi_flash.h @@ -40,7 +40,6 @@ typedef struct { #define ESP_ROM_SPIFLASH_BP2 BIT4 #define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2) #define ESP_ROM_SPIFLASH_QE BIT9 -#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2) #define FLASH_OP_MODE_RDCMD_DOUT 0x3B #define ESP_ROM_FLASH_SECTOR_SIZE 0x1000 diff --git a/components/esp_rom/include/esp32s2/rom/spi_flash.h b/components/esp_rom/include/esp32s2/rom/spi_flash.h index c93b4e27c1..beb2fcdf30 100644 --- a/components/esp_rom/include/esp32s2/rom/spi_flash.h +++ b/components/esp_rom/include/esp32s2/rom/spi_flash.h @@ -119,7 +119,6 @@ extern "C" { #define ESP_ROM_SPIFLASH_BP2 BIT4 #define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2) #define ESP_ROM_SPIFLASH_QE BIT9 -#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2) #define FLASH_ID_GD25LQ32C 0xC86016 diff --git a/components/esp_rom/include/esp32s3/rom/spi_flash.h b/components/esp_rom/include/esp32s3/rom/spi_flash.h index 63768168a2..4bb085cb36 100644 --- a/components/esp_rom/include/esp32s3/rom/spi_flash.h +++ b/components/esp_rom/include/esp32s3/rom/spi_flash.h @@ -111,7 +111,6 @@ extern "C" { #define ESP_ROM_SPIFLASH_BP2 BIT4 #define ESP_ROM_SPIFLASH_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2) #define ESP_ROM_SPIFLASH_QE BIT9 -#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2) #define FLASH_ID_GD25LQ32C 0xC86016 diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index b8e230adb7..5f8328626b 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -67,6 +67,7 @@ #endif #include "bootloader_flash_config.h" +#include "bootloader_flash.h" #include "esp_private/crosscore_int.h" #include "esp_flash_encrypt.h" @@ -489,7 +490,7 @@ void IRAM_ATTR call_start_cpu0(void) extern void esp_rom_spiflash_attach(uint32_t, bool); esp_rom_spiflash_attach(esp_rom_efuse_get_flash_gpio_info(), false); - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); #else // This assumes that DROM is the first segment in the application binary, i.e. that we can read // the binary header through cache by accessing SOC_DROM_LOW address. diff --git a/components/spi_flash/esp32/spi_flash_rom_patch.c b/components/spi_flash/esp32/spi_flash_rom_patch.c index 9ec05c37e7..d25150d852 100644 --- a/components/spi_flash/esp32/spi_flash_rom_patch.c +++ b/components/spi_flash/esp32/spi_flash_rom_patch.c @@ -63,7 +63,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp about interrupts, CPU coordination, flash mapping. However some of the functions in esp_spi_flash.c call it. */ -esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void) +__attribute__((__unused__)) esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void) { uint32_t status; uint32_t new_status; diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index efafd28463..544860b7e1 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -49,6 +49,7 @@ #include "cache_utils.h" #include "esp_flash.h" #include "esp_attr.h" +#include "bootloader_flash.h" esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size); @@ -239,11 +240,8 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock(void) static bool unlocked = false; if (!unlocked) { spi_flash_guard_start(); - esp_rom_spiflash_result_t rc = esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); spi_flash_guard_end(); - if (rc != ESP_ROM_SPIFLASH_RESULT_OK) { - return rc; - } unlocked = true; } return ESP_ROM_SPIFLASH_RESULT_OK; diff --git a/components/spi_flash/sim/flash_mock.cpp b/components/spi_flash/sim/flash_mock.cpp index a8e2ed7361..03ddeceefc 100644 --- a/components/spi_flash/sim/flash_mock.cpp +++ b/components/spi_flash/sim/flash_mock.cpp @@ -75,6 +75,11 @@ extern "C" int spi_flash_get_erase_cycles(size_t sector) return spiflash.get_erase_cycles(sector); } +extern "C" esp_err_t bootloader_flash_unlock(void) +{ + return ESP_OK; +} + esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest, int32_t len) { return spiflash.read(target, dest, len);