Skip to content

install: add Hikvision U-Boot migration for HiWatch DS-I203 - #137

Merged
openipc-ai merged 5 commits into
OpenIPC:masterfrom
ArthurKoba:install/stock-uboot-migration
Sep 16, 2026
Merged

openipc-ai merged 5 commits into
OpenIPC:masterfrom
ArthurKoba:install/stock-uboot-migration

Conversation

@ArthurKoba

@ArthurKoba ArthurKoba commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

While porting OpenIPC to a HiWatch DS-I203 (HI3518EV100, IMX122, 256 MiB DDR3, 16 MiB SPI NOR), I found that Defib's normal HiSilicon boot-ROM recovery path is not usable through the camera's exposed UART.

The recovery path available on the stock camera is Hikvision U-Boot 2010.06: Ctrl+U enters the HKVS # console, and loady can receive a replacement U-Boot over YMODEM.

The board also needs the DDR3/256 MiB OpenIPC U-Boot added here: OpenIPC/u-boot-hi3516cv100#6
The matching OpenIPC device profile is here: OpenIPC/builder#159

This PR connects that stock Hikvision recovery path to Defib's normal install flow and hardens the shared install/transport code found during review.

What changed

  • Added a reusable stock-U-Boot bootstrap interface and registry, with Hikvision as the first implementation. This remains separate from BootProtocol: boot protocols describe SoC boot-ROM recovery, while vendors.* starts from an already-running vendor U-Boot.
  • Added Hikvision console handling for warm attach, Ctrl+U, HKVS #, factory-MAC capture, loady/YMODEM, go, and detection of an already-running OpenIPC U-Boot.
  • Stock-console commands that can alter control flow, including loady and go, now use UART echo verification before the terminating CR is sent.
  • Added classic U-Boot variants so hi3518ev100:hiwatch-ds-i203 resolves to u-boot-hi3518ev100-ddr3-256m-universal.bin without changing the generic hi3518ev100 artifact. --uboot remains available for a local override.
  • Chainload now sends the raw U-Boot binary over YMODEM. Padding to the fixed boot partition size is only applied to the flash image.
  • Moved the installer implementation out of cli/app.py into defib.install.
  • Added runtime NOR detection and standard OpenIPC 8/16/32 MiB layouts. --nor-size is a genuine explicit override; the CLI default is now 0, meaning auto-detect.
  • Restored the historical explicit RAM address on generic/download-mode tftpboot; only the stock-U-Boot path uses the short loadaddr form.
  • Added an explicit hi3518ev100 RAM base instead of relying on prefix/fallback ordering.
  • Made TFTP and flash CRC verification fail closed when U-Boot output is missing or malformed.
  • Added full rootfs_data erase verification.
  • Preserved the factory ethaddr across environment replacement. Installer-only values such as DS-I203 phyaddru=3 remain transient; persistent runtime board policy stays in the Builder profile.
  • Persistent environment verification now checks the actual SPI contents after saveenv: Defib re-probes SPI, reads the env partition, computes its data CRC, and compares it to the stored environment CRC.
  • Transport failures, including TransportTimeout, now use the controlled installer failure path and release UART/power/TFTP resources. Pre-TFTP environment verification failures are covered as well.
  • SerialTransport.flush_output() uses a bounded out_waiting drain instead of unbounded tcdrain() or output-buffer purging.
  • RFC2217 cannot provide a reliable remote TX-drain primitive through pyserial, so its flush_output() is intentionally an explicit no-op and never uses PURGE_DATA.
  • Restored --output json error behavior for installer preflight failures.
  • Tightened U-Boot command-result error parsing so unrelated banner text such as optional calibration failures does not look like a destructive flash-command failure.
  • Removed unused environment/layout helpers and duplicate imports, exposed stock-U-Boot selectors through list-chips, and added protocol-level YMODEM tests including retry and final-handshake behavior.
  • Added --stage / --skip-stage install controls in a separate commit for targeted development/recovery validation. The normal no-flag production flow is unchanged; explicit stage selection only performs the requested persistent operations and starts TFTP only when required.

Hardware verification

