Skip to content

Commit 6913b01

Browse files
widgetiiclaude
andauthored
agent: post-erase verify must use register-mode read past 1 MB (#91)
## Summary `flash_verify_erased` (the post-erase smoke test) read sample bytes directly from `FLASH_MEM` (the memory-mapped window). On hi3516ev300 — and apparently every SoC where `flash_read_full` already takes the register-mode-read path with the comment *"boot mode memory window wraps at 1 MB on some SoCs"* — that direct read **wraps at 1 MB**. For any sector at flash offset ≥ `0x100000` the verify read returned bytes from `(offset % 0x100000)` instead of the actual just-erased sector, the bytes weren't `0xFF`, and the smoke test reported `ACK_FLASH_ERROR (0x02)` — even though the erase had completed cleanly. Visible on W25Q128 (16 MB NOR): 12 sectors of a kernel write completed, then sector 13 at flash offset `0x110000` failed. Same chip programmed fine via U-Boot's `sf write`, and the agent's higher-level CRC32 verify (which uses `flash_read()` indirectly) also succeeded when bypassing the smoke test — the bug was localised to this one read path. Fix: route the verify reads through `flash_read()`, the same register-mode SPI READ path `flash_read_full` has used since the 1 MB window workaround originally landed. ## Verification on rack pod `10.216.128.69` (hi3516ev300 + W25Q128) ``` Before fix: 0x00050000: OK in 6.4s CRC match=True ← <1 MB 0x000C0000: OK in 6.8s CRC match=True ← <1 MB 0x00110000: FAIL in 6.1s ← =1 MB + 0x10000 0x00350000: FAIL in 6.1s ← 3.3 MB 0x00F00000: FAIL in 6.1s ← 15 MB After fix: 0x00050000: OK in 6.4s CRC match=True 0x000C0000: OK in 6.3s CRC match=True 0x00110000: OK in 6.2s CRC match=True ✓ 0x00350000: OK in 6.3s CRC match=True ✓ 0x00F00000: OK in 6.3s CRC match=True ✓ ``` Full OpenIPC nor-neo install through the agent (kernel 2.0 MB + rootfs 4.2 MB) now completes end-to-end in **92 s at 81 KB/s sustained**, Linux boots to `openipc-hi3516ev300 login:`. ## Test plan - [ ] `uv run pytest tests/ -x -v --ignore=tests/fuzz` — 480 passed / 2 skipped - [ ] `make -C agent test HOST_CC=gcc` — 5406/5406 agent C tests pass - [ ] Regression: smaller writes (<1 MB) continue to work — verified on 0x50000 and 0xC0000 offsets. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Dmitry Ilyin <widgetii@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 01253cf commit 6913b01

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

agent/spi_flash.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -649,16 +649,26 @@ uint8_t flash_read_status(void) {
649649
* Samples first/last 16 bytes of the range — fast (one register-mode
650650
* cycle each) and high-signal: real silent-erase leaves the original
651651
* data verbatim, which is essentially never all-FF in practice.
652-
* Returns 0 if all sampled bytes are 0xFF, -1 otherwise. */
652+
* Returns 0 if all sampled bytes are 0xFF, -1 otherwise.
653+
*
654+
* Uses flash_read() (register-mode SPI READ) instead of direct
655+
* memory-mapped access. The boot-mode memory window at FLASH_MEM
656+
* wraps at 1 MB on some SoCs (hi3516ev300 confirmed), so for any
657+
* sector at offset ≥ 0x100000 a direct memory-mapped read returns
658+
* stale data from sector (addr % 0x100000) and falsely fails the
659+
* verify even though the erase succeeded. flash_read() goes through
660+
* the FMC's normal-mode SPI READ path which addresses the full chip. */
653661
static int flash_verify_erased(uint32_t addr, uint32_t len) {
654-
const uint8_t *p = (const uint8_t *)(FLASH_MEM + addr);
655-
uint32_t head = len < 16 ? len : 16;
662+
uint8_t buf[16];
663+
uint32_t head = len < sizeof(buf) ? len : sizeof(buf);
664+
flash_read(addr, buf, head);
656665
for (uint32_t i = 0; i < head; i++) {
657-
if (p[i] != 0xFF) return -1;
666+
if (buf[i] != 0xFF) return -1;
658667
}
659668
if (len > 32) {
660-
for (uint32_t i = len - 16; i < len; i++) {
661-
if (p[i] != 0xFF) return -1;
669+
flash_read(addr + len - sizeof(buf), buf, sizeof(buf));
670+
for (uint32_t i = 0; i < sizeof(buf); i++) {
671+
if (buf[i] != 0xFF) return -1;
662672
}
663673
}
664674
return 0;

0 commit comments

Comments
 (0)