diff --git a/README.md b/README.md index 892c6e6..1e73941 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ or firmware changes may not align exactly with calendar weeks. | Identification | Observed manufacturing codes | What is known | |---|---|---| -| `EF-S7-V1.0.30B` / Ingenic T23 / 8 MiB `ZB25VQ64` family | `0226`, `0526` (two units), `1526` | This is the supported layout on which the failure has been observed. Continue below. | +| `EF-S7-V1.0.30B` / Ingenic T23 / 8 MiB `ZB25VQ64` family | `0226`, `0526` (two units), `1526`, `2526` | This is the supported layout on which the failure has been observed. Continue below. | | `EF-S7-V1.0.30B` / Ingenic T23 / 16 MiB `P25Q128H` family | `4025` | This early layout is materially different. The known erase defect has not been established on it, and the current fix does not apply. | | `EF-S7-V1.0.30D` / likely TX5110 | Not established | This revision is not affected by the known failure. This guide does not apply. | | Any other revision, processor or flash layout | Not established | It has not been investigated. We do not know whether it is affected, and this guide does not apply. | diff --git a/cc2camera/cli.py b/cc2camera/cli.py index 75a28cc..02eb03d 100644 --- a/cc2camera/cli.py +++ b/cc2camera/cli.py @@ -149,8 +149,9 @@ def command_identify_camera(args) -> int: ) print( "30B includes more than one flash-layout variant. The affected 8 MiB " - "variant has been observed with PCB date codes 0226, 0526 and 1526; " - "an earlier 4025 unit uses a different, unsupported 16 MiB layout." + "variant has been observed with PCB date codes 0226, 0526, 1526 and " + "2526; an earlier 4025 unit uses a different, unsupported 16 MiB " + "layout." ) print( "The stream cannot distinguish those variants. Date codes are supporting " diff --git a/cc2camera/image.py b/cc2camera/image.py index 2fc1dd3..823d13f 100644 --- a/cc2camera/image.py +++ b/cc2camera/image.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ Strict, self-contained recovery builder for the Elegoo Centauri Carbon 2 -stock camera firmware family observed in four independent 8 MiB dumps. +stock camera firmware family observed in five independent 8 MiB dumps. The tool: * validates every invariant firmware byte against fingerprints derived @@ -82,7 +82,9 @@ HW_UOID_END = 0x7D206F HW_DATE_START = HW_UOID_END HW_DATE_END = 0x7D2073 -HW_SUPPORTED_DATES = frozenset((date(2026, 3, 2), date(2026, 4, 1))) +HW_SUPPORTED_DATES = frozenset( + (date(2026, 3, 2), date(2026, 4, 1), date(2026, 4, 17)) +) JFFS2_MAGIC = 0x1985 JFFS2_NODE_ACCURATE = 0x2000 @@ -99,8 +101,8 @@ b"dev_config.cfg", } -SERIAL_PATTERN = re.compile(rb"^serial=(12PSSSS[34][A-Z0-9]{28})\n$") -UOID_PATTERN = re.compile(rb"^12PSSSS[34][A-Za-z0-9+/=]{86}$") +SERIAL_PATTERN = re.compile(rb"^serial=(12PSSSS[345][A-Z0-9]{28})\n$") +UOID_PATTERN = re.compile(rb"^12PSSSS[345][A-Za-z0-9+/=]{86}$") # Exact byte ranges shared by every supported HWCONFIG variant. REFERENCE_SEGMENTS = { diff --git a/hardware-recovery/CC2_RECOVERY_VERIFICATION.md b/hardware-recovery/CC2_RECOVERY_VERIFICATION.md index a9f4bae..af21e34 100644 --- a/hardware-recovery/CC2_RECOVERY_VERIFICATION.md +++ b/hardware-recovery/CC2_RECOVERY_VERIFICATION.md @@ -5,7 +5,7 @@ > audit record, not as the current compatibility contract. See > [TECHNICAL_DETAILS.md](TECHNICAL_DETAILS.md) for the current supported > HWCONFIG/identity structures and [TEST_RESULTS.md](TEST_RESULTS.md) for the -> four-camera validation record. +> five-camera validation record. Review date: 2026-08-22 Reviewed tool: `cc2_sig_tool.py` v1.1.0 diff --git a/hardware-recovery/REFERENCE_FINGERPRINTS.json b/hardware-recovery/REFERENCE_FINGERPRINTS.json index f75858a..80e375c 100644 --- a/hardware-recovery/REFERENCE_FINGERPRINTS.json +++ b/hardware-recovery/REFERENCE_FINGERPRINTS.json @@ -75,9 +75,9 @@ "check_range": "0x7D200A-0x7D200C", "date_encoding": "little-endian uint16 year, uint8 month, uint8 day; only physically observed values are accepted", "date_range": "0x7D206F-0x7D2072", - "observed_date_values": ["2026-03-02", "2026-04-01"], - "serial_prefixes": ["12PSSSS3", "12PSSSS4"], - "uoid_prefixes": ["12PSSSS3", "12PSSSS4"] + "observed_date_values": ["2026-03-02", "2026-04-01", "2026-04-17"], + "serial_prefixes": ["12PSSSS3", "12PSSSS4", "12PSSSS5"], + "uoid_prefixes": ["12PSSSS3", "12PSSSS4", "12PSSSS5"] }, "reference_images": [ { @@ -109,9 +109,19 @@ "label": "Fourth camera bricked image", "sha256": "574c71e6572093ea1b65c3ca9c3e5e1dd1e55d71443ca7b282db8ce700c45c85", "state": "stock system, exhausted config, 12PSSSS3 identity, 261-byte HWCONFIG record" + }, + { + "label": "Fifth camera bricked image", + "sha256": "f41e33b9927cfd8a5b11180872a2b940d645ad33ca3d13d5271476e930eb352e", + "state": "stock system, exhausted config, 12PSSSS5 identity, 2026-04-17 HWCONFIG date, 261-byte HWCONFIG record" + }, + { + "label": "Fifth camera permanent full readback", + "sha256": "45acd32f451428513f14ca1238815e52a0508636060ef6973fa3ebafe0228a41", + "state": "audited bashrc patch, rebuilt config, physically boot-tested" } ], - "scope": "Exact CC2 stock-camera firmware family, 12PSSSS3 and 12PSSSS4 identities, and the 256- and 261-byte type-12 payload lengths observed in four independent cameras; unit check/date fields and five-byte extension contents are normalized", + "scope": "Exact CC2 stock-camera firmware family, 12PSSSS3, 12PSSSS4 and 12PSSSS5 identities, and the 256- and 261-byte type-12 payload lengths observed in five independent cameras; unit check/date fields and five-byte extension contents are normalized", "segments": { "boot": { "end_exclusive": "0x040000", diff --git a/hardware-recovery/TECHNICAL_DETAILS.md b/hardware-recovery/TECHNICAL_DETAILS.md index 7ed39f3..3219100 100644 --- a/hardware-recovery/TECHNICAL_DETAILS.md +++ b/hardware-recovery/TECHNICAL_DETAILS.md @@ -112,7 +112,7 @@ compatibility boundary: | Observed layout | Manufacturing-code observations | Evidence | |---|---|---| -| Supported 8 MiB `ZB25VQ64` family | `0226`, `0526` (two units), `1526` | The failure was reported on all four cameras. The `1526` unit and one recovered bricked unit have been physically recovered or protected with this fix. | +| Supported 8 MiB `ZB25VQ64` family | `0226`, `0526` (two units), `1526`, `2526` | The failure was reported on all five cameras. The `1526` unit and two recovered bricked units, including the `2526` unit, have been physically recovered or protected with this fix. | | Early 16 MiB `P25Q128H` family | `4025` | Two supplied full dumps share the 16 MiB partition map. Photographs show the T23, 30B PCB label, manufacturing code and 128-Mbit flash. Their JFFS2 cleanmarkers occur at `0x8000` intervals, matching an erase size handled by their kernel. The known `0x4000` erase mismatch has not been established on this layout. | The early dumps were analyzed privately and remain outside the repository. @@ -120,7 +120,7 @@ The 16 MiB family is not accepted by the builder or installers. A manufacturing code, PCB revision or stream signature never overrides firmware, capacity, partition-map or erase-geometry validation. -This release intentionally accepts only the exact firmware family already verified in four independent camera dumps with different unit identities: +This release intentionally accepts only the exact firmware family already verified in five independent camera dumps with different unit identities: - 8 MiB `ZB25VQ64` SPI NOR - Ingenic T23N @@ -147,8 +147,8 @@ string. JFFS2 appends new nodes on each boot, so two otherwise identical cameras naturally have different raw `config` histories. The HWCONFIG partition also contains a per-unit UOID, an associated three-byte value, and a four-byte little-endian year/month/day field. The date interpretation is based -on the observed `2026-03-02` and `2026-04-01` byte sequences; its exact vendor -semantics remain unknown. +on the observed `2026-03-02`, `2026-04-01`, and `2026-04-17` byte sequences; +its exact vendor semantics remain unknown. The known 256-byte prefix of its type-12 length-prefixed record is validated. Two payload lengths have been directly observed: @@ -165,12 +165,13 @@ The record length exactly includes the five added bytes. The camera's own `ucamera` executable advances over this structure as a little-endian 16-bit type, a little-endian 16-bit payload length, and that many payload bytes. -The observed serial and UOID families begin with either `12PSSSS3` or -`12PSSSS4`. Both retain the same total lengths and remaining character +The observed serial and UOID families begin with `12PSSSS3`, `12PSSSS4`, or +`12PSSSS5`. All retain the same total lengths and remaining character structure. The fourth camera established that the adjacent check field spans -three bytes and that the calendar-shaped field varies by unit. Both fields are -preserved exactly and validated structurally rather than treated as firmware -bytes. +three bytes and that the calendar-shaped field varies by unit. The fifth +camera established the `12PSSSS5` family and `2026-04-17` date value. Both +fields are preserved exactly and validated structurally rather than treated +as firmware bytes. The meaning of the five-byte extension is unknown, so its value is not treated as a firmware invariant. The validator requires record type 12 and one of the @@ -204,7 +205,7 @@ The validator therefore performs the strongest safe equivalent of “100% match - an all-zero or all-`FF` check value is reported as a warning; - the UOID must have an observed prefix and the expected 94-byte structure; - the four-byte date must decode as one of the physically observed calendar - values, `2026-03-02` or `2026-04-01`; + values, `2026-03-02`, `2026-04-01`, or `2026-04-17`; - `config` must contain CRC-valid JFFS2 nodes; - serial-only accepts only the six known filenames unless `--wipe-unknown-config` is explicit; @@ -244,11 +245,11 @@ only for the observed five-byte extension. A future Elegoo/Jovision build, another payload length, or a different record type or known-prefix layout must be analyzed and fingerprinted separately. -Four real unit identities were directly inspected, including two with the -261-byte HWCONFIG record and one `12PSSSS3` identity. Invariant firmware and -the known HWCONFIG prefix must match exactly after unit-field and extension -normalization, while the UOID and serial must each match their observed -structure. +Five real unit identities were directly inspected, including three with the +261-byte HWCONFIG record, one `12PSSSS3` identity, and one `12PSSSS5` +identity. Invariant firmware and the known HWCONFIG prefix must match exactly +after unit-field and extension normalization, while the UOID and serial must +each match their observed structure. All unit-specific HWCONFIG bytes and the recovered serial payload are preserved exactly. Because the proprietary full serial↔UOID derivation is unknown, the tool does not infer or enforce a relationship between those independently diff --git a/hardware-recovery/TEST_RESULTS.md b/hardware-recovery/TEST_RESULTS.md index dcaa392..1409c0c 100644 --- a/hardware-recovery/TEST_RESULTS.md +++ b/hardware-recovery/TEST_RESULTS.md @@ -89,6 +89,38 @@ one representative file, rather than all independently acquired copies, was available in the test environment. This is software validation only; the generated image was not flashed or boot-tested. +## `12PSSSS5` identity and `2026-04-17` HWCONFIG validation + +Validation date: 2026-10-24 + +A fifth physical `EF-S7-V1.0.30B` camera with manufacturing code `2526`, an +Ingenic T23 processor, and an 8 MiB `ZB25VQ64D`-family flash supplied three +consecutive byte-identical reads. The reads were acquired at 1 MHz through a +Pico-compatible `serprog` programmer without moving the SOIC-8 clip. All +copies have SHA-256 +`f41e33b9927cfd8a5b11180872a2b940d645ad33ca3d13d5271476e930eb352e`. +The raw dumps and full unit identifiers remain outside the repository. + +- Boot, kernel, root, every invariant system byte, the stock startup-script + window, and the normalized HWCONFIG fingerprint match the supported firmware + exactly. +- The type-12 record has the observed 261-byte shape and five-byte extension + `00 00 00 08 40`. +- The serial and UOID use the structurally matching `12PSSSS5` family while + retaining their established lengths and character sets. +- The calendar-shaped field at `0x7D206F–0x7D2072` decodes to the newly + observed value `2026-04-17`. +- Config was 98.457% non-`FF`, with 659 CRC-valid nodes, 657 obsolete nodes, + and only a live CRC-valid `serial.cfg`. +- A three-read serial-only recovery build passed complete post-build + validation. It preserved HWCONFIG byte-for-byte and changed only the audited + startup-script window and config partition. +- The generated recovery image has SHA-256 + `45acd32f451428513f14ca1238815e52a0508636060ef6973fa3ebafe0228a41`. + After programming, a complete 8 MiB readback was byte-for-byte identical to + that image. The camera then booted, enumerated normally, and produced a + working video feed. + ## Independent filesystem and patch checks - A separate read-only SquashFS v4/XZ parser extracted `/home/bashrc.sh` from stock and generated images. @@ -110,7 +142,7 @@ generated image was not flashed or boot-tested. bytes passed strict analysis and recovery generation; the generated image retained HWCONFIG byte-for-byte. This is software validation, not a physical flash or boot test of that camera. -- Four distinct real serial/UOID identities were inspected; the available +- Five distinct real serial/UOID identities were inspected; the available full-ROM regression inputs were accepted and preserved. - An additional structurally valid synthetic identity with two confirmation reads (three total): accepted and preserved. @@ -126,6 +158,9 @@ generated image was not flashed or boot-tested. ## Scope of the recovery claim -Software-level recovery is verified: the available bricked dumps are transformed into strict, canonical images with the documented hashes, readable patched startup logic, preserved unit identity, and valid filesystem structures. One output also equals the earlier recorded permanent readback hash. +Software-level recovery is verified: the available bricked dumps are transformed into strict, canonical images with the documented hashes, readable patched startup logic, preserved unit identity, and valid filesystem structures. Two outputs have recorded permanent full readbacks equal to their generated images. -Hardware-level recovery is not newly proven by this review. That requires flashing a physical bricked camera, making a full byte-identical readback, and observing a successful boot/USB enumeration. The tool's generated `FLASHING.txt` makes that the required final validation step. +Hardware-level recovery is verified on the fifth camera by a controlled flash, +complete byte-identical readback, successful boot/USB enumeration, and working +video feed. The tool's generated `FLASHING.txt` retains those checks as the +required final validation sequence for each camera. diff --git a/tests/test_cli.py b/tests/test_cli.py index acb44e1..22f1cef 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -49,7 +49,7 @@ def test_identify_camera_is_read_only_and_reports_known_family(self): hid.assert_not_called() report = output.getvalue() self.assertIn("EF-S7-V1.0.30B", report) - self.assertIn("0226, 0526 and 1526", report) + self.assertIn("0226, 0526, 1526 and 2526", report) self.assertIn("4025", report) self.assertIn("stream cannot distinguish", report) self.assertIn("read-only family-identification aid", report) diff --git a/tests/test_hwconfig_variants.py b/tests/test_hwconfig_variants.py index e1edd5a..057517a 100644 --- a/tests/test_hwconfig_variants.py +++ b/tests/test_hwconfig_variants.py @@ -23,7 +23,7 @@ def hwconfig_record(length: int, extension: bytes = b"") -> bytes: class HwconfigVariantTests(unittest.TestCase): def test_observed_identity_prefixes_are_supported(self): - for family in (b"3", b"4"): + for family in (b"3", b"4", b"5"): with self.subTest(family=family.decode("ascii")): serial = b"serial=12PSSSS" + family + b"A" * 28 + b"\n" uoid = b"12PSSSS" + family + b"A" * 86 @@ -31,7 +31,7 @@ def test_observed_identity_prefixes_are_supported(self): self.assertIsNotNone(tool.UOID_PATTERN.fullmatch(uoid)) def test_unobserved_identity_prefixes_are_rejected(self): - for family in (b"2", b"5"): + for family in (b"2", b"6"): with self.subTest(family=family.decode("ascii")): serial = b"serial=12PSSSS" + family + b"A" * 28 + b"\n" uoid = b"12PSSSS" + family + b"A" * 86 @@ -96,10 +96,10 @@ def test_unit_fields_are_normalized_without_hiding_neighbors(self): observed = bytearray(hwconfig_record(0x105, bytes.fromhex("0000000840"))) observed[tool.HW_CHECK_START:tool.HW_CHECK_END] = b"\x04\x05\x06" observed[tool.HW_UOID_START:tool.HW_UOID_END] = ( - b"12PSSSS3" + b"B" * 86 + b"12PSSSS5" + b"B" * 86 ) observed[tool.HW_DATE_START:tool.HW_DATE_END] = ( - (2026).to_bytes(2, "little") + bytes((3, 2)) + (2026).to_bytes(2, "little") + bytes((4, 17)) ) original = bytes(original) @@ -132,6 +132,11 @@ def test_hwconfig_date_is_validated(self): ) self.assertEqual(tool.decode_hwconfig_date(bytes(image)), "2026-04-01") + image[tool.HW_DATE_START:tool.HW_DATE_END] = ( + (2026).to_bytes(2, "little") + bytes((4, 17)) + ) + self.assertEqual(tool.decode_hwconfig_date(bytes(image)), "2026-04-17") + image[tool.HW_DATE_START:tool.HW_DATE_END] = ( (2026).to_bytes(2, "little") + bytes((2, 30)) )