Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,13 @@ Backends: `spi_flash.c` (fmc100), `spi_flash_hisfc350.c` (V1-era parts),
`u-boot-<chip>-universal.bin`; the SoCs in `PER_FLASH_TYPE_UBOOT` (the
u-boot-xmedia ones: hi3516ev200/ev300, hi3518ev300, hi3516dv200,
gk7205v500/v510/v530; and the OpenIPC/u-boot gk7202v300, gk7205v200/v300,
gk7605v100) publish `u-boot-<chip>-nor.bin` and `-nand.bin` instead, so
gk7605v100, hi3516cv300, hi3516ev100 -- ev100 has no NAND build) publish `u-boot-<chip>-nor.bin` and `-nand.bin` instead, so
callers pass `flash_type` (NOR when unknown). On NAND those SoCs install the
UBI-only layout (`NAND_UBI_LAYOUT` in `install/layout.py`: 768k boot, 256k
env, rest ubi), whose mtdparts/bootcmd/bootargs come from the U-Boot default
env; `NAND_LAYOUT` is the legacy split layout for other chips. The
`NOR_SPLIT_SOCS` (gk7205v200 family, hi3516cv6xx, hi3519dv500; keyed by
`NOR_SPLIT_SOCS` (gk7205v200 family, hi3516cv6xx, hi3519dv500,
hi3516cv300/ev100; keyed by
package board, `FIRMWARE_PACKAGE_BOARD` maps cv608/cv610/cv613 to
`hi3516cv6xx` and hi3516dv500 to `hi3519dv500`) install the split NOR
package: one `firmware.bin.<board>` where the boot area ends (0x50000;
Expand Down
23 changes: 19 additions & 4 deletions src/defib/firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,39 @@
# Boot-image SoCs are deliberately absent — see BOOT_IMAGE_VARIANTS below.
AVAILABLE_FIRMWARE: set[str] = {
"hi3516av100", "hi3516av200", "hi3516av300",
"hi3516cv100", "hi3516cv200", "hi3516cv300", "hi3516cv500",
"hi3516cv100", "hi3516cv200", "hi3516cv500",
"hi3516dv100", "hi3516dv300",
"hi3516ev100",
"hi3518av100", "hi3518cv100", "hi3518ev100", "hi3518ev200",
"hi3519v101", "hi3520dv200", "hi3536cv100", "hi3536dv100",
"t40a", "t40n", "t40xp",
}

# SoCs whose OpenIPC U-Boot is published once per flash type as
# u-boot-{chip}-nor.bin / u-boot-{chip}-nand.bin: the u-boot-xmedia SoCs, and
# the gk7205v200 family built from OpenIPC/u-boot (openipc-hisilicon). The
# the gk7205v200 family and hi3516cv300/hi3516ev100 built from OpenIPC/u-boot
# (openipc-hisilicon). The
# NAND build owns the UBI-only layout (768k boot, 256k env, rest ubi) in its
# default environment, so a NOR image must never be installed on NAND or vice
# versa. The old u-boot-{chip}-universal.bin of these SoCs came from retired
# per-SoC repositories (u-boot-hi3516ev200, u-boot-gk7205v200); it is never
# downloaded for them again.
# downloaded for them again. hi3516cv300/hi3516ev100 still have a
# u-boot-*-universal.bin (U-Boot 2010.06), which knows no split layout.
PER_FLASH_TYPE_UBOOT: frozenset[str] = frozenset({
"hi3516ev200", "hi3516ev300", "hi3518ev300", "hi3516dv200",
"gk7205v500", "gk7205v510", "gk7205v530",
"gk7202v300", "gk7205v200", "gk7205v300", "gk7605v100",
"hi3516cv300", "hi3516ev100",
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Resolved
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
Resolved
})

# Of those, the SoCs with no NAND build published: asking for one finds none.
NOR_ONLY_UBOOT: frozenset[str] = frozenset({"hi3516ev100"})

# Of those, the SoCs whose old u-boot-*-universal.bin must never stand in for
# the -nor build, even offline: it is U-Boot 2010.06, which knows no split
# layout, and install writes the split firmware and loads that U-Boot's
# default environment.
NO_UNIVERSAL_FALLBACK: frozenset[str] = frozenset({"hi3516cv300", "hi3516ev100"})

# The firmware package a chip installs, where it is not the chip's own: one
# hi3516cv6xx build serves the whole CV6xx family, and hi3516dv500 is the
# hi3519dv500 die. Packages are openipc.<board>-<flash>-<variant>[-split].tgz.
Expand All @@ -75,6 +86,7 @@
NOR_SPLIT_SOCS: frozenset[str] = frozenset({
"gk7202v300", "gk7205v200", "gk7205v300", "gk7605v100",
"hi3516cv6xx", "hi3519dv500",
"hi3516cv300", "hi3516ev100",
})

# NOR boot area -- (boot partition size, env partition size) -- where it is
Expand Down Expand Up @@ -210,6 +222,8 @@ def asset_name(chip: str, flash_type: str | None = None) -> str | None:
name = CHIP_TO_FIRMWARE.get(base.lower(), base.lower())

if name in PER_FLASH_TYPE_UBOOT:
if kind == "nand" and name in NOR_ONLY_UBOOT:
return None
# Board variants (e.g. ``:emmc``) do not change the per-flash build.
return f"u-boot-{name}-{kind}.bin"

Expand Down Expand Up @@ -415,6 +429,7 @@ def download_firmware(
if (
uses_per_flash_type_uboot(chip)
and normalize_flash_type(flash_type) == "nor"
and package_board(chip) not in NO_UNIVERSAL_FALLBACK
):
legacy = _cached_file(_legacy_cache_name(chip))
if legacy is not None:
Expand Down
39 changes: 32 additions & 7 deletions tests/test_firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def test_aliased_chip(self):
assert "hi3518ev200" in url

def test_url_format(self):
url = firmware_url("hi3516cv300")
assert url == "https://github.com/OpenIPC/firmware/releases/download/latest/u-boot-hi3516cv300-universal.bin"
url = firmware_url("hi3516cv200")
assert url == "https://github.com/OpenIPC/firmware/releases/download/latest/u-boot-hi3516cv200-universal.bin"


class TestHasFirmware:
Expand All @@ -55,10 +55,10 @@ def test_aliased_chip(self):

class TestAvailableFirmware:
def test_at_least_20_chips(self):
assert len(AVAILABLE_FIRMWARE) >= 20
assert len(AVAILABLE_FIRMWARE | PER_FLASH_TYPE_UBOOT) >= 20

def test_common_chips_included(self):
for chip in ["hi3516cv300", "hi3518ev200"]:
for chip in ["hi3516cv200", "hi3518ev200"]:
assert chip in AVAILABLE_FIRMWARE

def test_per_flash_type_socs_are_not_universal(self):
Expand Down Expand Up @@ -185,10 +185,10 @@ def test_empty_cache_file_is_ignored(self, tmp_path, monkeypatch):

class TestClassicChipsUnchanged:
def test_universal_naming_preserved(self):
assert asset_name("hi3516cv300") == "u-boot-hi3516cv300-universal.bin"
assert asset_name("hi3516cv200") == "u-boot-hi3516cv200-universal.bin"

def test_flash_type_does_not_rename_universal_images(self):
assert asset_name("hi3516cv300", "nand") == "u-boot-hi3516cv300-universal.bin"
assert asset_name("hi3516cv200", "nand") == "u-boot-hi3516cv200-universal.bin"

def test_variant_still_ignored_for_classic_socs(self):
assert asset_name("hi3516ev300:emmc") == asset_name("hi3516ev300")
Expand Down Expand Up @@ -272,10 +272,17 @@ class TestPerFlashTypeUBoot:
"hi3516ev200", "hi3516ev300", "hi3518ev300", "hi3516dv200",
"gk7205v500", "gk7205v510", "gk7205v530",
"gk7202v300", "gk7205v200", "gk7205v300", "gk7605v100",
"hi3516cv300",
)
NOR_ONLY = ("hi3516ev100",)

def test_set_matches_the_published_socs(self):
assert set(self.SOCS) == PER_FLASH_TYPE_UBOOT
assert set(self.SOCS + self.NOR_ONLY) == PER_FLASH_TYPE_UBOOT

@pytest.mark.parametrize("soc", NOR_ONLY)
def test_nor_only_has_no_nand_build(self, soc):
assert asset_name(soc) == f"u-boot-{soc}-nor.bin"
assert asset_name(soc, "nand") is None

@pytest.mark.parametrize("soc", SOCS)
def test_name_per_flash_type(self, soc):
Expand Down Expand Up @@ -351,6 +358,24 @@ def offline(url, dest, on_progress=None):
with pytest.raises(ConnectionError):
download_firmware("hi3516ev300", flash_type="nand")

@pytest.mark.parametrize("soc", ["hi3516cv300", "hi3516ev100"])
def test_no_universal_fallback_for_split_2010_06_socs(self, soc, tmp_path, monkeypatch):
# Their universal image is U-Boot 2010.06, which cannot boot the
# split layout install writes: offline, fail rather than flash it.
from defib import firmware

monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path))
monkeypatch.setattr("sys.platform", "linux")
(get_cache_dir() / f"u-boot-{soc}-universal.bin").write_bytes(b"U" * 4096)

