-
Notifications
You must be signed in to change notification settings - Fork 3
[Rebase & FF] patina_boot: import crate from patina feature/patina-boot branch #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
kat-perez
merged 2 commits into
OpenDevicePartnership:main
from
kat-perez:kat-perez/move-patina-boot
May 12, 2026
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
|
makubacki marked this conversation as resolved.
makubacki marked this conversation as resolved.
|
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,22 @@ | ||
| [workspace] | ||
| resolver = "3" | ||
| members = ["placeholder"] | ||
|
|
||
| [workspace.package] | ||
| version = "0.0.1" | ||
| license = "Apache-2.0" | ||
| edition = "2024" | ||
| rust-version = "1.89" | ||
| description = "Patina components" | ||
| repository = "https://github.com/OpenDevicePartnership/patina-components" | ||
|
|
||
| [workspace.dependencies] | ||
| patina = { version = "21"} | ||
|
|
||
| [workspace.lints.clippy] | ||
| undocumented_unsafe_blocks = "warn" | ||
| [workspace] | ||
| resolver = "3" | ||
| members = ["patina_boot"] | ||
|
|
||
| [workspace.package] | ||
| version = "0.0.1" | ||
| license = "Apache-2.0" | ||
| edition = "2024" | ||
| rust-version = "1.89" | ||
| description = "Patina components" | ||
| repository = "https://github.com/OpenDevicePartnership/patina-components" | ||
|
|
||
| [workspace.dependencies] | ||
| log = { version = "0.4", default-features = false } | ||
| mockall = { version = "0.13" } | ||
| r-efi = { version = "5", default-features = false } | ||
| spin = { version = "0.9", default-features = false, features = ["spin_mutex"] } | ||
| zerocopy = { version = "0.8", default-features = false } | ||
| zerocopy-derive = { version = "0.8" } | ||
|
|
||
| [workspace.lints.clippy] | ||
| undocumented_unsafe_blocks = "warn" |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| [package] | ||
| name = "patina_boot" | ||
| version = "0.1.0" | ||
| repository.workspace = true | ||
| license.workspace = true | ||
| edition.workspace = true | ||
| description = "Boot orchestration components for Patina firmware." | ||
|
|
||
| [lints] | ||
| workspace = true | ||
|
|
||
| [dependencies] | ||
| log = { workspace = true } | ||
| # patina/patina_macro are pulled from feature/patina-boot until a patina release | ||
| # containing the required APIs (HardDrive::try_from_node, create_event_ex_unchecked | ||
| # Option<fn>, etc.) is published to crates.io. | ||
| patina = { git = "https://github.com/OpenDevicePartnership/patina", branch = "feature/patina-boot", features = ["unstable-device-path"] } | ||
| patina_macro = { git = "https://github.com/OpenDevicePartnership/patina", branch = "feature/patina-boot" } | ||
| r-efi = { workspace = true } | ||
| spin = { workspace = true } | ||
| zerocopy = { workspace = true } | ||
| zerocopy-derive = { workspace = true } | ||
|
|
||
| [dev-dependencies] | ||
| patina = { git = "https://github.com/OpenDevicePartnership/patina", branch = "feature/patina-boot", features = ["mockall", "unstable-device-path"] } | ||
| mockall = { workspace = true } | ||
|
|
||
| [features] | ||
| default = [] | ||
| doc = [] | ||
| std = [] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Patina Boot | ||
|
|
||
| Boot orchestration component for Patina-based firmware implementing UEFI boot manager functionality. | ||
|
|
||
| ## Components | ||
|
|
||
| - **BootDispatcher**: Installs the BDS architectural protocol and delegates to a `BootOrchestrator` implementation. | ||
| - **BootOrchestrator**: Trait for custom boot flows. Platforms implement this to define boot behavior. | ||
| - **SimpleBootManager**: Default `BootOrchestrator` for platforms with straightforward boot topologies. | ||
|
|
||
| ## Usage | ||
|
|
||
| ```rust | ||
| use patina_boot::{BootDispatcher, SimpleBootManager, config::BootConfig}; | ||
|
|
||
| // Minimal boot: | ||
| let orchestrator = SimpleBootManager::new( | ||
| BootConfig::new(nvme_esp_path()) | ||
| .with_device(nvme_recovery_path()), | ||
| ); | ||
| add.component(BootDispatcher::new(orchestrator)); | ||
|
|
||
| // Custom orchestrator: | ||
| add.component(BootDispatcher::new(MyCustomOrchestrator::new())); | ||
| ``` | ||
|
|
||
| ## Helper Functions | ||
|
|
||
| For custom boot flows, use the helper functions in the `helpers` module: | ||
|
|
||
| - `connect_all()` - Connect all controllers for device enumeration | ||
| - `signal_bds_phase_entry()` - Signal EndOfDxe event | ||
| - `signal_ready_to_boot()` - Signal ReadyToBoot event | ||
| - `discover_console_devices()` - Populate console variables | ||
| - `boot_from_device_path()` - Load and start a boot image |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| //! Boot dispatcher component. | ||
| //! | ||
| //! [`BootDispatcher`] is the Patina component that installs the BDS architectural | ||
| //! protocol and delegates to a [`BootOrchestrator`] | ||
| //! implementation when invoked by the DXE core. | ||
| //! | ||
| //! ## License | ||
| //! | ||
| //! Copyright (c) Microsoft Corporation. | ||
| //! | ||
| //! SPDX-License-Identifier: Apache-2.0 | ||
| //! | ||
| extern crate alloc; | ||
|
|
||
| use alloc::boxed::Box; | ||
| use core::ffi::c_void; | ||
|
|
||
| use patina::{ | ||
| boot_services::{BootServices, StandardBootServices}, | ||
| component::{ | ||
| component, | ||
| params::Handle, | ||
| service::{Service, dxe_dispatch::DxeDispatch}, | ||
| }, | ||
| error::{EfiError, Result}, | ||
| pi::protocols::bds, | ||
| runtime_services::StandardRuntimeServices, | ||
| }; | ||
| use spin::Once; | ||
|
|
||
| use crate::boot_orchestrator::BootOrchestrator; | ||
|
|
||
| /// Context stored in a static for the BDS protocol callback to access. | ||
| struct BdsContext { | ||
| orchestrator: Box<dyn BootOrchestrator>, | ||
| dxe_dispatch: &'static dyn DxeDispatch, | ||
| boot_services: StandardBootServices, | ||
| runtime_services: StandardRuntimeServices, | ||
| image_handle: r_efi::efi::Handle, | ||
| } | ||
|
|
||
| // SAFETY: BdsContext is only accessed from the BDS entry point which runs on the | ||
| // BSP (Bootstrap Processor) at TPL_APPLICATION. UEFI is single-threaded at this point. | ||
| unsafe impl Send for BdsContext {} | ||
| // SAFETY: BdsContext is stored in a spin::Once and only read after initialization. | ||
| // Access is single-threaded (BSP at TPL_APPLICATION during BDS phase). | ||
| unsafe impl Sync for BdsContext {} | ||
|
|
||
| /// Static storage for the BDS context. Initialized once during component dispatch, | ||
| /// consumed once when the DXE core invokes the BDS protocol. | ||
| static BDS_CONTEXT: Once<BdsContext> = Once::new(); | ||
|
|
||
| /// Boot dispatcher component. | ||
| /// | ||
| /// This is the single Patina component for driving boot orchestration. It: | ||
| /// - Accepts a [`BootOrchestrator`] implementation via [`BootDispatcher::new()`] | ||
| /// - Installs the BDS architectural protocol during component dispatch | ||
| /// - Consumes the [`DxeDispatch`] service via dependency injection | ||
| /// - When the DXE core invokes BDS: delegates to `orchestrator.execute()` | ||
| /// | ||
| /// ## Usage | ||
| /// | ||
| /// ```rust,ignore | ||
| /// use patina_boot::{BootDispatcher, SimpleBootManager, config::BootConfig}; | ||
| /// | ||
| /// // Minimal boot: | ||
| /// add.component(BootDispatcher::new(SimpleBootManager::new( | ||
| /// BootConfig::new(nvme_esp_path()) | ||
| /// .with_device(nvme_recovery_path()), | ||
| /// ))); | ||
| /// | ||
| /// // Custom orchestrator: | ||
| /// add.component(BootDispatcher::new(MyCustomOrchestrator::new())); | ||
| /// ``` | ||
| pub struct BootDispatcher { | ||
| orchestrator: Box<dyn BootOrchestrator>, | ||
| } | ||
|
|
||
| impl BootDispatcher { | ||
| /// Create a new `BootDispatcher` with the given orchestrator. | ||
| /// | ||
| /// The orchestrator is boxed internally — callers pass any type that | ||
| /// implements [`BootOrchestrator`]. | ||
| pub fn new(orchestrator: impl BootOrchestrator) -> Self { | ||
| Self { orchestrator: Box::new(orchestrator) } | ||
| } | ||
| } | ||
|
|
||
| #[component] | ||
| impl BootDispatcher { | ||
| /// Entry point: stores context and installs the BDS architectural protocol. | ||
| /// | ||
| /// The actual boot flow does not execute here. It executes later when the | ||
| /// DXE core calls `bds_entry_point` after all architectural protocols are | ||
| /// satisfied and all DXE drivers have been dispatched. | ||
| #[coverage(off)] // Component integration — tested via integration tests | ||
| fn entry_point( | ||
| self, | ||
| boot_services: StandardBootServices, | ||
| runtime_services: StandardRuntimeServices, | ||
| dxe_dispatch: Service<dyn DxeDispatch>, | ||
| image_handle: Option<Handle>, | ||
| ) -> Result<()> { | ||
| let handle = image_handle.ok_or_else(|| { | ||
| log::error!("Handle not provided — required for LoadImage parent handle"); | ||
| EfiError::InvalidParameter | ||
| })?; | ||
|
|
||
| // Store the orchestrator and services for the BDS callback | ||
| BDS_CONTEXT.call_once(|| BdsContext { | ||
| orchestrator: self.orchestrator, | ||
| dxe_dispatch: *dxe_dispatch, | ||
| boot_services: boot_services.clone(), | ||
| runtime_services: runtime_services.clone(), | ||
| image_handle: *handle, | ||
| }); | ||
|
|
||
| // Install the BDS architectural protocol | ||
| let protocol = Box::leak(Box::new(bds::Protocol { entry: bds_entry_point })); | ||
|
|
||
| // SAFETY: protocol is a valid, leaked BDS protocol struct with a valid entry function pointer. | ||
| // Using unchecked variant because bds::Protocol does not implement ProtocolInterface. | ||
| unsafe { | ||
| boot_services.as_ref().install_protocol_interface_unchecked( | ||
| None, | ||
| &bds::PROTOCOL_GUID, | ||
| protocol as *mut _ as *mut c_void, | ||
| ) | ||
| } | ||
| .map_err(EfiError::from)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| /// BDS architectural protocol entry point. | ||
| /// | ||
| /// Called by the DXE core after all architectural protocols are installed and | ||
| /// all DXE drivers have been dispatched. Retrieves the stored context and | ||
| /// delegates to the orchestrator. | ||
| #[coverage(off)] // Extern "efiapi" callback — tested via integration tests | ||
| extern "efiapi" fn bds_entry_point(_this: *mut bds::Protocol) { | ||
| let Some(context) = BDS_CONTEXT.get() else { | ||
| panic!("BDS context not initialized — BootDispatcher entry_point was not called"); | ||
| }; | ||
|
|
||
| let Err(e) = context.orchestrator.execute( | ||
| &context.boot_services, | ||
| &context.runtime_services, | ||
| context.dxe_dispatch, | ||
| context.image_handle, | ||
| ); | ||
| panic!("BootOrchestrator::execute() failed: {e:?}"); | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use patina::{ | ||
| boot_services::StandardBootServices, component::service::dxe_dispatch::DxeDispatch, | ||
| runtime_services::StandardRuntimeServices, | ||
| }; | ||
| use r_efi::efi; | ||
|
|
||
| struct MockOrchestrator; | ||
|
|
||
| impl BootOrchestrator for MockOrchestrator { | ||
| fn execute( | ||
| &self, | ||
| _boot_services: &StandardBootServices, | ||
| _runtime_services: &StandardRuntimeServices, | ||
| _dxe_dispatch: &dyn DxeDispatch, | ||
| _image_handle: efi::Handle, | ||
| ) -> core::result::Result<!, patina::error::EfiError> { | ||
| Err(patina::error::EfiError::NotFound) | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_new_boot_dispatcher() { | ||
| let _dispatcher = BootDispatcher::new(MockOrchestrator); | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| //! Boot orchestrator trait definition. | ||
| //! | ||
| //! Defines the [`BootOrchestrator`] trait that platforms implement to customize | ||
| //! boot behavior. The [`BootDispatcher`](crate::BootDispatcher) component holds | ||
| //! a `Box<dyn BootOrchestrator>` and delegates to it when the DXE core invokes | ||
| //! the BDS architectural protocol. | ||
| //! | ||
| //! ## License | ||
| //! | ||
| //! Copyright (c) Microsoft Corporation. | ||
| //! | ||
| //! SPDX-License-Identifier: Apache-2.0 | ||
| //! | ||
| use patina::{ | ||
| boot_services::StandardBootServices, component::service::dxe_dispatch::DxeDispatch, error::EfiError, | ||
| runtime_services::StandardRuntimeServices, | ||
| }; | ||
| use r_efi::efi; | ||
|
|
||
| /// Trait for boot orchestration. | ||
| /// | ||
| /// Platforms implement this trait to define custom boot flows. The implementation | ||
| /// is passed to [`BootDispatcher::new()`](crate::BootDispatcher::new) and invoked | ||
| /// when the DXE core calls the BDS architectural protocol entry point. | ||
| /// | ||
| /// ## Built-in Implementation | ||
| /// | ||
| /// [`SimpleBootManager`](crate::SimpleBootManager) provides a default implementation | ||
| /// for platforms with straightforward boot topologies (primary/secondary devices, | ||
| /// optional hotkey). | ||
| /// | ||
| /// ## Custom Implementation | ||
| /// | ||
| /// ```rust,ignore | ||
| /// use patina_boot::BootOrchestrator; | ||
| /// | ||
| /// struct MyCustomBoot { /* ... */ } | ||
| /// | ||
| /// impl BootOrchestrator for MyCustomBoot { | ||
| /// fn execute( | ||
| /// &self, | ||
| /// boot_services: &StandardBootServices, | ||
| /// runtime_services: &StandardRuntimeServices, | ||
| /// dxe_services: &dyn DxeDispatch, | ||
| /// image_handle: efi::Handle, | ||
| /// ) -> Result<!, EfiError> { | ||
| /// // Custom boot flow... | ||
| /// // Return Err if all boot options are exhausted | ||
| /// Err(EfiError::NotFound) | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| pub trait BootOrchestrator: Send + Sync + 'static { | ||
| /// Execute the boot flow. | ||
| /// | ||
| /// Called by [`BootDispatcher`](crate::BootDispatcher) when the DXE core invokes | ||
| /// the BDS architectural protocol. This method should: | ||
| /// | ||
| /// 1. Enumerate devices (e.g., `connect_all()`) | ||
| /// 2. Signal BDS phase events (EndOfDxe, ReadyToBoot) | ||
| /// 3. Attempt to boot from configured device paths | ||
| /// 4. Handle boot failures | ||
| /// | ||
| /// A successful boot transfers control to the boot image and never returns. | ||
| /// If all boot options are exhausted, the implementation returns | ||
| /// `Err(EfiError)`. The `Ok` variant is uninhabitable (`!`), enforcing at | ||
| /// the type level that this method can only "succeed" by not returning. | ||
| fn execute( | ||
| &self, | ||
| boot_services: &StandardBootServices, | ||
| runtime_services: &StandardRuntimeServices, | ||
| dxe_services: &dyn DxeDispatch, | ||
| image_handle: efi::Handle, | ||
| ) -> Result<!, EfiError>; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.