Skip to content

Commit de17f20

Browse files
committed
std: uefi: Add basic Env variables
- Implement environment variable functions - Using EFI Shell GUID for reading/storing variables. Signed-off-by: Ayush Singh <[email protected]>
1 parent ac77e88 commit de17f20

File tree

1 file changed

+212
-14
lines changed
  • library/std/src/sys/pal/uefi

1 file changed

+212
-14
lines changed

library/std/src/sys/pal/uefi/os.rs

+212-14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::error::Error as StdError;
66
use crate::ffi::{OsStr, OsString};
77
use crate::marker::PhantomData;
88
use crate::os::uefi;
9+
use crate::os::uefi::ffi::OsStringExt;
910
use crate::path::{self, PathBuf};
1011
use crate::ptr::NonNull;
1112
use 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

176199
impl 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

184206
impl 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

191237
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()
195240
}
196241
}
197242

198243
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 }
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

214267
pub fn temp_dir() -> PathBuf {
@@ -239,3 +292,148 @@ pub fn exit(code: i32) -> ! {
239292
pub 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

Comments
 (0)