F-11034: advance the address in the P1021 multi-block NAND erase loop

ext_flash_erase() decremented the remaining length each iteration but
never advanced the address, so the derived page (address / page_size) was identical
every pass: the first block of the range was re-erased for the whole
loop and every later block was left intact (the caller then programmed
an update image into un-erased NAND). The sibling ext_flash_write()
loop already advanced address/pos/data and was not affected.

Advance address by block_size after each successful erase.

unit-p1021-erase-advance runs the real extracted function against
mocked ELBC register access and records the page programmed per erase
command: two blocks must hit page 0 then page 32 (16 KiB block / 512
page), and a failing command must stop the loop after one attempt.

Verification:
- Built: powerpc-linux-gnu-gcc -fsyntax-only -Wall with
  TARGET_nxp_p1021: clean.
- Tested: unit-p1021-erase-advance 2/2; pre-fix the second erase
  re-targeted page 0.
- Pitfalls: single-block erases (len <= block_size) behave exactly as
  before; the error path is unchanged (break on hal_flash_command
  failure).
- Style: cstyle-check.sh flag count on hal/nxp_p1021.c unchanged from
  the pre-change file; the new test trips only the uncrustify
  pointer-alignment class the sibling unit tests trip.
- Message: F-11034: prefix, no co-author trailers.
- Unverified: no P1021 board execution.
pull/870/head
Daniele Lacamera 2026-08-24 18:37:39 +02:00
parent 1ae15e3abb
commit f29309ed3f
3 changed files with 177 additions and 0 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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 <check.h>
#include <stdint.h>
#include <string.h>
/* 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;
}