Files
MeshCore/scripts/rp2040_application.py
T

50 lines
2.6 KiB
Python

"""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])