mod time;
pub use time::*;
use crate::data_types::PhysicalAddress;
use crate::table::{self, Revision};
use crate::{CStr16, Error, Result, Status, StatusExt};
use core::fmt::Debug;
#[cfg(feature = "alloc")]
use core::fmt::{self, Display, Formatter};
use core::ptr::{self, NonNull};
use uefi_raw::table::boot::MemoryDescriptor;
#[cfg(feature = "alloc")]
use {
crate::CString16,
crate::Guid,
crate::mem::make_boxed,
alloc::borrow::ToOwned,
alloc::boxed::Box,
alloc::{vec, vec::Vec},
};
pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader};
pub use uefi_raw::table::runtime::{
ResetType, TimeCapabilities, VariableAttributes, VariableVendor,
};
pub use uefi_raw::time::Daylight;
fn runtime_services_raw_panicking() -> NonNull<uefi_raw::table::runtime::RuntimeServices> {
let st = table::system_table_raw_panicking();
let st = unsafe { st.as_ref() };
NonNull::new(st.runtime_services).expect("runtime services are not active")
}
pub fn get_time() -> Result<Time> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let mut time = Time::invalid();
let time_ptr: *mut Time = &mut time;
unsafe { (rt.get_time)(time_ptr.cast(), ptr::null_mut()) }.to_result_with_val(|| time)
}
pub fn get_time_and_caps() -> Result<(Time, TimeCapabilities)> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let mut time = Time::invalid();
let time_ptr: *mut Time = &mut time;
let mut caps = TimeCapabilities::default();
unsafe { (rt.get_time)(time_ptr.cast(), &mut caps) }.to_result_with_val(|| (time, caps))
}
pub unsafe fn set_time(time: &Time) -> Result {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let time: *const Time = time;
unsafe { (rt.set_time)(time.cast()) }.to_result()
}
pub fn variable_exists(name: &CStr16, vendor: &VariableVendor) -> Result<bool> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let attributes = ptr::null_mut();
let data = ptr::null_mut();
let mut data_size = 0;
let status = unsafe {
(rt.get_variable)(
name.as_ptr().cast(),
&vendor.0,
attributes,
&mut data_size,
data,
)
};
match status {
Status::BUFFER_TOO_SMALL => Ok(true),
Status::NOT_FOUND => Ok(false),
_ => Err(Error::from(status)),
}
}
pub fn get_variable<'buf>(
name: &CStr16,
vendor: &VariableVendor,
buf: &'buf mut [u8],
) -> Result<(&'buf mut [u8], VariableAttributes), Option<usize>> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let mut attributes = VariableAttributes::empty();
let mut data_size = buf.len();
let status = unsafe {
(rt.get_variable)(
name.as_ptr().cast(),
&vendor.0,
&mut attributes,
&mut data_size,
buf.as_mut_ptr(),
)
};
match status {
Status::SUCCESS => Ok((&mut buf[..data_size], attributes)),
Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(data_size))),
_ => Err(Error::new(status, None)),
}
}
#[cfg(feature = "alloc")]
pub fn get_variable_boxed(
name: &CStr16,
vendor: &VariableVendor,
) -> Result<(Box<[u8]>, VariableAttributes)> {
let mut out_attr = VariableAttributes::empty();
let get_var = |buf| {
get_variable(name, vendor, buf).map(|(val, attr)| {
out_attr = attr;
val
})
};
make_boxed(get_var).map(|val| (val, out_attr))
}
pub fn get_next_variable_key(
name: &mut [u16],
vendor: &mut VariableVendor,
) -> Result<(), Option<usize>> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let mut name_size_in_bytes = size_of_val(name);
let status = unsafe {
(rt.get_next_variable_name)(&mut name_size_in_bytes, name.as_mut_ptr(), &mut vendor.0)
};
match status {
Status::SUCCESS => Ok(()),
Status::BUFFER_TOO_SMALL => Err(Error::new(
status,
Some(name_size_in_bytes / size_of::<u16>()),
)),
_ => Err(Error::new(status, None)),
}
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn variable_keys() -> VariableKeys {
VariableKeys::new()
}
#[cfg(feature = "alloc")]
#[derive(Debug)]
pub struct VariableKeys {
name: Vec<u16>,
vendor: VariableVendor,
is_done: bool,
}
#[cfg(feature = "alloc")]
impl VariableKeys {
fn new() -> Self {
let name = vec![0; 512];
Self {
name,
vendor: VariableVendor(Guid::default()),
is_done: false,
}
}
}
#[cfg(feature = "alloc")]
impl Iterator for VariableKeys {
type Item = Result<VariableKey>;
fn next(&mut self) -> Option<Result<VariableKey>> {
if self.is_done {
return None;
}
let mut result = get_next_variable_key(&mut self.name, &mut self.vendor);
if let Err(err) = &result
&& let Some(required_size) = err.data()
{
self.name.resize(*required_size, 0u16);
result = get_next_variable_key(&mut self.name, &mut self.vendor);
}
match result {
Ok(()) => {
let Ok(name) = CStr16::from_u16_until_nul(&self.name) else {
return Some(Err(Status::UNSUPPORTED.into()));
};
Some(Ok(VariableKey {
name: name.to_owned(),
vendor: self.vendor,
}))
}
Err(err) => {
self.is_done = true;
if err.status() == Status::NOT_FOUND {
None
} else {
Some(Err(err.to_err_without_payload()))
}
}
}
}
}
pub fn set_variable(
name: &CStr16,
vendor: &VariableVendor,
attributes: VariableAttributes,
data: &[u8],
) -> Result {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
unsafe {
(rt.set_variable)(
name.as_ptr().cast(),
&vendor.0,
attributes,
data.len(),
data.as_ptr(),
)
.to_result()
}
}
pub fn delete_variable(name: &CStr16, vendor: &VariableVendor) -> Result {
set_variable(name, vendor, VariableAttributes::empty(), &[])
}
pub fn query_variable_info(attributes: VariableAttributes) -> Result<VariableStorageInfo> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
if rt.header.revision < Revision::EFI_2_00 {
return Err(Status::UNSUPPORTED.into());
}
let mut info = VariableStorageInfo::default();
unsafe {
(rt.query_variable_info)(
attributes,
&mut info.maximum_variable_storage_size,
&mut info.remaining_variable_storage_size,
&mut info.maximum_variable_size,
)
.to_result_with_val(|| info)
}
}
pub fn update_capsule(
capsule_header_array: &[&CapsuleHeader],
capsule_block_descriptors: &[CapsuleBlockDescriptor],
) -> Result {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
unsafe {
(rt.update_capsule)(
capsule_header_array.as_ptr().cast(),
capsule_header_array.len(),
capsule_block_descriptors.as_ptr() as PhysicalAddress,
)
.to_result()
}
}
pub fn query_capsule_capabilities(capsule_header_array: &[&CapsuleHeader]) -> Result<CapsuleInfo> {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let mut info = CapsuleInfo::default();
unsafe {
(rt.query_capsule_capabilities)(
capsule_header_array.as_ptr().cast(),
capsule_header_array.len(),
&mut info.maximum_capsule_size,
&mut info.reset_type,
)
.to_result_with_val(|| info)
}
}
pub fn reset(reset_type: ResetType, status: Status, data: Option<&[u8]>) -> ! {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let (size, data) = data
.map(|data| (data.len(), data.as_ptr()))
.unwrap_or((0, ptr::null()));
unsafe { (rt.reset_system)(reset_type, status, size, data) }
}
pub unsafe fn set_virtual_address_map(
map: &mut [MemoryDescriptor],
new_system_table_virtual_addr: *const uefi_raw::table::system::SystemTable,
) -> Result {
let rt = runtime_services_raw_panicking();
let rt = unsafe { rt.as_ref() };
let map_size = size_of_val(map);
let entry_size = size_of::<MemoryDescriptor>();
let entry_version = MemoryDescriptor::VERSION;
let map_ptr = map.as_mut_ptr();
unsafe { (rt.set_virtual_address_map)(map_size, entry_size, entry_version, map_ptr) }
.to_result()?;
unsafe { table::set_system_table(new_system_table_virtual_addr) };
Ok(())
}
#[cfg(feature = "alloc")]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VariableKey {
pub vendor: VariableVendor,
pub name: CString16,
}
#[cfg(feature = "alloc")]
impl Display for VariableKey {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "VariableKey {{ name: \"{}\", vendor: ", self.name)?;
if self.vendor == VariableVendor::GLOBAL_VARIABLE {
write!(f, "GLOBAL_VARIABLE")?;
} else {
write!(f, "{}", self.vendor.0)?;
}
write!(f, " }}")
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct VariableStorageInfo {
pub maximum_variable_storage_size: u64,
pub remaining_variable_storage_size: u64,
pub maximum_variable_size: u64,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CapsuleInfo {
pub maximum_capsule_size: u64,
pub reset_type: ResetType,
}