mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Merge branch 'debug/idf-15875-fix_v6.0' into 'release/v6.0'
fix(psram): suspend external cache during CPU freq switch on esp32p4 (v6.0) See merge request espressif/esp-idf!51685
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
#include "esp_private/esp_sleep_internal.h"
|
#include "esp_private/esp_sleep_internal.h"
|
||||||
#include "esp_private/esp_clk.h"
|
#include "esp_private/esp_clk.h"
|
||||||
#include "esp_private/esp_pmu.h"
|
#include "esp_private/esp_pmu.h"
|
||||||
|
#include "esp_private/esp_cache_private.h"
|
||||||
#include "esp_rom_serial_output.h"
|
#include "esp_rom_serial_output.h"
|
||||||
#include "esp_rom_sys.h"
|
#include "esp_rom_sys.h"
|
||||||
|
|
||||||
@@ -49,6 +50,22 @@ void IRAM_ATTR esp_rtc_init(void)
|
|||||||
#endif //SOC_PMU_SUPPORTED
|
#endif //SOC_PMU_SUPPORTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform the CPU frequency switch with the external memory (PSRAM) cache
|
||||||
|
* suspended. The frequency switch stalls the HP_ROOT clock and toggles PLL /
|
||||||
|
* clock gating; when the switch code runs from PSRAM-XIP, a cache-coherency
|
||||||
|
* transient during the switch can corrupt the external .bss region. Suspending
|
||||||
|
* the external-memory cache for the duration of the switch keeps the CPU off
|
||||||
|
* the XIP path, isolating the two.
|
||||||
|
* Must be IRAM-resident because it runs while the external cache is suspended.
|
||||||
|
*/
|
||||||
|
static void IRAM_ATTR esp_clk_cpu_freq_set_config_isolated(const rtc_cpu_freq_config_t *config)
|
||||||
|
{
|
||||||
|
esp_cache_suspend_ext_mem_cache();
|
||||||
|
rtc_clk_cpu_freq_set_config(config);
|
||||||
|
esp_cache_resume_ext_mem_cache();
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
{
|
{
|
||||||
assert(rtc_clk_xtal_freq_get() == SOC_XTAL_FREQ_40M);
|
assert(rtc_clk_xtal_freq_get() == SOC_XTAL_FREQ_40M);
|
||||||
@@ -110,7 +127,7 @@ __attribute__((weak)) void esp_clk_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
rtc_clk_cpu_freq_set_config(&new_config);
|
esp_clk_cpu_freq_set_config_isolated(&new_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re calculate the ccount to make time calculation correct.
|
// Re calculate the ccount to make time calculation correct.
|
||||||
|
|||||||
@@ -49,11 +49,15 @@ components/spi_flash/test_apps/esp_flash_stress:
|
|||||||
|
|
||||||
components/spi_flash/test_apps/flash_encryption:
|
components/spi_flash/test_apps/flash_encryption:
|
||||||
disable:
|
disable:
|
||||||
|
- if: CONFIG_NAME == "xip_psram" and SOC_SPIRAM_XIP_SUPPORTED != 1
|
||||||
- if: IDF_TARGET == "esp32h4"
|
- if: IDF_TARGET == "esp32h4"
|
||||||
temporary: true
|
temporary: true
|
||||||
reason: not support yet # TODO: [ESP32H4] IDF-12388
|
reason: not support yet # TODO: [ESP32H4] IDF-12388
|
||||||
disable_test:
|
disable_test:
|
||||||
- if: IDF_TARGET in ["esp32c2", "esp32s2", "esp32c6", "esp32h2", "esp32p4", "esp32c5", "esp32c61", "esp32h21", "esp32h4"]
|
# release/verify/rom_impl pytest covers esp32/esp32c3/esp32s3; other FE-capable
|
||||||
|
# chips without XIP still lack runners for those configs. XIP-capable chips are
|
||||||
|
# covered by the xip_psram case via SOC_SPIRAM_XIP_SUPPORTED.
|
||||||
|
- if: CONFIG_NAME != "xip_psram" and SOC_FLASH_ENC_SUPPORTED == 1 and SOC_SPIRAM_XIP_SUPPORTED != 1 and IDF_TARGET not in ["esp32", "esp32c3"]
|
||||||
temporary: true
|
temporary: true
|
||||||
reason: No runners # IDF-5634
|
reason: No runners # IDF-5634
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "esp_flash.h"
|
#include "esp_flash.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
#include <spi_flash_mmap.h>
|
#include <spi_flash_mmap.h>
|
||||||
#include <esp_attr.h>
|
#include <esp_attr.h>
|
||||||
#include <esp_flash_encrypt.h>
|
#include <esp_flash_encrypt.h>
|
||||||
@@ -495,4 +497,124 @@ TEST_CASE("Test flash encrypted write over boundary", "[flash_encryption]")
|
|||||||
}
|
}
|
||||||
#endif //CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
|
#endif //CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||||
|
// IDF-15875: when the .ext_ram.bss segment is placed in external PSRAM and the CPU
|
||||||
|
// frequency switch (run from PSRAM-XIP) corrupts it during boot, this buffer
|
||||||
|
// would contain non-zero words. Assert it is all-zero after boot.
|
||||||
|
#define PSRAM_BSS_SIZE 171936
|
||||||
|
EXT_RAM_BSS_ATTR static uint8_t s_big_bss[PSRAM_BSS_SIZE];
|
||||||
|
|
||||||
|
static int count_nonzero_psram_bss_words(int *first, uint32_t *first_v)
|
||||||
|
{
|
||||||
|
volatile uint32_t *w = (volatile uint32_t *)s_big_bss;
|
||||||
|
const int n = PSRAM_BSS_SIZE / 4;
|
||||||
|
int nz = 0;
|
||||||
|
|
||||||
|
*first = -1;
|
||||||
|
*first_v = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
uint32_t v = w[i];
|
||||||
|
if (v != 0) {
|
||||||
|
if (*first < 0) {
|
||||||
|
*first = i;
|
||||||
|
*first_v = v;
|
||||||
|
}
|
||||||
|
nz++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assert_psram_heap_integrity(const char *label)
|
||||||
|
{
|
||||||
|
bool ok = heap_caps_check_integrity_all(true);
|
||||||
|
|
||||||
|
printf("[%s] heap integrity: %s\n", label, ok ? "OK" : "CORRUPT");
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(ok,
|
||||||
|
"heap integrity check failed while reproducing flash-encryption xip_psram path");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exercise_flash_encryption_psram_repro_rounds(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
SemaphoreHandle_t mutex = xSemaphoreCreateMutex();
|
||||||
|
uint8_t *internal = heap_caps_malloc(256, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||||
|
uint8_t *psram = heap_caps_malloc(1024, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(mutex, "failed to create mutex during repro setup");
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(internal, "failed to allocate internal buffer during repro setup");
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(psram, "failed to allocate psram buffer during repro setup");
|
||||||
|
|
||||||
|
memset(internal, 0x5a, 256);
|
||||||
|
memset(psram, 0xa5, 1024);
|
||||||
|
|
||||||
|
assert_psram_heap_integrity("after-round");
|
||||||
|
|
||||||
|
vSemaphoreDelete(mutex);
|
||||||
|
free(psram);
|
||||||
|
free(internal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_psram_bss_and_check(size_t bytes, const char *label)
|
||||||
|
{
|
||||||
|
printf("touching ext_ram .bss: first %u bytes ...\n", (unsigned)bytes);
|
||||||
|
memset(s_big_bss, 0xa5, bytes);
|
||||||
|
assert_psram_heap_integrity(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assert_psram_bss_zero_initialized(const char *stage)
|
||||||
|
{
|
||||||
|
const int n = PSRAM_BSS_SIZE / 4;
|
||||||
|
int first = -1;
|
||||||
|
uint32_t first_v = 0;
|
||||||
|
int nz = count_nonzero_psram_bss_words(&first, &first_v);
|
||||||
|
|
||||||
|
printf("[%s] PSRAM .bss @ %p words=%d NON-ZERO words = %d / %d\n",
|
||||||
|
stage, (void *)s_big_bss, n, nz, n);
|
||||||
|
if (nz) {
|
||||||
|
printf("[%s] first non-zero @word %d (0x%" PRIxPTR ") = 0x%" PRIx32 "\n",
|
||||||
|
stage,
|
||||||
|
first, (uintptr_t)s_big_bss + first * 4, first_v);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(0, nz,
|
||||||
|
"external .bss was not zero-initialized (corrupted during boot, see IDF-15875)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("external PSRAM .bss is zero-initialized after boot", "[flash_encryption][psram][bss]")
|
||||||
|
{
|
||||||
|
assert_psram_bss_zero_initialized("boot");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("external PSRAM .bss survives flash-encryption heap activity", "[flash_encryption][psram][bss_repro]")
|
||||||
|
{
|
||||||
|
const size_t touch_steps[] = {
|
||||||
|
4 * 1024,
|
||||||
|
8 * 1024,
|
||||||
|
16 * 1024,
|
||||||
|
32 * 1024,
|
||||||
|
64 * 1024,
|
||||||
|
};
|
||||||
|
const char *labels[] = {
|
||||||
|
"after-bss-touch 4KB",
|
||||||
|
"after-bss-touch 8KB",
|
||||||
|
"after-bss-touch 16KB",
|
||||||
|
"after-bss-touch 32KB",
|
||||||
|
"after-bss-touch 64KB",
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(sizeof(touch_steps) / sizeof(touch_steps[0]), sizeof(labels) / sizeof(labels[0]));
|
||||||
|
|
||||||
|
assert_psram_bss_zero_initialized("boot");
|
||||||
|
assert_psram_heap_integrity("boot");
|
||||||
|
exercise_flash_encryption_psram_repro_rounds();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(touch_steps) / sizeof(touch_steps[0]); i++) {
|
||||||
|
touch_psram_bss_and_check(touch_steps[i], labels[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||||
|
|
||||||
#endif // CONFIG_SECURE_FLASH_ENC_ENABLED
|
#endif // CONFIG_SECURE_FLASH_ENC_ENABLED
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from pytest_embedded import Dut
|
from pytest_embedded import Dut
|
||||||
from pytest_embedded_idf.utils import idf_parametrize
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
from pytest_embedded_idf.utils import soc_filtered_targets
|
||||||
|
|
||||||
|
|
||||||
|
def get_xip_psram_marks(target: str) -> tuple[pytest.MarkDecorator, ...]:
|
||||||
|
if target == 'esp32s3':
|
||||||
|
return (pytest.mark.flash_encryption_f8r8,)
|
||||||
|
|
||||||
|
return (pytest.mark.flash_encryption,)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.flash_encryption
|
@pytest.mark.flash_encryption
|
||||||
@@ -58,3 +66,16 @@ def test_flash_encryption_f4r8(dut: Dut) -> None:
|
|||||||
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
|
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
|
||||||
def test_flash_encryption_f8r8(dut: Dut) -> None:
|
def test_flash_encryption_f8r8(dut: Dut) -> None:
|
||||||
dut.run_all_single_board_cases()
|
dut.run_all_single_board_cases()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.temp_skip_ci(targets=['esp32h4'], reason='not support yet') # TODO: [ESP32H4] IDF-12388
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config, target',
|
||||||
|
[
|
||||||
|
pytest.param('xip_psram', target, marks=get_xip_psram_marks(target))
|
||||||
|
for target in soc_filtered_targets('SOC_SPIRAM_XIP_SUPPORTED == 1')
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
def test_flash_encryption_psram(dut: Dut) -> None:
|
||||||
|
dut.run_all_single_board_cases()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Flash Encryption + external PSRAM .bss regression config for IDF-15875.
|
||||||
|
# Places .ext_ram.bss in external PSRAM so a boot-time CPU frequency switch
|
||||||
|
# under PSRAM-XIP can be exercised; without the fix the external .bss is
|
||||||
|
# corrupted during boot.
|
||||||
|
# On esp32p4, CONFIG_CACHE_L2_CACHE_LINE_128B is required to reproduce.
|
||||||
|
CONFIG_CACHE_L2_CACHE_LINE_128B=y
|
||||||
|
CONFIG_SPIRAM=y
|
||||||
|
CONFIG_SPIRAM_BOOT_INIT=y
|
||||||
|
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||||
|
CONFIG_SPIRAM_SPEED_250M=y
|
||||||
|
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
CONFIG_SPIRAM_MODE_OCT=y
|
||||||
|
CONFIG_SPIRAM_TYPE_AUTO=y
|
||||||
|
CONFIG_ESPTOOLPY_OCT_FLASH=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_OPI=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_32MB=y
|
||||||
|
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
|
||||||
|
CONFIG_SPIRAM_RODATA=y
|
||||||
Reference in New Issue
Block a user