feat(vela): complete firmware flash-to-block-device capability - #223
Merged
Conversation
- Add vela-flasher crate with BlockDeviceWriter and FpkInstaller - Refactor vela-slotmgr SlotManager: generic over SlotProvider, real write_slot - Wire vela-lifecycle Installing phase with actual firmware flashing - Fix vela-ffi #[no_mangle] -> #[unsafe(no_mangle)] for Rust 2024 edition Closes #222
- Add vela-flasher crate with BlockDeviceWriter and FpkInstaller - Refactor vela-slotmgr SlotManager: generic over SlotProvider, real write_slot - Wire vela-lifecycle Installing phase with actual firmware flashing - Fix vela-ffi #[no_mangle] to #[unsafe(no_mangle)] for Rust 2024 edition Closes #222
Contributor
There was a problem hiding this comment.
Pull request overview
This PR completes the Vela OTA “Installing” path by introducing a new vela-flasher crate for block-device I/O and wiring it into vela-lifecycle, while refactoring vela-slotmgr to support real device-backed slot writing.
Changes:
- Added
vela-flasherwithBlockDeviceWriterandFpkInstallerfor flashing.fpkpayloads to a device. - Refactored
vela-slotmgr::SlotManagertoward provider-based slot detection and device-path driven writes. - Updated
vela-lifecycleto validate.fpkbundles and perform real installation writes during the Installing phase.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 23 comments.
Show a summary per file
| File | Description |
|---|---|
| src/vela/vela-core/crates/vela-slotmgr/src/manager.rs | Refactors SlotManager to use a SlotProvider, adds refresh/layout/device helpers, and delegates writes to BlockDeviceWriter. |
| src/vela/vela-core/crates/vela-slotmgr/src/lib.rs | Adds SlotError::FlashError for flasher integration. |
| src/vela/vela-core/crates/vela-slotmgr/Cargo.toml | Adds vela-flasher dependency and enables Tokio runtime feature. |
| src/vela/vela-core/crates/vela-lifecycle/src/lib.rs | Extends lifecycle context/errors for flashing inputs and install reporting. |
| src/vela/vela-core/crates/vela-lifecycle/src/engine.rs | Implements real Validating/Installing logic using FlashPack reader + flasher installer; adds tests. |
| src/vela/vela-core/crates/vela-lifecycle/Cargo.toml | Adds vela-flasher plus new dev-deps for .fpk test building. |
| src/vela/vela-core/crates/vela-flasher/src/lib.rs | Introduces the flasher public API (FlashConfig, FlasherError, exports). |
| src/vela/vela-core/crates/vela-flasher/src/direct_writer.rs | Implements chunked write + fsync + optional read-back verification + SHA tracking; adds tests. |
| src/vela/vela-core/crates/vela-flasher/src/fpk_installer.rs | Implements .fpk install pipeline (verify → decompress → write → hash check); adds tests. |
| src/vela/vela-core/crates/vela-flasher/Cargo.toml | Defines new crate dependencies for flashing and .fpk handling. |
| src/vela/vela-core/crates/vela-ffi/src/lib.rs | Updates #[no_mangle] to Rust 2024’s #[unsafe(no_mangle)]. |
| src/vela/vela-core/Cargo.toml | Adds vela-flasher to the workspace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| //! `vela_flasher::BlockDeviceWriter`, and label management. | ||
|
|
||
| use tracing::{debug, instrument}; | ||
| use tracing::{debug, info, instrument, warn}; |
Comment on lines
+48
to
62
| pub struct SlotManager<P: SlotProvider + ?Sized = MockSlotProvider> { | ||
| provider: Box<P>, | ||
| active: SlotLabel, | ||
| /// Cached slot layout, refreshed via `refresh()`. | ||
| layout: Option<SlotLayout>, | ||
| /// Override for the target device path (for testing). | ||
| device_override: Option<String>, | ||
| } | ||
|
|
||
| impl SlotManager { | ||
| /// Create a SlotManager from a mock provider (for testing). | ||
| impl SlotManager<MockSlotProvider> { | ||
| /// Create a SlotManager backed by a `MockSlotProvider` (for testing). | ||
| pub fn with_mock(mock: MockSlotProvider) -> Self { | ||
| Self { | ||
| provider: Box::new(mock), | ||
| active: SlotLabel::Primary, |
Comment on lines
+93
to
+101
| pub fn refresh(&mut self) -> SlotResult<()> { | ||
| // This is a synchronous wrapper; in production, callers should use | ||
| // an async-aware wrapper. We create a single-threaded tokio runtime | ||
| // to bridge the async provider trait to sync callers. | ||
| let rt = tokio::runtime::Builder::new_current_thread() | ||
| .enable_time() | ||
| .build() | ||
| .map_err(|e| SlotError::DetectionFailed(format!("failed to create runtime: {e}")))?; | ||
| let layout = rt.block_on(self.provider.detect_slots())?; |
Comment on lines
+100
to
+108
| .map_err(|e| SlotError::DetectionFailed(format!("failed to create runtime: {e}")))?; | ||
| let layout = rt.block_on(self.provider.detect_slots())?; | ||
| debug!( | ||
| primary = %layout.primary.device_path, | ||
| alternate = %layout.alternate.device_path, | ||
| "Slot layout refreshed" | ||
| ); | ||
| self.layout = Some(layout); | ||
| Ok(()) |
Comment on lines
+138
to
+145
| pub fn swap_and_commit(&mut self) -> SlotResult<()> { | ||
| // Persist the swap via the provider | ||
| let rt = tokio::runtime::Builder::new_current_thread() | ||
| .enable_time() | ||
| .build() | ||
| .map_err(|e| SlotError::SwapFailed(format!("failed to create runtime: {e}")))?; | ||
| rt.block_on(self.provider.swap_slots())?; | ||
|
|
Comment on lines
+259
to
+266
| let calls = std::sync::Arc::new(std::sync::Mutex::new(Vec::<(u64, u64)>::new())); | ||
| let calls_ref = calls.clone(); | ||
| let cb: ProgressCallback = Box::new(move |w, t| { | ||
| calls_ref.lock().unwrap().push((w, t)); | ||
| }); | ||
|
|
||
| let bytes = installer.install(Some(&cb)).unwrap(); | ||
| assert_eq!(bytes, payload.len() as u64); |
| use std::sync::Mutex; | ||
| use std::io::Write; | ||
| use tempfile::NamedTempFile; | ||
| use vela_flashpack::header::{FpkHeader, PayloadType}; |
Comment on lines
+144
to
+150
| // Open and verify the FlashPack | ||
| let reader = vela_flashpack::FlashPackReader::open(Path::new(&fpk_path)) | ||
| .map_err(|e| LifecycleError::InstallError(format!("FPK open failed: {e}")))?; | ||
|
|
||
| let _bundle_hash = reader | ||
| .verify_checksums() | ||
| .map_err(|e| LifecycleError::InstallError(format!("Checksum verification failed: {e}")))?; |
Comment on lines
+66
to
+69
| // 2. Verify archive checksums (header + payload) | ||
| let _bundle_hash = reader.verify_checksums()?; | ||
| info!("FlashPack checksum verification passed"); | ||
|
|
| SwapFailed(String), | ||
|
|
||
| #[error("Flash error: {0}")] | ||
| FlashError(Box<vela_flasher::FlasherError>), |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #222
Summary
Completes the Vela OTA firmware upgrade capability by adding real block device I/O and wiring the lifecycle Installing phase.
New: vela-flasher crate
Refactored: vela-slotmgr SlotManager
Wired: vela-lifecycle engine
Fixed: vela-ffi for Rust 2024 edition