diff --git a/hal/nxp_p1021.c b/hal/nxp_p1021.c index 021cc5cb..763f1476 100644 --- a/hal/nxp_p1021.c +++ b/hal/nxp_p1021.c @@ -1835,6 +1835,7 @@ int ext_flash_erase(uintptr_t address, int len) wolfBoot_printf("erase page %d, status %x\n", page, status); #endif (void)status; + address += block_size; len -= block_size; } diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 8f6839c3..1b160dfa 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -114,6 +114,7 @@ TESTS+=unit-versal-qspi-dma TESTS+=unit-versal-ext-write TESTS+=unit-t10xx-qe-firmware TESTS+=unit-t10xx-flash-status +TESTS+=unit-p1021-erase-advance TESTS+=unit-aurix-erased-fill TESTS+=unit-aurix-erased-fill-invert TESTS+=unit-t2080-fman-loader @@ -1019,6 +1020,34 @@ t10xx_flash_status_extract.h: ../../hal/nxp_t10xx.c unit-t10xx-flash-status: unit-t10xx-flash-status.c t10xx_flash_status_extract.h gcc -o $@ unit-t10xx-flash-status.c $(CFLAGS) $(LDFLAGS) +# unit-p1021-erase-advance runs the real ext_flash_erase() from +# hal/nxp_p1021.c against mocked ELBC access (F-11034: the loop never +# advanced the address, re-erasing the first block of the range). +p1021_erase_extract.h: ../../hal/nxp_p1021.c + sed -n '/#define ELBC_BASE /p' $< > $@ + sed -n '/#define ELBC_MDR /p' $< >> $@ + sed -n '/#define ELBC_FIR /p' $< >> $@ + sed -n '/#define ELBC_FCR /p' $< >> $@ + sed -n '/#define ELBC_FBCR /p' $< >> $@ + sed -n '/#define ELBC_FIR_OP(/p' $< >> $@ + sed -n '/#define ELBC_FIR_OP_PA /p' $< >> $@ + sed -n '/#define ELBC_FIR_OP_CM0 /p' $< >> $@ + sed -n '/#define ELBC_FIR_OP_CM2 /p' $< >> $@ + sed -n '/#define ELBC_FIR_OP_CW1 /p' $< >> $@ + sed -n '/#define ELBC_FIR_OP_RS /p' $< >> $@ + sed -n '/#define ELBC_FCR_CMD(/p' $< >> $@ + sed -n '/#define NAND_CMD_STATUS /p' $< >> $@ + sed -n '/#define NAND_CMD_BLOCK_ERASE1 /p' $< >> $@ + sed -n '/#define NAND_CMD_BLOCK_ERASE2 /p' $< >> $@ + sed -n '/#define FLASH_PAGE_SIZE /p' $< >> $@ + +p1021_erase_fn_extract.h: ../../hal/nxp_p1021.c + sed -n '/^int ext_flash_erase/,/^}/p' $< > $@ + +unit-p1021-erase-advance: unit-p1021-erase-advance.c p1021_erase_extract.h \ + p1021_erase_fn_extract.h + gcc -o $@ unit-p1021-erase-advance.c $(CFLAGS) $(LDFLAGS) + # unit-ecc-raw-der runs the real wolfCrypt raw-to-DER conversion and # verification (F-11024: the wolfHSM verify path in src/image.c passed # minimal field sizes with field-start pointers to diff --git a/tools/unit-tests/unit-p1021-erase-advance.c b/tools/unit-tests/unit-p1021-erase-advance.c new file mode 100644 index 00000000..3d4b4c50 --- /dev/null +++ b/tools/unit-tests/unit-p1021-erase-advance.c @@ -0,0 +1,147 @@ +/* unit-p1021-erase-advance.c + * + * Regression test for F-11034: ext_flash_erase() in hal/nxp_p1021.c + * decremented the remaining length but never advanced `address`, so + * every loop iteration derived the same page and re-erased the first + * block while the rest of the requested range stayed intact. + * + * The real function is extracted by the Makefile together with the + * ELBC register macros it uses; the ELBC register access (set32/get32) + * and the flash helpers are mocked, and the test records the page + * address programmed for each erase command. + * + * 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 + +/* ELBC_BASE is built from CCSRBAR in the real file; pin it here. */ +#define CCSRBAR 0x0 + +/* ELBC register macros + NAND command codes from hal/nxp_p1021.c + * (extracted by the Makefile). */ +#include "p1021_erase_extract.h" + +#define MAX_TRACKED_PAGES 32 +static int g_pages[MAX_TRACKED_PAGES]; +static int g_page_calls; +static int g_cmd_calls; +static int g_cmd_ret; + +static void mock_reset(int cmd_ret) +{ + g_page_calls = 0; + g_cmd_calls = 0; + g_cmd_ret = cmd_ret; +} + +static void hal_flash_set_addr(int page, int col) +{ + (void)col; + + if (g_page_calls < MAX_TRACKED_PAGES) { + g_pages[g_page_calls] = page; + } + g_page_calls++; +} + +static int hal_flash_command(uint8_t iswrite) +{ + (void)iswrite; + + g_cmd_calls++; + return g_cmd_ret; +} + +/* Mocks for the ELBC register access helpers (hal/nxp_ppc.h). */ +static void set32(volatile unsigned int *addr, unsigned int val) +{ + (void)addr; + (void)val; +} + +static uint32_t get32(volatile unsigned int *addr) +{ + (void)addr; + return 0; /* MDR status: no error */ +} + +/* The real ext_flash_erase() from hal/nxp_p1021.c (extracted). */ +#include "p1021_erase_fn_extract.h" + +/* Small-page geometry from the extracted FLASH_PAGE_SIZE (512): + * a 16 KiB block spans 32 pages. */ +#define TEST_PAGE_SIZE 512u +#define TEST_BLOCK_SIZE (16u * 1024u) + +START_TEST (test_erase_advances_through_blocks) +{ + int ret; + + mock_reset(0); + + ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE); + + ck_assert_int_eq(ret, 0); + /* Two blocks erased: page 0, then page 32 (one block later). */ + ck_assert_int_eq(g_page_calls, 2); + ck_assert_int_eq(g_pages[0], 0); + ck_assert_int_eq(g_pages[1], (int)(TEST_BLOCK_SIZE / TEST_PAGE_SIZE)); +} +END_TEST + +START_TEST (test_erase_stops_on_command_error) +{ + int ret; + + mock_reset(-1); + + ret = ext_flash_erase(0, 2 * (int)TEST_BLOCK_SIZE); + + ck_assert_int_eq(ret, -1); + ck_assert_int_eq(g_page_calls, 1); + ck_assert_int_eq(g_pages[0], 0); +} +END_TEST + +Suite *p1021_erase_suite(void) +{ + Suite *s = suite_create("p1021 erase advance"); + TCase *tc = tcase_create("erase-address"); + + tcase_add_test(tc, test_erase_advances_through_blocks); + tcase_add_test(tc, test_erase_stops_on_command_error); + tcase_set_timeout(tc, 10); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int fails; + Suite *s = p1021_erase_suite(); + SRunner *sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +}