@@ -6,6 +6,7 @@ use crate::error::Error as StdError;
66use crate :: ffi:: { OsStr , OsString } ;
77use crate :: marker:: PhantomData ;
88use crate :: os:: uefi;
9+ use crate :: os:: uefi:: ffi:: OsStringExt ;
910use crate :: path:: { self , PathBuf } ;
1011use crate :: ptr:: NonNull ;
1112use crate :: { fmt, io} ;
@@ -171,44 +172,70 @@ pub fn current_exe() -> io::Result<PathBuf> {
171172 helpers:: device_path_to_text ( protocol) . map ( PathBuf :: from)
172173}
173174
174- pub struct Env ( !) ;
175+ #[ derive( Clone ) ]
176+ pub struct Env {
177+ last_var_name : Vec < u16 > ,
178+ last_var_guid : r_efi:: efi:: Guid ,
179+ }
175180
176181impl Env {
177182 // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
178183 pub fn str_debug ( & self ) -> impl fmt:: Debug + ' _ {
179- let Self ( inner) = self ;
180- match * inner { }
184+ self
181185 }
182186}
183187
184188impl Iterator for Env {
185189 type Item = ( OsString , OsString ) ;
190+
186191 fn next ( & mut self ) -> Option < ( OsString , OsString ) > {
187- self . 0
192+ let ( key, guid) =
193+ uefi_vars:: get_next_variable_name ( & self . last_var_name , self . last_var_guid ) . ok ( ) ?;
194+
195+ self . last_var_name = key;
196+ self . last_var_guid = guid;
197+
198+ if self . last_var_guid == uefi_vars:: SHELL_VARIABLE_GUID {
199+ let k = OsString :: from_wide ( & self . last_var_name [ ..( self . last_var_name . len ( ) - 1 ) ] ) ;
200+ let v = uefi_vars:: get ( self . last_var_name . as_mut_slice ( ) ) ?;
201+
202+ Some ( ( k, v) )
203+ } else {
204+ self . next ( )
205+ }
188206 }
189207}
190208
191209impl fmt:: Debug for Env {
192- fn fmt ( & self , _: & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
193- let Self ( inner) = self ;
194- match * inner { }
210+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
211+ let iter: Env = self . clone ( ) ;
212+ let mut list = f. debug_list ( ) ;
213+ for ( a, b) in iter {
214+ list. entry ( & ( a. to_str ( ) . unwrap ( ) , b. to_str ( ) . unwrap ( ) ) ) ;
215+ }
216+ list. finish ( )
195217 }
196218}
197219
198220pub fn env ( ) -> Env {
199- panic ! ( "not supported on this platform" )
221+ Env { last_var_name : Vec :: from ( [ 0 ] ) , last_var_guid : uefi_vars :: SHELL_VARIABLE_GUID }
200222}
201223
202- pub fn getenv ( _: & OsStr ) -> Option < OsString > {
203- None
224+ pub fn getenv ( key : & OsStr ) -> Option < OsString > {
225+ let mut key = uefi_vars:: key ( key) ?;
226+ uefi_vars:: get ( key. as_mut_slice ( ) )
204227}
205228
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" ) )
229+ pub unsafe fn setenv ( k : & OsStr , v : & OsStr ) -> io:: Result < ( ) > {
230+ let mut k =
231+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
232+ uefi_vars:: set ( k. as_mut_slice ( ) , v)
208233}
209234
210- pub unsafe fn unsetenv ( _: & OsStr ) -> io:: Result < ( ) > {
211- Err ( io:: const_io_error!( io:: ErrorKind :: Unsupported , "cannot unset env vars on this platform" ) )
235+ pub unsafe fn unsetenv ( k : & OsStr ) -> io:: Result < ( ) > {
236+ let mut k =
237+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
238+ uefi_vars:: unset ( k. as_mut_slice ( ) )
212239}
213240
214241pub fn temp_dir ( ) -> PathBuf {
@@ -239,3 +266,148 @@ pub fn exit(code: i32) -> ! {
239266pub fn getpid ( ) -> u32 {
240267 panic ! ( "no pids on this platform" )
241268}
269+
270+ mod uefi_vars {
271+ use super :: helpers;
272+ use crate :: ffi:: { OsStr , OsString } ;
273+ use crate :: io;
274+ use crate :: mem:: size_of;
275+ use crate :: os:: uefi:: ffi:: { OsStrExt , OsStringExt } ;
276+ use crate :: ptr:: NonNull ;
277+
278+ // Using Shell Variable Guid from edk2/ShellPkg
279+ // https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Guid/ShellVariableGuid.h
280+ pub ( crate ) const SHELL_VARIABLE_GUID : r_efi:: efi:: Guid = r_efi:: efi:: Guid :: from_fields (
281+ 0x158def5a ,
282+ 0xf656 ,
283+ 0x419c ,
284+ 0xb0 ,
285+ 0x27 ,
286+ & [ 0x7a , 0x31 , 0x92 , 0xc0 , 0x79 , 0xd2 ] ,
287+ ) ;
288+
289+ pub ( crate ) fn key ( k : & OsStr ) -> Option < Vec < u16 > > {
290+ let key = k. encode_wide ( ) . chain ( Some ( 0 ) ) . collect :: < Vec < u16 > > ( ) ;
291+ if key[ ..key. len ( ) - 1 ] . contains ( & 0 ) {
292+ return None ;
293+ } else {
294+ Some ( key)
295+ }
296+ }
297+
298+ pub ( crate ) fn get ( key : & mut [ u16 ] ) -> Option < OsString > {
299+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
300+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
301+
302+ let mut len = 0usize ;
303+ let mut guid = SHELL_VARIABLE_GUID ;
304+
305+ let ret = unsafe {
306+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
307+ key. as_mut_ptr ( ) ,
308+ & mut guid,
309+ crate :: ptr:: null_mut ( ) ,
310+ & mut len,
311+ crate :: ptr:: null_mut ( ) ,
312+ )
313+ } ;
314+
315+ if ret != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
316+ return None ;
317+ }
318+
319+ let mut val = Vec :: < u16 > :: with_capacity ( len / size_of :: < u16 > ( ) ) ;
320+ let ret = unsafe {
321+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
322+ key. as_mut_ptr ( ) ,
323+ & mut guid,
324+ crate :: ptr:: null_mut ( ) ,
325+ & mut len,
326+ val. as_mut_ptr ( ) . cast ( ) ,
327+ )
328+ } ;
329+
330+ if ret. is_error ( ) {
331+ None
332+ } else {
333+ unsafe { val. set_len ( len / size_of :: < u16 > ( ) ) } ;
334+ Some ( OsString :: from_wide ( & val) )
335+ }
336+ }
337+
338+ pub ( crate ) fn set ( key : & mut [ u16 ] , val : & OsStr ) -> io:: Result < ( ) > {
339+ // UEFI variable value does not need to be NULL terminated.
340+ let mut val = val. encode_wide ( ) . collect :: < Vec < u16 > > ( ) ;
341+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
342+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
343+ let mut guid = SHELL_VARIABLE_GUID ;
344+
345+ let r = unsafe {
346+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
347+ key. as_mut_ptr ( ) ,
348+ & mut guid,
349+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
350+ val. len ( ) * size_of :: < u16 > ( ) ,
351+ val. as_mut_ptr ( ) . cast ( ) ,
352+ )
353+ } ;
354+
355+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
356+ }
357+
358+ pub ( crate ) fn unset ( key : & mut [ u16 ] ) -> io:: Result < ( ) > {
359+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
360+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
361+ let mut guid = SHELL_VARIABLE_GUID ;
362+
363+ let r = unsafe {
364+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
365+ key. as_mut_ptr ( ) ,
366+ & mut guid,
367+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
368+ 0 ,
369+ crate :: ptr:: null_mut ( ) ,
370+ )
371+ } ;
372+
373+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
374+ }
375+
376+ pub ( crate ) fn get_next_variable_name (
377+ last_var_name : & [ u16 ] ,
378+ last_guid : r_efi:: efi:: Guid ,
379+ ) -> io:: Result < ( Vec < u16 > , r_efi:: efi:: Guid ) > {
380+ let mut var_name = Vec :: from ( last_var_name) ;
381+ let mut var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
382+ let mut guid: r_efi:: efi:: Guid = last_guid;
383+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
384+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
385+
386+ let r = unsafe {
387+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
388+ } ;
389+
390+ if !r. is_error ( ) {
391+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
392+ return Ok ( ( var_name, guid) ) ;
393+ }
394+
395+ if r != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
396+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
397+ }
398+
399+ var_name. reserve ( ( var_size / size_of :: < u16 > ( ) ) - var_name. capacity ( ) + 1 ) ;
400+ var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
401+
402+ let r = unsafe {
403+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
404+ } ;
405+
406+ if r. is_error ( ) {
407+ Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) )
408+ } else {
409+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
410+ Ok ( ( var_name, guid) )
411+ }
412+ }
413+ }
0 commit comments