@@ -6,6 +6,7 @@ use crate::error::Error as StdError;
6
6
use crate :: ffi:: { OsStr , OsString } ;
7
7
use crate :: marker:: PhantomData ;
8
8
use crate :: os:: uefi;
9
+ use crate :: os:: uefi:: ffi:: OsStringExt ;
9
10
use crate :: path:: { self , PathBuf } ;
10
11
use crate :: ptr:: NonNull ;
11
12
use crate :: { fmt, io} ;
@@ -171,44 +172,96 @@ pub fn current_exe() -> io::Result<PathBuf> {
171
172
helpers:: device_path_to_text ( protocol) . map ( PathBuf :: from)
172
173
}
173
174
174
- pub struct Env ( !) ;
175
+ pub struct Env {
176
+ vars : Vec < ( OsString , OsString ) > ,
177
+ pos : usize ,
178
+ }
179
+
180
+ struct EnvIter {
181
+ last_var_name : Vec < u16 > ,
182
+ last_var_guid : r_efi:: efi:: Guid ,
183
+ }
184
+
185
+ pub struct EnvStrDebug < ' a > {
186
+ iter : & ' a [ ( OsString , OsString ) ] ,
187
+ }
188
+
189
+ impl fmt:: Debug for EnvStrDebug < ' _ > {
190
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
191
+ let mut list = f. debug_list ( ) ;
192
+ for ( a, b) in self . iter {
193
+ list. entry ( & ( a. to_str ( ) . unwrap ( ) , b. to_str ( ) . unwrap ( ) ) ) ;
194
+ }
195
+ list. finish ( )
196
+ }
197
+ }
175
198
176
199
impl Env {
177
200
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
178
201
pub fn str_debug ( & self ) -> impl fmt:: Debug + ' _ {
179
- let Self ( inner) = self ;
180
- match * inner { }
202
+ EnvStrDebug { iter : self . vars . as_slice ( ) }
181
203
}
182
204
}
183
205
184
206
impl Iterator for Env {
185
207
type Item = ( OsString , OsString ) ;
208
+
186
209
fn next ( & mut self ) -> Option < ( OsString , OsString ) > {
187
- self . 0
210
+ let res = self . vars . get ( self . pos ) ?;
211
+ self . pos += 1 ;
212
+ Some ( res. clone ( ) )
213
+ }
214
+ }
215
+
216
+ impl Iterator for EnvIter {
217
+ type Item = ( OsString , OsString ) ;
218
+
219
+ fn next ( & mut self ) -> Option < ( OsString , OsString ) > {
220
+ let ( key, guid) =
221
+ uefi_vars:: get_next_variable_name ( & self . last_var_name , self . last_var_guid ) . ok ( ) ?;
222
+
223
+ self . last_var_name = key;
224
+ self . last_var_guid = guid;
225
+
226
+ if self . last_var_guid == uefi_vars:: SHELL_VARIABLE_GUID {
227
+ let k = OsString :: from_wide ( & self . last_var_name [ ..( self . last_var_name . len ( ) - 1 ) ] ) ;
228
+ let v = uefi_vars:: get ( self . last_var_name . as_mut_slice ( ) ) ?;
229
+
230
+ Some ( ( k, v) )
231
+ } else {
232
+ self . next ( )
233
+ }
188
234
}
189
235
}
190
236
191
237
impl fmt:: Debug for Env {
192
- fn fmt ( & self , _: & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
193
- let Self ( inner) = self ;
194
- match * inner { }
238
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
239
+ f. debug_list ( ) . entries ( & self . vars ) . finish ( )
195
240
}
196
241
}
197
242
198
243
pub fn env ( ) -> Env {
199
- panic ! ( "not supported on this platform" )
244
+ let iter =
245
+ EnvIter { last_var_name : Vec :: from ( [ 0 ] ) , last_var_guid : uefi_vars:: SHELL_VARIABLE_GUID } ;
246
+
247
+ Env { vars : iter. collect ( ) , pos : 0 }
200
248
}
201
249
202
- pub fn getenv ( _: & OsStr ) -> Option < OsString > {
203
- None
250
+ pub fn getenv ( key : & OsStr ) -> Option < OsString > {
251
+ let mut key = uefi_vars:: key ( key) ?;
252
+ uefi_vars:: get ( key. as_mut_slice ( ) )
204
253
}
205
254
206
- pub unsafe fn setenv ( _: & OsStr , _: & OsStr ) -> io:: Result < ( ) > {
207
- Err ( io:: const_io_error!( io:: ErrorKind :: Unsupported , "cannot set env vars on this platform" ) )
255
+ pub unsafe fn setenv ( k : & OsStr , v : & OsStr ) -> io:: Result < ( ) > {
256
+ let mut k =
257
+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
258
+ uefi_vars:: set ( k. as_mut_slice ( ) , v)
208
259
}
209
260
210
- pub unsafe fn unsetenv ( _: & OsStr ) -> io:: Result < ( ) > {
211
- Err ( io:: const_io_error!( io:: ErrorKind :: Unsupported , "cannot unset env vars on this platform" ) )
261
+ pub unsafe fn unsetenv ( k : & OsStr ) -> io:: Result < ( ) > {
262
+ let mut k =
263
+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
264
+ uefi_vars:: unset ( k. as_mut_slice ( ) )
212
265
}
213
266
214
267
pub fn temp_dir ( ) -> PathBuf {
@@ -239,3 +292,148 @@ pub fn exit(code: i32) -> ! {
239
292
pub fn getpid ( ) -> u32 {
240
293
panic ! ( "no pids on this platform" )
241
294
}
295
+
296
+ mod uefi_vars {
297
+ use super :: helpers;
298
+ use crate :: ffi:: { OsStr , OsString } ;
299
+ use crate :: io;
300
+ use crate :: mem:: size_of;
301
+ use crate :: os:: uefi:: ffi:: { OsStrExt , OsStringExt } ;
302
+ use crate :: ptr:: NonNull ;
303
+
304
+ // Using Shell Variable Guid from edk2/ShellPkg
305
+ // https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Guid/ShellVariableGuid.h
306
+ pub ( crate ) const SHELL_VARIABLE_GUID : r_efi:: efi:: Guid = r_efi:: efi:: Guid :: from_fields (
307
+ 0x158def5a ,
308
+ 0xf656 ,
309
+ 0x419c ,
310
+ 0xb0 ,
311
+ 0x27 ,
312
+ & [ 0x7a , 0x31 , 0x92 , 0xc0 , 0x79 , 0xd2 ] ,
313
+ ) ;
314
+
315
+ pub ( crate ) fn key ( k : & OsStr ) -> Option < Vec < u16 > > {
316
+ let key = k. encode_wide ( ) . chain ( Some ( 0 ) ) . collect :: < Vec < u16 > > ( ) ;
317
+ if key[ ..key. len ( ) - 1 ] . contains ( & 0 ) {
318
+ return None ;
319
+ } else {
320
+ Some ( key)
321
+ }
322
+ }
323
+
324
+ pub ( crate ) fn get ( key : & mut [ u16 ] ) -> Option < OsString > {
325
+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
326
+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
327
+
328
+ let mut len = 0usize ;
329
+ let mut guid = SHELL_VARIABLE_GUID ;
330
+
331
+ let ret = unsafe {
332
+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
333
+ key. as_mut_ptr ( ) ,
334
+ & mut guid,
335
+ crate :: ptr:: null_mut ( ) ,
336
+ & mut len,
337
+ crate :: ptr:: null_mut ( ) ,
338
+ )
339
+ } ;
340
+
341
+ if ret != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
342
+ return None ;
343
+ }
344
+
345
+ let mut val = Vec :: < u16 > :: with_capacity ( len / size_of :: < u16 > ( ) ) ;
346
+ let ret = unsafe {
347
+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
348
+ key. as_mut_ptr ( ) ,
349
+ & mut guid,
350
+ crate :: ptr:: null_mut ( ) ,
351
+ & mut len,
352
+ val. as_mut_ptr ( ) . cast ( ) ,
353
+ )
354
+ } ;
355
+
356
+ if ret. is_error ( ) {
357
+ None
358
+ } else {
359
+ unsafe { val. set_len ( len / size_of :: < u16 > ( ) ) } ;
360
+ Some ( OsString :: from_wide ( & val) )
361
+ }
362
+ }
363
+
364
+ pub ( crate ) fn set ( key : & mut [ u16 ] , val : & OsStr ) -> io:: Result < ( ) > {
365
+ // UEFI variable value does not need to be NULL terminated.
366
+ let mut val = val. encode_wide ( ) . collect :: < Vec < u16 > > ( ) ;
367
+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
368
+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
369
+ let mut guid = SHELL_VARIABLE_GUID ;
370
+
371
+ let r = unsafe {
372
+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
373
+ key. as_mut_ptr ( ) ,
374
+ & mut guid,
375
+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
376
+ val. len ( ) * size_of :: < u16 > ( ) ,
377
+ val. as_mut_ptr ( ) . cast ( ) ,
378
+ )
379
+ } ;
380
+
381
+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
382
+ }
383
+
384
+ pub ( crate ) fn unset ( key : & mut [ u16 ] ) -> io:: Result < ( ) > {
385
+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
386
+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
387
+ let mut guid = SHELL_VARIABLE_GUID ;
388
+
389
+ let r = unsafe {
390
+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
391
+ key. as_mut_ptr ( ) ,
392
+ & mut guid,
393
+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
394
+ 0 ,
395
+ crate :: ptr:: null_mut ( ) ,
396
+ )
397
+ } ;
398
+
399
+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
400
+ }
401
+
402
+ pub ( crate ) fn get_next_variable_name (
403
+ last_var_name : & [ u16 ] ,
404
+ last_guid : r_efi:: efi:: Guid ,
405
+ ) -> io:: Result < ( Vec < u16 > , r_efi:: efi:: Guid ) > {
406
+ let mut var_name = Vec :: from ( last_var_name) ;
407
+ let mut var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
408
+ let mut guid: r_efi:: efi:: Guid = last_guid;
409
+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
410
+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
411
+
412
+ let r = unsafe {
413
+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
414
+ } ;
415
+
416
+ if !r. is_error ( ) {
417
+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
418
+ return Ok ( ( var_name, guid) ) ;
419
+ }
420
+
421
+ if r != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
422
+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
423
+ }
424
+
425
+ var_name. reserve ( ( var_size / size_of :: < u16 > ( ) ) - var_name. capacity ( ) + 1 ) ;
426
+ var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
427
+
428
+ let r = unsafe {
429
+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
430
+ } ;
431
+
432
+ if r. is_error ( ) {
433
+ Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) )
434
+ } else {
435
+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
436
+ Ok ( ( var_name, guid) )
437
+ }
438
+ }
439
+ }
0 commit comments