fix: restore the build and make the unit suite exercise real code - #58
fix: restore the build and make the unit suite exercise real code#58Kartikey1306 wants to merge 9 commits into
Conversation
master does not compile. Several PRs that fixed the same defects, or that added new files, were squash-merged on stale bases, and nothing re-verified master afterwards -- `CI - eBoot` has been red since. Build breakage: - core/recovery.c declared `slot_size` twice (embeddedos-org#33 and embeddedos-org#50 both landed the same bounds check). - include/eos_image.h declared `int eos_crc32(uint32_t, size_t, uint32_t *)` while core/image_verify.c defines `uint32_t eos_crc32(uint32_t, size_t)` (embeddedos-org#38 vs embeddedos-org#52). The header now matches the implementation. - core/sha512.c and core/rollback.c were never added to CMakeLists.txt, so the SHA-512 support from embeddedos-org#46 and the anti-rollback counter from embeddedos-org#54 were merged as dead code. - Two SHA-512 APIs survived the merge: eos_crypto_boot.h declares eos_sha512_*, include/eos_sha512.h declared sha512_*, and only the latter was implemented. Consolidated on the eos_sha512_* API that the rest of the tree already refers to; include/eos_sha512.h is removed. - The body of eos_ed25519_verify() was lost. What remained was two spliced hash blocks and `return diff == 0` with `diff` undeclared -- the group operation that actually checks the signature was gone. Restored: recompute R' = [S]B + [k](-A) and compare its encoding against R in constant time. - The EBLDR_BOARD dispatch chain was duplicated (83 boards listed twice, 121 lines), with a stray message(FATAL_ERROR ...) spliced into the kalimba branch. tests/unit/test_cmake_board_dispatch.py already covered this. Test suite: - tests/unit/test_slot_manager.c has not compiled since embeddedos-org#37, which committed two versions of the file spliced together: a main() calling ~20 functions that do not exist, a duplicated test, and fixture variables used before they are declared. Rebuilt on the coherent pre-embeddedos-org#37 harness and given real coverage for the boot-attempt counter embeddedos-org#37 was meant to add. - tests/unit/test_boot_log.c defined its own eos_boot_log_* functions, so the linker never pulled core/boot_log.c out of libeboot_core.a: the test exercised its own stubs and reported PASS. Rewritten against the real implementation, stubbing only flash and the tick counter. It now covers append-before-init, head persistence and wrapping, read bounds, and that a failed erase does not reset the head. - include/eos_boot_log.h declared an API that exists nowhere -- init(void), count(), flush(), get_latest(), event_name(). Every one of them lived only in the old test's stubs. The header now documents what core/boot_log.c implements, which is what recovery.c and stage1 already call. - The ARM job in ci.yml pointed CMAKE_TOOLCHAIN_FILE at cmake/arm-cortex-m4.cmake, which does not exist, and passed -DBUILD_TESTS=OFF, which is not this project's option name. Pointed at toolchains/arm-none-eabi.cmake with EBLDR_BOARD=stm32f4. Verified: host build clean in Debug and Release; ctest 16/16 pass; pytest tests/ 13 passed, 1 skipped; `cmake -DEBLDR_BOARD=kalimba` configures. Not verified locally: the ARM cross-build, for lack of an arm-none-eabi toolchain on this machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…iles
stage0/jump_stage1.c uses eos_sha256_ctx_t and the eos_sha256_* functions
under EBLDR_VERIFY_STAGE1 without including eos_crypto_boot.h. That option
defaults to ON, so every cross-compiled board build fails:
stage0/jump_stage1.c:70:9: error: unknown type name 'eos_sha256_ctx_t'
The host build never caught it because EBLDR_BOARD defaults to "none" and
stage0 is only added for a real board -- so the first link in the secure-boot
chain, stage-0 verifying stage-1 before jumping to it, has never been
compiled. Surfaced by the Cross-compile STM32F4 job on this PR.
Verified with `clang -fsyntax-only -DEBLDR_VERIFY_STAGE1` over every stage0/
and stage1/ source: clean afterwards, apart from reset_entry.c's weak aliases,
which clang rejects on darwin regardless.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.coveragerc sets fail_under = 100. Measured coverage is 23.06%, most of the gap being tests/production_test_suite.py (736 statements) which nothing imports. The step therefore failed on the coverage number even when all 27 Python tests passed -- so this job could never go green regardless of the code. ebuild hit exactly this and resolved it by passing --cov-fail-under=0 in CI, with the reasoning recorded in its .coveragerc: the repo-wide ratchet belongs in codecov.yml, and TESTING.md's 95% target is a patch target, not a repo-wide one. Same fix here, for consistency across the two repos. Both numbers are left alone -- raising .coveragerc to a real floor, or enforcing one here, is a maintainer decision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
With the include fixed, ebldr_stage0 compiles but does not link:
undefined reference to `stage1_expected_size'
undefined reference to `stage1_expected_hash'
stage0/jump_stage1.c declares both extern and hashes stage-1 in flash against
them before jumping. Nothing in the tree defined them. tools/embed_stage1_hash.py
exists to produce them and is never invoked by the build -- and even if it
were, it emitted a header declaring `static const uint8_t stage1_expected_hash`,
which cannot satisfy an extern in another translation unit, and never emitted
stage1_expected_size at all.
So stage-0 verifying stage-1 -- the first link of the secure boot chain, and
ON by default via EBLDR_VERIFY_STAGE1 -- has never been built on any board.
- tools/embed_stage1_hash.py now emits a C source file defining both symbols
with external linkage, sized from the input binary.
- CMakeLists.txt generates it from eboot_firmware.bin and compiles it into
ebldr_stage0. The custom command DEPENDS on eboot_firmware, so the hash is
taken from the stage-1 image this build produced.
- EBLDR_VERIFY_STAGE1 with a board that has no stage-1 linker script is now a
configure-time error naming the flag to turn off, rather than a link failure
a hundred lines of output later.
Verified: the generated file compiles and satisfies the externs (linked against
a probe TU declaring them, digest and size match hashlib); a simulated
cross-configure shows `stage1_hash.c: eboot_firmware.elf` in the dependency
graph and stage1_hash.c.obj in ebldr_stage0's objects. The host build is
untouched -- ctest 16/16, pytest 13 passed 1 skipped.
Not verified locally: the ARM link itself, for lack of an arm-none-eabi
toolchain. The board_stm32f4.c assembly cannot be assembled by host clang.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every job in Simulation Sanity Test dies at "Install EoSim":
ERROR: HTTP error 404 ... EoSim/releases/download/v0.1.0/eosim-0.1.0-py3-none-any.whl
embeddedos-org/EoSim has no v0.1.0 release, and none of its releases publish a
wheel — the newest asset is a promo video. So all 11 simulate jobs, all 3
cross-platform jobs, and the gate that depends on them have failed on master
and on every branch since the workflow was written, without a single
simulation ever running.
ebuild hit exactly this and disabled the steps in its own simulation-test.yml
("EoSim repository not found. Skipping simulation tests."). Same treatment
here: the pip install, the eosim invocations and the artifact upload are
commented out rather than deleted, so restoring them is a one-line revert once
EoSim ships a release.
Left alone: .github/workflows/eosim-sanity.yml has the same broken install but
runs on a nightly schedule rather than on pull requests, and ebuild left its
copy untouched too. Whether to disable a nightly diagnostic is a maintainer
call, not something to fold into a build-fix PR.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ci.yml is the only workflow in this repo without a concurrency group, and it is the heaviest one -- a matrix spanning ubuntu, macos and windows. Every push to a pull request therefore left the previous run queued, and all of them competed for the same scarce windows/macos runners. On this branch three superseded runs sat ahead of the current one for over an hour, testing commits that were no longer HEAD. Uses the same group expression the sibling workflows already use, with cancel-in-progress: true, because a superseded commit's result is not wanted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`pytest --cov` writes a .coverage SQLite file into the repo root, and it was not gitignored, so a `git add -A` swept 52 KB of local coverage state into this branch. Removed, and gitignored so it cannot happen again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
srpatcha
left a comment
There was a problem hiding this comment.
Verified independently against current master: merges clean, builds with no errors, ctest 16/16.
I confirmed master is red before this lands — it is not a stale CI badge:
core/image_verify.c:63:10: error: conflicting types for 'eos_crc32'; have 'uint32_t(uint32_t, size_t)'
The header declares int eos_crc32(uint32_t, size_t, uint32_t *) and the implementation defines uint32_t eos_crc32(uint32_t, size_t). Two PRs, two API shapes, neither rebased on the other.
The finding that matters most is the one about eos_ed25519_verify() losing its body — what survived was two spliced hash blocks and return diff == 0 with diff never declared, and the group operation that actually checks a signature was simply gone. A signature verifier that does not verify is the worst possible failure mode for a bootloader, and it reached master because the file did not compile so nobody ran it. Restoring it with a constant-time comparison is right.
Equally important and easier to overlook: core/sha512.c and core/rollback.c were never added to CMakeLists.txt. The SHA-512 support from #46 and the anti-rollback counter from #54 are both on master as dead code — two merged security features that were never compiled into anything. That is worth a post-mortem beyond this PR.
And stage0/jump_stage1.c never having been built on any board, with stage1_expected_hash and stage1_expected_size undefined anywhere while EBLDR_VERIFY_STAGE1 defaults ON — that is stage-0 shipping a verification step that could not have linked.
Your diagnosis of the process failure is the right one and I am taking it seriously: squash-merging green branches on stale bases without re-verifying the result. I have been re-verifying each PR against current master before merging for exactly this reason, and it has already caught two PRs in eos that GitHub reported as MERGEABLE and that did not compile once merged.
Not verified here: the STM32F4 cross-compile half. No ARM toolchain on this machine, so that rests on CI.
…d-tests (rebased) Resolving for re-submission against current master, which has moved significantly since this branch was opened: embeddedos-org#69, embeddedos-org#67, embeddedos-org#64, embeddedos-org#61, embeddedos-org#60, and embeddedos-org#71 have all merged, and every substantive fix in this PR -- the eos_crc32 signature match, the restored eos_ed25519_verify() body, SHA-512 API consolidation, core/sha512.c and core/rollback.c added to the build, the EBLDR_BOARD dispatch de-duplication, the stage-1 hash generation and stage0 verification wiring, test_boot_log.c exercising the real implementation, and the CI workflow fixes -- is now already present on master byte-for-byte identical, confirmed by diffing every file this PR touches against current master before resolving. The one real conflict was tests/unit/test_slot_manager.c, which master already had a working (if more elaborate, simulated-flash-based) fix for. Took this PR's version: it mocks eos_hal_slot_addr/size and eos_hal_flash_erase directly rather than simulating a full flash backend, which is simpler and sufficient since this test only links eboot_core (not eboot_hal), and it already has srpatcha's review. core/recovery.c's one-line hunk (removing a slot_size declaration) no longer applies -- that line was part of a different, already-merged fix for the same original defect -- so it was dropped from this merge to avoid reintroducing an undeclared-identifier build break. Verified post-merge: cmake configure + build, 0 errors. ctest 32/32 (including valgrind), 0 failures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VvWBEZhDegTQMaqVtry2mM
Every decision secure boot makes rests on which public key the device trusts.
keystore.c resolves that key, and three of its paths resolve it to something
weaker when hardware misbehaves rather than refusing.
**An unreadable revocation store revokes nothing.** eos_keystore_init() applies
the OTP revocation flags only when the read succeeds:
uint8_t revoke_flags = 0;
if (eos_hal_otp_read(OTP_REVOKE_OFFSET, &revoke_flags, 1) == EOS_OK) {
if (revoke_flags & 0x01) ks->slots[0].revoked = true;
On failure the flags stay clear and every key looks live. Revocation exists to
retire a key whose private half is believed compromised, so "I could not check"
must mean "do not use". It now marks every OTP slot revoked, and
eos_keystore_get_active_key() reports EOS_ERR_KEY.
**A failed OTP read is treated as no OTP at all.** Any non-EOS_OK result skipped
the whole OTP block and fell through to the compiled-in key. EOS_ERR_NOT_SUPPORTED
(this board has no OTP) and EOS_ERR_FLASH (this board has one and reading it
failed) are not interchangeable: the second means the provisioned trust anchor
is unknown, and quietly substituting the compiled-in key lets a fault on the OTP
bus choose which key the device trusts. Only the former now falls back.
**Revoking a slot cleared the others, and reported success when it did not
stick.** eos_keystore_revoke_slot() ignored the return of its read-modify-write:
uint8_t revoke_flags = 0;
eos_hal_otp_read(OTP_REVOKE_OFFSET, &revoke_flags, 1);
revoke_flags |= (1U << slot);
eos_hal_otp_write(OTP_REVOKE_OFFSET, &revoke_flags, 1);
If the read failed, revoke_flags stayed 0 and writing it back cleared every
other slot's revocation bit -- revoking slot 1 un-revoked slot 0. If the write
failed, the revocation lived in RAM only and was gone at the next reset, while
this returned EOS_OK and the caller believed the key was permanently retired.
Both are now reported. The in-RAM revocation stands either way, so the current
boot still refuses the key.
Also: the compiled-in default key is the public half of TEST 1 in RFC 8032
section 7.1, whose private key is printed in the RFC -- anyone can sign an image
a bootloader trusting it will accept. It is a reasonable default for bring-up
and for these tests, and shipping it silently is the failure mode, so a build
without EBLDR_PRODUCTION_KEY now says so on every compile. No -Werror here, so
this does not break a build.
Verified: ctest 16/16 pass, pytest 13 passed 1 skipped. Five new keystore tests
cover each path above; against the unpatched keystore.c the first of them fails
on exactly the assertion it exists to make.
Stacked on embeddedos-org#58, which restores the build -- master does not compile, so these
tests cannot run without it. Rebase target once embeddedos-org#58 or an equivalent lands.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Rebased onto current master to clear the conflict (this branch had drifted significantly behind -- #69/#67/#64/#61/#60/#71 all landed since it opened). Diffed every file this PR touches against current master before resolving: everything except tests/unit/test_slot_manager.c was already byte-for-byte identical on master (the eos_crc32 fix, the restored eos_ed25519_verify() body, SHA-512 consolidation, core/sha512.c and core/rollback.c added to the build, the EBLDR_BOARD dedup, the stage-1 hash generation/verification wiring, test_boot_log.c, and the CI fixes -- #69 and #67 had converged on the same fixes independently, as you noted in your review on #69). For test_slot_manager.c, took this PR's version -- the direct eos_hal_slot_addr/size/flash_erase mocks over a simulated flash backend, since this test only links eboot_core and you'd already reviewed it. One thing worth flagging: core/recovery.c's one-line hunk in this PR (removing a slot_size declaration) no longer applies cleanly -- git's own 3-way merge applied it without conflict, but silently reintroduced an undeclared-identifier build break, because that line is now part of a different, already-merged fix (#69) for the same original defect. Dropped that hunk from the merge to avoid reintroducing the break. Follow-up: the first push here still failed CI -- test_cmake_test_registration.py::test_every_c_suite_is_built caught that tests/unit/test_image_abi.c (added by #67) was never registered in tests/CMakeLists.txt, so it silently never built or ran. That's a master-level gap independent of this PR, so fixed it on master directly (038f624) and merged master into this branch again (5e7bd07). Verified on the current branch tip: cmake configure + build, 0 errors. ctest 34/34 including valgrind. pytest tests/ 30/30 pass (matches CI's own invocation). Pushing dismissed your approval, so this needs a fresh look from you (or another reviewer) before it can merge -- I'm the one who pushed, so I can't be the one to approve it. |
embeddedos-org#67 added tests/unit/test_image_abi.c (pins the .efw image header wire format against eFirmware's own copy of the same contract) but never added an add_executable()/add_test() for it in tests/CMakeLists.txt, so it silently never built or ran -- caught by CI on embeddedos-org#58 (test_cmake_test_registration.py::test_every_c_suite_is_built, the meta-test that exists specifically to catch this class of gap). Registered it alongside test_image_verify, and added it to the valgrind suite list for consistency with every other test here. Verified: cmake configure + build, 0 errors. ctest 34/34 including valgrind. pytest tests/unit/test_cmake_test_registration.py 3/3 pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VvWBEZhDegTQMaqVtry2mM
) Every decision secure boot makes rests on which public key the device trusts. keystore.c resolves that key, and three of its paths resolve it to something weaker when hardware misbehaves rather than refusing. **An unreadable revocation store revokes nothing.** eos_keystore_init() applies the OTP revocation flags only when the read succeeds: uint8_t revoke_flags = 0; if (eos_hal_otp_read(OTP_REVOKE_OFFSET, &revoke_flags, 1) == EOS_OK) { if (revoke_flags & 0x01) ks->slots[0].revoked = true; On failure the flags stay clear and every key looks live. Revocation exists to retire a key whose private half is believed compromised, so "I could not check" must mean "do not use". It now marks every OTP slot revoked, and eos_keystore_get_active_key() reports EOS_ERR_KEY. **A failed OTP read is treated as no OTP at all.** Any non-EOS_OK result skipped the whole OTP block and fell through to the compiled-in key. EOS_ERR_NOT_SUPPORTED (this board has no OTP) and EOS_ERR_FLASH (this board has one and reading it failed) are not interchangeable: the second means the provisioned trust anchor is unknown, and quietly substituting the compiled-in key lets a fault on the OTP bus choose which key the device trusts. Only the former now falls back. **Revoking a slot cleared the others, and reported success when it did not stick.** eos_keystore_revoke_slot() ignored the return of its read-modify-write: uint8_t revoke_flags = 0; eos_hal_otp_read(OTP_REVOKE_OFFSET, &revoke_flags, 1); revoke_flags |= (1U << slot); eos_hal_otp_write(OTP_REVOKE_OFFSET, &revoke_flags, 1); If the read failed, revoke_flags stayed 0 and writing it back cleared every other slot's revocation bit -- revoking slot 1 un-revoked slot 0. If the write failed, the revocation lived in RAM only and was gone at the next reset, while this returned EOS_OK and the caller believed the key was permanently retired. Both are now reported. The in-RAM revocation stands either way, so the current boot still refuses the key. Also: the compiled-in default key is the public half of TEST 1 in RFC 8032 section 7.1, whose private key is printed in the RFC -- anyone can sign an image a bootloader trusting it will accept. It is a reasonable default for bring-up and for these tests, and shipping it silently is the failure mode, so a build without EBLDR_PRODUCTION_KEY now says so on every compile. No -Werror here, so this does not break a build. Verified: ctest 16/16 pass, pytest 13 passed 1 skipped. Five new keystore tests cover each path above; against the unpatched keystore.c the first of them fails on exactly the assertion it exists to make. Stacked on #58, which restores the build -- master does not compile, so these tests cannot run without it. Rebase target once #58 or an equivalent lands. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@Aswin-V thank you for the rebase and for the write-up — especially catching that the Closing this: there is nothing left in it. I merged current Since it needs a fresh review it cannot get from you, and there is nothing to review, closing is cleaner than leaving it open. One thing that came out of checking:
|
The architectural problem
masterdoes not compile, and hasn't for a while —CI — eBootis red on39b0925.The individual breakages below are symptoms of one process failure: PRs that fix the same defect, or that add new files, are squash-merged on stale bases and nothing re-verifies
masterafterwards. Each PR was green on its own branch. Their combination never was. The result is that three separately-merged security features — SHA-512 (#46), the anti-rollback counter (#54), and full-header signing (#40) — are onmasteras code that either does not build or is not linked into anything.Status: 23/23 checks pass on this branch.
Build breakage
core/recovery.cdeclaredslot_sizetwice; Fix OOB flash write and OOB stack read in recovery firmware-update path #33 and fix(image): verify_slot() must reject image_size larger than the real slot capacity #50 both landed the same bounds check.include/eos_image.hdeclaredint eos_crc32(uint32_t, size_t, uint32_t *)whilecore/image_verify.cdefinesuint32_t eos_crc32(uint32_t, size_t)(fix(image_verify): fail closed when the CRC32 integrity path cannot read flash #38 vs fix(eboot): stop a failed flash read from passing the integrity check #52). The header now matches the implementation.core/sha512.candcore/rollback.cwere never added toCMakeLists.txt. The SHA-512 support from Add SHA-512 implementation and Ed25519 RFC 8032 verification #46 and the anti-rollback security counter from Enforce and commit the anti-rollback security counter #54 were merged as dead code — nothing compiles them.eos_crypto_boot.hdeclareseos_sha512_*,include/eos_sha512.hdeclaredsha512_*, and only the latter was implemented — whiletest_ed25519.ccalls the former. Consolidated oneos_sha512_*;include/eos_sha512.his removed.eos_ed25519_verify()was lost. What remained was two spliced hash blocks andreturn diff == 0withdiffnever declared. The group operation that actually checks a signature —[S]B + [k](-A)againstR— was gone entirely. Restored, with a constant-time comparison.EBLDR_BOARDdispatch chain was duplicated: 83 boards listed twice across 121 lines, with a straymessage(FATAL_ERROR ...)spliced into thekalimbabranch, so a supported board aborted configuration.Stage-0 never verified stage-1
Once the host build compiled, the
Cross-compile STM32F4job surfaced the more serious finding.EBLDR_VERIFY_STAGE1defaults to ON, andstage0/jump_stage1.chashes stage-1 in flash before jumping to it. That code has never been built on any board:eos_sha256_ctx_tand theeos_sha256_*functions without includingeos_crypto_boot.h—error: unknown type name 'eos_sha256_ctx_t'.stage1_expected_hashorstage1_expected_size.tools/embed_stage1_hash.pyexists to produce them, is never invoked by the build, emitted astaticarray that cannot satisfy anexternin another translation unit, and never emitted the size at all.The host build never caught either, because
EBLDR_BOARDdefaults tononeand stage0 is only added for a real board. So the first link of the secure boot chain has been dead code since it was written.Fixed: the tool now emits a C source defining both symbols with external linkage, and
CMakeLists.txtgenerates it fromeboot_firmware.binand compiles it intoebldr_stage0.EBLDR_VERIFY_STAGE1on a board with no stage-1 linker script is now a configure-time error naming the flag to turn off, rather than a link failure a hundred lines later.Test-suite integrity
Two files in
tests/unit/were not testing anything, which is why none of the above was caught locally.test_slot_manager.chas not compiled since feat(slot_manager): boot attempt counter with rollback detection + fix fw_update_abort ordering #37. That commit landed two versions of the file spliced together: amain()calling ~20 functions that do not exist, a duplicatedtest_scan_no_valid_slots, and fixture variables used before they are declared. Rebuilt on the coherent pre-feat(slot_manager): boot attempt counter with rollback detection + fix fw_update_abort ordering #37 harness, and given real coverage for the boot-attempt counter feat(slot_manager): boot attempt counter with rollback detection + fix fw_update_abort ordering #37 was meant to add.test_boot_log.cdefined its owneos_boot_log_*functions. The linker therefore never pulledcore/boot_log.cout oflibeboot_core.a: the test exercised its own stubs and reported PASS while touching zero production code. Rewritten against the real implementation, stubbing only the platform. It now covers append-before-init, head persistence across resets, ring wrap, read bounds, and that a failed erase does not reset the head.include/eos_boot_log.hdeclared an API that exists nowhere.init(void),count(),flush(),get_latest(),event_name()— every one of them lived only inside that test's stubs, whilecore/recovery.candstage1/call a completely different set of functions through localexterndeclarations. The header now documents whatcore/boot_log.cimplements.CI could not go green regardless of the code
The ARM job pointed
CMAKE_TOOLCHAIN_FILEatcmake/arm-cortex-m4.cmake, which does not exist, and passed-DBUILD_TESTS=OFF, which is not this project's option name. Nowtoolchains/arm-none-eabi.cmakewithEBLDR_BOARD=stm32f4..coveragercsetsfail_under = 100against 23.06% measured, most of the gap beingtests/production_test_suite.py(736 statements) which nothing imports. The step failed on the coverage number with all 27 Python tests passing. ebuild resolved this identically with--cov-fail-under=0; both numbers are left alone here.Simulation Sanity Test404s on every job.embeddedos-org/EoSimhas nov0.1.0release and publishes no wheel — its newest asset is a promo video. All 11 simulate jobs, all 3 cross-platform jobs and the gate have failed since the workflow was written, without a single simulation ever running. ebuild hit this and disabled the steps in its ownsimulation-test.yml; same treatment here, commented rather than deleted so restoring is a one-line revert.Verification
cmake --build(Debug and Release,EBLDR_BUILD_TESTS=ON)ctest --no-tests=errorpytest tests/cmake -DEBLDR_BOARD=kalimbastage1_hash.chashlibNot verified locally: the ARM link itself, for lack of an
arm-none-eabitoolchain on this machine —board_stm32f4.c's assembly cannot be assembled by host clang. CI'sCross-compile ARM Cortex-M4job is the check, and it passes on this branch.Left alone:
.github/workflows/eosim-sanity.ymlhas the same broken EoSim install but runs on a nightly schedule rather than on pull requests, and ebuild left its copy untouched too. Disabling a nightly diagnostic is a maintainer call, not something to fold into a build-fix PR.Relationship to open PRs
#55 and #57 also restore the build, and either would fix most of the compile errors above. Neither touches
test_slot_manager.cortest_boot_log.c, so with either one merged the test suite still does not compile underEBLDR_BUILD_TESTS=ON— which is what CI uses. Neither addresses the stage-1 hash. Happy to rebase onto whichever lands first.🤖 Generated with Claude Code