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_elem(&self, elem: &BootIndex) -> BootOrder {
82        let mut v = self.0.clone();
83        if let Some(index) = self.position(elem) {
84            v.remove(index);
85        }
86        BootOrder(v)
87    }
88}
89
90impl fmt::Display for BootOrder {
91    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92        let s: Vec<String> = self.0.iter().map(|a| format!("{a}")).collect();
93        write!(f, "{}", s.join(","))
94    }
95}
96
97impl From<&BootOrder> for Vec<u8> {
98    fn from(value: &BootOrder) -> Vec<u8> {
99        value.0.iter().flat_map(Vec::from).collect()
100    }
101}
102
103// structs and consts for boot entries
104pub const LOAD_OPTION_ACTIVE: u32 = 0x01;
105pub const LOAD_OPTION_FORCE_RECONNECT: u32 = 0x02;
106pub const LOAD_OPTION_HIDDEN: u32 = 0x08;
107
108pub const LOAD_OPTION_CATEGORY: u32 = 0x1f00;
109pub const LOAD_OPTION_CATEGORY_BOOT: u32 = 0x1f00;
110pub const LOAD_OPTION_CATEGORY_APP: u32 = 0x0100;
111
112#[derive(Debug)]
113pub enum BootEntryOptData {
114    None,
115    String { string: String },
116    Guid { guid: Guid },
117    Data { bytes: Vec<u8> },
118}
119
120impl fmt::Display for BootEntryOptData {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        match self {
123            BootEntryOptData::String { string } => {
124                write!(f, "\"{string}\"")
125            }
126            BootEntryOptData::Guid { guid } => {
127                write!(f, "{guid}")
128            }
129            BootEntryOptData::Data { bytes } => {
130                write!(f, "{bytes:?}")
131            }
132            BootEntryOptData::None => Ok(()),
133        }
134    }
135}
136
137#[derive(Debug)]
138pub struct BootEntry {
139    pub attributes: u32,
140    pub title: String,
141    pub devpath: DevPath,
142    pub optdata: BootEntryOptData,
143}
144
145impl BootEntry {
146    pub fn new_boot(title: &str, devpath: DevPath, optdata: Option<String>) -> BootEntry {
147        BootEntry {
148            attributes: LOAD_OPTION_ACTIVE,
149            title: title.to_string(),
150            devpath,
151            optdata: match optdata {
152                Some(s) => BootEntryOptData::String { string: s },
153                None => BootEntryOptData::None,
154            },
155        }
156    }
157
158    pub fn is_active(&self) -> bool {
159        (self.attributes & LOAD_OPTION_ACTIVE) != 0
160    }
161
162    pub fn is_hidden(&self) -> bool {
163        (self.attributes & LOAD_OPTION_HIDDEN) != 0
164    }
165
166    fn category(&self) -> u32 {
167        self.attributes & LOAD_OPTION_CATEGORY
168    }
169
170    pub fn is_boot(&self) -> bool {
171        self.category() == LOAD_OPTION_CATEGORY_BOOT
172    }
173
174    pub fn is_app(&self) -> bool {
175        self.category() == LOAD_OPTION_CATEGORY_APP
176    }
177}