@@ -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,96 @@ pub fn current_exe() -> io::Result<PathBuf> {
171172 helpers:: device_path_to_text ( protocol) . map ( PathBuf :: from)
172173}
173174
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+ }
175198
176199impl Env {
177200 // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
178201 pub fn str_debug ( & self ) -> impl fmt:: Debug + ' _ {
179- let Self ( inner) = self ;
180- match * inner { }
202+ EnvStrDebug { iter : self . vars . as_slice ( ) }
181203 }
182204}
183205
184206impl Iterator for Env {
185207 type Item = ( OsString , OsString ) ;
208+
186209 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+ }
188234 }
189235}
190236
191237impl 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 ( )
195240 }
196241}
197242
198243pub 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 }
200248}
201249
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 ( ) )
204253}
205254
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)
208259}
209260
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 ( ) )
212265}
213266
214267pub fn temp_dir ( ) -> PathBuf {
@@ -239,3 +292,148 @@ pub fn exit(code: i32) -> ! {
239292pub fn getpid ( ) -> u32 {
240293 panic ! ( "no pids on this platform" )
241294}
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