va416x0: use pointer-width alignment masks in iram_write/iram_fill

iram_write() and iram_fill() compute the enclosing 32-bit word with
(uintptr_t)dst & ~3u. ~3u is a 32-bit constant, so on 64-bit
platforms the AND zeroes the high word of the destination pointer and
the subsequent word read/write faults (verified: both helpers segfault
on x86_64 for any 64-bit destination). On the 32-bit va416x0 target
the mask happens to cover the whole pointer, so this only bites where
the code runs with 64-bit addresses.

Use ~(uintptr_t)3 so the mask is always pointer-width. No behavior
change on 32-bit targets.

Also make the EXT_FLASH section of hal/va416x0.c host-testable (split
the WOLFBOOT_UNIT_TEST_VA416X0_FRAM guard around it, mock the
SYSCONFIG register block the lock/unlock pair touches) and add
unaligned iram_write/iram_fill regression tests that would segfault
pre-fix on the host.

Verified: unit-va416x0-fram 3/3 green post-fix (both new tests
segfault pre-fix), tools/unit-tests suite green (112 binaries).
pull/862/head
Daniele Lacamera 2026-08-18 03:30:55 +02:00
parent 4608292b8b
commit cc8564ae75
2 changed files with 68 additions and 4 deletions

View File

@ -369,7 +369,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
(void)len;
return 0;
}
#endif /* !WOLFBOOT_UNIT_TEST_VA416X0_FRAM */
#ifdef EXT_FLASH
void ext_flash_lock(void)
@ -395,7 +395,9 @@ void ext_flash_unlock(void)
static void iram_write(void *dst, const void *src, int len)
{
while (len > 0) {
uintptr_t addr = (uintptr_t)dst & ~3u;
/* Pointer-width mask: ~3u is 32 bits and would zero the high
* word of the address on 64-bit platforms. */
uintptr_t addr = (uintptr_t)dst & ~(uintptr_t)3;
uint32_t off = (uintptr_t)dst & 3u;
uint32_t word = *(volatile uint32_t *)addr;
uint8_t *wp = (uint8_t *)&word;
@ -412,7 +414,8 @@ static void iram_write(void *dst, const void *src, int len)
static void iram_fill(void *dst, uint8_t val, int len)
{
while (len > 0) {
uintptr_t addr = (uintptr_t)dst & ~3u;
/* Pointer-width mask (see iram_write). */
uintptr_t addr = (uintptr_t)dst & ~(uintptr_t)3;
uint32_t off = (uintptr_t)dst & 3u;
uint32_t word = *(volatile uint32_t *)addr;
uint8_t *wp = (uint8_t *)&word;
@ -527,6 +530,7 @@ static int test_ext_flash(void)
#endif /* TEST_EXT_FLASH */
#endif /* EXT_FLASH */
#ifndef WOLFBOOT_UNIT_TEST_VA416X0_FRAM
#ifdef __WOLFBOOT /* build for wolfBoot only */
/* Configure Error Detection and Correction (EDAC) */
static void ConfigEdac(uint32_t ramScrub, uint32_t romScrub)

View File

@ -4,8 +4,12 @@
#include <string.h>
#define WOLFBOOT_UNIT_TEST_VA416X0_FRAM
#define EXT_FLASH
#define FRAM_SIZE (256U * 1024U)
/* The host shadow IRAM lives at a 64-bit pointer address, so keep the
* FRAM bounds check from rejecting the test buffer (the hardware value
* is 256 KiB). */
#define FRAM_SIZE (0xFFFFFFFFU)
#define ROM_SPI_BANK 0
#define SPI_NUM_BANKS 1
#define SPI_STATUS_TFE_Msk 0x01U
@ -61,6 +65,16 @@ enum {
static mock_spi_regs_t mock_vor_spi;
#define VOR_SPI (&mock_vor_spi)
/* Mock of the Vorago SYSCONFIG register block (vendor headers are not
* available on the host). */
typedef struct {
uint32_t ROM_PROT;
} mock_vor_sysconfig_t;
static mock_vor_sysconfig_t mock_vor_sysconfig;
#define VOR_SYSCONFIG (&mock_vor_sysconfig)
#define SYSCONFIG_ROM_PROT_WREN_Msk (1U << 0)
static hal_status_t transmit_script[8];
static bool transmit_close_flags[8];
static int transmit_script_len;
@ -169,12 +183,58 @@ START_TEST(test_fram_write_command_failure_aborts_split_transaction)
}
END_TEST
/* Shadow IRAM buffer for the ext_flash_erase() tests: the HAL writes
* the erased bytes through the raw address, so the address must point
* at real host memory. */
static uint8_t g_iram[64];
/* iram_write/iram_fill compute the enclosing word with
* (uintptr_t)dst & ~3u - a 32-bit mask. That is the whole pointer on
* the 32-bit va416x0 target, but on 64-bit platforms it zeroes the
* high word of the address. Unaligned start and end so off != 0 and
* the word boundary crossing are exercised. */
START_TEST(test_iram_write_unaligned_64bit_addr)
{
uint8_t data[12];
int i;
memset(g_iram, 0x00, sizeof(g_iram));
for (i = 0; i < 12; i++)
data[i] = (uint8_t)(0x40 + i);
iram_write((void *)(g_iram + 1), data, 12);
for (i = 0; i < 12; i++)
ck_assert_uint_eq(g_iram[1 + i], data[i]);
/* Neighboring bytes untouched. */
ck_assert_uint_eq(g_iram[0], 0x00);
ck_assert_uint_eq(g_iram[13], 0x00);
}
END_TEST
START_TEST(test_iram_fill_unaligned_64bit_addr)
{
int i;
memset(g_iram, 0x00, sizeof(g_iram));
iram_fill((void *)(g_iram + 2), 0xEE, 10);
for (i = 0; i < 10; i++)
ck_assert_uint_eq(g_iram[2 + i], 0xEE);
ck_assert_uint_eq(g_iram[0], 0x00);
ck_assert_uint_eq(g_iram[1], 0x00);
ck_assert_uint_eq(g_iram[12], 0x00);
}
END_TEST
Suite *va416x0_fram_suite(void)
{
Suite *s = suite_create("va416x0-fram");
TCase *tc = tcase_create("fram");
tcase_add_test(tc, test_fram_write_command_failure_aborts_split_transaction);
tcase_add_test(tc, test_iram_write_unaligned_64bit_addr);
tcase_add_test(tc, test_iram_fill_unaligned_64bit_addr);
suite_add_tcase(s, tc);
return s;