Skip to main content

virtfw_libefi/efivar/
boot.rs

1//! efi boot configuration
2
3extern crate alloc;
4use alloc::format;
5use alloc::string::{String, ToString};
6use alloc::vec;
7use alloc::vec::Vec;
8
9use core::fmt;
10
11use uguid::Guid;
12
13use crate::efivar::devpath::DevPath;
14
15// efi variable names
16pub const BOOT_CURRENT: &str = "BootCurrent";
17pub const BOOT_NEXT: &str = "BootNext";
18pub const BOOT_ORDER: &str = "BootOrder";
19pub const SECURE_BOOT: &str = "SecureBoot";
20
21// struct for boot index
22#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
23pub struct BootIndex(pub u16);
24
25impl fmt::Display for BootIndex {
26    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27        write!(f, "{:04X}", self.0)
28    }
29}
30
31impl From<&BootIndex> for Vec<u8> {
32    fn from(value: &BootIndex) -> Vec<u8> {
33        let bytes = value.0.to_le_bytes();
34        bytes.to_vec()
35    }
36}
37
38// struct for boot order (boot index list)
39#[derive(Debug)]
40pub struct BootOrder(pub Vec<BootIndex>);
41
42impl BootOrder {
43    pub fn new(elem: &BootIndex) -> BootOrder {
44        let v = vec![*elem];
45        BootOrder(v)
46    }
47
48    pub fn empty() -> BootOrder {
49        let v = Vec::new();
50        BootOrder(v)
51    }
52
53    pub fn len(&self) -> usize {
54        self.0.len()
55    }
56
57    pub fn is_empty(&self) -> bool {
58        self.0.is_empty()
59    }
60
61    pub fn position(&self, elem: &BootIndex) -> Option<usize> {
62        (0..self.0.len()).find(|&pos| elem == self.0.get(pos).unwrap())
63    }
64
65    pub fn insert(&self, index: usize, elem: &BootIndex) -> BootOrder {
66        let mut v = self.0.clone();
67        if self.position(elem).is_none() && index <= v.len() {
68            v.insert(index, *elem);
69        }
70        BootOrder(v)
71    }
72
73    pub fn remove(&self, index: usize) -> BootOrder {
74        let mut v = self.0.clone();
75        if index < v.len() {
76            v.remove(index);
77        }
78        BootOrder(v)
79    }
80
81    pub fn remove_elems(&self, elems: &[&BootIndex]) -> BootOrder {
82        let mut v = self.0.clone();
83        for elem in elems {
84            if let Some(index) = v.iter().position(|i| i == *elem) {
85                v.remove(index);
86            }
87        }
88        BootOrder(v)
89    }
90
91    pub fn remove_elem(&self, elem: &BootIndex) -> BootOrder {
92        let elems = vec![elem];
93        self.remove_elems(elems.as_slice())
94    }
95}
96
97impl fmt::Display for BootOrder {
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        let s: Vec<String> = self.0.iter().map(|a| format!("{a}")).collect();
100        write!(f, "{}", s.join(","))
101    }
102}
103
104impl From<&BootOrder> for Vec<u8> {
105    fn from(value: &BootOrder) -> Vec<u8> {
106        value.0.iter().flat_map(Vec::from).collect()
107    }
108}
109
110// structs and consts for boot entries
111pub const LOAD_OPTION_ACTIVE: u32 = 0x01;
112pub const LOAD_OPTION_FORCE_RECONNECT: u32 = 0x02;
113pub const LOAD_OPTION_HIDDEN: u32 = 0x08;
114
115pub const LOAD_OPTION_CATEGORY: u32 = 0x1f00;
116pub const LOAD_OPTION_CATEGORY_BOOT: u32 = 0x1f00;
117pub const LOAD_OPTION_CATEGORY_APP: u32 = 0x0100;
118
119#[derive(Debug)]
120pub enum BootEntryOptData {
121    None,
122    String { string: String },
123    Guid { guid: Guid },
124    Data { bytes: Vec<u8> },
125}
126
127impl fmt::Display for BootEntryOptData {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        match self {
130            BootEntryOptData::String { string } => {
131                write!(f, "\"{string}\"")
132            }
133            BootEntryOptData::Guid { guid } => {
134                write!(f, "{guid}")
135            }
136            BootEntryOptData::Data { bytes } => {
137                write!(f, "{bytes:?}")
138            }
139            BootEntryOptData::None => Ok(()),
140        }
141    }
142}
143
144#[derive(Debug)]
145pub struct BootEntry {
146    pub attributes: u32,
147    pub title: String,
148    pub devpath: DevPath,
149    pub optdata: BootEntryOptData,
150}
151
152impl BootEntry {
153    pub fn new_boot(title: &str, devpath: DevPath, optdata: Option<String>) -> BootEntry {
154        BootEntry {
155            attributes: LOAD_OPTION_ACTIVE,
156            title: title.to_string(),
157            devpath,
158            optdata: match optdata {
159                Some(s) => BootEntryOptData::String { string: s },
160                None => BootEntryOptData::None,
161            },
162        }
163    }
164
165    pub fn is_active(&self) -> bool {
166        (self.attributes & LOAD_OPTION_ACTIVE) != 0
167    }
168
169    pub fn is_hidden(&self) -> bool {
170        (self.attributes & LOAD_OPTION_HIDDEN) != 0
171    }
172
173    fn category(&self) -> u32 {
174        self.attributes & LOAD_OPTION_CATEGORY
175    }
176
177    pub fn is_boot(&self) -> bool {
178        self.category() == LOAD_OPTION_CATEGORY_BOOT
179    }
180
181    pub fn is_app(&self) -> bool {
182        self.category() == LOAD_OPTION_CATEGORY_APP
183    }
184}