def offline(url, dest, on_progress=None):
raise ConnectionError("offline")

monkeypatch.setattr(firmware, "_download", offline)
assert get_cached_path(soc) is None
with pytest.raises(ConnectionError):
download_firmware(soc)

def test_gk7205v5xx_shares_the_v500_donor_cache_entry(self, tmp_path, monkeypatch):
# download_v500_donor() caches u-boot-xmedia's NOR build under the
# same name the OpenIPC/firmware release uses, so either source
Expand Down
3 changes: 2 additions & 1 deletion tests/test_install_nor_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,9 @@ def test_package_board_and_boot_area_are_data():
assert package_board("gk7205v210") == "gk7205v200"
assert "hi3516cv6xx" in split_boards("hi3516cv608")
for chip in ("hi3516cv608", "hi3516cv610", "hi3516cv613", "hi3519dv500",
"hi3516dv500", "gk7205v200"):
"hi3516dv500", "gk7205v200", "hi3516cv300", "hi3516ev100"):
assert supports_nor_split(chip), chip
assert nor_boot_area("hi3516cv300") == (0x40000, 0x10000)
assert not supports_nor_split("hi3516ev300")

assert nor_boot_area("hi3516cv610") == (0x40000, 0x10000)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_nand_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ async def test_nand_install_without_crc32_keeps_legacy_compatibility(
rootfs = b"R" * 2048
with tarfile.open(firmware_tar, "w:gz") as archive:
for name, payload in (
("uImage.hi3516cv300", kernel),
("rootfs.squashfs.hi3516cv300", rootfs),
("uImage.hi3516cv200", kernel),
("rootfs.squashfs.hi3516cv200", rootfs),
):
info = tarfile.TarInfo(name)
info.size = len(payload)
Expand Down Expand Up @@ -260,7 +260,7 @@ async def fake_send_command(

await run_install(
InstallRequest(
chip="hi3516cv300",
chip="hi3516cv200",
firmware_path=str(firmware_tar),
uboot_path=str(uboot),
port="COM15",
Expand Down
8 changes: 5 additions & 3 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,11 @@ <h2 style="color:var(--accent)">Serial Console</h2>
const ddrAddr = parseInt(profile.ADDRESS[0], 16);
const splAddr = parseInt(profile.ADDRESS[1], 16);
const ubootAddr = parseInt(profile.ADDRESS[2], 16);
// Only the u-boot-xmedia images are cut at their payload: the profiles here
// carry no SRAMLIMIT, and on other chips the profile length is what the
// Web UI has always sent safely.
// Only the -nor images (u-boot-xmedia, and hi3516cv300/ev100's
// OpenIPC/u-boot build, whose SPL runs to 0x5400 past the profile's
// 0x4F00) are cut at their payload: the profiles here carry no SRAMLIMIT,
// and on other chips the profile length is what the Web UI has always
// sent safely.
const profileMax = parseInt(profile.FILELEN[1], 16);
const splMax = XMEDIA_SOCS.has(fwNameForChip(chip))
? detectSplSize(firmware, profileMax) : profileMax;
Expand Down
9 changes: 6 additions & 3 deletions web/protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,16 @@ const FW_DIRECT_BASE =
'https://github.com/OpenIPC/firmware/releases/download/latest';

// Asset name → chip, e.g. u-boot-gk7205v300-universal.bin → gk7205v300.
// The u-boot-xmedia SoCs publish per flash type instead (-nor.bin, -nand.bin);
// a recovery loads U-Boot into RAM and needs no flash layout, so it takes the
// -nor one, as `defib burn` does without --nand.
// The u-boot-xmedia SoCs, and hi3516cv300/hi3516ev100 from OpenIPC/u-boot,
// publish per flash type instead (-nor.bin, -nand.bin); a recovery loads
// U-Boot into RAM and needs no flash layout, so it takes the -nor one, as
// `defib burn` does without --nand. The -universal image still published for
// cv300/ev100 is U-Boot 2010.06, which cannot boot their split NOR layout.
const FW_ASSET_RE = /^u-boot-(.+)-(universal|nor)\.bin$/;
const XMEDIA_SOCS = new Set([
'hi3516ev200', 'hi3516ev300', 'hi3518ev300', 'hi3516dv200',
'gk7205v500', 'gk7205v510', 'gk7205v530',
'hi3516cv300', 'hi3516ev100',
]);

/** The published U-Boot a firmware name resolves to. */
Expand Down
14 changes: 14 additions & 0 deletions web/protocol.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,20 @@ describe('fwAssetName', () => {
assert.equal(fwAssetName('hi3516ev300'), 'u-boot-hi3516ev300-nor.bin');
assert.equal(fwAssetName('gk7205v510'), 'u-boot-gk7205v510-nor.bin');
assert.equal(fwAssetName('gk7205v300'), 'u-boot-gk7205v300-universal.bin');
assert.equal(fwAssetName('hi3516cv300'), 'u-boot-hi3516cv300-nor.bin');
assert.equal(fwAssetName('hi3516ev100'), 'u-boot-hi3516ev100-nor.bin');
});
it('takes the -nor build of hi3516cv300/ev100 over their 2010.06 universal', () => {
const idx = parseReleaseAssets({ assets: [
{ name: 'u-boot-hi3516cv300-universal.bin', size: 1, digest: null },
{ name: 'u-boot-hi3516cv300-nor.bin', size: 2, digest: null },
{ name: 'u-boot-hi3516cv300-nand.bin', size: 3, digest: null },
{ name: 'u-boot-hi3516ev100-universal.bin', size: 4, digest: null },
{ name: 'u-boot-hi3516ev100-nor.bin', size: 5, digest: null },
] });
assert.equal(idx.get('hi3516cv300').name, 'u-boot-hi3516cv300-nor.bin');
assert.equal(idx.get('hi3516ev100').name, 'u-boot-hi3516ev100-nor.bin');
assert.equal(idx.size, 2);
});
it('takes each chip\'s own asset and ignores a stale -universal one', () => {
const idx = parseReleaseAssets({ assets: [
Expand Down
Loading