@@ -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,71 @@ 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+ // The Guid should be ignored, so just passing anything
222+ Env { last_var_name : Vec :: from ( [ 0 ] ) , last_var_guid : uefi_vars:: SHELL_VARIABLE_GUID }
200223}
201224
202- pub fn getenv ( _: & OsStr ) -> Option < OsString > {
203- None
225+ pub fn getenv ( key : & OsStr ) -> Option < OsString > {
226+ let mut key = uefi_vars:: key ( key) ?;
227+ uefi_vars:: get ( key. as_mut_slice ( ) )
204228}
205229
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" ) )
230+ pub unsafe fn setenv ( k : & OsStr , v : & OsStr ) -> io:: Result < ( ) > {
231+ let mut k =
232+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
233+ uefi_vars:: set ( k. as_mut_slice ( ) , v)
208234}
209235
210- pub unsafe fn unsetenv ( _: & OsStr ) -> io:: Result < ( ) > {
211- Err ( io:: const_io_error!( io:: ErrorKind :: Unsupported , "cannot unset env vars on this platform" ) )
236+ pub unsafe fn unsetenv ( k : & OsStr ) -> io:: Result < ( ) > {
237+ let mut k =
238+ uefi_vars:: key ( k) . ok_or ( io:: const_io_error!( io:: ErrorKind :: InvalidInput , "Invalid key" ) ) ?;
239+ uefi_vars:: unset ( k. as_mut_slice ( ) )
212240}
213241
214242pub fn temp_dir ( ) -> PathBuf {
@@ -239,3 +267,148 @@ pub fn exit(code: i32) -> ! {
239267pub fn getpid ( ) -> u32 {
240268 panic ! ( "no pids on this platform" )
241269}
270+
271+ mod uefi_vars {
272+ use super :: helpers;
273+ use crate :: ffi:: { OsStr , OsString } ;
274+ use crate :: io;
275+ use crate :: mem:: size_of;
276+ use crate :: os:: uefi:: ffi:: { OsStrExt , OsStringExt } ;
277+ use crate :: ptr:: NonNull ;
278+
279+ // Using Shell Variable Guid from edk2/ShellPkg
280+ // https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Guid/ShellVariableGuid.h
281+ pub ( crate ) const SHELL_VARIABLE_GUID : r_efi:: efi:: Guid = r_efi:: efi:: Guid :: from_fields (
282+ 0x158def5a ,
283+ 0xf656 ,
284+ 0x419c ,
285+ 0xb0 ,
286+ 0x27 ,
287+ & [ 0x7a , 0x31 , 0x92 , 0xc0 , 0x79 , 0xd2 ] ,
288+ ) ;
289+
290+ pub ( crate ) fn key ( k : & OsStr ) -> Option < Vec < u16 > > {
291+ let key = k. encode_wide ( ) . chain ( Some ( 0 ) ) . collect :: < Vec < u16 > > ( ) ;
292+ if key[ ..key. len ( ) - 1 ] . contains ( & 0 ) {
293+ return None ;
294+ } else {
295+ Some ( key)
296+ }
297+ }
298+
299+ pub ( crate ) fn get ( key : & mut [ u16 ] ) -> Option < OsString > {
300+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
301+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
302+
303+ let mut len = 0usize ;
304+ let mut guid = SHELL_VARIABLE_GUID ;
305+
306+ let ret = unsafe {
307+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
308+ key. as_mut_ptr ( ) ,
309+ & mut guid,
310+ crate :: ptr:: null_mut ( ) ,
311+ & mut len,
312+ crate :: ptr:: null_mut ( ) ,
313+ )
314+ } ;
315+
316+ if ret != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
317+ return None ;
318+ }
319+
320+ let mut val = Vec :: < u16 > :: with_capacity ( len / size_of :: < u16 > ( ) ) ;
321+ let ret = unsafe {
322+ ( ( * rt. as_ptr ( ) ) . get_variable ) (
323+ key. as_mut_ptr ( ) ,
324+ & mut guid,
325+ crate :: ptr:: null_mut ( ) ,
326+ & mut len,
327+ val. as_mut_ptr ( ) . cast ( ) ,
328+ )
329+ } ;
330+
331+ if ret. is_error ( ) {
332+ None
333+ } else {
334+ unsafe { val. set_len ( len / size_of :: < u16 > ( ) ) } ;
335+ Some ( OsString :: from_wide ( & val) )
336+ }
337+ }
338+
339+ pub ( crate ) fn set ( key : & mut [ u16 ] , val : & OsStr ) -> io:: Result < ( ) > {
340+ // UEFI variable value does not need to be NULL terminated.
341+ let mut val = val. encode_wide ( ) . collect :: < Vec < u16 > > ( ) ;
342+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
343+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
344+ let mut guid = SHELL_VARIABLE_GUID ;
345+
346+ let r = unsafe {
347+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
348+ key. as_mut_ptr ( ) ,
349+ & mut guid,
350+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
351+ val. len ( ) * size_of :: < u16 > ( ) ,
352+ val. as_mut_ptr ( ) . cast ( ) ,
353+ )
354+ } ;
355+
356+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
357+ }
358+
359+ pub ( crate ) fn unset ( key : & mut [ u16 ] ) -> io:: Result < ( ) > {
360+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
361+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
362+ let mut guid = SHELL_VARIABLE_GUID ;
363+
364+ let r = unsafe {
365+ ( ( * rt. as_ptr ( ) ) . set_variable ) (
366+ key. as_mut_ptr ( ) ,
367+ & mut guid,
368+ r_efi:: efi:: VARIABLE_BOOTSERVICE_ACCESS ,
369+ 0 ,
370+ crate :: ptr:: null_mut ( ) ,
371+ )
372+ } ;
373+
374+ if r. is_error ( ) { Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) } else { Ok ( ( ) ) }
375+ }
376+
377+ pub ( crate ) fn get_next_variable_name (
378+ last_var_name : & [ u16 ] ,
379+ last_guid : r_efi:: efi:: Guid ,
380+ ) -> io:: Result < ( Vec < u16 > , r_efi:: efi:: Guid ) > {
381+ let mut var_name = Vec :: from ( last_var_name) ;
382+ let mut var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
383+ let mut guid: r_efi:: efi:: Guid = last_guid;
384+ let rt: NonNull < r_efi:: efi:: RuntimeServices > =
385+ helpers:: runtime_services ( ) . expect ( "UEFI Runtime Services Missing" ) . cast ( ) ;
386+
387+ let r = unsafe {
388+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
389+ } ;
390+
391+ if !r. is_error ( ) {
392+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
393+ return Ok ( ( var_name, guid) ) ;
394+ }
395+
396+ if r != r_efi:: efi:: Status :: BUFFER_TOO_SMALL {
397+ return Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) ) ;
398+ }
399+
400+ var_name. reserve ( ( var_size / size_of :: < u16 > ( ) ) - var_name. capacity ( ) + 1 ) ;
401+ var_size = var_name. capacity ( ) * size_of :: < u16 > ( ) ;
402+
403+ let r = unsafe {
404+ ( ( * rt. as_ptr ( ) ) . get_next_variable_name ) ( & mut var_size, var_name. as_mut_ptr ( ) , & mut guid)
405+ } ;
406+
407+ if r. is_error ( ) {
408+ Err ( io:: Error :: from_raw_os_error ( r. as_usize ( ) ) )
409+ } else {
410+ unsafe { var_name. set_len ( var_size / size_of :: < u16 > ( ) ) } ;
411+ Ok ( ( var_name, guid) )
412+ }
413+ }
414+ }
0 commit comments