diff --git a/components/esp_driver_bitscrambler/include/driver/bitscrambler.h b/components/esp_driver_bitscrambler/include/driver/bitscrambler.h index 47e7099c957..296c7ea91a3 100644 --- a/components/esp_driver_bitscrambler/include/driver/bitscrambler.h +++ b/components/esp_driver_bitscrambler/include/driver/bitscrambler.h @@ -89,6 +89,7 @@ esp_err_t bitscrambler_load_program(bitscrambler_handle_t handle, const void *pr * @return * - ESP_OK * - ESP_ERR_INVALID_ARG: Invalid handle or lut pointer + * - ESP_ERR_INVALID_SIZE: LUT data exceeds hardware capacity */ esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t size_bytes); diff --git a/components/esp_driver_bitscrambler/src/bitscrambler.c b/components/esp_driver_bitscrambler/src/bitscrambler.c index 942bb1b02a2..e5ede7cdb5c 100644 --- a/components/esp_driver_bitscrambler/src/bitscrambler.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler.c @@ -30,6 +30,8 @@ static const char *TAG = "bitscrambler"; #define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with #define BITSCRAMBLER_HW_REV 0 +// LUT index register is 11 bits wide, so the LUT address space is 2048 bytes. +#define BITSCRAMBLER_LUT_MAX_BYTES (1U << 11) // After a reset, it can take a few cycles for the BitScrambler to actually be // reset. We check this many times for this; if it takes longer the hardware @@ -247,12 +249,28 @@ esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t if (!handle || !lut) { return ESP_ERR_INVALID_ARG; } - uint32_t *lut_words = (uint32_t*)lut; + if (size_bytes > BITSCRAMBLER_LUT_MAX_BYTES) { + return ESP_ERR_INVALID_SIZE; + } + const uint8_t *lut_bytes = (const uint8_t *)lut; bitscrambler_lut_width_t lut_width = bitscrambler_ll_get_lut_width(handle->hw, handle->cfg.dir); bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, BITSCRAMBLER_LUT_WIDTH_32BIT); size_t size_words = (size_bytes + 3) / 4; for (int w = 0; w < size_words; w++) { - bitscrambler_ll_lutmem_write(handle->hw, handle->cfg.dir, w, lut_words[w]); + // Assemble each LUT entry byte-wise before writing it to hardware: + // 1) callers are allowed to pass unaligned buffers, so reading via a + // uint32_t * could fault or perform an unaligned access on some targets; + // 2) the final LUT word may be only partially supplied, and copying the + // valid bytes into a zero-initialized word avoids reading past the end + // of the caller's buffer and leaking adjacent memory into the LUT. + uint32_t lut_word = 0; + size_t offset = w * sizeof(lut_word); + size_t bytes_to_copy = size_bytes - offset; + if (bytes_to_copy > sizeof(lut_word)) { + bytes_to_copy = sizeof(lut_word); + } + memcpy(&lut_word, lut_bytes + offset, bytes_to_copy); + bitscrambler_ll_lutmem_write(handle->hw, handle->cfg.dir, w, lut_word); } bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, lut_width); return ESP_OK; diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c index 805b89408e2..2743fc059f1 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c @@ -5,6 +5,7 @@ */ #include #include +#include #include "sdkconfig.h" #include "unity.h" #include "unity_test_utils.h" @@ -171,6 +172,51 @@ TEST_CASE("BitScrambler with LUT32", "[bs]") free(data_out); } +TEST_CASE("BitScrambler loads partial LUT from unaligned buffer", "[bs]") +{ + const size_t len = 32; + const uint32_t expected_lut_words[] = { + 0xA0011111, + 0xA0022222, + 0xA0033333, + 0x00000044, + }; + const uint32_t lut_source_words[] = { + 0xA0011111, + 0xA0022222, + 0xA0033333, + 0xA0044444, + }; + uint8_t lut_bytes[sizeof(lut_source_words) + 4] = {0}; + uint8_t *unaligned_lut = lut_bytes + 1; + const size_t lut_size = 13; + uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + uint32_t *data_out = heap_caps_aligned_calloc(8, 1, len * 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(data_in); + TEST_ASSERT_NOT_NULL(data_out); + memcpy(unaligned_lut, lut_source_words, sizeof(lut_source_words)); + unaligned_lut[lut_size] = 0xAB; + unaligned_lut[lut_size + 1] = 0xCD; + unaligned_lut[lut_size + 2] = 0xEF; + + bitscrambler_handle_t bs; + TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_GPSPI2, len * 4)); + TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_lut32)); + TEST_ESP_OK(bitscrambler_load_lut(bs, unaligned_lut, lut_size)); + + size_t res_len = 0; + TEST_ESP_OK(bitscrambler_loopback_run(bs, data_in, len, data_out, len * 4, &res_len)); + bitscrambler_free(bs); + + for (size_t i = 0; i < res_len / 4; i++) { + TEST_ASSERT_EQUAL(expected_lut_words[i % 4], data_out[i]); + } + TEST_ASSERT_EQUAL(len * 4, res_len); + + free(data_in); + free(data_out); +} + TEST_CASE("BitScrambler with loop instruction", "[bs]") { uint8_t data_in[] = {0x00, 0xFF, 0x55};