Verify RP2040 capabilities against matching UF2 application
This commit is contained in:
1 parent
9c78ccde57
commit
5df3e4ae53
6 files changed
+253
-2
No files matched your search
@@ -11,6 +11,7 @@ import sys
|
||||
import struct
|
||||
import zipfile
|
||||
import zlib
|
||||
from rp2040_application import read_application as read_rp2040_application
|
||||
from package_meshtext_license import (CAPABILITY as MESHTEXT_CAPABILITY,
|
||||
meshtext_evidence, notice_bytes as meshtext_notice_bytes,
|
||||
notice_record as meshtext_notice_record)
|
||||
@@ -81,6 +82,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--firmware-bin", type=Path)
|
||||
parser.add_argument("--partitions", type=Path)
|
||||
parser.add_argument("--dfu-package", type=Path)
|
||||
parser.add_argument("--uf2-package", type=Path)
|
||||
parser.add_argument(
|
||||
"--expect",
|
||||
action="append",
|
||||
@@ -96,7 +98,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument(
|
||||
"--expect-application", action="append", default=[],
|
||||
metavar="CAPABILITY=TEXT",
|
||||
help="Require TEXT in the packaged firmware.bin or DFU application, not ELF metadata.",
|
||||
help="Require TEXT in the packaged BIN, DFU application, or verified RP2040 UF2 application, not ELF metadata.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
@@ -124,6 +126,10 @@ def linked_gzip_assets(image):
|
||||
|
||||
|
||||
def read_application(args):
|
||||
if args.platform == "RP2040_PLATFORM" or args.uf2_package is not None:
|
||||
if args.platform != "RP2040_PLATFORM" or args.uf2_package is None or args.firmware_bin is None:
|
||||
raise ValueError("RP2040 application checks require both --uf2-package and --firmware-bin")
|
||||
return read_rp2040_application(args.uf2_package, args.firmware_bin)
|
||||
if args.firmware_bin is not None:
|
||||
return args.firmware_bin.read_bytes()
|
||||
if args.dfu_package is not None:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Verify that an RP2040 UF2 carries the exact application offered as BIN."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
|
||||
BLOCK_BYTES = 512
|
||||
PAYLOAD_BYTES = 256
|
||||
FLASH_BASE = 0x10000000
|
||||
FLASH_END = 0x11000000 # RP2040's maximum 16 MiB XIP flash window.
|
||||
FAMILY = 0xE48BFF56
|
||||
|
||||
|
||||
def read_application(uf2_path: Path, bin_path: Path) -> bytes:
|
||||
"""Return real flash bytes, excluding UF2 headers and alignment padding.
|
||||
|
||||
Both public formats must agree. ELF strings, unused sector bytes, and
|
||||
bytes outside the BIN's application extent cannot supply capability proof.
|
||||
The board's ordinary linker/size checks retain its stricter flash limit.
|
||||
"""
|
||||
size = uf2_path.stat().st_size
|
||||
if not size or size % BLOCK_BYTES or size > (FLASH_END - FLASH_BASE) * 2:
|
||||
raise ValueError("RP2040 UF2 has an invalid sector extent")
|
||||
application_size = bin_path.stat().st_size
|
||||
if not 0 < application_size <= FLASH_END - FLASH_BASE:
|
||||
raise ValueError("RP2040 BIN is empty or exceeds the XIP flash window")
|
||||
data = uf2_path.read_bytes()
|
||||
if len(data) != size:
|
||||
raise ValueError("RP2040 UF2 changed during application qualification")
|
||||
count = size // BLOCK_BYTES
|
||||
payload = bytearray()
|
||||
for index in range(count):
|
||||
offset = index * BLOCK_BYTES
|
||||
magic0, magic1, flags, address, length, number, total, family = struct.unpack_from("<8I", data, offset)
|
||||
trailer = struct.unpack_from("<I", data, offset + 508)[0]
|
||||
if (magic0, magic1, trailer) != (0x0A324655, 0x9E5D5157, 0x0AB16F30):
|
||||
raise ValueError("RP2040 UF2 has an invalid sector header/trailer")
|
||||
if flags != 0x2000 or family != FAMILY:
|
||||
raise ValueError("RP2040 UF2 must contain only main-flash blocks with the RP2040 family")
|
||||
if length != PAYLOAD_BYTES or number != index or total != count:
|
||||
raise ValueError("RP2040 UF2 has inconsistent payload lengths or block numbers")
|
||||
if address != FLASH_BASE + index * PAYLOAD_BYTES or address + length > FLASH_END:
|
||||
raise ValueError("RP2040 UF2 has gaps, overlaps, or addresses outside its XIP flash window")
|
||||
payload.extend(data[offset + 32:offset + 32 + length])
|
||||
application = bin_path.read_bytes()
|
||||
tail = payload[application_size:]
|
||||
if len(payload) < application_size or len(tail) >= PAYLOAD_BYTES or payload[:application_size] != application:
|
||||
raise ValueError("RP2040 UF2 application does not match the packaged BIN")
|
||||
if tail and set(tail) not in ({0}, {255}):
|
||||
raise ValueError("RP2040 UF2 has nonuniform application-alignment padding")
|
||||
return bytes(payload[:application_size])
|
||||
Reference in new issue
Block a user