Skip to content

OTA tests + essential fixes for the key-store branch: devicetree-safe FlashWorker region, ComQueue arming fix, hardware-in-the-loop OTA CI - #498

Open
Mikefly123 wants to merge 5 commits into
hmac-to-storagefrom
ota-tests-and-fixes-for-hmac-to-storage
Open

OTA tests + essential fixes for the key-store branch: devicetree-safe FlashWorker region, ComQueue arming fix, hardware-in-the-loop OTA CI#498
Mikefly123 wants to merge 5 commits into
hmac-to-storagefrom
ota-tests-and-fixes-for-hmac-to-storage

Conversation

@Mikefly123

Copy link
Copy Markdown
Contributor

Adds the OTA integration test suite and two essential fixes to hmac-to-storage (PR #472), all bench-validated on 2026-08-03/04 against exactly this content merged with the OTA topology (two V5e units).

Commits

  1. fix(FlashWorker): resolve the update region from the devicetree — minimal port of PR OTA: fix the flash-area ID that bricks the board, add an integration test and a CI job #492's fix, and load-bearing on this branch specifically: adding keystore_partition renumbers Zephyr's flash-area IDs (assigned by devicetree dependency ordinal; the keystore node sorts first via the fstab reference):

    Flash area ID main this branch
    0 mcuboot keystore
    1 slot0 mcuboot
    2 slot1 ✅ slot0 — the running image 💀
    3 slot2 slot1

    Verified in this branch's generated devicetree_generated.h. With the old hardcoded REGION_NUMBER = 2, the first PREPARE_UPDATE erases the running firmware — an instant brick, before any image is uplinked. Now resolved via PARTITION_ID(slot1_partition) plus a static_assert that the staging region can never overlap the running code partition.

  2. fix(comms): fprime pin bump — ComQueue comStatusIn becomes block — fixes issue ComQueue permanently wedges in WAITING when the one-shot arming comStatus is dropped at boot (silent total downlink loss; exposed by PR #472's littlefs Kconfig) #494: the one-shot arming comStatus=SUCCESS shares ComQueue's IPC queue with the boot event burst; when dropped (invisibly — the FW_ASSERT reports to a disabled console and continues) the downlink wedges in WAITING forever, every boot, both UART and LoRa. This branch's littlefs mount shifts boot timing enough to lose that race deterministically — unfixed, this branch merged with an OTA-workload topology produced a 100%-reproducible mute board. Same fork commit (03c645123) as PR fix(comms): make ComQueue comStatusIn blocking so the one-shot arming status can't be dropped (#494) #495 (the standalone fix against main); whichever merges first makes the other's pin bump trivial.

  3. test(ota): the OTA image-swap suite — full swap-and-revert lifecycle (uplink → on-board CRC verify → PREPARE/UPDATE/CONFIGURE_NEXT_BOOT[TEST] → reboot, assert new build-id → reboot without CONFIRM, assert MCUBoot auto-revert) plus negative paths, parametrized uart/lora. Includes make ota-test-image (unique build-id via ephemeral git tag, signing untouched), the --ota-image/--ota-build-id fixtures, an autouse post-reboot sequence-number resync (issue Svc::FileUplink emits FileReceived before the file is durably closed, causing spurious file-not-found #461 write-ahead persistence desyncs ground on every in-suite reboot), and UART-only downlink-repeater configuration inside the test (RAM-only setting, re-applied after each reboot, each application followed by a resync — an authenticated send between resync and COLD_RESET reliably desyncs otherwise).

  4. ci: hardware-in-the-loop OTA — new ota-test-image job (parallel with build); integration-uart runs the UART OTA test last in the GDS phase; the slow LoRa variant runs on a new nightly cron only. With this, the FlashWorker regression class is caught by CI on the first PREPARE_UPDATE instead of on a bricked bench board.

Bench evidence

Caveats for reviewers

🤖 Generated with Claude Code

https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo

Mikefly123 and others added 4 commits August 4, 2026 07:30
…a hardcoded ID

Minimal port of PR #492's FlashWorker fix. This branch adds keystore_partition
to the devicetree, and Zephyr assigns flash-area IDs by devicetree dependency
ordinal -- the keystore node (referenced by the fstab) sorts first, renumbering
every area: keystore=0, mcuboot=1, slot0=2, slot1=3 (verified in this branch's
generated devicetree_generated.h). The old hardcoded REGION_NUMBER=2 therefore
points at slot0 -- the RUNNING image -- and the first PREPARE_UPDATE would
boot_erase_img_bank() the firmware out from under itself, bricking the board
before any image is uplinked.

Resolve via PARTITION_ID(slot1_partition) and add a static_assert that the
staging region can never overlap the running code partition, so this class of
renumbering can't be reintroduced silently.

Bench-verified on 2026-08-03/04: PARTITION_ID resolves to 3 on this devicetree,
static_assert passes, and the full OTA swap-and-revert cycle runs clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…(issue #494)

Picks up Open-Source-Space-Foundation/fprime branch
fix/comqueue-blocking-comstatus-main (03c645123, one FPP change based directly
on this branch's current pin 8a62e455a):

    async input port comStatusIn: Fw.SuccessCondition block

Without it, the one-shot arming comStatus=SUCCESS emitted at startup by the
ComAggregator preamble shares ComQueue's IPC message queue with the boot event
burst; if the queue is momentarily full the message is silently dropped (the
FW_ASSERT reports to a disabled console and execution continues) and ComQueue
stays WAITING forever -- no telemetry, events, or command acks on any boot of
that binary. This branch's littlefs mount shifts boot timing enough to lose
that race deterministically: unfixed, this branch merged with an OTA-workload
topology produced a 100%-reproducible mute board on two bench V5e units.

Same fork commit as PCR PR #495 (the standalone fix against main); whichever
merges first makes the other's pin bump trivial. Full analysis in issue #494.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
Adds ota_update_test.py: the full swap-and-revert lifecycle against real
hardware (uplink signed image -> on-board CRC verify -> PREPARE/UPDATE/
CONFIGURE_NEXT_BOOT[TEST] -> reboot, assert new build-id -> reboot without
CONFIRM, assert MCUBoot auto-revert) plus negative paths (UPDATE without
PREPARE, wrong CRC), parametrized over uart (fast bench link) and lora
(lossy radio, marked slow, CRC-driven re-uplink loop).

Support plumbing, each piece earned on the bench:
- make ota-test-image: builds a second signed image whose project version
  carries OTA_BUILD_ID via an ephemeral git tag (git-describe injection),
  so the swap test can prove WHICH image booted, with signing unchanged.
- conftest: --ota-image/--ota-build-id options + ota_config fixture (skip
  when absent), and an autouse resync_sequence_number_after_reboot fixture --
  the deframer's write-ahead seq persistence (issue #461) means every
  in-suite reboot desyncs ground, silently rejecting authenticated commands.
- common.py: resync_sequence_number helper, also invoked from the command
  retry path so a mid-test reboot self-heals.
- The test configures the downlink repeater itself for uart runs
  (CHANNEL_ENABLED = uart-only, re-applied after each reboot because the
  setting is RAM-only, each application followed by a seq resync -- an
  authenticated send between resync and COLD_RESET otherwise reliably
  desyncs the counter; found the hard way, 2/2 reproducible).
- pytest.ini: ota marker; slow reworded for the LoRa uplink duration.

Validated on a bench V5e on 2026-08-03/04 against this branch's content
merged with the OTA topology: swap+revert+negatives green in under 5 minutes
at --file-uplink-cooldown 0.05.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…-loop

- New ota-test-image job (parallel with build, no needs:) produces the
  uniquely-marked signed image via make ota-test-image, with
  OTA_BUILD_ID=<sha8>-<run_id> exported for the integration jobs.
- integration-uart runs ota_update_test.py LAST in the GDS phase (it reboots
  the board twice, so it must not precede the other UART tests), FILTER
  'not slow', wired to the artifact + build-id.
- The radio job runs the slow LoRa OTA variant on the nightly schedule only
  (new cron 03:00 UTC); PR/push runs keep to the fast UART variant.

With this, the FlashWorker region regression class (erasing the running slot
after a devicetree change) is caught by CI on the first PREPARE_UPDATE instead
of on a bricked bench board.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6f5225d0-c10d-4a4d-8416-3dd61204567a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The previous pin bump recorded 03c645123ba8933e33e4c621bafd433d5c1a56de,
which does not exist -- the true tip of fix/comqueue-blocking-comstatus-main
on the OSSF fprime fork is 03c6451237d4442248703947693c1985a1948e88 (the
coordinator supplied a full SHA hand-expanded from the short form, and the
tooling recorded it unverified). CI's submodule fetch failed with 'not our
ref', which is what broke the unit-test job on this PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant