p1021: read the full page so the bad-block marker is valid

The bad-block check reads flash_buf[bad_marker], which sits in the
spare region. A BC != 0 transfer loads only the requested main-area
bytes, so the marker byte is leftover FCM RAM: a stale non-0xFF value
makes ext_flash_read() declare the block bad, skip to the next erase
block and return data from the wrong address while still returning
len. BC = 0 is also the only setting that checks ECC.

Latent until the FBCR shift fix made BC take effect. read_size still
bounds the copy out of the buffer, so the F-7976 sizing fix stands.
pull/862/head
Daniele Lacamera 2026-08-18 09:05:33 +02:00
parent 52abd2f404
commit f669292ce1
2 changed files with 47 additions and 10 deletions

View File

@ -1743,15 +1743,13 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
/* read page into FCM buffer */
hal_flash_set_addr(page, col);
/* full page + spare (the only ECC-checking setting) only when
* the whole page is read from column 0; otherwise transfer
* exactly read_size bytes starting at column col */
if (col == 0 && read_size == page_size) {
set32(ELBC_FBCR, 0);
}
else {
set32(ELBC_FBCR, ELBC_FBCR_BC(read_size));
}
/* Always transfer the full page + spare (BC = 0). It is the
* only setting that checks ECC, and the bad-block marker
* tested below lives in the spare region, which a BC != 0
* transfer never loads -- the check would then sample stale
* FCM RAM. read_size still governs how much is copied out
* of the buffer and how far the loop advances. */
set32(ELBC_FBCR, 0);
ret = hal_flash_command(0);
if (ret != 0)

View File

@ -198,6 +198,13 @@ static void emulate_fcm_transfer(void)
if (ci + bc > 512) {
bc = 512 - ci;
}
/* g_nand only models 16 pages. A page past the end means the code
* under test walked off the emulated device (e.g. a spurious
* bad-block skip): leave the FCM buffer alone rather than running
* off the array, so the caller's data assertions report it. */
if (page >= 16) {
return;
}
nand = &g_nand[page * (512 + 64)];
if (((fir >> 16) & 0xF) == ELBC_FIR_OP_RBW) {
@ -492,6 +499,35 @@ START_TEST(test_p1021_read_full_page)
}
END_TEST
/* A short read must still transfer the spare region, because the
* bad-block marker lives there. With BC != 0 the FCM only loads the
* requested main-area bytes and flash_buf[bad_marker] is leftover FCM
* RAM from an earlier operation: a stale non-0xFF byte then makes
* ext_flash_read() declare the block bad, skip to the next erase block
* and return data from the wrong address -- while still returning len.
* Here FCM buffer 0's marker byte is dirtied before the read. */
START_TEST(test_p1021_read_short_spare_loaded)
{
uint8_t data[16];
size_t i;
for (i = 0; i < 512; i++)
g_nand[i] = (uint8_t)(0x80 + i);
/* small page: bad marker at page_size + 5 in FCM buffer 0 */
g_fcm8k[512 + 5] = 0x00;
memset(data, 0xEE, sizeof(data));
ck_assert_int_eq(ext_flash_read(0, data, 4), 4);
ck_assert_int_eq(g_fbcr_n, 1);
ck_assert_uint_eq(g_fbcr_log[0], 0);
for (i = 0; i < 4; i++)
ck_assert_uint_eq(data[i], g_nand[i]);
for (i = 4; i < sizeof(data); i++)
ck_assert_uint_eq(data[i], 0xEE);
}
END_TEST
/* Multi-page read: the second iteration must copy only the remaining
* 88 bytes. Pre-fix it copied a full page, writing 424 bytes past the
* caller's buffer. */
@ -509,8 +545,10 @@ START_TEST(test_p1021_read_multipart)
ck_assert_int_eq(ext_flash_read(0, data, 600), 600);
ck_assert_int_eq(g_fbcr_n, 2);
/* reads always transfer the full page + spare (BC = 0); read_size
* only bounds how much is copied out of the FCM buffer */
ck_assert_uint_eq(g_fbcr_log[0], 0);
ck_assert_uint_eq(g_fbcr_log[1], 88u);
ck_assert_uint_eq(g_fbcr_log[1], 0);
for (i = 0; i < 512; i++)
ck_assert_uint_eq(data[i], g_nand[i]);
for (i = 0; i < 88; i++)
@ -538,6 +576,7 @@ Suite *p1021_fcm_suite(void)
tcase_add_test(tc, test_p1021_write_multipart);
tcase_add_test(tc, test_p1021_write_unaligned);
tcase_add_test(tc, test_p1021_read_full_page);
tcase_add_test(tc, test_p1021_read_short_spare_loaded);
tcase_add_test(tc, test_p1021_read_multipart);
suite_add_tcase(s, tc);