mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
fix(sdmmc): Validate DMA buffer address range
Closes https://github.com/espressif/esp-idf/issues/18714
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -1286,13 +1286,38 @@ esp_err_t sdmmc_host_get_dma_info(int slot, esp_dma_mem_info_t *dma_mem_info)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool sdmmc_dma_accessible(const void *ptr)
|
||||
{
|
||||
if (esp_ptr_external_ram(ptr)) {
|
||||
#if SOC_SDMMC_PSRAM_DMA_CAPABLE
|
||||
return esp_ptr_dma_ext_capable(ptr);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return esp_ptr_dma_capable(ptr);
|
||||
}
|
||||
|
||||
bool sdmmc_host_check_buffer_alignment(int slot, const void *buf, size_t size)
|
||||
{
|
||||
//for future-proof
|
||||
(void)slot;
|
||||
|
||||
if (!buf || !size) {
|
||||
return ESP_FAIL;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t start = (uintptr_t)buf;
|
||||
if (size - 1 > UINTPTR_MAX - start) {
|
||||
return false;
|
||||
}
|
||||
const void *end = (const void *)(start + size - 1);
|
||||
|
||||
bool not_dma_accessible = !sdmmc_dma_accessible(buf) || !sdmmc_dma_accessible(end);
|
||||
bool different_memory_types = esp_ptr_external_ram(buf) != esp_ptr_external_ram(end);
|
||||
if (not_dma_accessible || different_memory_types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
+25
@@ -73,6 +73,31 @@ void sdmmc_test_rw_highprio_task(sdmmc_card_t* card);
|
||||
*/
|
||||
void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_size);
|
||||
|
||||
/**
|
||||
* @brief Test read/write with PSRAM-allocated buffers
|
||||
*
|
||||
* This function verifies that the driver correctly handles buffers allocated in PSRAM.
|
||||
* When the host reports the PSRAM buffer as directly usable (via check_buffer_alignment),
|
||||
* data is transferred directly without intermediate copying. Otherwise, the driver uses
|
||||
* double-buffering through internal RAM.
|
||||
*
|
||||
* The test covers:
|
||||
* - Writing from PSRAM, reading to internal RAM
|
||||
* - Writing from internal RAM, reading to PSRAM
|
||||
* - Both writing and reading from PSRAM
|
||||
*
|
||||
* If PSRAM is not enabled (CONFIG_SPIRAM), this function is a no-op. Callers
|
||||
* must skip the test (TEST_IGNORE) BEFORE initializing the slot/controller, so
|
||||
* that this function is not relied upon to abort the test. Doing the skip here
|
||||
* would longjmp out of the test before the caller's cleanup runs and leak the
|
||||
* SD slot.
|
||||
*
|
||||
* This test function works both with SDMMC and SDSPI hosts.
|
||||
*
|
||||
* @param card Pointer to the card object, must be initialized before calling this function.
|
||||
*/
|
||||
void sdmmc_test_rw_psram_buffer(sdmmc_card_t *card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
+69
@@ -10,6 +10,7 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_timer.h"
|
||||
#include "test_utils.h"
|
||||
#include "sdkconfig.h"
|
||||
@@ -294,3 +295,71 @@ void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void sdmmc_test_rw_psram_buffer(sdmmc_card_t *card)
|
||||
{
|
||||
#if !CONFIG_SPIRAM
|
||||
/* Callers must skip before initializing the hardware so cleanup is not bypassed. */
|
||||
(void)card;
|
||||
#else
|
||||
const size_t block_size = card->csd.sector_size;
|
||||
const size_t block_count = 8;
|
||||
const size_t buffer_size = block_size * block_count;
|
||||
|
||||
uint8_t *psram_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(psram_buf, "Failed to allocate PSRAM buffer; is PSRAM enabled?");
|
||||
TEST_ASSERT_MESSAGE(esp_ptr_external_ram(psram_buf), "Buffer not in PSRAM");
|
||||
|
||||
uint8_t *internal_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED);
|
||||
TEST_ASSERT_NOT_NULL(internal_buf);
|
||||
|
||||
bool host_can_use_psram_directly =
|
||||
card->host.check_buffer_alignment(card->host.slot, psram_buf, buffer_size);
|
||||
printf("Testing PSRAM buffer R/W: %d blocks, host can use PSRAM directly=%s\n",
|
||||
(int)block_count, host_can_use_psram_directly ? "true" : "false");
|
||||
|
||||
const uint32_t seed_a = 0xABCD1234;
|
||||
fill_buffer(seed_a, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, psram_buf, 0, block_count));
|
||||
memset(internal_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, internal_buf, 0, block_count));
|
||||
check_buffer(seed_a, internal_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
const uint32_t seed_b = 0x5678EFAB;
|
||||
fill_buffer(seed_b, internal_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, internal_buf, 0, block_count));
|
||||
memset(psram_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, psram_buf, 0, block_count));
|
||||
check_buffer(seed_b, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
const uint32_t seed_c = 0xDEAD9876;
|
||||
fill_buffer(seed_c, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, psram_buf, 0, block_count));
|
||||
memset(psram_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, psram_buf, 0, block_count));
|
||||
check_buffer(seed_c, psram_buf, buffer_size / sizeof(uint32_t));
|
||||
|
||||
const size_t misalign = 1;
|
||||
uint8_t *unaligned_psram = heap_caps_malloc(buffer_size + misalign,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(unaligned_psram, "Failed to allocate PSRAM buffer");
|
||||
uint8_t *src = unaligned_psram + misalign;
|
||||
TEST_ASSERT_MESSAGE(esp_ptr_external_ram(src), "Buffer not in PSRAM");
|
||||
|
||||
const uint32_t seed_d = 0x13572468;
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
src[i] = (uint8_t)(seed_d + i);
|
||||
}
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, src, 0, block_count));
|
||||
memset(internal_buf, 0xcc, buffer_size);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, internal_buf, 0, block_count));
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
TEST_ASSERT_EQUAL_HEX8((uint8_t)(seed_d + i), internal_buf[i]);
|
||||
}
|
||||
|
||||
free(unaligned_psram);
|
||||
free(psram_buf);
|
||||
free(internal_buf);
|
||||
printf("PSRAM buffer R/W test passed\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ endif()
|
||||
|
||||
set(priv_requires "sdmmc"
|
||||
"esp_driver_sdmmc"
|
||||
"esp_psram"
|
||||
"sdmmc_test_boards"
|
||||
"common_test_flows"
|
||||
"unity"
|
||||
|
||||
+29
-1
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include "unity.h"
|
||||
#include "esp_psram.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "sdmmc_test_begin_end_sd.h"
|
||||
#include "sdmmc_test_rw_common.h"
|
||||
@@ -128,3 +129,30 @@ TEST_CASE("sdmmc read/write with concurrent high-prio task, slot 1, 4-bit", "[sd
|
||||
{
|
||||
do_one_sdmmc_rw_test_highprio_task(1, 4);
|
||||
}
|
||||
|
||||
static void do_one_sdmmc_rw_test_psram_dma_buffer(int slot, int width)
|
||||
{
|
||||
sdmmc_card_t card;
|
||||
int freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#if !CONFIG_SPIRAM
|
||||
TEST_IGNORE_MESSAGE("PSRAM is not enabled");
|
||||
#else
|
||||
if (!esp_psram_is_initialized()) {
|
||||
TEST_IGNORE_MESSAGE("PSRAM is not available");
|
||||
}
|
||||
#endif
|
||||
sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, NO_DDR, NO_EMMC);
|
||||
sdmmc_test_sd_begin(slot, width, freq_khz, 0, &card);
|
||||
sdmmc_test_rw_psram_buffer(&card);
|
||||
sdmmc_test_sd_end(&card);
|
||||
}
|
||||
|
||||
TEST_CASE("sdmmc read/write using PSRAM DMA accessible buffer, slot 0, 4-bit", "[sdmmc]")
|
||||
{
|
||||
do_one_sdmmc_rw_test_psram_dma_buffer(0, 4);
|
||||
}
|
||||
|
||||
TEST_CASE("sdmmc read/write using PSRAM DMA accessible buffer, slot 1, 4-bit", "[sdmmc]")
|
||||
{
|
||||
do_one_sdmmc_rw_test_psram_dma_buffer(1, 4);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set(srcs "test_app_main.c")
|
||||
set(srcs "test_app_main.c" "test_sdmmc_buffer.c")
|
||||
|
||||
set(priv_requires
|
||||
# tests reside in this component, also available for `sdmmc_console`
|
||||
@@ -7,6 +7,7 @@ set(priv_requires
|
||||
unity
|
||||
# for PSRAM tests
|
||||
esp_psram
|
||||
esp_driver_sdmmc
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "soc/soc.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_psram.h"
|
||||
|
||||
#if SOC_RTC_FAST_MEM_SUPPORTED
|
||||
TEST_CASE("SDMMC rejects an RTC fast RAM DMA buffer", "[sdmmc]")
|
||||
{
|
||||
const size_t buffer_size = 512;
|
||||
void *internal_dma_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_CACHE_ALIGNED);
|
||||
const size_t offset = 0x20;
|
||||
const void *rtc_fast_ram_buf = (const void *)(SOC_RTC_DRAM_LOW + offset);
|
||||
TEST_ASSERT_NOT_NULL(internal_dma_buf);
|
||||
|
||||
TEST_ASSERT_TRUE(sdmmc_host_check_buffer_alignment(0, internal_dma_buf, buffer_size));
|
||||
TEST_ASSERT_FALSE(sdmmc_host_check_buffer_alignment(0, rtc_fast_ram_buf, buffer_size));
|
||||
TEST_ASSERT_FALSE(sdmmc_host_check_buffer_alignment(0, (const void *)(UINTPTR_MAX - 255), buffer_size));
|
||||
|
||||
heap_caps_free(internal_dma_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPIRAM && !SOC_SDMMC_PSRAM_DMA_CAPABLE
|
||||
TEST_CASE("SDMMC rejects a PSRAM buffer when PSRAM is not DMA capable", "[sdmmc]")
|
||||
{
|
||||
const size_t buffer_size = 512;
|
||||
if (!esp_psram_is_initialized()) {
|
||||
TEST_IGNORE_MESSAGE("PSRAM is not available");
|
||||
}
|
||||
|
||||
void *psram_buf = heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_CACHE_ALIGNED);
|
||||
TEST_ASSERT_NOT_NULL(psram_buf);
|
||||
TEST_ASSERT_TRUE(esp_ptr_external_ram(psram_buf));
|
||||
|
||||
TEST_ASSERT_FALSE(sdmmc_host_check_buffer_alignment(0, psram_buf, buffer_size));
|
||||
|
||||
heap_caps_free(psram_buf);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
|
||||
Reference in New Issue
Block a user