Skip to main content

cargo_image_runner/bootloader/
limine.rs

1use super::{Bootloader, BootloaderFiles, ConfigFile};
2use crate::config::BootType;
3use crate::core::context::Context;
4use crate::core::error::{Error, Result};
5use std::path::PathBuf;
6
7#[cfg(feature = "limine")]
8use super::GitFetcher;
9
10/// Limine bootloader implementation.
11///
12/// Limine is a modern, feature-rich bootloader that supports both BIOS and UEFI.
13/// This implementation fetches Limine binaries from the official repository and
14/// configures them based on the user's limine.conf file.
15pub struct LimineBootloader {
16    repo_url: String,
17}
18
19impl LimineBootloader {
20    /// Limine repository URL.
21    const DEFAULT_REPO_URL: &'static str = "https://github.com/limine-bootloader/limine.git";
22
23    /// Create a new Limine bootloader instance.
24    pub fn new() -> Self {
25        Self {
26            repo_url: Self::DEFAULT_REPO_URL.to_string(),
27        }
28    }
29
30    /// Create a Limine bootloader with a custom repository URL.
31    pub fn with_repo_url(repo_url: String) -> Self {
32        Self { repo_url }
33    }
34
35    /// Get the Limine version from config.
36    fn get_version<'a>(&self, ctx: &'a Context) -> &'a str {
37        &ctx.config.bootloader.limine.version
38    }
39
40    /// Fetch Limine binaries from git.
41    #[cfg(feature = "limine")]
42    fn fetch_limine(&self, ctx: &Context) -> Result<PathBuf> {
43        let version = self.get_version(ctx);
44        let cache_dir = ctx.cache_dir.join("bootloaders");
45
46        let fetcher = GitFetcher::new(cache_dir, ctx.config.verbose);
47        fetcher.fetch_ref(&self.repo_url, "limine", version)
48    }
49
50    /// Stub when limine feature is disabled.
51    #[cfg(not(feature = "limine"))]
52    fn fetch_limine(&self, _ctx: &Context) -> Result<PathBuf> {
53        Err(Error::feature_not_enabled("limine"))
54    }
55}
56
57impl Default for LimineBootloader {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl Bootloader for LimineBootloader {
64    fn prepare(&self, ctx: &Context) -> Result<BootloaderFiles> {
65        let limine_repo = self.fetch_limine(ctx)?;
66
67        let mut files = BootloaderFiles::new();
68
69        // Prepare BIOS files if needed
70        if ctx.config.boot.boot_type.needs_bios() {
71            // Copy limine-bios.sys to boot directory
72            let limine_bios = limine_repo.join("limine-bios.sys");
73            if !limine_bios.exists() {
74                return Err(Error::bootloader(
75                    "limine-bios.sys not found in Limine repository. \
76                     Make sure you're using a binary release (e.g., v8.x-binary)."
77                        .to_string(),
78                ));
79            }
80
81            files = files.add_system_file(limine_bios, "limine-bios.sys".into());
82
83            // CD-specific BIOS boot binary for ISO images
84            let limine_bios_cd = limine_repo.join("limine-bios-cd.bin");
85            if !limine_bios_cd.exists() {
86                return Err(Error::bootloader(
87                    "limine-bios-cd.bin not found in Limine repository. \
88                     Make sure you're using a binary release (e.g., v8.x-binary)."
89                        .to_string(),
90                ));
91            }
92            files = files.add_system_file(limine_bios_cd, "limine-bios-cd.bin".into());
93        }
94
95        // Prepare UEFI files if needed
96        if ctx.config.boot.boot_type.needs_uefi() {
97            // Copy BOOTX64.EFI to EFI/BOOT directory
98            let bootx64 = limine_repo.join("BOOTX64.EFI");
99            if !bootx64.exists() {
100                return Err(Error::bootloader(
101                    "BOOTX64.EFI not found in Limine repository. \
102                     Make sure you're using a binary release (e.g., v8.x-binary)."
103                        .to_string(),
104                ));
105            }
106
107            files = files.add_uefi_file(bootx64, "efi/boot/bootx64.efi".into());
108
109            // CD-specific UEFI boot binary for ISO images
110            let limine_uefi_cd = limine_repo.join("limine-uefi-cd.bin");
111            if !limine_uefi_cd.exists() {
112                return Err(Error::bootloader(
113                    "limine-uefi-cd.bin not found in Limine repository. \
114                     Make sure you're using a binary release (e.g., v8.x-binary)."
115                        .to_string(),
116                ));
117            }
118            files = files.add_system_file(limine_uefi_cd, "limine-uefi-cd.bin".into());
119        }
120
121        // Copy the kernel executable to the boot directory
122        files = files.add_system_file(
123            ctx.executable.clone(),
124            PathBuf::from("boot").join(
125                ctx.executable
126                    .file_name()
127                    .ok_or_else(|| Error::config("invalid executable path"))?,
128            ),
129        );
130
131        Ok(files)
132    }
133
134    fn config_files(&self, ctx: &Context) -> Result<Vec<ConfigFile>> {
135        let mut configs = Vec::new();
136
137        // Check for limine.conf in the workspace or specified path
138        let config_path = if let Some(ref path) = ctx.config.bootloader.config_file {
139            ctx.workspace_root.join(path)
140        } else {
141            ctx.workspace_root.join("limine.conf")
142        };
143
144        if config_path.exists() {
145            configs.push(
146                ConfigFile::new(config_path, "limine.conf".into())
147                    .with_template_processing(),
148            );
149        } else {
150            // Generate a default limine.conf if none exists
151            return Err(Error::config(format!(
152                "limine.conf not found at {}. Please create a Limine configuration file.",
153                config_path.display()
154            )));
155        }
156
157        Ok(configs)
158    }
159
160    fn boot_type(&self) -> BootType {
161        // Limine supports both BIOS and UEFI
162        BootType::Hybrid
163    }
164
165    fn name(&self) -> &str {
166        "Limine"
167    }
168
169    fn validate_config(&self, ctx: &Context) -> Result<()> {
170        // Check that version is specified
171        let version = self.get_version(ctx);
172        if version.is_empty() {
173            return Err(Error::config(
174                "Limine version not specified in configuration",
175            ));
176        }
177
178        // Recommend binary releases
179        if !version.contains("binary") {
180            eprintln!(
181                "Warning: Limine version '{}' may require building from source. \
182                 Consider using a binary release like 'v8.x-binary' for faster builds.",
183                version
184            );
185        }
186
187        Ok(())
188    }
189}