bootmgr_rs_core/
boot.rs

1// SPDX-FileCopyrightText: 2025 some100 <ootinnyoo@outlook.com>
2// SPDX-License-Identifier: MIT
3
4//! Provides [`BootMgr`], a struct which abstracts most of loading a [`Config`].
5
6use alloc::vec::Vec;
7use log::error;
8use uefi::Handle;
9
10use crate::{
11    BootResult,
12    boot::{action::add_special_boot, config::BootConfig, loader::load_boot_option},
13    config::{Config, scan_configs},
14    system::drivers::load_drivers,
15};
16
17pub mod action;
18pub mod bli;
19pub mod config;
20pub mod devicetree;
21pub mod loader;
22pub mod secure_boot;
23
24/// The storage for configuration files.
25pub struct BootMgr {
26    /// The configuration of the boot manager.
27    pub boot_config: BootConfig,
28
29    /// The boot options.
30    configs: Vec<Config>,
31}
32
33impl BootMgr {
34    /// Creates a new [`BootMgr`], load drivers, then populate it with [`Config`]s.
35    ///
36    /// It will also add special boot options, like Reboot, Shutdown, and Reset to Firmware.
37    /// This will also parse the main configuration file located at `\\loader\\bootmgr-rs.conf`
38    /// for user settings.
39    ///
40    /// # Errors
41    ///
42    /// May return an `Error` if a fatal error occurred when parsing the [`BootConfig`] (such as the image handle not
43    /// supporting `SimpleFileSystem`) or when parsing the [`Config`]s.
44    pub fn new() -> BootResult<Self> {
45        let _ = bli::export_variables();
46
47        let boot_config = BootConfig::new()?;
48        if boot_config.drivers {
49            load_drivers(&boot_config.driver_path)?; // load drivers before configs from other fs are parsed
50        }
51
52        let mut configs = scan_configs()?;
53        add_special_boot(&mut configs, &boot_config);
54
55        if let Some(default) = boot_config.default {
56            let _ = bli::set_default_entry(&configs, default);
57        }
58
59        let _ = bli::set_loader_entries(&configs);
60
61        Ok(Self {
62            boot_config,
63            configs,
64        })
65    }
66
67    /// Load a boot option from a [`Config`] given the index.
68    ///
69    /// # Errors
70    ///
71    /// May return an `Error` if an error occurred while loading the boot option.
72    pub fn load(&mut self, selected: usize) -> BootResult<Handle> {
73        let config = &self.configs[selected];
74        match load_boot_option(config) {
75            Ok(handle) => {
76                let _ = bli::generate_random_seed();
77                let _ = bli::record_exit_time();
78                Ok(handle)
79            }
80            Err(e) => {
81                self.configs[selected].bad = true;
82                Err(e) // after setting as bad, finally return the error
83            }
84        }
85    }
86
87    /// Returns a reference to the inner [`Vec<Config>`].
88    #[must_use = "Has no effect if the result is unused"]
89    pub const fn list(&self) -> &Vec<Config> {
90        &self.configs
91    }
92
93    /// Returns a mutable reference to the inner [`Vec<Config>`].
94    #[must_use = "Has no effect if the result is unused"]
95    pub const fn list_mut(&mut self) -> &mut Vec<Config> {
96        &mut self.configs
97    }
98
99    /// Returns a mutable reference to an inner [`Config`].
100    pub fn get_config(&mut self, option: usize) -> &mut Config {
101        &mut self.configs[option]
102    }
103
104    /// Gets the default boot option.
105    ///
106    /// If the default boot option is not set, then 0 is returned
107    #[must_use = "Has no effect if the result is unused"]
108    pub fn get_default(&self) -> usize {
109        if let Some(default) = bli::get_default_entry(&self.configs)
110            && default < self.configs.len()
111        {
112            default
113        } else {
114            0
115        }
116    }
117
118    /// Sets the default boot option by index.
119    ///
120    /// This is stored in a UEFI variable. Due to the poor quality of how UEFI variables are stored sometimes, this
121    /// cannot be completely reliable across all firmware implementations.
122    pub fn set_default(&self, option: usize) {
123        if option < self.configs.len()
124            && let Err(e) = bli::set_default_entry(&self.configs, option)
125        {
126            error!("Failed to set LoaderEntryDefault UEFI variable: {e}");
127        }
128    }
129
130    /// Validates the inner [`Vec<Config>`] through various criteria.
131    ///
132    /// If any of the [`Config`]s are found to be invalid, then they will be
133    /// filtered.
134    pub fn validate(&mut self) {
135        self.configs.retain(Config::is_good);
136    }
137}