# Firmware Upstream Rebase Plan (2026-07-03) Rebase the `firmware/` submodule (fork `dangerous-tac0s/proxmark3-pm3py`) onto current RRG `upstream/master` **without losing our sim / sniff / inventory patch**. Backed by a non-destructive dry-run (worktree + merge-tree). ## Current state (measured) - **merge-base:** `c7086c227` (2026-02-20) — where we branched. - **our patch:** 33 commits on top of merge-base (main == `35db99fa5`). - **upstream ahead:** 1049 commits (tip `87388b389`, 2026-07-03) — ~4.5 months. - Firmware is on `main`; ALM branch already scrapped. ## Dry-run findings — the conflict surface is small and concentrated A **squash-then-rebase** (all 33 commits applied as one combined diff, per `git merge-tree`) conflicts in **exactly 3 files**; everything else auto-merges or is untouched: | File | Verdict | ours vs upstream (lines) | |------|---------|--------------------------| | `armsrc/iso15693.c` | ⚠️ real work — 13 hunks | +599/−146 vs +523/−214 | | `client/src/cmdhf15.c` | ⚠️ moderate | +372/−7 vs +544/−45 | | `include/pm3_cmd.h` | ✅ trivial (textual re-append) | +21/−0 vs +94/−113 | | `appmain.c`, `iclass.c`, `iso15693.h`, `iso15.h`, `Makefile` | ✅ auto-merge | — | | `sim_table.c/h`, `sim_crypto.c/h`, `Standalone/hf_15sniff.c`, `Standalone/hf_unisniff.c` | ✅ upstream never touched | — | A **sequential** rebase (replay 33 commits) is worse: it conflicts on commit #1 and, because **24 of the 33 commits touch `iso15693.c`**, it re-conflicts that file many times. → **Squash first, resolve once.** ### CMD-ID collision check — CLEAR ✅ All six IDs we added are free in upstream (upstream's `0x033x` block only reaches `CSETUID_V2 0x0333`; `0x095x` is empty): ``` 0x0336 CMD_HF_ISO15693_SIM_TRACE 0x0950 CMD_SIM_TABLE_UPLOAD 0x0337 CMD_HF_SNIFF_STREAM 0x0951 CMD_SIM_TABLE_CLEAR 0x0338 CMD_HF_SNIFF_STATUS 0x0952 CMD_SIM_TABLE_UPDATE ``` So `pm3_cmd.h` is a pure textual re-append (drop our `#define`s at a clean spot in the churned region), **not** a semantic collision. ### `iso15693.c` hunk map (13 hunks, 2 functions) - **15693 sniff loop (~L1598–1853):** our streaming-sniff feature (ring buffer, button-toggle, idle flush) vs upstream sniff edits. Biggest hunk ~53 lines (our idle/flush block) — mostly *keep-ours-add-both*. - **SimTag / sim loop (~L2475–3372):** our sim handler (table lookup, access control, random UID, periodic field report) + the `CheckCrc15` 4-byte-frame fix vs upstream. ~10 hunks: three large (span 36–43, our inserted blocks → take-ours), the rest small (≤16) needing genuine line-level reconciliation — notably **our `CheckCrc15` vs upstream's `CalculateCrc15`** at ~L2591 (keep ours; it fixes 4-byte frames). Effort estimate: **~half a day** of focused merge work, dominated by `iso15693.c`; `cmdhf15.c` moderate; `pm3_cmd.h` ~10 min. ## Strategy: squash-to-one → rebase (single 3-file resolution) → validate → re-split Resolve conflicts exactly once (matching the merge-tree preview), validate the build, then restore clean per-file commits for cheap future rebases (the design-doc "atomic single-file commit" model). ### Procedure (all in an isolated worktree — real checkout never moves) ```bash FW=/home/work/pm3py/firmware WT=/fw-rebase git -C $FW fetch upstream master git -C $FW worktree add -b rebase-wip "$WT" 35db99fa5 cd "$WT" # 1. collapse 33 commits into one diff on our base (no upstream yet → no conflicts) MB=$(git merge-base HEAD upstream/master) git reset --soft "$MB" git commit -m "pm3py sim+sniff+inventory patch (squashed for rebase)" # 2. rebase the single commit → conflicts in the 3 known files, resolved once git rebase upstream/master # resolve iso15693.c, cmdhf15.c, pm3_cmd.h # ... resolve, git add, git rebase --continue # 3. re-split into logical per-file commits (soft reset + staged adds) git reset --soft upstream/master # commit A: sim_table.c/h + sim_crypto.c/h (new modules) # commit B: pm3_cmd.h + iso15.h + iso15693.h (IDs + struct fields) # commit C: appmain.c (dispatch) # commit D: iso15693.c (sim + sniff + inventory) # commit E: cmdhf15.c (client reader/inventory) # commit F: Makefile + Standalone/* (build glue) ``` ### Validation (all local per build-workflow — ARM builds here, not the Mac) 1. `make -C "$WT" clean && make -C "$WT" PLATFORM=PM3GENERIC` — ARM compiles. 2. `make -C "$WT" client` — validates `cmdhf15.c`. 3. `python -m pytest tests/` in pm3py — sim tests are transport-mocked, so they stay green; confirms no Python-side regression. 4. **Hardware smoke test (user, has PM3):** flash, then `hf 15` scan + 15693 sim + a phone read (the known-good sim path). This is the only check the dry-run can't cover — upstream may have altered 15693 wire behavior around our insertions. ### Landing - Show resolved `iso15693.c` diff for review **before** anything touches `main`. - Fast-forward `firmware` main to `rebase-wip`, bump the submodule pointer in a pm3py commit, force-push the fork (`--force-with-lease`). - Remove the worktree (`git worktree remove`, no `--force`). ## Risks & rollback - **Semantic drift in `iso15693.c`:** upstream may have changed shared helpers our sim path calls. Compile catches signature breaks; behavior needs the hardware smoke test. - **Rollback:** old main is preserved. Recovery ref for pre-rebase tip: `35db99fa5`. Nothing is force-pushed until build + review pass. - **Safety Net:** `branch -D`, `rm -rf`, `git clean -f`, `worktree remove --force` are blocked — use `-d`/plain `rm`/`worktree remove` (no force), or hand force-ops to the user. ## Go-forward (close the gap that let us drift 1049 commits) From the earlier maintenance discussion, still unbuilt: - `pm3py/firmware_compat.py` — pin `UPSTREAM_TESTED_COMMIT` = `87388b389`. - A **real** rebase-check CI (the existing `firmware/.github/workflows/ rebase.yml` is upstream's Changelog Reminder, not ours). ```