From 124a33a484b5fcdbca74a9d9decaf47eebd4ed55 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:13:42 +0300 Subject: [PATCH 1/2] hi3536dv100: drop the nbd8010s variant now that OpenIPC U-Boot handles the E The variant (#150) uploaded the Xiongmai NBD8010S-KL-V2's stock SPL because the OpenIPC SPL reset that board mid-upload. The real cause was in the U-Boot, not the board: Xiongmai's "Hi3536E" is a Hi3536DV100 die that reads 0x02000000 at SYSCTRL+0xEE0 and needs its own PLL, DMC timing and PHY settings. Without them, software DDR training fails and calls reset_cpu(). OpenIPC/u-boot-hi3536dv100#1 now applies those settings on that chip ID and is published as u-boot-hi3536dv100-universal.bin in OpenIPC/firmware latest. This removes the variant, the 0x5840-byte vendor SPL blob and the tests that pinned it. burn keeps honouring SPL_BLOB, which hi3516av300:emmc still uses, and the session test still checks that blob's 0x5000 length. Verified on the NBD8010S-KL-V2 with plain -c hi3536dv100 and the published U-Boot (its SPL window is byte-identical to the build tested on hardware before merging): defib agent upload -c hi3536dv100 -p /dev/ttyUSB2 --power-cycle -> Agent ready; flash 0..0xF80000 CRC 0xa63bd6ca = backup defib burn -c hi3536dv100 -p /dev/ttyUSB2 --power-cycle -> "OpenIPC #", U-Boot 2010.06 (Oct 15 2026 - 17:06:41), DDR PLL CRG+0x2c = 0x209b (the E setting) --- agent/README.md | 10 +++++----- .../profiles/data/hi3536dv100-nbd8010s-spl.bin | Bin 22592 -> 0 bytes src/defib/profiles/data/hi3536dv100.json | 2 +- tests/test_agent_binary_lookup.py | 3 --- tests/test_profiles.py | 13 ------------- tests/test_recovery_session.py | 1 - 6 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 src/defib/profiles/data/hi3536dv100-nbd8010s-spl.bin diff --git a/agent/README.md b/agent/README.md index c5b1b22..b359781 100644 --- a/agent/README.md +++ b/agent/README.md @@ -89,11 +89,11 @@ gk7205v500 also serves gk7205v510/v530. The V500 bootrom has no SPL stage: u-boot-xmedia image (downloaded, or `-f` for your own), whose DDR-init code runs first; the bootrom then executes the slot in place at `0x41007000`. -hi3536dv100 is the DVR/NVR part Xiongmai sells as "Hi3536E". Its UART runs -off a fixed 24 MHz clock, so `SET_BAUD` reaches 921600 with no clock -switching. The OpenIPC SPL for it doesn't bring up DDR on the Xiongmai -NBD8010S-KL-V2 board; use `-c hi3536dv100:nbd8010s` there, which uploads the -board's own SPL instead. +hi3536dv100 also covers the "Hi3536E" that Xiongmai puts in its NVRs, a +variant of the same die. Its UART runs off a fixed 24 MHz clock, so +`SET_BAUD` reaches 921600 with no clock switching. The E variant needs its +own DDR settings; OpenIPC's u-boot-hi3536dv100 applies them when +SYSCTRL+0xEE0 reads 0x02000000, so plain `-c hi3536dv100` works on both. Addresses from [qemu-hisilicon](https://github.com/OpenIPC/LoTool) hardware definitions. diff --git a/src/defib/profiles/data/hi3536dv100-nbd8010s-spl.bin b/src/defib/profiles/data/hi3536dv100-nbd8010s-spl.bin deleted file mode 100644 index e3bb63ec4230eb440707224d60b3d82281aef745..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22592 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 ZSn9(%=tub-o{VuC&ey=F+fyWs|35+T_22*i diff --git a/src/defib/profiles/data/hi3536dv100.json b/src/defib/profiles/data/hi3536dv100.json index 94c3119..85caadd 100644 --- a/src/defib/profiles/data/hi3536dv100.json +++ b/src/defib/profiles/data/hi3536dv100.json @@ -1 +1 @@ -{"name": "hi3536dv100", "PRESTEP0": [4, 224, 45, 229, 36, 0, 159, 229, 36, 16, 159, 229, 0, 16, 128, 229, 32, 0, 159, 229, 32, 16, 159, 229, 4, 16, 128, 228, 0, 224, 128, 229, 4, 240, 157, 228, 239, 190, 173, 222, 239, 190, 173, 222, 239, 190, 173, 222, 60, 1, 5, 18, 78, 87, 79, 68, 64, 1, 5, 18, 117, 106, 105, 122], "DDRSTEP0": [4, 224, 45, 229, 36, 0, 159, 229, 36, 16, 159, 229, 0, 16, 128, 229, 32, 0, 159, 229, 32, 16, 159, 229, 4, 16, 128, 228, 0, 224, 128, 229, 4, 240, 157, 228, 239, 190, 173, 222, 239, 190, 173, 222, 239, 190, 173, 222, 60, 1, 5, 18, 120, 86, 52, 18, 64, 1, 5, 18, 117, 106, 105, 122], "ADDRESS": ["0x04012000", "0x04010500", "0x81000000"], "FILELEN": ["0x0040", "0x5000"], "STEPLEN": ["0x0040", "0x0050"], "variants": {"nbd8010s": {"SPL_BLOB": "hi3536dv100-nbd8010s-spl.bin", "FILELEN": ["0x0040", "0x5840"]}}} \ No newline at end of file +{"name": "hi3536dv100", "PRESTEP0": [4, 224, 45, 229, 36, 0, 159, 229, 36, 16, 159, 229, 0, 16, 128, 229, 32, 0, 159, 229, 32, 16, 159, 229, 4, 16, 128, 228, 0, 224, 128, 229, 4, 240, 157, 228, 239, 190, 173, 222, 239, 190, 173, 222, 239, 190, 173, 222, 60, 1, 5, 18, 78, 87, 79, 68, 64, 1, 5, 18, 117, 106, 105, 122], "DDRSTEP0": [4, 224, 45, 229, 36, 0, 159, 229, 36, 16, 159, 229, 0, 16, 128, 229, 32, 0, 159, 229, 32, 16, 159, 229, 4, 16, 128, 228, 0, 224, 128, 229, 4, 240, 157, 228, 239, 190, 173, 222, 239, 190, 173, 222, 239, 190, 173, 222, 60, 1, 5, 18, 120, 86, 52, 18, 64, 1, 5, 18, 117, 106, 105, 122], "ADDRESS": ["0x04012000", "0x04010500", "0x81000000"], "FILELEN": ["0x0040", "0x5000"], "STEPLEN": ["0x0040", "0x0050"]} \ No newline at end of file diff --git a/tests/test_agent_binary_lookup.py b/tests/test_agent_binary_lookup.py index 52cd2e2..55ad1da 100644 --- a/tests/test_agent_binary_lookup.py +++ b/tests/test_agent_binary_lookup.py @@ -43,9 +43,6 @@ def test_a_variant_suffix_still_resolves(self, monkeypatch, tmp_path): (tmp_path / "agent-hi3516cv500.bin").write_bytes(b"\x00" * 16) assert get_agent_binary("hi3516av300:emmc") is not None - def test_a_board_variant_uses_the_chip_build(self): - assert agent_binary_for("hi3536dv100:nbd8010s") == "hi3536dv100" - def test_every_path_is_absolute(self): assert all(Path(p).is_absolute() for p in _agent_search_path("hi3516cv300")) diff --git a/tests/test_profiles.py b/tests/test_profiles.py index 2e8f330..77cddc9 100644 --- a/tests/test_profiles.py +++ b/tests/test_profiles.py @@ -220,19 +220,6 @@ def test_real_av300_emmc_variant_loads_spl_blob(self): # SPL extracted at 0x5000 (above the gzip boundary at ~0x5220) assert len(p.spl_data) == 0x5000 - def test_real_hi3536dv100_nbd8010s_variant_loads_spl_blob(self): - """The Xiongmai NBD8010S-KL-V2 NVR's DDR differs from the board the - OpenIPC hi3536dv100 SPL was built for, so its variant ships the SPL - from the board's own U-Boot. The blob runs right up to the LZMA - payload at 0x5840, and FILELEN grows to match so none of it is cut.""" - p = load_profile("hi3536dv100:nbd8010s", PROFILES_DIR) - assert p.spl_blob == "hi3536dv100-nbd8010s-spl.bin" - assert p.spl_data is not None - assert p.spl_data[:4] == bytes.fromhex("170800ea") - assert len(p.spl_data) == 0x5840 - assert p.spl_max_size == 0x5840 - assert load_profile("hi3536dv100", PROFILES_DIR).spl_data is None - class TestSplBlob: """SPL_BLOB lets a profile (typically a board variant) ship pre-built diff --git a/tests/test_recovery_session.py b/tests/test_recovery_session.py index fc4387c..1f8c6aa 100644 --- a/tests/test_recovery_session.py +++ b/tests/test_recovery_session.py @@ -57,7 +57,6 @@ async def test_full_standard_session(self): @pytest.mark.asyncio @pytest.mark.parametrize("chip, spl_len", [ - ("hi3536dv100:nbd8010s", 0x5840), # 0x5000 blob under an inherited FILELEN of 0x6000: must not be # padded out to the profile maximum. ("hi3516av300:emmc", 0x5000), From 10ef3f7f52c554a3ada4538109f21c1767cada5b Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:20:52 +0300 Subject: [PATCH 2/2] firmware: re-download a cached hi3536dv100 U-Boot from before the Hi3536E fix Qodo review on this branch. With the nbd8010s variant gone, the Hi3536E relies on the published u-boot-hi3536dv100-universal.bin. But the cache never refreshes: a copy downloaded before OpenIPC/u-boot-hi3536dv100#1 keeps being served under the same name, and its SPL resets a Hi3536E during DDR training. get_cached_path() now runs a per-asset content check and treats an entry that fails it as missing, so burn, install and agent upload fetch the current build. The hi3536dv100 check looks for two of the E variant's DMC timing values (0x34421a12, 0x4144b48d) in the SPL window. They sit in the literal pool whatever registers a rebuild allocates. The last pre-fix published build fails the check, and the current one passes. Explicit -f files are never checked. Offline, the stale copy is still used, with a warning: it works on plain DV100 boards. _download() deletes its destination on failure, so download_firmware() saves the stale bytes first and puts them back. Verified on the NBD8010S-KL-V2: with the pre-fix build seeded into the cache, `defib agent upload -c hi3536dv100 --power-cycle` downloaded the current one and the agent came up READY. --- src/defib/firmware.py | 39 +++++++++++++++++++++++++++++++-- tests/test_firmware.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/defib/firmware.py b/src/defib/firmware.py index 8aa897b..f7d9902 100644 --- a/src/defib/firmware.py +++ b/src/defib/firmware.py @@ -214,6 +214,28 @@ def _cached_file(name: str) -> Path | None: return None +def _has_hi3536e_ddr_config(data: bytes) -> bool: + """Built after OpenIPC/u-boot-hi3536dv100#1, whose SPL programs the + Hi3536E's DDR. Its DMC timing values sit in the SPL's literal pool + whichever registers the compiler picks; older builds lack them, and on + a Hi3536E their DDR training resets the chip.""" + spl = data[:0x5000] + return all(v.to_bytes(4, "little") in spl for v in (0x34421A12, 0x4144B48D)) + + +# Cache entries that a later published build fixed in a way recovery depends +# on. One that fails its check is downloaded again; offline it is still used +# (it works on the boards the fix is not about), with a warning. +_CACHE_CHECKS: dict[str, Callable[[bytes], bool]] = { + "u-boot-hi3536dv100-universal.bin": _has_hi3536e_ddr_config, +} + + +def _is_stale(path: Path) -> bool: + check = _CACHE_CHECKS.get(path.name) + return check is not None and not check(path.read_bytes()) + + def get_cached_path(chip: str, flash_type: str | None = None) -> Path | None: """Get the path to cached firmware, or None if not cached. @@ -236,7 +258,7 @@ def get_cached_path(chip: str, flash_type: str | None = None) -> Path | None: if not candidate: continue path = _cached_file(candidate) - if path is not None: + if path is not None and not _is_stale(path): return path return None @@ -321,9 +343,22 @@ def download_firmware( # Download name = asset_name(chip, flash_type) assert name is not None # firmware_url() is None otherwise + dest = get_cache_dir() / name + # A stale entry is about to be replaced, and _download() deletes dest + # when it fails; keep its bytes to put back if we turn out to be offline. + stale = _cached_file(name) + stale_data = stale.read_bytes() if stale is not None else None try: - return _download(url, get_cache_dir() / name, on_progress) + return _download(url, dest, on_progress) except ConnectionError: + if stale_data is not None: + dest.write_bytes(stale_data) + logger.warning( + "Download of %s failed; using the cached copy, which predates " + "a fix this chip may need", + name, + ) + return dest # A universal image cached before the per-flash-type split still boots # a NOR board, so keep it usable offline. It is never downloaded, and # never used for NAND, whose split layout it carries is retired. diff --git a/tests/test_firmware.py b/tests/test_firmware.py index a8e6a86..91116be 100644 --- a/tests/test_firmware.py +++ b/tests/test_firmware.py @@ -359,3 +359,52 @@ def test_gk7205v5xx_shares_the_v500_donor_cache_entry(self, tmp_path, monkeypatc donor.write_bytes(b"D" * 4096) assert get_cached_path("gk7205v510") == donor assert get_cached_path("gk7205v510", "nand") is None + + +class TestStaleCacheRefresh: + """A cached u-boot-hi3536dv100-universal.bin from before + OpenIPC/u-boot-hi3536dv100#1 resets a Hi3536E during DDR training, so + the cache must not keep serving it once the fixed build is published.""" + + FIXED = b"\x00" * 0x100 + (0x34421A12).to_bytes(4, "little") \ + + (0x4144B48D).to_bytes(4, "little") + b"\x00" * 0x4000 + OLD = b"\x00" * 0x4108 + + def _seed(self, tmp_path, monkeypatch, data): + monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path)) + monkeypatch.setattr("sys.platform", "linux") + path = get_cache_dir() / "u-boot-hi3536dv100-universal.bin" + path.write_bytes(data) + return path + + def test_a_fixed_build_is_used_from_cache(self, tmp_path, monkeypatch): + path = self._seed(tmp_path, monkeypatch, self.FIXED) + assert get_cached_path("hi3536dv100") == path + + def test_an_old_build_is_downloaded_again(self, tmp_path, monkeypatch): + from defib import firmware + + path = self._seed(tmp_path, monkeypatch, self.OLD) + assert get_cached_path("hi3536dv100") is None + + def fake_download(url, dest, on_progress=None): + dest.write_bytes(self.FIXED) + return dest + + monkeypatch.setattr(firmware, "_download", fake_download) + assert download_firmware("hi3536dv100") == path + assert path.read_bytes() == self.FIXED + + def test_offline_the_old_build_is_still_used(self, tmp_path, monkeypatch): + from defib import firmware + + path = self._seed(tmp_path, monkeypatch, self.OLD) + + def offline(url, dest, on_progress=None): + if dest.exists(): + dest.unlink() # what _download() does on failure + raise ConnectionError("offline") + + monkeypatch.setattr(firmware, "_download", offline) + assert download_firmware("hi3536dv100") == path + assert path.read_bytes() == self.OLD