#!/usr/bin/env python3 """Re-homing acceptance test: a pureboot image mistakenly programmed at address 0 of a patched-vector chip (a raw .bin handed to a programmer, which defaults to offset 0) must still be a working loader — position-independent, guarding its accidental slot — and the ordinary --update-loader flow must re-home a build into the canonical top slot: the staging install and the word-0 redirect both run from copies whose slots are not page 0's, so the running-slot guard never blocks the flow. Flashing an application through the healed resident then overwrites the stale copy, vector surgery included, and the banner proves the launch. Usage: pbrehome.py """ import os import sys def fail(message): print(f"FAIL: {message}") sys.exit(1) def main(): (device_bin, elf, update_bin, mcu, hz, base_hex, page, baud, app_bin, tool, workdir) = sys.argv[1:] base, baud = int(base_hex, 0), int(baud) sys.path.insert(0, os.path.dirname(os.path.abspath(tool))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import pbsim import pureboot as pb os.makedirs(workdir, exist_ok=True) dump = os.path.join(workdir, "dump.bin") state = os.path.join(workdir, "rehome.pbstate") if os.path.exists(state): os.unlink(state) # The runner's base argument places the firmware: 0 is the misplaced # flash, and the patched-vector reset at word 0 lands in it directly. device = pbsim.Device(device_bin, elf, mcu, hz, "0x0", int(page), baud, dump, reset_hex="0") try: port = pb.Port(device.pty, baud) loader = pb.Loader(port) info = loader.connect(25) if info.base != base: fail(f"the misplaced copy reports base {info.base:#06x} — the info block must stay canonical") # The accidental slot still guards itself; the canonical base slot # must be writable from it (that is what re-homing rides on). before = loader.read_flash(0, info.page) loader.write_page(0, bytes(info.page)) if loader.read_flash(0, info.page) != before: fail("the misplaced copy's guard let its own slot change") # The ordinary update flow re-homes the build into the top slot. pb.op_update_loader(loader, 25, update_bin, state, None) update = open(update_bin, "rb").read() if loader.read_flash(base, len(update)) != update: fail("the canonical slot does not hold the update image") # An application flashed through the healed resident overwrites the # stale copy (surgery included) and launches. pages = pb.plan_flash(open(app_bin, "rb").read(), loader.info) for address in pb.covered(pages, loader.info, skip_blank=False): loader.write_page(address, pages[address]) pb.verify_pages(loader, pages) loader.run_application() if port.read_exact(3, 5.0) != b"APP": fail("application does not banner after the re-home") port.close() finally: device.stop() print("pbrehome: a misplaced loader re-homes through the ordinary update flow") if __name__ == "__main__": main()