recovery/session: blast proactive handshake magic before power-cycle#109
Merged
Conversation
Proactive-blast handshakes (HiSiliconV500, HiSiliconCV6xx) require the host's magic stream to already be on the wire when the bootrom's UART listen window opens (~tens of ms after reset release); otherwise the bootrom gives up and jumps to flash boot. The old non-frame-blast path ran `power_cycle -> drain_until_silent(0.5s/5s) -> handshake_task`, which lost the race even on healthy boards. Reproduced on a Vectis-bridged hi3516cv608: `defib burn --power-cycle` silently missed the window, while `~/git/burn/burn` (open serial, immediately blast, expect manual reset) succeeded on the same hardware. Restructure the non-frame-blast power+handshake block to spawn the handshake task first, yield 50 ms so a few magic frames hit the wire, then trigger reset. Drop `drain_until_silent` on this path — it was eating the catch window even when ordering was right. Frame-blast (HiSiliconStandard 0x20->0xAA) keeps the existing reset-first order because it is reactive on bootrom markers. End-to-end verified through `RecoverySession.run` against a live hi3516cv608 over rfc2217://127.0.0.1:35240 with `VectisController`: the "Starting HiSilicon CV6xx handshake" log emits before the Vectis SET-CONTROL DTR/RTS pulse, and `BootROM handshake complete` fires. All 25 tests in test_recovery_session.py, test_handshake_resilience.py and test_autoboot.py still pass.
5 tasks
widgetii
added a commit
that referenced
this pull request
Jun 3, 2026
## Summary Adds end-to-end flash agent support for the HiSilicon CV6xx family (hi3516cv608, hi3516cv610, hi3516dv500, hi3519dv500), verified live on a Vectis-bridged hi3516cv608. CV6xx fastboot is structurally different from HiSilicon Standard: a single composite (GSL + DDR tables + U-Boot-shaped section) gets uploaded and the bootrom parses the 1024-byte U-Boot header to decide where to jump. That changed the whole agent-bring-up shape, and also surfaced two stale V3/V4 addresses in the existing cv610 stanza that turn out to never have been exercised on real CV6xx silicon. ## What's in the change set - **`HiSiliconCV6xx.send_firmware(..., uboot_override=)`** + new module helper **`wrap_cv6xx_payload(header_source, payload)`** — clones the 1024-byte U-Boot header from a known-good composite and patches the length field at byte 36 so the bootrom validates and jumps to the wrapped payload. - **`_agent_upload_async`** in `cli/app.py` dispatches on protocol via `find_protocol(chip)`. HiSiliconStandard path unchanged; CV6xx takes a new `-f/--file` (composite for GSL + DDR), wraps the agent, and runs handshake → GSL → DDR → wrapped-agent. Uses the spawn-handshake-before- power-cycle ordering from #109 so it works with Vectis `--power-cycle`. - **`src/defib/agent/client.py`** — `hi3516cv608` → `hi3516cv610` agent alias, mirroring the existing same-family aliases. - **`agent/Makefile`** hi3516cv610 stanza, fixing several latent V3/V4 copy-paste errors: - `LOAD_ADDR` `0x41000000` → `0x41000400` so the agent's link-time addresses match where the bootrom actually jumps after the U-Boot-header offset (`load_addr + 0x400`). - `CRG_BASE` `0x12010000` → `0x11010000`, `WDT_BASE` and `SYSCTRL_REBOOT` likewise moved into `0x11xxxxxx` (matches the UART address and the kernel's `earlycon=pl011,0x11040000` arg). - `FLASH_MEM` `0x14000000` → `0x0F000000` (FMC MMIO window on CV6xx). - New `FMC_CRG_OFFSET=0x3F40` + `FMC_CRG_CLK_BIT=4` because the FMC clock-gate register moved from `CRG+0x144` bit1 to `CRG+0x3F40` bit4. - New `SPI_PIN_BASE=0x10260000` + `SPI_PINS_CV6XX=1` because the SPI pinctrl block moved from `0x100C0000` and the per-pin register offsets + pad-drive values changed. - **`agent/spi_flash.c`** uses the new `FMC_CRG_OFFSET` / `FMC_CRG_CLK_BIT` and `SPI_PIN_BASE` / `SPI_PINS_CV6XX` macros, falling back to V3/V4 defaults when not defined. Source of truth for the CV6xx pinmux + FMC sequencing is the dimerr u-boot fork: `drivers/mtd/fmc_hi3516cv610.c::hi3516cv610_spi_io_config()`. ## Test plan - [x] `uv run pytest tests/ -x -v --ignore=tests/fuzz` — 68/68 pass - [x] `uv run ruff check src/` and `uv run mypy src/defib/` clean - [x] `make -C agent test HOST_CC=gcc` — 5412/5412 agent C tests pass - [x] `make -C agent SOC=hi3516cv610` — builds `agent-hi3516cv610.bin` (18 KiB) - [x] End-to-end on live hi3516cv608 over Vectis (`rfc2217://127.0.0.1:35240` with `DEFIB_POWER_TYPE=vectis`): ``` defib agent upload -c hi3516cv608 -f boot-hi3516cv608-nor.bin \ -p rfc2217://127.0.0.1:35240 --power-cycle → Agent ready! RAM: 0x40000000 Flash: 16384KB JEDEC: a14018 (FM25Q128A 16 MiB) ``` Flash MMIO read from `0x0F000000` returns the burned U-Boot blob byte-for-byte, confirming the SPI path works end-to-end. --------- Co-authored-by: Dmitry Ilyin <widgetii@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
HiSiliconV500,HiSiliconCV6xx) require magic to be on the wire before the bootrom's UART listen window opens (~tens of ms after reset release). The old non-frame-blast path ranpower_cycle → drain_until_silent(0.5s/5s) → handshake_task, losing the race even on healthy boards.hi3516cv608:defib burn --power-cyclesilently missed the window; the~/git/burn/burnreference (open serial → immediately blast → user power-cycles manually) succeeded on the same hardware.src/defib/recovery/session.py: for non-frame-blast with programmatic power, spawn the handshake task first, yield 50 ms so a few magic frames land on the wire, then trigger reset. Dropdrain_until_silenton this path — it was eating the catch window even when ordering was right.HiSiliconStandard0x20→0xAA) keeps the existing reset-first order — it's reactive on bootrom markers, not a blaster.Test plan
uv run pytest tests/test_recovery_session.py tests/test_handshake_resilience.py tests/test_autoboot.py— 25/25 passuv run ruff check src/defib/recovery/session.pycleanuv run mypy src/defib/recovery/session.py --ignore-missing-importscleanhi3516cv608overrfc2217://127.0.0.1:35240withVectisController: log order proves the fix —Starting HiSilicon CV6xx handshakeemits before the Vectis SET-CONTROL DTR/RTS pulse, thenBootROM handshake completefires.defib burn -c hi3516cv608 -f <blob> -p rfc2217://...once a CV608 composite is available (OpenIPC has no prebuilt; handshake stage verified independently above).