@@ -9,7 +9,10 @@ use crate::{table, CStr16, Error, Result, Status, StatusExt};
9
9
use core:: ptr:: { self , NonNull } ;
10
10
11
11
#[ cfg( feature = "alloc" ) ]
12
- use { crate :: mem:: make_boxed, alloc:: boxed:: Box } ;
12
+ use {
13
+ crate :: mem:: make_boxed, crate :: Guid , alloc:: borrow:: ToOwned , alloc:: boxed:: Box ,
14
+ alloc:: vec:: Vec , core:: mem,
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,145 @@ 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 < ' n , ' v > (
177
+ name : & ' n mut [ u16 ] ,
178
+ vendor : & ' v 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 = name. len ( ) * mem:: size_of :: < u16 > ( ) ;
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
+ pub fn variable_keys ( ) -> VariableKeys {
203
+ VariableKeys :: new ( )
204
+ }
205
+
206
+ /// Iterator over all UEFI variables.
207
+ ///
208
+ /// Each iteration yields a `Result<`[`VariableKey`]`>`. Error values:
209
+ ///
210
+ /// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
211
+ /// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
212
+ /// after exiting boot services.
213
+ #[ cfg( feature = "alloc" ) ]
214
+ #[ derive( Debug ) ]
215
+ pub struct VariableKeys {
216
+ name : Vec < u16 > ,
217
+ vendor : VariableVendor ,
218
+ is_done : bool ,
219
+ }
220
+
221
+ #[ cfg( feature = "alloc" ) ]
222
+ impl VariableKeys {
223
+ fn new ( ) -> Self {
224
+ // Create a the name buffer with a reasonable default capacity, and
225
+ // initialize it to an empty null-terminated string.
226
+ let mut name = Vec :: with_capacity ( 32 ) ;
227
+ name. push ( 0 ) ;
228
+
229
+ Self {
230
+ // Give the name buffer a reasonable default capacity.
231
+ name,
232
+ // The initial vendor GUID is arbitrary.
233
+ vendor : VariableVendor ( Guid :: default ( ) ) ,
234
+ is_done : false ,
235
+ }
236
+ }
237
+ }
238
+
239
+ #[ cfg( feature = "alloc" ) ]
240
+ impl Iterator for VariableKeys {
241
+ type Item = Result < VariableKey > ;
242
+
243
+ fn next ( & mut self ) -> Option < Result < VariableKey > > {
244
+ if self . is_done {
245
+ return None ;
246
+ }
247
+
248
+ let mut result = get_next_variable_key ( & mut self . name , & mut self . vendor ) ;
249
+
250
+ // If the name buffer was too small, resize it to be big enough and call
251
+ // `get_next_variable_key` again.
252
+ if let Err ( err) = & result {
253
+ if let Some ( required_size) = err. data ( ) {
254
+ self . name . resize ( * required_size, 0u16 ) ;
255
+ result = get_next_variable_key ( & mut self . name , & mut self . vendor ) ;
256
+ }
257
+ }
258
+
259
+ match result {
260
+ Ok ( ( ) ) => {
261
+ // Copy the name buffer, truncated after the first null
262
+ // character (if one is present).
263
+ let name = if let Some ( nul_pos) = self . name . iter ( ) . position ( |c| * c == 0 ) {
264
+ self . name [ ..=nul_pos] . to_owned ( )
265
+ } else {
266
+ self . name . clone ( )
267
+ } ;
268
+ Some ( Ok ( VariableKey {
269
+ name,
270
+ vendor : self . vendor ,
271
+ } ) )
272
+ }
273
+ Err ( err) => {
274
+ if err. status ( ) == Status :: NOT_FOUND {
275
+ // This status indicates the end of the list. The final variable
276
+ // has already been yielded at this point, so return `None`.
277
+ self . is_done ;
278
+ None
279
+ } else {
280
+ // Return the error and end iteration.
281
+ self . is_done = true ;
282
+ Some ( Err ( err. to_err_without_payload ( ) ) )
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+
144
289
/// Sets the value of a variable. This can be used to create a new variable,
145
290
/// update an existing variable, or (when the size of `data` is zero)
146
291
/// delete a variable.
0 commit comments