From b1436633c57941741bc1ae1a47cbd760d5826f04 Mon Sep 17 00:00:00 2001 From: gaoxiaojie Date: Tue, 11 Jul 2023 10:46:59 +0800 Subject: [PATCH] fix(esp32s3): patch Cache_WriteBack_Addr api avoid accessing cachelines that are being writebacked --- components/esp_rom/CMakeLists.txt | 2 +- .../esp_rom/esp32s3/Kconfig.soc_caps.in | 4 + components/esp_rom/esp32s3/esp_rom_caps.h | 1 + components/esp_rom/esp32s3/ld/esp32s3.rom.ld | 2 +- .../patches/esp_rom_cache_esp32s2_esp32s3.c | 90 +++++++++++++++++++ 5 files changed, 97 insertions(+), 2 deletions(-) diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index 992e6ba75e..da42cce443 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -46,7 +46,7 @@ if(CONFIG_HAL_WDT_USE_ROM_IMPL) list(APPEND sources "patches/esp_rom_wdt.c") endif() -if(CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG) +if(CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG OR CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG) list(APPEND sources "patches/esp_rom_cache_esp32s2_esp32s3.c") endif() diff --git a/components/esp_rom/esp32s3/Kconfig.soc_caps.in b/components/esp_rom/esp32s3/Kconfig.soc_caps.in index 938fa458c9..94b178a10e 100644 --- a/components/esp_rom/esp32s3/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32s3/Kconfig.soc_caps.in @@ -82,3 +82,7 @@ config ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG config ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG bool default y + +config ESP_ROM_HAS_CACHE_WRITEBACK_BUG + bool + default y diff --git a/components/esp_rom/esp32s3/esp_rom_caps.h b/components/esp_rom/esp32s3/esp_rom_caps.h index 354002eee0..abb4bd39e0 100644 --- a/components/esp_rom/esp32s3/esp_rom_caps.h +++ b/components/esp_rom/esp32s3/esp_rom_caps.h @@ -26,3 +26,4 @@ #define ESP_ROM_RAM_APP_NEEDS_MMU_INIT (1) // ROM doesn't init cache MMU when it's a RAM APP, needs MMU hal to init #define ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG (1) // ROM api Cache_Count_Flash_Pages will return unexpected value #define ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG (1) // ROM api Cache_Suspend_I/DCache and Cache_Freeze_I/DCache_Enable does not waiti +#define ESP_ROM_HAS_CACHE_WRITEBACK_BUG (1) // ROM api Cache_WriteBack_Addr address or size misalignment may cause cache hit with wrong value. diff --git a/components/esp_rom/esp32s3/ld/esp32s3.rom.ld b/components/esp_rom/esp32s3/ld/esp32s3.rom.ld index 9a4571b5c6..c6a22d790f 100644 --- a/components/esp_rom/esp32s3/ld/esp32s3.rom.ld +++ b/components/esp_rom/esp32s3/ld/esp32s3.rom.ld @@ -381,7 +381,7 @@ PROVIDE( Cache_WriteBack_Items = 0x40001698 ); PROVIDE( Cache_Op_Addr = 0x400016a4 ); PROVIDE( Cache_Invalidate_Addr = 0x400016b0 ); PROVIDE( Cache_Clean_Addr = 0x400016bc ); -PROVIDE( Cache_WriteBack_Addr = 0x400016c8 ); +PROVIDE( rom_Cache_WriteBack_Addr = 0x400016c8 ); PROVIDE( Cache_Invalidate_ICache_All = 0x400016d4 ); PROVIDE( Cache_Invalidate_DCache_All = 0x400016e0 ); PROVIDE( Cache_Clean_All = 0x400016ec ); diff --git a/components/esp_rom/patches/esp_rom_cache_esp32s2_esp32s3.c b/components/esp_rom/patches/esp_rom_cache_esp32s2_esp32s3.c index 27eab2386d..d89a45796e 100644 --- a/components/esp_rom/patches/esp_rom_cache_esp32s2_esp32s3.c +++ b/components/esp_rom/patches/esp_rom_cache_esp32s2_esp32s3.c @@ -8,10 +8,14 @@ #include #include "soc/soc_caps.h" #include "soc/extmem_reg.h" +#include "xtensa/xtruntime.h" #if CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/cache.h" #endif +#define ALIGN_UP(addr, align) (((addr) + (align)-1) & ~((align)-1)) +#define ALIGN_DOWN(addr, align) ((addr) & ~((align) - 1)) + // this api is renamed for patch extern uint32_t rom_Cache_Count_Flash_Pages(uint32_t bus, uint32_t * page0_mapped); uint32_t Cache_Count_Flash_Pages(uint32_t bus, uint32_t * page0_mapped) @@ -83,3 +87,89 @@ void Cache_Freeze_DCache_Enable(cache_freeze_mode_t mode) extern void Cache_Freeze_DCache_Enable(cache_freeze_mode_t mode); #endif #endif + +#if CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG +static void __attribute__((optimize("-O2"))) Cache_WriteBack_Items_Freeze(uint32_t addr, uint32_t items) +{ + /* Please do not modify this function, it must strictly follow the current execution sequence, + * otherwise it may cause unexpected errors. + */ + REG_WRITE(EXTMEM_DCACHE_SYNC_ADDR_REG, addr); + REG_SET_FIELD(EXTMEM_DCACHE_SYNC_SIZE_REG, EXTMEM_DCACHE_SYNC_SIZE, items); + + /*enable dcache freeze, mode = CACHE_FREEZE_ACK_BUSY*/ + REG_CLR_BIT(EXTMEM_DCACHE_FREEZE_REG, EXTMEM_DCACHE_FREEZE_MODE); + REG_SET_BIT(EXTMEM_DCACHE_FREEZE_REG, EXTMEM_DCACHE_FREEZE_ENA); + while (!REG_GET_BIT(EXTMEM_DCACHE_FREEZE_REG, EXTMEM_DCACHE_FREEZE_DONE)); + + REG_SET_BIT(EXTMEM_DCACHE_SYNC_CTRL_REG, EXTMEM_DCACHE_WRITEBACK_ENA); + while(!REG_GET_BIT(EXTMEM_DCACHE_SYNC_CTRL_REG, EXTMEM_DCACHE_SYNC_DONE)); + + /*disable dcache freeze*/ + REG_CLR_BIT(EXTMEM_DCACHE_FREEZE_REG, EXTMEM_DCACHE_FREEZE_ENA); + while (REG_GET_BIT(EXTMEM_DCACHE_FREEZE_REG, EXTMEM_DCACHE_FREEZE_DONE)); +} + +// renamed for patch +extern int rom_Cache_WriteBack_Addr(uint32_t addr, uint32_t size); +int Cache_WriteBack_Addr(uint32_t addr, uint32_t size) +{ + /* Do special processing for unaligned memory at the start and end of the cache writeback memory. + * 1. Disable the interrupt to prevent the current CPU accessing the same cacheline. + * 2. Enable dcache freeze to prevent the another CPU accessing the same cacheline. + */ + uint32_t irq_status; + uint32_t start_len, end_len; + uint32_t start, end; + uint32_t dcache_line_size; + uint32_t autoload; + int ret = 0; + start = addr; + end = addr + size; + dcache_line_size = Cache_Get_DCache_Line_Size(); + + if (size == 0) { + return 0; + } + + /*the start address is unaligned*/ + if (start & (dcache_line_size -1)) { + addr = ALIGN_UP(start, dcache_line_size); + start_len = addr - start; + size = (size < start_len) ? 0 : (size - start_len); + + /*writeback start unaligned mem, one cacheline*/ + irq_status = XTOS_SET_INTLEVEL(XCHAL_NMILEVEL);//mask all interrupts + Cache_WriteBack_Items_Freeze(start, 1); + XTOS_RESTORE_INTLEVEL(irq_status); + + if (size == 0) { + return 0; + } + } + + /*the end address is unaligned*/ + if (end & (dcache_line_size -1)) { + end = ALIGN_DOWN(end, dcache_line_size); + end_len = addr + size - end; + size = (size - end_len); + + /*writeback end unaligned mem, one cacheline*/ + irq_status = XTOS_SET_INTLEVEL(XCHAL_NMILEVEL);//mask all interrupts + Cache_WriteBack_Items_Freeze(end, 1); + XTOS_RESTORE_INTLEVEL(irq_status); + + if (size == 0) { + return 0; + } + } + + /*suspend autoload, avoid load cachelines being written back*/ + autoload = Cache_Suspend_DCache_Autoload(); + ret = rom_Cache_WriteBack_Addr(addr, size); + Cache_Resume_DCache_Autoload(autoload); + + return ret; +} +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); +#endif