Verify RP2040 capabilities against matching UF2 application

This commit is contained in:
mikecarper committed 2026-10-18 20:40:06 -07:00
1 parent 9c78ccde57
commit 5df3e4ae53
6 files changed
+253 -2

No files matched your search

+7 -1
View File
@@ -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:
+49
View File
@@ -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])