6
6
//! of the UEFI specification for details.
7
7
8
8
use crate :: { table, CStr16 , Error , Result , Status , StatusExt } ;
9
+ use core:: mem;
9
10
use core:: ptr:: { self , NonNull } ;
10
11
11
12
#[ 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
+ } ;
13
16
14
17
#[ cfg( all( feature = "unstable" , feature = "alloc" ) ) ]
15
18
use alloc:: alloc:: Global ;
@@ -18,6 +21,9 @@ pub use crate::table::runtime::{Daylight, Time, TimeCapabilities, TimeError, Tim
18
21
pub use uefi_raw:: capsule:: { CapsuleBlockDescriptor , CapsuleFlags , CapsuleHeader } ;
19
22
pub use uefi_raw:: table:: runtime:: { ResetType , VariableAttributes , VariableVendor } ;
20
23
24
+ #[ cfg( feature = "alloc" ) ]
25
+ pub use crate :: table:: runtime:: VariableKey ;
26
+
21
27
fn runtime_services_raw_panicking ( ) -> NonNull < uefi_raw:: table:: runtime:: RuntimeServices > {
22
28
let st = table:: system_table_raw_panicking ( ) ;
23
29
// SAFETY: valid per requirements of `set_system_table`.
@@ -141,6 +147,146 @@ pub fn get_variable_boxed(
141
147
}
142
148
}
143
149
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
+
144
290
/// Sets the value of a variable. This can be used to create a new variable,
145
291
/// update an existing variable, or (when the size of `data` is zero)
146
292
/// delete a variable.
0 commit comments