66//! of the UEFI specification for details.
77
88use crate :: { table, CStr16 , Error , Result , Status , StatusExt } ;
9+ use core:: mem;
910use core:: ptr:: { self , NonNull } ;
1011
1112#[ cfg( feature = "alloc" ) ]
12- use { crate :: mem:: make_boxed, alloc:: boxed:: Box } ;
13+ use {
14+ crate :: mem:: make_boxed, crate :: Guid , alloc:: borrow:: ToOwned , alloc:: boxed:: Box , alloc:: vec:: Vec ,
15+ } ;
1316
1417#[ cfg( all( feature = "unstable" , feature = "alloc" ) ) ]
1518use alloc:: alloc:: Global ;
@@ -18,6 +21,9 @@ pub use crate::table::runtime::{Daylight, Time, TimeCapabilities, TimeError, Tim
1821pub use uefi_raw:: capsule:: { CapsuleBlockDescriptor , CapsuleFlags , CapsuleHeader } ;
1922pub use uefi_raw:: table:: runtime:: { ResetType , VariableAttributes , VariableVendor } ;
2023
24+ #[ cfg( feature = "alloc" ) ]
25+ pub use crate :: table:: runtime:: VariableKey ;
26+
2127fn runtime_services_raw_panicking ( ) -> NonNull < uefi_raw:: table:: runtime:: RuntimeServices > {
2228 let st = table:: system_table_raw_panicking ( ) ;
2329 // SAFETY: valid per requirements of `set_system_table`.
@@ -141,6 +147,146 @@ pub fn get_variable_boxed(
141147 }
142148}
143149
150+ /// Gets each variable key (name and vendor) one at a time.
151+ ///
152+ /// This is used to iterate over variable keys. See [`variable_keys`] for a more
153+ /// convenient interface that requires the `alloc` feature.
154+ ///
155+ /// To get the first variable key, `name` must be initialized to start with a
156+ /// null character. The `vendor` value is arbitrary. On success, the first
157+ /// variable's name and vendor will be written out to `name` and `vendor`. Keep
158+ /// calling `get_next_variable_key` with the same `name` and `vendor` references
159+ /// to get the remaining variable keys.
160+ ///
161+ /// All variable names should be valid strings, but this may not be enforced by
162+ /// firmware. To convert to a string, truncate at the first null and call
163+ /// [`CStr16::from_u16_with_nul`].
164+ ///
165+ /// # Errors
166+ ///
167+ /// * [`Status::NOT_FOUND`]: indicates end of iteration, the last variable keys
168+ /// was retrieved by the previous call to `get_next_variable_key`.
169+ /// * [`Status::BUFFER_TOO_SMALL`]: `name` is not large enough. The required
170+ /// size (in `u16` characters, not bytes) will be returned in the error data.
171+ /// * [`Status::INVALID_PARAMETER`]: `name` does not contain a null character, or
172+ /// the `name` and `vendor` are not an existing variable.
173+ /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
174+ /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
175+ /// after exiting boot services.
176+ pub fn get_next_variable_key (
177+ name : & mut [ u16 ] ,
178+ vendor : & mut VariableVendor ,
179+ ) -> Result < ( ) , Option < usize > > {
180+ let rt = runtime_services_raw_panicking ( ) ;
181+ let rt = unsafe { rt. as_ref ( ) } ;
182+
183+ let mut name_size_in_bytes = mem:: size_of_val ( name) ;
184+
185+ let status = unsafe {
186+ ( rt. get_next_variable_name ) ( & mut name_size_in_bytes, name. as_mut_ptr ( ) , & mut vendor. 0 )
187+ } ;
188+ match status {
189+ Status :: SUCCESS => Ok ( ( ) ) ,
190+ Status :: BUFFER_TOO_SMALL => Err ( Error :: new (
191+ status,
192+ Some ( name_size_in_bytes / mem:: size_of :: < u16 > ( ) ) ,
193+ ) ) ,
194+ _ => Err ( Error :: new ( status, None ) ) ,
195+ }
196+ }
197+
198+ /// Get an iterator over all UEFI variables.
199+ ///
200+ /// See [`VariableKeys`] for details.
201+ #[ cfg( feature = "alloc" ) ]
202+ #[ must_use]
203+ pub fn variable_keys ( ) -> VariableKeys {
204+ VariableKeys :: new ( )
205+ }
206+
207+ /// Iterator over all UEFI variables.
208+ ///
209+ /// Each iteration yields a `Result<`[`VariableKey`]`>`. Error values:
210+ ///
211+ /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
212+ /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
213+ /// after exiting boot services.
214+ #[ cfg( feature = "alloc" ) ]
215+ #[ derive( Debug ) ]
216+ pub struct VariableKeys {
217+ name : Vec < u16 > ,
218+ vendor : VariableVendor ,
219+ is_done : bool ,
220+ }
221+
222+ #[ cfg( feature = "alloc" ) ]
223+ impl VariableKeys {
224+ fn new ( ) -> Self {
225+ // Create a the name buffer with a reasonable default capacity, and
226+ // initialize it to an empty null-terminated string.
227+ let mut name = Vec :: with_capacity ( 32 ) ;
228+ name. push ( 0 ) ;
229+
230+ Self {
231+ // Give the name buffer a reasonable default capacity.
232+ name,
233+ // The initial vendor GUID is arbitrary.
234+ vendor : VariableVendor ( Guid :: default ( ) ) ,
235+ is_done : false ,
236+ }
237+ }
238+ }
239+
240+ #[ cfg( feature = "alloc" ) ]
241+ impl Iterator for VariableKeys {
242+ type Item = Result < VariableKey > ;
243+
244+ fn next ( & mut self ) -> Option < Result < VariableKey > > {
245+ if self . is_done {
246+ return None ;
247+ }
248+
249+ let mut result = get_next_variable_key ( & mut self . name , & mut self . vendor ) ;
250+
251+ // If the name buffer was too small, resize it to be big enough and call
252+ // `get_next_variable_key` again.
253+ if let Err ( err) = & result {
254+ if let Some ( required_size) = err. data ( ) {
255+ self . name . resize ( * required_size, 0u16 ) ;
256+ result = get_next_variable_key ( & mut self . name , & mut self . vendor ) ;
257+ }
258+ }
259+
260+ match result {
261+ Ok ( ( ) ) => {
262+ // Copy the name buffer, truncated after the first null
263+ // character (if one is present).
264+ let name = if let Some ( nul_pos) = self . name . iter ( ) . position ( |c| * c == 0 ) {
265+ self . name [ ..=nul_pos] . to_owned ( )
266+ } else {
267+ self . name . clone ( )
268+ } ;
269+ Some ( Ok ( VariableKey {
270+ name,
271+ vendor : self . vendor ,
272+ } ) )
273+ }
274+ Err ( err) => {
275+ if err. status ( ) == Status :: NOT_FOUND {
276+ // This status indicates the end of the list. The final variable
277+ // has already been yielded at this point, so return `None`.
278+ self . is_done = true ;
279+ None
280+ } else {
281+ // Return the error and end iteration.
282+ self . is_done = true ;
283+ Some ( Err ( err. to_err_without_payload ( ) ) )
284+ }
285+ }
286+ }
287+ }
288+ }
289+
144290/// Sets the value of a variable. This can be used to create a new variable,
145291/// update an existing variable, or (when the size of `data` is zero)
146292/// delete a variable.
0 commit comments