use core::ffi::c_void;
use core::fmt::{Debug, Formatter};
use core::marker::PhantomData;
use core::ptr::NonNull;
use core::{ptr, slice};
use crate::proto::console::text;
use crate::{CStr16, Char16, Handle, Result, ResultExt, Status};
use super::boot::{BootServices, MemoryDescriptor, MemoryMapIter};
use super::runtime::RuntimeServices;
use super::{cfg, Header, Revision};
pub trait SystemTableView {}
pub struct Boot;
impl SystemTableView for Boot {}
pub struct Runtime;
impl SystemTableView for Runtime {}
#[repr(transparent)]
#[derive(Debug)]
pub struct SystemTable<View: SystemTableView> {
table: &'static SystemTableImpl,
_marker: PhantomData<View>,
}
impl<View: SystemTableView> SystemTable<View> {
#[must_use]
pub fn firmware_vendor(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(self.table.fw_vendor) }
}
#[must_use]
pub const fn firmware_revision(&self) -> u32 {
self.table.fw_revision
}
#[must_use]
pub const fn uefi_revision(&self) -> Revision {
self.table.header.revision
}
#[allow(clippy::missing_const_for_fn)] #[must_use]
pub fn config_table(&self) -> &[cfg::ConfigTableEntry] {
unsafe { slice::from_raw_parts(self.table.cfg_table, self.table.nr_cfg) }
}
pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
NonNull::new(ptr.cast()).map(|ptr| Self {
table: ptr.as_ref(),
_marker: PhantomData,
})
}
}
impl SystemTable<Boot> {
pub fn stdin(&mut self) -> &mut text::Input {
unsafe { &mut *self.table.stdin }
}
pub fn stdout(&mut self) -> &mut text::Output {
unsafe { &mut *self.table.stdout.cast() }
}
pub fn stderr(&mut self) -> &mut text::Output {
unsafe { &mut *self.table.stderr.cast() }
}
#[must_use]
pub const fn runtime_services(&self) -> &RuntimeServices {
self.table.runtime
}
#[must_use]
pub const fn boot_services(&self) -> &BootServices {
unsafe { &*self.table.boot }
}
pub fn exit_boot_services(
self,
image: Handle,
mmap_buf: &mut [u8],
) -> Result<(SystemTable<Runtime>, MemoryMapIter<'_>)> {
unsafe {
let boot_services = self.boot_services();
loop {
let mmap_buf = &mut *(mmap_buf as *mut [u8]);
let mmap_comp = boot_services.memory_map(mmap_buf)?;
let (mmap_key, mmap_iter) = mmap_comp;
let result = boot_services.exit_boot_services(image, mmap_key);
if result.status() == Status::INVALID_PARAMETER {
continue;
} else {
return result.map(|_| {
let st = SystemTable {
table: self.table,
_marker: PhantomData,
};
(st, mmap_iter)
});
}
}
}
}
#[must_use]
pub const unsafe fn unsafe_clone(&self) -> Self {
SystemTable {
table: self.table,
_marker: PhantomData,
}
}
}
impl Debug for SystemTable<Boot> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.table.fmt(f)
}
}
impl SystemTable<Runtime> {
#[must_use]
pub const unsafe fn runtime_services(&self) -> &RuntimeServices {
self.table.runtime
}
pub unsafe fn set_virtual_address_map(
self,
map: &mut [MemoryDescriptor],
new_system_table_virtual_addr: u64,
) -> Result<Self> {
let map_size = core::mem::size_of_val(map);
let entry_size = core::mem::size_of::<MemoryDescriptor>();
let entry_version = crate::table::boot::MEMORY_DESCRIPTOR_VERSION;
let map_ptr = map.as_mut_ptr();
(self.table.runtime.set_virtual_address_map)(map_size, entry_size, entry_version, map_ptr)
.into_with_val(|| {
let new_table_ref =
&mut *(new_system_table_virtual_addr as usize as *mut SystemTableImpl);
Self {
table: new_table_ref,
_marker: PhantomData,
}
})
}
#[must_use]
pub fn get_current_system_table_addr(&self) -> u64 {
self.table as *const _ as usize as u64
}
}
#[repr(C)]
struct SystemTableImpl {
header: Header,
fw_vendor: *const Char16,
fw_revision: u32,
stdin_handle: Handle,
stdin: *mut text::Input,
stdout_handle: Handle,
stdout: *mut text::Output<'static>,
stderr_handle: Handle,
stderr: *mut text::Output<'static>,
runtime: &'static RuntimeServices,
boot: *const BootServices,
nr_cfg: usize,
cfg_table: *const cfg::ConfigTableEntry,
}
impl<View: SystemTableView> super::Table for SystemTable<View> {
const SIGNATURE: u64 = 0x5453_5953_2049_4249;
}
impl Debug for SystemTableImpl {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("UefiSystemTable")
.field("header", &self.header)
.field("fw_vendor", &(unsafe { CStr16::from_ptr(self.fw_vendor) }))
.field("fw_revision", &self.fw_revision)
.field("stdin_handle", &self.stdin_handle)
.field("stdin", &self.stdin)
.field("stdout_handle", &self.stdout_handle)
.field("stdout", &self.stdout)
.field("stderr_handle", &self.stderr_handle)
.field("stderr", &self.stderr)
.field("runtime", &self.runtime)
.field("boot", &(unsafe { ptr::read(self.boot) }))
.field("nf_cfg", &self.nr_cfg)
.field("cfg_table", &self.cfg_table)
.finish()
}
}