runtime_services/
variable_services.rs

1use core::mem;
2
3use alloc::vec::Vec;
4use fallible_streaming_iterator::FallibleStreamingIterator;
5use r_efi::efi::{self, Guid};
6
7use crate::RuntimeServices;
8
9/// Status information returned by [`RuntimeServices::get_variable_unchecked`]
10#[derive(Debug)]
11pub enum GetVariableStatus {
12    /// The variable was unable to be retrieved
13    Error(efi::Status),
14    /// The variable was found, but the buffer provided wasn't large enough
15    BufferTooSmall {
16        /// The size of a buffer needed to retrieve the variable data
17        data_size: usize,
18        /// The attributes of the variable
19        attributes: u32,
20    },
21    /// The variable was successfully retrieved
22    Success {
23        /// The size of the variable data retrieved
24        data_size: usize,
25        /// The attributes of the variable
26        attributes: u32,
27    },
28}
29
30/// Variable information returned by [`RuntimeServices::query_variable_info`]
31#[derive(Debug)]
32pub struct VariableInfo {
33    /// The maximum size of the storage space available for the EFI variables associated with the attributes specified
34    pub maximum_variable_storage_size: u64,
35    /// The remaining size of the storage space available for EFI variables associated with the attributes specified
36    pub remaining_variable_storage_size: u64,
37    // The maximum size of an individual EFI variable associated with the attributes specified
38    pub maximum_variable_size: u64,
39}
40
41/// Uniquely identifies a UEFI variable
42#[derive(Debug)]
43pub struct VariableIdentifier {
44    /// The name of a UEFI variable
45    name: Vec<u16>,
46    /// The namespace of a UEFI variable
47    namespace: efi::Guid,
48}
49
50/// Provides a [`FallibleStreamingIterator`] over UEFI variable names
51///
52/// Produces an EFI status on error.
53///
54/// # Examples
55///
56/// ## Iterating through all UEFI variable names
57/// ```ignore
58/// pub static RUNTIME_SERVICES: StandardRuntimeServices =
59///     StandardRuntimeServices::new(&(*runtime_services_ptr));
60/// let mut iter = VariableNameIterator::new_from_first(runtime_services);
61/// while let Some(variable_identifier) = iter.next()? {
62///     some_function(variable_identifier.name, variable_identifier.namespace);
63/// }
64/// ```
65///
66/// ## Iterating through UEFI variable names, starting with a known one
67/// ```ignore
68/// let mut iter = VariableNameIterator::new_from_variable(
69///     &SOME_VARIABLE_NAME,
70///     &SOME_VARIABLE_NAMESPACE,
71///     runtime_services
72/// );
73///
74/// while let Some(variable_identifier) = iter.next()? {
75///     some_function(variable_identifier.name, variable_identifier.namespace);
76/// }
77/// ```
78#[derive(Debug)]
79pub struct VariableNameIterator<'a, R: RuntimeServices> {
80    rs: &'a R,
81
82    current: VariableIdentifier,
83    next: VariableIdentifier,
84    finished: bool,
85}
86
87impl<'a, R: RuntimeServices> VariableNameIterator<'a, R> {
88    /// Produce a new iterator from the beginning of the UEFI variable list
89    pub fn new_from_first(runtime_services: &'a R) -> Self {
90        Self {
91            rs: &runtime_services,
92            current: VariableIdentifier {
93                name: {
94                    // Previous name should be an empty string to get the first variable
95                    let mut prev_name = Vec::<u16>::with_capacity(1);
96                    prev_name.resize(1, 0);
97
98                    prev_name
99                },
100                // When calling with an empty name, the GUID is ignored.
101                // We can just set it to zero.
102                namespace: Guid::from_bytes(&[0x0; 16]),
103            },
104            next: VariableIdentifier { name: Vec::<u16>::new(), namespace: Guid::from_bytes(&[0x0; 16]) },
105            finished: false,
106        }
107    }
108
109    /// Produce a new iterator, starting from a given variable
110    pub fn new_from_variable(name: &[u16], namespace: &efi::Guid, runtime_services: &'a R) -> Self {
111        Self {
112            rs: &runtime_services,
113            current: VariableIdentifier { name: name.to_vec(), namespace: namespace.clone() },
114            next: VariableIdentifier { name: Vec::<u16>::new(), namespace: Guid::from_bytes(&[0x0; 16]) },
115            finished: false,
116        }
117    }
118}
119
120impl<'a, R: RuntimeServices> FallibleStreamingIterator for VariableNameIterator<'a, R> {
121    type Item = VariableIdentifier;
122    type Error = efi::Status;
123
124    fn advance(&mut self) -> Result<(), Self::Error> {
125        unsafe {
126            // Don't do anything if we've reached the end already
127            if self.finished {
128                return Ok(());
129            }
130
131            let status = self.rs.get_next_variable_name_unchecked(
132                &self.current.name,
133                &self.current.namespace,
134                &mut self.next.name,
135                &mut self.next.namespace,
136            );
137
138            mem::swap(&mut self.current, &mut self.next);
139
140            if status.is_err() && status.unwrap_err() == efi::Status::NOT_FOUND {
141                self.finished = true;
142                return Ok(());
143            } else {
144                return status;
145            }
146        }
147    }
148
149    fn get(&self) -> Option<&Self::Item> {
150        if self.finished {
151            None
152        } else {
153            Some(&self.current)
154        }
155    }
156}
157
158#[cfg(test)]
159mod test {
160    use efi;
161
162    use super::*;
163    use crate::StandardRuntimeServices;
164    use core::mem;
165
166    use crate::test::*;
167
168    #[test]
169    fn test_variable_name_iterator_from_first() {
170        let rs: &StandardRuntimeServices<'_> =
171            runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
172
173        let mut iter = VariableNameIterator::new_from_first(rs);
174
175        // Make sure the first result corresponds to DUMMY_FIRST_NAME
176        let mut status = iter.next();
177        assert!(status.is_ok());
178        assert!(status.unwrap().is_some());
179        let mut variable_identifier = status.unwrap().unwrap();
180        assert_eq!(variable_identifier.name, DUMMY_FIRST_NAME);
181        assert_eq!(variable_identifier.namespace, DUMMY_FIRST_NAMESPACE);
182
183        // Make sure the second result corresponds to DUMMY_SECOND_NAME
184        status = iter.next();
185        assert!(status.is_ok());
186        assert!(status.unwrap().is_some());
187        variable_identifier = status.unwrap().unwrap();
188        assert_eq!(variable_identifier.name, DUMMY_SECOND_NAME);
189        assert_eq!(variable_identifier.namespace, DUMMY_SECOND_NAMESPACE);
190
191        // Make sure the third result indicates we've reached the end
192        status = iter.next();
193        assert!(status.is_ok());
194        assert!(status.unwrap().is_none());
195    }
196
197    #[test]
198    fn test_variable_name_iterator_from_second() {
199        let rs: &StandardRuntimeServices<'_> =
200            runtime_services!(get_next_variable_name = mock_efi_get_next_variable_name);
201
202        let mut iter = VariableNameIterator::new_from_variable(&DUMMY_FIRST_NAME, &DUMMY_FIRST_NAMESPACE, rs);
203
204        // Make sure the first result corresponds to DUMMY_SECOND_NAME
205        let mut status = iter.next();
206        assert!(status.is_ok());
207        assert!(status.unwrap().is_some());
208        let variable_identifier = status.unwrap().unwrap();
209        assert_eq!(variable_identifier.name, DUMMY_SECOND_NAME);
210        assert_eq!(variable_identifier.namespace, DUMMY_SECOND_NAMESPACE);
211
212        // Make sure the second result indicates we've reached the end
213        status = iter.next();
214        assert!(status.is_ok());
215        assert!(status.unwrap().is_none());
216    }
217}