From 5929760ee8c619091b7191122d068e8e89127225 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 7 Jul 2026 12:03:56 -0700 Subject: [PATCH 1/3] 6588 - Index LS1028A XSPI TX FIFO by word instead of by byte --- hal/nxp_ls1028a.c | 4 +- tools/unit-tests/Makefile | 4 + tools/unit-tests/unit-xspi-tfd-index.c | 125 +++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 tools/unit-tests/unit-xspi-tfd-index.c diff --git a/hal/nxp_ls1028a.c b/hal/nxp_ls1028a.c index 63dcc6db4c..c603adaed7 100644 --- a/hal/nxp_ls1028a.c +++ b/hal/nxp_ls1028a.c @@ -555,7 +555,7 @@ void xspi_flash_write(uintptr_t address, const uint8_t *data, uint32_t len) for(j = 0; j < XSPI_IP_WM_SIZE; j+=4) { memcpy(&tx_data, data, 4); data += 4; - xspi_writereg((uint32_t*)XSPI_TFD_BASE + j, tx_data); + xspi_writereg((uint32_t*)XSPI_TFD_BASE + (j / 4), tx_data); } /* Reset fifo */ @@ -574,7 +574,7 @@ void xspi_flash_write(uintptr_t address, const uint8_t *data, uint32_t len) rem_size = ((remaining - j) < 4) ? (remaining - j) : 4; memcpy(&tx_data, data, rem_size); data += rem_size; - xspi_writereg((uint32_t*)XSPI_TFD_BASE + j, tx_data); + xspi_writereg((uint32_t*)XSPI_TFD_BASE + (j / 4), tx_data); } /* Reset fifo */ diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 961a66fc5d..e0c3c14555 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -89,6 +89,7 @@ TESTS+=unit-flash-write-mcxa TESTS+=unit-flash-write-samr21 TESTS+=unit-flash-write-same51 TESTS+=unit-imx-rt-cache-align +TESTS+=unit-xspi-tfd-index # linux_loader.c is x86 32-bit only, so its unit tests need a working 32-bit # (multilib) toolchain. Probe whether "gcc -m32" can link, and only add the @@ -332,6 +333,9 @@ unit-arm-tee-psa-ipc: ../../include/target.h unit-arm-tee-psa-ipc.c ../../src/ar unit-va416x0-fram: unit-va416x0-fram.c ../../hal/va416x0.c gcc -o $@ unit-va416x0-fram.c $(CFLAGS) $(LDFLAGS) +unit-xspi-tfd-index: unit-xspi-tfd-index.c + gcc -o $@ unit-xspi-tfd-index.c $(CFLAGS) -I../../hal $(LDFLAGS) + unit-max-space: ../../include/target.h unit-max-space.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-xspi-tfd-index.c b/tools/unit-tests/unit-xspi-tfd-index.c new file mode 100644 index 0000000000..0216ce93fa --- /dev/null +++ b/tools/unit-tests/unit-xspi-tfd-index.c @@ -0,0 +1,125 @@ +/* unit-xspi-tfd-index.c + * + * Unit tests for the LS1028A XSPI TX FIFO fill indexing. + * + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#include "nxp_ls1028a.h" + +/* Back the XSPI register space with host memory instead of MMIO. */ +#undef XSPI_BASE +static uint32_t g_xspi_regs[0x400 / sizeof(uint32_t)]; +#define XSPI_BASE ((uintptr_t)g_xspi_regs) + +#define WORD0 0xA1B2C3D4u +#define WORD1 0x11223344u + +static void xspi_store(uint32_t *addr, uint32_t val) +{ + *(volatile uint32_t *)addr = val; +} + +static void fill_burst_bytestride(const uint8_t *data) +{ + uint32_t j = 0, tx = 0; + + for (j = 0; j < XSPI_IP_WM_SIZE; j += 4) { + memcpy(&tx, data, 4); + data += 4; + xspi_store((uint32_t *)XSPI_TFD_BASE + j, tx); + } +} + +static void fill_burst_wordindex(const uint8_t *data) +{ + uint32_t j = 0, tx = 0; + + for (j = 0; j < XSPI_IP_WM_SIZE; j += 4) { + memcpy(&tx, data, 4); + data += 4; + xspi_store((uint32_t *)XSPI_TFD_BASE + (j / 4), tx); + } +} + +static void make_payload(uint8_t *payload) +{ + uint32_t w0 = WORD0, w1 = WORD1; + + memcpy(payload, &w0, 4); + memcpy(payload + 4, &w1, 4); +} + +START_TEST(test_bytestride_loses_second_word) +{ + uint8_t payload[XSPI_IP_WM_SIZE]; + + memset(g_xspi_regs, 0, sizeof(g_xspi_regs)); + make_payload(payload); + fill_burst_bytestride(payload); + + ck_assert_uint_eq(XSPI_TFD(0), WORD0); + ck_assert_uint_ne(XSPI_TFD(1), WORD1); + ck_assert_uint_eq(XSPI_TFD(4), WORD1); +} +END_TEST + +START_TEST(test_wordindex_preserves_burst) +{ + uint8_t payload[XSPI_IP_WM_SIZE]; + + memset(g_xspi_regs, 0, sizeof(g_xspi_regs)); + make_payload(payload); + fill_burst_wordindex(payload); + + ck_assert_uint_eq(XSPI_TFD(0), WORD0); + ck_assert_uint_eq(XSPI_TFD(1), WORD1); + ck_assert_uint_eq(XSPI_TFD(4), 0u); +} +END_TEST + +Suite *xspi_tfd_suite(void) +{ + Suite *s = suite_create("xspi-tfd-index"); + TCase *tc = tcase_create("tx-fifo-fill"); + + tcase_add_test(tc, test_bytestride_loses_second_word); + tcase_add_test(tc, test_wordindex_preserves_burst); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int fails; + Suite *s = xspi_tfd_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + + return fails; +} From de8d5a986305f68929ba6a23aefb0ceb7514c97b Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 7 Jul 2026 12:14:19 -0700 Subject: [PATCH 2/3] 6560 - Clear PFSWAP bit in pic32_fcw_pfswap_set read-modify-write --- hal/pic32c.c | 2 +- tools/unit-tests/Makefile | 4 + tools/unit-tests/unit-pic32-pfswap.c | 114 +++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tools/unit-tests/unit-pic32-pfswap.c diff --git a/hal/pic32c.c b/hal/pic32c.c index 945019e6ef..aa31097df1 100644 --- a/hal/pic32c.c +++ b/hal/pic32c.c @@ -293,7 +293,7 @@ static void pic32_fcw_pfswap_set(int sw) uint32_t reg; reg = FCW_SWAP; - reg &= FCW_SWAP_PFSWAP; + reg &= ~FCW_SWAP_PFSWAP; if (sw) reg |= FCW_SWAP_PFSWAP; FCW_KEY = FCW_UNLOCK_SWAPKEY; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index e0c3c14555..3a1b466086 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -90,6 +90,7 @@ TESTS+=unit-flash-write-samr21 TESTS+=unit-flash-write-same51 TESTS+=unit-imx-rt-cache-align TESTS+=unit-xspi-tfd-index +TESTS+=unit-pic32-pfswap # linux_loader.c is x86 32-bit only, so its unit tests need a working 32-bit # (multilib) toolchain. Probe whether "gcc -m32" can link, and only add the @@ -336,6 +337,9 @@ unit-va416x0-fram: unit-va416x0-fram.c ../../hal/va416x0.c unit-xspi-tfd-index: unit-xspi-tfd-index.c gcc -o $@ unit-xspi-tfd-index.c $(CFLAGS) -I../../hal $(LDFLAGS) +unit-pic32-pfswap: unit-pic32-pfswap.c + gcc -o $@ unit-pic32-pfswap.c $(CFLAGS) $(LDFLAGS) + unit-max-space: ../../include/target.h unit-max-space.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-pic32-pfswap.c b/tools/unit-tests/unit-pic32-pfswap.c new file mode 100644 index 0000000000..c848733988 --- /dev/null +++ b/tools/unit-tests/unit-pic32-pfswap.c @@ -0,0 +1,114 @@ +/* unit-pic32-pfswap.c + * + * Unit tests for the PIC32 FCW dual-bank PFSWAP read-modify-write. + * + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include + +#define FCW_SWAP_PFSWAP (1u << 8) +#define FCW_UNLOCK_SWAPKEY 0x91C32C02u +#define FCW_OTHER_BITS 0x000000A5u + +static uint32_t g_fcw_swap; +static uint32_t g_fcw_key; + +static int pfswap_get(void) +{ + return !!(g_fcw_swap & FCW_SWAP_PFSWAP); +} + +static void pfswap_set_bytemask(int sw) +{ + uint32_t reg; + + reg = g_fcw_swap; + reg &= FCW_SWAP_PFSWAP; + if (sw) + reg |= FCW_SWAP_PFSWAP; + g_fcw_key = FCW_UNLOCK_SWAPKEY; + g_fcw_swap = reg; +} + +static void pfswap_set_clearbit(int sw) +{ + uint32_t reg; + + reg = g_fcw_swap; + reg &= ~FCW_SWAP_PFSWAP; + if (sw) + reg |= FCW_SWAP_PFSWAP; + g_fcw_key = FCW_UNLOCK_SWAPKEY; + g_fcw_swap = reg; +} + +START_TEST(test_masked_set_cannot_clear_bit) +{ + g_fcw_swap = FCW_OTHER_BITS; + + pfswap_set_bytemask(!pfswap_get()); + ck_assert_int_eq(pfswap_get(), 1); + + pfswap_set_bytemask(!pfswap_get()); + ck_assert_int_eq(pfswap_get(), 1); + ck_assert_uint_eq(g_fcw_swap & FCW_OTHER_BITS, 0u); +} +END_TEST + +START_TEST(test_cleared_set_toggles_and_preserves) +{ + g_fcw_swap = FCW_OTHER_BITS; + + pfswap_set_clearbit(!pfswap_get()); + ck_assert_int_eq(pfswap_get(), 1); + ck_assert_uint_eq(g_fcw_swap & FCW_OTHER_BITS, FCW_OTHER_BITS); + + pfswap_set_clearbit(!pfswap_get()); + ck_assert_int_eq(pfswap_get(), 0); + ck_assert_uint_eq(g_fcw_swap & FCW_OTHER_BITS, FCW_OTHER_BITS); +} +END_TEST + +Suite *pic32_pfswap_suite(void) +{ + Suite *s = suite_create("pic32-pfswap"); + TCase *tc = tcase_create("dualbank-swap"); + + tcase_add_test(tc, test_masked_set_cannot_clear_bit); + tcase_add_test(tc, test_cleared_set_toggles_and_preserves); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int fails; + Suite *s = pic32_pfswap_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + + return fails; +} From 95a3ad241c4f2857385c5489c61be0fddfa7e2ce Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 7 Jul 2026 12:14:34 -0700 Subject: [PATCH 3/3] 6567 - Zeroize static ENCRYPT_CACHE on WOLFBOOT_SMALL_STACK path --- src/libwolfboot.c | 16 ++++- tools/unit-tests/Makefile | 7 ++ tools/unit-tests/unit-enc-key-zeroize.c | 93 +++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 tools/unit-tests/unit-enc-key-zeroize.c diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 51a4577775..e420a0dd3c 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1910,6 +1910,17 @@ void RAMFUNCTION wolfBoot_crypto_set_iv(const uint8_t *nonce, uint32_t iv_counte } #endif /* EXT_ENCRYPTED && (__WOLFBOOT || UNIT_TEST || MMU) */ +#if !defined(NVM_FLASH_WRITEONCE) && !defined(WOLFBOOT_ENCRYPT_CACHE) && \ + !defined(MMU) && !defined(WOLFBOOT_RENESAS_TSIP) +static void wolfBoot_secure_zero(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + + while (len-- > 0) + *p++ = 0; +} +#endif + static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) { #ifdef WOLFBOOT_RENESAS_TSIP @@ -1988,9 +1999,8 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); #endif exit_lock: -#if !defined(WOLFBOOT_SMALL_STACK) && !defined(NVM_FLASH_WRITEONCE) && \ - !defined(WOLFBOOT_ENCRYPT_CACHE) - ForceZero(ENCRYPT_CACHE, NVM_CACHE_SIZE); +#if !defined(NVM_FLASH_WRITEONCE) && !defined(WOLFBOOT_ENCRYPT_CACHE) + wolfBoot_secure_zero(ENCRYPT_CACHE, NVM_CACHE_SIZE); #endif hal_flash_lock(); return ret; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 3a1b466086..c29fe1a34d 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -91,6 +91,7 @@ TESTS+=unit-flash-write-same51 TESTS+=unit-imx-rt-cache-align TESTS+=unit-xspi-tfd-index TESTS+=unit-pic32-pfswap +TESTS+=unit-enc-key-zeroize # linux_loader.c is x86 32-bit only, so its unit tests need a working 32-bit # (multilib) toolchain. Probe whether "gcc -m32" can link, and only add the @@ -495,6 +496,12 @@ unit-diagnostics-256: ../../include/target.h unit-diagnostics.c unit-enc-nvm: ../../include/target.h unit-enc-nvm.c gcc -o $@ $(WOLFCRYPT_SRC) unit-enc-nvm.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) +unit-enc-key-zeroize:CFLAGS+=-DWOLFBOOT_SMALL_STACK -DMOCK_PARTITIONS -DEXT_ENCRYPTED \ + -DENCRYPT_WITH_CHACHA -DEXT_FLASH -DHAVE_CHACHA +unit-enc-key-zeroize:WOLFCRYPT_SRC+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/chacha.c +unit-enc-key-zeroize: ../../include/target.h unit-enc-key-zeroize.c + gcc -o $@ $(WOLFCRYPT_SRC) unit-enc-key-zeroize.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) + unit-enc-nvm-flagshome: ../../include/target.h unit-enc-nvm.c gcc -o $@ $(WOLFCRYPT_SRC) unit-enc-nvm.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-enc-key-zeroize.c b/tools/unit-tests/unit-enc-key-zeroize.c new file mode 100644 index 0000000000..785d54670e --- /dev/null +++ b/tools/unit-tests/unit-enc-key-zeroize.c @@ -0,0 +1,93 @@ +/* unit-enc-key-zeroize.c + * + * Unit test that hal_set_key zeroizes the static ENCRYPT_CACHE on the + * WOLFBOOT_SMALL_STACK path. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ +#define WOLFBOOT_HASH_SHA256 +#define IMAGE_HEADER_SIZE 256 +#define MOCK_ADDRESS 0xCC000000 +#define MOCK_ADDRESS_BOOT 0xCD000000 +#define MOCK_ADDRESS_SWAP 0xCE000000 +const char ENCRYPT_KEY[] = "0123456789abcdef0123456789abcdef0123456789abcdef"; +#include +#include +#include "image.h" +#include "encrypt.h" +#include "libwolfboot.c" +#include +#include +#include +#include + +const char *argv0; + +#include "unit-mock-flash.c" + +START_TEST(test_hal_set_key_zeroizes_static_cache) +{ + uint8_t key[ENCRYPT_KEY_SIZE]; + uint8_t nonce[ENCRYPT_NONCE_SIZE]; + int ret, i; + + ret = mmap_file("/tmp/wolfboot-unit-boot.bin", (void *)MOCK_ADDRESS_BOOT, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-file.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-swap.bin", (void *)MOCK_ADDRESS_SWAP, + WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + memset(key, 0x5A, sizeof(key)); + memset(nonce, 0xA5, sizeof(nonce)); + + ret = hal_set_key(key, nonce); + ck_assert_int_eq(ret, 0); + + for (i = 0; i < (int)NVM_CACHE_SIZE; i++) + ck_assert_uint_eq(ENCRYPT_CACHE[i], 0); +} +END_TEST + +Suite *enc_key_zeroize_suite(void) +{ + Suite *s = suite_create("enc-key-zeroize"); + TCase *tc = tcase_create("hal_set_key"); + + tcase_add_test(tc, test_hal_set_key_zeroizes_static_cache); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + int fails; + Suite *s = enc_key_zeroize_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + + return fails; +}