Final end-to-end acceptance was performed on a physical HiWatch DS-I203:

  • Hi3518EV100
  • Sony IMX122
  • 256 MiB DDR3
  • GD25Q128 16 MiB SPI NOR
  • factory MAC preserved

The final migration run used:

python -m defib install `
  -c hi3518ev100:hiwatch-ds-i203 `
  --firmware "$HOME\Downloads\hiwatch-ds-i203-202609151816.tgz" `
  --uboot "$HOME\Downloads\u-boot-hi3518ev100-ddr3-256m-universal.bin" `
  --wipe-env `
  -p COM15 `
  --tftp-via host `
  --nic "Беспроводная сеть" `
  --host-ip 192.168.1.11 `
  --device-ip 192.168.1.64 `
  --no-final-reset `
  -d

The run completed:

  • genuine Hikvision U-Boot 2010.06 autoboot interruption and HKVS # entry;
  • verified UART echo for loady and go;
  • raw U-Boot YMODEM transfer;
  • OpenIPC U-Boot chainload;
  • 16 MiB NOR detection;
  • CRC-verified U-Boot, kernel and rootfs flashing;
  • verified rootfs_data erase;
  • explicit environment erase and mandatory internal reset;
  • compiled OpenIPC defaults loading;
  • detected-layout mtdparts persistence;
  • factory MAC restoration;
  • saveenv;
  • SPI re-probe and physical environment readback;
  • stored/data CRC comparison (SPI CRC 7A58A0B5);
  • final device reset intentionally skipped, leaving the board at the OpenIPC U-Boot prompt.

The environment-only stage path was also exercised against an already-running OpenIPC U-Boot while validating the post-reset SPI re-probe and persistent CRC check.

Earlier complete device-profile acceptance also confirmed 256 MiB physical RAM, the intended 128 MiB Linux / 128 MiB MMZ split, PHY address 3 / MDIO0, IMX122, and Majestic startup.

Testing

Final local gate on the published tree:

  • 873 passed, 10 skipped, 4 deselected in the full Python suite.
  • The four deselected tests are the already-reproduced Windows host baseline cases: localized netsh decoding and Windows symlink privilege.
  • 16 passed in tests/fuzz.
  • Ruff 0.15.8 from the repository lockfile: clean on src/ and tests/.
  • strict mypy: clean across 77 source files.
  • git diff --check: clean.
  • Targeted installer/UART/YMODEM/environment/stage regression suites also pass.

The first three commits were also rewritten to include the rationale, verification commands, and hardware evidence required by CLAUDE.md.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Hikvision U-Boot migration for HiWatch DS-I203

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Adds reusable Hikvision stock U-Boot chainloading for HiWatch DS-I203 via YMODEM.
• Refactors installation with flash detection, verified writes, environment migration, and MAC
 preservation.
• Adds dedicated U-Boot resolution, UART safety, documentation, and end-to-end contract tests.
Diagram

graph TD
  CLI["Install CLI"] --> RES["Artifact Resolver"] --> MODE{"Recovery Path"}
  MODE -->|Stock| HIK["Hikvision Bootstrap"] --> SHELL["OpenIPC Shell"] --> INST["TFTP Installer"] --> NOR["SPI NOR"]
  MODE -->|Boot ROM| ROM["Boot ROM"] --> SHELL
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Separate migration command
  • ➕ Clearly separates stock bootloader replacement from normal firmware installation.
  • ➕ Allows operators to validate the chainloaded U-Boot before starting flash operations.
  • ➖ Duplicates target selection, transport setup, and recovery handling.
  • ➖ Creates a manual handoff where transient network settings or preserved identity could be lost.
  • ➖ Risks divergent verification and safety behavior between migration and installation commands.
2. Use an external YMODEM package
  • ➕ Could reduce ownership of packet framing and protocol edge cases.
  • ➕ May provide broader interoperability with other YMODEM receivers.
  • ➖ Common implementations are synchronous and tied directly to file or pyserial APIs.
  • ➖ Would require adapters for Defib's asynchronous Transport abstraction and progress events.
  • ➖ Hikvision-specific timing, stray-byte handling, and flush semantics would still need custom logic.

Recommendation: Keep the PR's integrated installer and reusable bootstrap registry. It preserves one verified flashing pipeline for both recovery paths, confines Hikvision behavior to a vendor implementation, and keeps board-specific metadata declarative. The focused async YMODEM sender is justified by Defib's transport abstraction and hardware-specific timing requirements; a separate migration command would add operational handoffs without improving safety.

Files changed (25) +3449 / -814

Enhancement (9) +2146 / -8
firmware.pyResolve dedicated classic U-Boot board variants +35/-8

Resolve dedicated classic U-Boot board variants

• Maps the DS-I203 selector to its DDR3/256 MiB U-Boot artifact. Prevents registered variants from falling back to incompatible chip-wide cache entries and improves unknown-variant errors.

src/defib/firmware.py

layout.pyCentralize flash layouts and verification helpers +177/-0

Centralize flash layouts and verification helpers

• Defines standard NOR and NAND layouts, NOR-capacity parsing, MTD strings, alignment, erased-region CRCs, command-error detection, and verified runtime environment assignment.

src/defib/install/layout.py

orchestrator.pyImplement vendor-aware verified installation orchestration +1145/-0

Implement vendor-aware verified installation orchestration

• Moves the complete install workflow from the CLI into a dedicated module and integrates registered stock-U-Boot bootstraps. Adds runtime NOR selection, verified environment and flash operations, rootfs_data cleanup, factory-MAC preservation, persistent environment migration, and optional final reset.

src/defib/install/orchestrator.py

ymodem.pyAdd asynchronous YMODEM sender +215/-0

Add asynchronous YMODEM sender

• Implements CRC16 packet framing, metadata headers, 1 KiB data packets, retries, cancellation handling, EOT negotiation, progress reporting, and transfer statistics over Defib transports.

src/defib/recovery/ymodem.py

uboot_env.pyAdd environment migration and MAC-selection helpers +84/-0

Add environment migration and MAC-selection helpers

• Adds full printenv parsing, bounded variable expansion, semantic value comparison, and installation MAC selection. Vendor migrations can preserve factory identity and safely refuse generated replacements.

src/defib/uboot_env.py

__init__.pyExpose stock-U-Boot bootstrap interfaces +10/-0

Expose stock-U-Boot bootstrap interfaces

• Creates the vendor bootstrap package API and exports its protocol, result type, and factory.

src/defib/vendors/init.py

base.pyDefine reusable stock-U-Boot bootstrap protocol +35/-0

Define reusable stock-U-Boot bootstrap protocol

• Introduces the bootstrap result model and asynchronous protocol used to reach an OpenIPC U-Boot shell while preserving selected environment values.

src/defib/vendors/base.py

hikvision.pyImplement Hikvision stock U-Boot chainloading +360/-0

Implement Hikvision stock U-Boot chainloading

• Handles warm and cold console detection, Ctrl+U interruption, HKVS commands, factory-MAC capture, YMODEM transfer, go execution, and existing OpenIPC detection. Centralizes production timing with scalable test timing.

src/defib/vendors/hikvision.py

registry.pyRegister the DS-I203 Hikvision migration target +85/-0

Register the DS-I203 Hikvision migration target

• Adds reusable bootstrap factories and exact selector metadata. Registers the DS-I203 load address, dedicated Hikvision handler, and transient PHY address required during installation.

src/defib/vendors/registry.py

Bug fix (3) +121 / -12
flashdump.pyVerify legacy UART command echo before execution +111/-10

Verify legacy UART command echo before execution

• Adds optional byte-by-byte command echo verification with cancellation and retry handling. The terminating carriage return is withheld until the entire U-Boot command is acknowledged correctly.

src/defib/flashdump.py

rfc2217.pyDrain RFC2217 output instead of discarding it +5/-1

Drain RFC2217 output instead of discarding it

• Changes flush_output to wait for queued transmission through pyserial. This prevents bootloader commands and YMODEM frames from being truncated.

src/defib/transport/rfc2217.py

serial.pyDrain serial output instead of discarding it +5/-1

Drain serial output instead of discarding it

• Runs pyserial flush asynchronously rather than resetting the output buffer. Queued UART traffic now completes before protocol processing continues.

src/defib/transport/serial.py

Refactor (5) +145 / -787
app.pyDelegate install execution to the install package +51/-786

Delegate install execution to the install package

• Replaces the embedded installer with InstallRequest construction and run_install delegation. Adds U-Boot override, automatic NOR detection defaults, and final-reset control while retaining compatibility aliases for private layout imports.

src/defib/cli/app.py

__init__.pyExpose the modular installation API +6/-0

Expose the modular installation API

• Exports InstallRequest and run_install as the public entry points for installation orchestration.

src/defib/install/init.py

firmware.pyExtract and validate OpenIPC firmware bundles +59/-0

Extract and validate OpenIPC firmware bundles

• Introduces a typed firmware bundle loader that reads kernel and rootfs payloads and validates matching MD5 entries in one archive pass.

src/defib/install/firmware.py

model.pyDefine immutable installer input model +28/-0

Define immutable installer input model

• Adds InstallRequest to carry CLI inputs, including U-Boot override, NOR auto-detection, TFTP routing, and final-reset behavior.

src/defib/install/model.py

loader.pyNormalize protocol import ordering +1/-1

Normalize protocol import ordering

• Reorders local protocol imports without changing profile discovery behavior.

src/defib/profiles/loader.py

Tests (6) +900 / -1
test_ds_i203_final_contract.pyCover complete DS-I203 migration contracts +560/-0

Cover complete DS-I203 migration contracts

• Tests selector metadata, artifact resolution, warm and cold attach states, chainload failures, and missing factory identity. Simulates the full 16 MiB NOR installation to verify padding, offsets, erasure, MAC restoration, and policy isolation.

tests/test_ds_i203_final_contract.py

test_firmware.pyTest classic board-specific U-Boot resolution +32/-1

Test classic board-specific U-Boot resolution

• Verifies DS-I203 artifact and URL selection, preservation of the generic artifact, unknown-variant rejection, and cache isolation from incompatible universal images.

tests/test_firmware.py

test_install_flash_helpers.pyTest layout detection and flash safety helpers +118/-0

Test layout detection and flash safety helpers

• Covers alignment, NOR probe parsing, standard layouts, MTD strings, erased-region CRCs, flash-error recognition, and verified runtime environment retries.

tests/test_install_flash_helpers.py

test_uart_command_integrity.pyTest UART command echo verification +89/-0

Test UART command echo verification

• Verifies that commands execute only after complete echoes, corrupted attempts are cancelled before Enter, and consoles without acknowledgements fail safely.

tests/test_uart_command_integrity.py

test_uboot_env.pyTest environment parsing and identity preservation +81/-0

Test environment parsing and identity preservation

• Covers full environment parsing, reference expansion, semantic comparisons, factory-MAC precedence, vendor migration refusal, and generic rescue-MAC generation.

tests/test_uboot_env.py

test_ymodem.pyTest YMODEM packet primitives +20/-0

Test YMODEM packet primitives

• Validates the CRC16 reference vector, 1 KiB packet framing, sequence complement, and block-zero filename and size metadata.

tests/test_ymodem.py

Documentation (2) +137 / -6
README.mdDocument stock-U-Boot installation and automatic NOR layouts +30/-6

Document stock-U-Boot installation and automatic NOR layouts

• Updates the installation workflow to cover registered stock-U-Boot bootstraps, runtime NOR detection, and board-specific U-Boot variants. Adds a DS-I203 invocation and clarifies ownership of bootloader, installer, and device-profile policy.

README.md

hiwatch-ds-i203.mdDocument the DS-I203 migration contract +107/-0

Document the DS-I203 migration contract

• Describes Hikvision console entry, YMODEM chainloading, U-Boot artifact selection, standard 16 MiB layout, environment migration, and policy boundaries. Records the hardware acceptance results.

docs/boards/hiwatch-ds-i203.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Protocol tools miss Hikvision recovery 📘 Rule violation ⚙ Maintainability
Description
HikvisionUBootBootstrap implements the UART bootstrap independently of BootProtocol, and
_BOOTSTRAPS places it in a separate vendor registry without @register. Any command or extension
using find_protocol or list_protocols sees the generic chip handler rather than the Hikvision
recovery implementation, while only the install path knows about the parallel registry.
Code

src/defib/vendors/registry.py[R31-33]

+_BOOTSTRAPS: dict[str, BootstrapFactory] = {
+    "hikvision": HikvisionUBootBootstrap,
+}
Evidence
Compliance rule 3 requires new UART byte-stream recovery protocols to subclass BootProtocol and
use the supported registration mechanism. The Hikvision class communicates over a byte-stream
transport but is selected from the newly introduced _BOOTSTRAPS dictionary instead of the protocol
registry.

CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly: CLAUDE.md: UART Protocol Plugins Must Be Registered Correctly
src/defib/vendors/hikvision.py[67-79]
src/defib/vendors/registry.py[31-40]
src/defib/protocol/registry.py[22-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Hikvision UART recovery implementation bypasses the repository's `BootProtocol` registration and discovery mechanism by using a separate vendor registry.
## Fix Focus Areas
- src/defib/vendors/hikvision.py[67-360]
- src/defib/vendors/registry.py[31-48]
## Recommended Fix
Expose the Hikvision recovery implementation through a `BootProtocol`-compatible class, decorate it with `@register`, and add a `defib.protocols` entry point if packaged discovery requires one. Preserve any stock-environment metadata through an explicit protocol result extension rather than a separate discovery system.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Stock installs erase saved settings ✓ Resolved 📘 Rule violation ≡ Correctness
Description
run_install enters the environment-migration block whenever has_stock_uboot and not nand and
issues sf erase without checking the default-false wipe_env option. Every stock-U-Boot NOR
migration therefore removes persistent variables other than the few values reconstructed later, even
when the user did not request an environment wipe.
Code

src/defib/install/orchestrator.py[R978-980]

+                env_erase_resp = await _cmd(
+                    f"sf erase 0x{env_off:x} 0x{env_size:x}", timeout=30.0
+                )
Evidence
Compliance rule 7 requires the environment partition to remain preserved unless explicitly wiped.
The request model defaults wipe_env to false, but the new stock migration condition does not
consult it before erasing the environment partition.

CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved
src/defib/install/model.py[22-24]
src/defib/install/orchestrator.py[944-980]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Stock-U-Boot NOR migrations erase the persistent environment even when `wipe_env` remains at its default false value, contrary to the destructive-flash preservation requirement.
## Fix Focus Areas
- src/defib/install/orchestrator.py[944-980]
- src/defib/install/model.py[22-24]
## Recommended Fix
Preserve the environment partition by default. Gate the erase-and-default migration behind explicit user consent such as `wipe_env`, or introduce a separately named migration option that clearly warns which persistent values will be discarded.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing checksums still permit flashing ✓ Resolved 📘 Rule violation ☼ Reliability
Description
tftp_and_flash() compares the transferred image and NOR readback checksums only when each crc32
response matches ==> XXXXXXXX, so absent, truncated, malformed, or changed output skips
verification. Because send_command() can return empty or partial output on timeout, the image can
proceed through partition erasure and writing and ultimately reach the success path without either
checksum being validated.
Code

src/defib/install/orchestrator.py[R746-748]

+                m = re_mod.search(r"==>\s*([0-9a-fA-F]{8})", resp)
+                if m:
+                    ram_crc = int(m.group(1), 16)
Evidence
Both checksum checks are guarded by if m and have no failure branch when the regular expression
does not match, while the helper reports success afterward. Since the command reader returns
whatever output it has collected when its timeout expires, missing or incomplete CRC output is
reachable; the stricter environment and rootfs_data checks show the intended behavior by
explicitly rejecting a missing match, consistent with compliance rule 7's requirement for CRC32
verification by default.

CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved: CLAUDE.md: Destructive Flash Safety Properties Must Be Preserved
src/defib/install/orchestrator.py[740-761]
src/defib/install/orchestrator.py[802-829]
src/defib/install/orchestrator.py[740-756]
src/defib/install/orchestrator.py[817-834]
src/defib/flashdump.py[290-314]
src/defib/install/orchestrator.py[998-1010]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`tftp_and_flash()` silently skips RAM and NOR readback verification when U-Boot's `crc32` response lacks a parseable checksum. Empty, truncated, malformed, or changed command output must fail the installation rather than allowing destructive writes or reporting an unverified image as successful.
## Fix Focus Areas
- src/defib/install/orchestrator.py[742-761]
- src/defib/install/orchestrator.py[817-831]
## Recommended Fix
Require each CRC response to contain the expected checksum format and raise a controlled installation error when parsing fails. Reject a missing or mismatched RAM checksum before erasing flash, and reject a missing or mismatched NOR readback checksum before reporting success, applying the same mandatory-match behavior already used for the environment and `rootfs_data` CRC checks.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View action required (1)
4. Installer lines exceed project limit ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
run_install adds multiple statements longer than the configured 100-character limit, including the
device_label assignment on line 209. These unexcepted lines make the changed installer
inconsistent with the repository's required Python formatting contract and complicate later
automated formatting changes.
Code

src/defib/install/orchestrator.py[209]

+                device_label = port_basename.removeprefix("uart-") if port_basename.startswith("uart-") else port_basename
Evidence
Compliance rule 1 explicitly limits changed Python lines to 100 characters, matching the repository
configuration. Added installer lines such as line 209 exceed that limit without an exception.

CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking: CLAUDE.md: Python Changes Must Pass Repository Linting and Strict Type Checking
pyproject.toml[60-65]
src/defib/install/orchestrator.py[198-212]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The newly added installer contains lines longer than the repository's required 100-character maximum without an allowed exception.
## Fix Focus Areas
- src/defib/install/orchestrator.py[198-212]
- src/defib/install/orchestrator.py[726-732]
- src/defib/install/orchestrator.py[805-808]
## Recommended Fix
Wrap overlong assignments, exception clauses, calls, and formatted output using parentheses and one argument or expression component per line, then run the repository Ruff and strict mypy commands over all changed Python files.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Valid flash probes abort migration ✓ Resolved 🐞 Bug ≡ Correctness
Description
detect_nor_size_mb() omits recognized formats such as SPI Nor total size: 16MB and `SF: Detected
... total 16MB. When sf probe 0 emits one of these forms without a separate Chip:` field,
stock-U-Boot migration cannot select a layout and exits unless the user supplies an override.
Code

src/defib/install/layout.py[R154-158]

+    patterns = (
+        (r"\bChip:\s*(\d+)\s*MB\b", 1),
+        (r"\bspi\s+size:\s*(\d+)\s*MB\b", 1),
+        (r"\b(\d+)\s+MiB\b[^\n]*(?:hi_sfc|spi)", 1),
+        (r"\b(\d+)\s+KiB\b[^\n]*(?:hi_sfc|spi)", 1024),
Evidence
The installer aborts stock migrations when the new parser returns None. Existing repository tests
and the general flash parser explicitly recognize standalone total size formats that none of the
new parser's four expressions match.

src/defib/install/orchestrator.py[519-545]
src/defib/install/layout.py[150-168]
src/defib/flashdump.py[341-360]
tests/test_flashdump.py[139-141]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new NOR capacity parser recognizes fewer U-Boot probe formats than the repository's existing flash parser. Valid output containing `total size` or `total NMB` can therefore make automatic stock-U-Boot migration abort.
## Fix Focus Areas
- src/defib/install/layout.py[150-168]
- src/defib/install/orchestrator.py[528-545]
## Recommended Fix
Reuse the existing flash-size parser with byte-to-MiB conversion, or broaden `detect_nor_size_mb()` to recognize the repository's documented `SPI Nor total size` and `SF: Detected ... total NMB` forms while retaining protection against matching RAM sizes. Add focused tests for each existing format.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Oversized overrides crash the installer ✓ Resolved 🐞 Bug ☼ Reliability
Description
run_install() passes the newly supported local U-Boot payload directly to pad_to_size() without
handling its ValueError. If an override exceeds the boot partition, the command terminates with an
uncaught traceback instead of a controlled install error explaining that the artifact cannot fit.
Code

src/defib/install/orchestrator.py[R172-175]

+    # Install writes a fixed boot partition. Release/override payloads may be
+    # shorter, so preserve the existing installer contract by padding the tail
+    # with erased flash bytes before chainload and flashing.
+    uboot_data = pad_to_size(uboot_raw, b_sz)
Evidence
The PR adds a user-provided U-Boot path and reads its bytes without constraining their size.
pad_to_size() explicitly raises when those bytes exceed the target, and this call is outside
either of run_install()'s existing ValueError handlers.

src/defib/cli/app.py[2151-2158]
src/defib/install/orchestrator.py[152-175]
src/defib/firmware.py[183-195]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `--uboot` override accepts a local artifact, but an artifact larger than the selected boot partition raises an uncaught `ValueError` from `pad_to_size()`. The installer should reject this user input through its normal CLI error path.
## Fix Focus Areas
- src/defib/install/orchestrator.py[152-175]
- src/defib/firmware.py[183-195]
## Recommended Fix
Catch the size-validation `ValueError` around `pad_to_size()`, print a concise error containing the artifact and partition sizes, and raise `typer.Exit(1)` from the exception. Preserve `pad_to_size()` itself as the reusable validation boundary.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/defib/vendors/registry.py
Comment thread src/defib/install/orchestrator.py
Comment thread src/defib/install/orchestrator.py Outdated
Comment thread src/defib/install/orchestrator.py Outdated
Comment thread src/defib/install/layout.py
Comment thread src/defib/install/orchestrator.py Outdated
Move the install implementation out of the CLI into reusable defib.install modules and add a separate stock-U-Boot bootstrap abstraction for migrations that start from an already-running vendor bootloader rather than the SoC boot ROM.

Add the vendor bootstrap registry, the reusable Hikvision U-Boot console/YMODEM implementation, firmware artifact override support, standard NOR layout helpers, environment helpers, and installer plumbing needed for a board-specific stock migration. Keep this mechanism separate from BootProtocol because BootProtocol describes the SoC boot-ROM recovery dialect, while vendors.* starts from a running vendor U-Boot.

Verification:
python -m pytest tests/test_install_flash_helpers.py tests/test_uart_command_integrity.py tests/test_uboot_env.py tests/test_ymodem.py -q

Hardware verification is not standalone for this commit because it intentionally does not register a concrete board selector. The following HiWatch DS-I203 commit binds this reusable layer to physical hardware and carries the end-to-end stock-U-Boot migration evidence.
Bind the reusable stock-U-Boot migration path to the physical HiWatch DS-I203.

Register hi3518ev100:hiwatch-ds-i203 as a Hikvision stock-U-Boot target, map it to the DDR3/256 MiB OpenIPC U-Boot variant, use the safe 0x81000000 chainload address, and apply transient phyaddru=3 only for installer TFTP. Add the board documentation and migration contract tests covering the stock Hikvision console, existing-OpenIPC detection, factory MAC preservation, NOR layout ownership, and release artifact selection.

Verification:
python -m pytest tests/test_ds_i203_final_contract.py tests/test_firmware.py -q

Hardware verification used a physical HiWatch DS-I203 with Hi3518EV100, IMX122, 256 MiB DDR3 and 16 MiB GD25Q128 SPI NOR. Defib entered Hikvision U-Boot 2010.06 through Ctrl+U / HKVS, chainloaded the DDR3/256 MiB OpenIPC U-Boot over YMODEM, detected the 16 MiB NOR layout, flashed OpenIPC firmware, preserved the factory MAC, and booted the matching DS-I203 firmware profile successfully.

The validated path was:
Hikvision U-Boot -> Ctrl+U / HKVS -> YMODEM OpenIPC U-Boot -> TFTP flash -> environment migration -> OpenIPC boot.
Harden the stock-U-Boot migration path so destructive install steps fail closed instead of continuing on ambiguous or invalid state.

Require explicit --wipe-env for registered stock-U-Boot NOR migrations, reject oversized U-Boot overrides through the normal CLI error path, broaden NOR-size parsing for valid U-Boot probe formats, and require parseable CRC values for both TFTP RAM verification and flash readback instead of silently skipping verification when output is incomplete.

Keep vendor migrations from erasing the environment as part of the U-Boot partition write, preserve the captured factory ethaddr across the explicit environment migration, and add focused regression coverage for the destructive preflight and CRC failure cases.

Verification:
python -m pytest tests/test_ds_i203_final_contract.py tests/test_install_flash_helpers.py -q

Hardware verification used the HiWatch DS-I203 stock-U-Boot migration path with explicit --wipe-env. The board completed the migration on 16 MiB NOR, preserved the factory MAC and reached OpenIPC successfully; later review commits further strengthen transport and persistent-environment verification without changing this commit's fail-closed contract.
Fix the shared installer and transport issues found during review of the HiWatch DS-I203 stock-U-Boot migration.

Restore explicit TFTP RAM addressing for generic installs, bound serial TX draining, keep RFC2217 flush semantics explicit, verify persistent SPI environment contents after saveenv, handle TransportError cleanly, and protect Hikvision loady/go with UART echo verification.

Also make --nor-size a true override, add the hi3518ev100 RAM base, restore JSON error output, harden U-Boot error parsing, remove dead helpers, use raw U-Boot for YMODEM and pad only for flash, and expand YMODEM/transport/install regression coverage.

Hardware verification:
python -m defib install -c hi3518ev100:hiwatch-ds-i203 --firmware $HOME\Downloads\hiwatch-ds-i203-202609151816.tgz --uboot $HOME\Downloads\u-boot-hi3518ev100-ddr3-256m-universal.bin --wipe-env -p COM15 --tftp-via host --nic "Беспроводная сеть" --host-ip 192.168.1.11 --device-ip 192.168.1.64 --no-final-reset -d

The DS-I203 completed stock Hikvision U-Boot -> OpenIPC U-Boot migration on hardware, detected 16 MiB NOR, flashed and CRC-verified U-Boot/kernel/rootfs/rootfs_data, preserved the factory MAC, saved the environment, re-probed SPI, and physically verified the environment CRC before leaving the board at the OpenIPC prompt.
Add explicit install-stage selection for development, recovery, and targeted validation without replaying the complete production install.

--stage selects an exact set from uboot, kernel, rootfs, rootfs-data, env, and reset. --skip-stage subtracts stages from the normal production sequence, and the two forms cannot be combined. Explicit stage selection performs reset only when reset is selected.

Keep the default install path unchanged. Start TFTP only when selected stages need image transfer, keep environment erase coupled to the env stage, and reject unsafe partial persistent installs when a genuine stock U-Boot was only chainloaded temporarily.

Regression coverage verifies default and explicit stage resolution, skip semantics, env-only execution without TFTP or partition writes, reset selection, invalid combinations, and stock-migration safety.

Verification:
python -m pytest tests/test_install_stages.py -q

The stage controls were also exercised on the HiWatch DS-I203 with an env-only run against an already-running OpenIPC U-Boot. That run completed the environment migration and its physical SPI CRC verification and was used while validating the required post-reset sf probe handling.
@ArthurKoba
ArthurKoba force-pushed the install/stock-uboot-migration branch from aa18898 to ab20fbe Compare September 15, 2026 20:08
@ArthurKoba

Copy link
Copy Markdown
Contributor Author

Addressed the requested review changes and force-pushed the rewritten five-commit series.

The original three commits now have full bodies with rationale, verification commands, and hardware evidence. Review remediation is isolated in install: address stock migration review findings, with selectable install stages kept in a separate final commit.

The final tree was revalidated on the physical DS-I203 after the review fixes, including UART echo verification for loady/go, raw YMODEM chainload, 16 MiB NOR detection, flash CRC verification, factory-MAC preservation, and physical SPI environment CRC verification after saveenv.

Local final gate: 873 passed / 10 skipped / 4 known Windows-baseline tests deselected, fuzz 16 passed, locked Ruff 0.15.8 clean, strict mypy clean, and git diff --check clean.

@openipc-ai
openipc-ai merged commit 52582f0 into OpenIPC:master Sep 16, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant