//! Bundled iPXE boot binaries and default chain script. //! //! At build time, we expect the iPXE binaries to live at `assets/ipxe/` at //! the workspace root. They are embedded into the OpenPXE binary via //! `rust-embed` so the container image is self-contained. If a binary is //! missing, that architecture simply won't have PXE support — we log at //! startup and serve what we have. //! //! Filename convention (matches `ClientArch::ipxe_bootfile`): //! - `undionly.kpxe` — Legacy x86 BIOS //! - `snponly-i386.efi` — IA32 UEFI //! - `snponly.efi` — x86_64 UEFI //! - `snponly-arm32.efi` — ARM32 UEFI //! - `snponly-arm64.efi` — ARM64 UEFI //! - `ipxe.efi` (fallback) — UEFI with bundled drivers, if snponly fails on a NIC //! - `wimboot` — Windows boot shim (fetched separately for WIM chains) #![forbid(unsafe_code)] use openpxe_core::ClientArch; use rust_embed::Embed; #[derive(Embed)] #[folder = "../../assets/ipxe/"] #[include = "*.kpxe"] #[include = "*.efi"] #[include = "wimboot"] pub struct IpxeAssets; /// Return the embedded iPXE binary for `arch`, or `None` if we didn't bundle /// one for that architecture. #[must_use] pub fn bootfile_bytes(arch: ClientArch) -> Option> { let name = arch.ipxe_bootfile()?; IpxeAssets::get(name).map(|f| f.data.into_owned()) } /// Return a named asset directly (e.g. `wimboot`, or a fallback `ipxe.efi`). #[must_use] pub fn asset_bytes(name: &str) -> Option> { IpxeAssets::get(name).map(|f| f.data.into_owned()) } /// Same as [`asset_bytes`] but returns the embedded slice directly, /// avoiding the heap copy when the caller only needs to read the /// payload. Falls back to None for unknown names. #[must_use] pub fn asset_slice(name: &str) -> Option> { IpxeAssets::get(name).map(|f| f.data) } /// Enumerate embedded asset filenames. Useful for startup logging so the /// operator can immediately tell which architectures will work. pub fn list_assets() -> Vec { IpxeAssets::iter().map(std::borrow::Cow::into_owned).collect() } /// Log at startup which iPXE binaries are present and which are missing. pub fn log_availability() { let have: std::collections::HashSet = list_assets().into_iter().collect(); let needed = [ (ClientArch::LegacyX86, "undionly.kpxe"), (ClientArch::Ia32Uefi, "snponly-i386.efi"), (ClientArch::X64Uefi, "snponly.efi"), // ARM32 UEFI deferred — no upstream snponly binary published. (ClientArch::Arm64Uefi, "snponly-arm64.efi"), ]; for (arch, name) in needed { if have.contains(name) { tracing::info!(target: "openpxe::ipxe", "bundled iPXE for {}: {}", arch.as_str(), name); } else { tracing::warn!( target: "openpxe::ipxe", "MISSING iPXE binary for {}: {} — clients of this arch will not PXE boot", arch.as_str(), name ); } } }