mirror of https://github.com/wolfSSL/wolfBoot.git
F-7978: keep LS1028A xSPI Page Programs inside a physical NOR page
xspi_flash_write() capped each chunk to XSPI_IP_BUF_SIZE (256, the NOR page size) but not to the bytes remaining in the current physical page, so a write starting mid-page sent one Page Program across the page boundary. NOR wraps the write pointer to the page start, so the excess bytes clobbered the beginning of the next page (and the programmed data was corrupted to match). Cap each chunk to min(len, page room left in the current page). Aligned or page-start writes are unchanged; a mid-page start now sends a short first program plus the remaining full pages. The unit-ls1028a-xspi-write harness now models the NOR wrap (a crossing program clobbers the page start it wraps into), and gains test_write_crossing_page_no_wrap: a 256 byte write starting 16 bytes before a boundary must not touch the first 16 bytes of the next page (pre-fix the emulator shows the clobber and a 240+256 program). test_write_unaligned's expectations are updated to the correct 156/256/188 chunking. Verified: unit-ls1028a-xspi-write 5/5 green post-fix (2/5 fail pre-fix), tools/unit-tests suite green (113 binaries).pull/862/head
parent
da15852490
commit
43890cfb39
|
|
@ -535,7 +535,17 @@ void xspi_flash_write(uintptr_t address, const uint8_t *data, uint32_t len)
|
|||
uint32_t i = 0, j = 0;
|
||||
|
||||
while (len) {
|
||||
/* A NOR Page Program must not cross a physical page boundary:
|
||||
* the write pointer wraps to the start of the page and the
|
||||
* excess bytes clobber preceding data. Cap the chunk to the
|
||||
* bytes left in the current page (XSPI_IP_BUF_SIZE equals the
|
||||
* page size, so this only tightens the limit mid-page). */
|
||||
uint32_t page_room =
|
||||
FLASH_PAGE_SIZE - ((uint32_t)address % FLASH_PAGE_SIZE);
|
||||
|
||||
size = len > XSPI_IP_BUF_SIZE ? XSPI_IP_BUF_SIZE : len;
|
||||
if (size > page_room)
|
||||
size = page_room;
|
||||
|
||||
/* NOR flash clears its write-enable latch after each program
|
||||
* operation, so enable writes for every page, not just the
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ static void xspi_emu_start(void)
|
|||
uint32_t seq = ipcr1 >> 16;
|
||||
uint32_t len = ipcr1 & 0xFFFF;
|
||||
uint32_t addr = XSPI_IPCR0;
|
||||
uint32_t i;
|
||||
int e = g_xspi_log_n < XSPI_LOG_MAX ? g_xspi_log_n : XSPI_LOG_MAX - 1;
|
||||
|
||||
if (g_xspi_log_n < XSPI_LOG_MAX)
|
||||
|
|
@ -122,7 +123,17 @@ static void xspi_emu_start(void)
|
|||
else if (seq == LUT_INDEX_PP) {
|
||||
g_xspi_log[e].cmd = XSPI_CMD_PP;
|
||||
if (g_wel) {
|
||||
memcpy(&g_nor[addr], g_tfd_stream, len);
|
||||
/* Real NOR behavior: the write pointer wraps to the start
|
||||
* of the physical page it started in, so a Page Program
|
||||
* crossing the boundary clobbers the beginning of that
|
||||
* page. */
|
||||
{
|
||||
uint32_t base = addr & ~(uint32_t)(FLASH_PAGE_SIZE - 1);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
g_nor[base + ((addr + i) & (FLASH_PAGE_SIZE - 1))] =
|
||||
g_tfd_stream[i];
|
||||
}
|
||||
g_wel = 0; /* the program consumes the latch */
|
||||
}
|
||||
else {
|
||||
|
|
@ -258,7 +269,9 @@ START_TEST(test_write_multipart)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Multi-page write starting at an unaligned address. */
|
||||
/* Multi-page write starting at an unaligned address. The first
|
||||
* chunk must stop at the physical page boundary (156 bytes), not run
|
||||
* a full 256-byte Page Program across it. */
|
||||
START_TEST(test_write_unaligned)
|
||||
{
|
||||
uint8_t data[1024];
|
||||
|
|
@ -274,8 +287,11 @@ START_TEST(test_write_unaligned)
|
|||
ck_assert_int_eq(g_xspi_log[2 * i + 1].cmd, XSPI_CMD_PP);
|
||||
}
|
||||
ck_assert_uint_eq(g_xspi_log[1].addr, 100);
|
||||
ck_assert_uint_eq(g_xspi_log[3].addr, 356);
|
||||
ck_assert_uint_eq(g_xspi_log[5].addr, 612);
|
||||
ck_assert_uint_eq(g_xspi_log[1].len, 156);
|
||||
ck_assert_uint_eq(g_xspi_log[3].addr, 256);
|
||||
ck_assert_uint_eq(g_xspi_log[3].len, 256);
|
||||
ck_assert_uint_eq(g_xspi_log[5].addr, 512);
|
||||
ck_assert_uint_eq(g_xspi_log[5].len, 188);
|
||||
ck_assert_int_eq(any_rejected(), 0);
|
||||
for (i = 0; i < 600; i++)
|
||||
ck_assert_uint_eq(g_nor[100 + i], data[i]);
|
||||
|
|
@ -286,6 +302,47 @@ START_TEST(test_write_unaligned)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* A write that crosses a page boundary must not clobber the start of
|
||||
* the next page: NOR wraps the write pointer at the boundary, so a
|
||||
* 256-byte Page Program starting 16 bytes short of it would overwrite
|
||||
* the first 16 bytes of the page. */
|
||||
START_TEST(test_write_crossing_page_no_wrap)
|
||||
{
|
||||
uint8_t data[256];
|
||||
size_t i;
|
||||
|
||||
/* Pre-seed the whole flash with a pattern. */
|
||||
memset(g_nor, 0x5A, sizeof(g_nor));
|
||||
fill(data, sizeof(data), 0x60);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(240, data, 256), 256);
|
||||
|
||||
/* No Page Program may cross a physical page boundary. */
|
||||
for (i = 0; i < g_xspi_log_n; i++) {
|
||||
if (g_xspi_log[i].cmd == XSPI_CMD_PP)
|
||||
ck_assert_int_le(
|
||||
(int)(g_xspi_log[i].addr % FLASH_PAGE_SIZE) +
|
||||
(int)g_xspi_log[i].len, FLASH_PAGE_SIZE);
|
||||
}
|
||||
ck_assert_int_eq(g_xspi_log_n, 4); /* WEN+PP x2 */
|
||||
ck_assert_uint_eq(g_xspi_log[1].addr, 240);
|
||||
ck_assert_uint_eq(g_xspi_log[1].len, 16);
|
||||
ck_assert_uint_eq(g_xspi_log[3].addr, 256);
|
||||
ck_assert_uint_eq(g_xspi_log[3].len, 240);
|
||||
ck_assert_int_eq(any_rejected(), 0);
|
||||
|
||||
/* The written bytes. */
|
||||
for (i = 0; i < 256; i++)
|
||||
ck_assert_uint_eq(g_nor[240 + i], data[i]);
|
||||
|
||||
/* The page start that a wrapping program would clobber. */
|
||||
for (i = 0; i < 240; i++)
|
||||
ck_assert_uint_eq(g_nor[i], 0x5A);
|
||||
for (i = 496; i < sizeof(g_nor); i++)
|
||||
ck_assert_uint_eq(g_nor[i], 0x5A);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* The EXT_FLASH wrapper has the same single-WEN bug. */
|
||||
START_TEST(test_ext_write_multipart)
|
||||
{
|
||||
|
|
@ -321,6 +378,7 @@ Suite *ls1028a_xspi_suite(void)
|
|||
tcase_add_test(tc, test_write_single_page);
|
||||
tcase_add_test(tc, test_write_multipart);
|
||||
tcase_add_test(tc, test_write_unaligned);
|
||||
tcase_add_test(tc, test_write_crossing_page_no_wrap);
|
||||
tcase_add_test(tc, test_ext_write_multipart);
|
||||
|
||||
suite_add_tcase(s, tc);
|
||||
|
|
|
|||
Loading…
Reference in New Issue