diff --git a/Cargo.toml b/Cargo.toml index 781bf04..6b3372f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,6 @@ features = [ [target."cfg(unix)".dev-dependencies] mmap = { package = "mmap-fixed", version = "0.1.6" } + +[target."cfg(target_os=\"uefi\")".dependencies] +r-efi = "6.0.0" diff --git a/src/error.rs b/src/error.rs index 941dc77..f10a10d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,6 +22,7 @@ pub enum Error { /// A system call failed. SystemCall(io::Error), /// A macOS kernel call failed + #[cfg(target_os = "macos")] MachCall(libc::c_int), } @@ -33,6 +34,7 @@ impl fmt::Display for Error { Error::InvalidParameter(param) => write!(f, "Invalid parameter value: {}", param), Error::ProcfsInput(ref input) => write!(f, "Invalid procfs input: {}", input), Error::SystemCall(ref error) => write!(f, "System call failed: {}", error), + #[cfg(target_os = "macos")] Error::MachCall(code) => write!(f, "macOS kernel call failed: {}", code), } } diff --git a/src/lib.rs b/src/lib.rs index 17ed2de..46f30fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,21 +79,32 @@ //! # } //! ``` +#![cfg_attr(target_os = "uefi", feature(uefi_std))] + #[macro_use] extern crate bitflags; pub use alloc::{alloc, alloc_at, Allocation}; + pub use error::{Error, Result}; + +#[cfg(not(target_os = "uefi"))] pub use lock::{lock, unlock, LockGuard}; + pub use protect::{protect, protect_with_handle, ProtectGuard}; pub use query::{query, query_range, QueryIter}; mod alloc; mod error; + +#[cfg(not(target_os = "uefi"))] mod lock; + mod os; pub mod page; + mod protect; + mod query; mod util; diff --git a/src/os/mod.rs b/src/os/mod.rs index 80389f6..004fd1a 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -45,3 +45,9 @@ mod netbsd; #[cfg(target_os = "netbsd")] pub use self::netbsd::*; + +#[cfg(target_os = "uefi")] +mod uefi; + +#[cfg(target_os = "uefi")] +pub use self::uefi::*; diff --git a/src/os/uefi.rs b/src/os/uefi.rs new file mode 100644 index 0000000..a19abac --- /dev/null +++ b/src/os/uefi.rs @@ -0,0 +1,140 @@ +use crate::{Error, Protection, Region, Result}; +use r_efi::efi; +use std::{io, os::uefi::env, ptr::addr_of}; + +pub struct QueryIter { + upper_bound: usize, +} + +impl QueryIter { + pub fn new(origin: *const (), size: usize) -> Result { + Ok(Self { + upper_bound: (origin as usize).saturating_add(size), + }) + } + + pub fn upper_bound(&self) -> usize { + self.upper_bound + } +} + +impl Iterator for QueryIter { + type Item = Result; + + fn next(&mut self) -> Option { + None + } +} + +fn get_mem_attrib_proto() -> Result<*mut core::ffi::c_void> { + let boot_services = env::boot_services().unwrap().as_ptr() as *mut efi::BootServices; + + unsafe { + let mut guid = r_efi::protocols::memory_attribute::PROTOCOL_GUID; + let mut proto: *mut core::ffi::c_void = core::ptr::null_mut(); + let r = ((*boot_services).locate_protocol)(&mut guid, core::ptr::null_mut(), &mut proto); + + match r { + efi::Status::SUCCESS => Ok(proto), + efi::Status::NOT_FOUND => Err(Error::SystemCall(io::Error::new( + io::ErrorKind::NotFound, + "Could not locate EFI_MEMORY_ATTRIBUTE_PROTOCOL", + ))), + efi::Status::INVALID_PARAMETER => Err(Error::InvalidParameter("Protocol is NULL")), + _ => panic!(), + } + } +} + +fn get_prot(protection: Protection) -> u64 { + let mut prot = 0; + + if (protection & Protection::WRITE) != Protection::WRITE { + prot |= efi::MEMORY_RO; + } + + if (protection & Protection::EXECUTE) != Protection::EXECUTE { + prot |= efi::MEMORY_XP; + } + + if (protection & Protection::READ) != Protection::READ { + prot |= efi::MEMORY_RP; + } + + prot +} + +pub unsafe fn protect(base: *const (), size: usize, protection: Protection) -> Result<()> { + let proto = get_mem_attrib_proto()? as *mut r_efi::protocols::memory_attribute::Protocol; + let prot = get_prot(protection); + + let r = ((*proto).set_memory_attributes)( + proto, + addr_of!(base) as efi::PhysicalAddress, + size as efi::PhysicalAddress, + prot, + ); + + match r { + efi::Status::SUCCESS => Ok(()), + efi::Status::INVALID_PARAMETER => { + Err(Error::InvalidParameter("Length is 0 or invalid protection")) + } + efi::Status::UNSUPPORTED => Err(Error::InvalidParameter( + "System does not support this operation", + )), + efi::Status::OUT_OF_RESOURCES => Err(Error::InvalidParameter("Out of system resources")), + efi::Status::ACCESS_DENIED => Err(Error::InvalidParameter("Cannot modify firmware address")), + _ => panic!(), + } +} + +pub unsafe fn alloc(base: *const (), size: usize, protection: Protection) -> Result<*const ()> { + let boot_services = env::boot_services().unwrap().as_ptr() as *mut efi::BootServices; + + let pages = (size + 4095) / 4096; + + let mut addr = addr_of!(base) as efi::PhysicalAddress; + + let alloc_type = if base.is_null() { + efi::ALLOCATE_ANY_PAGES + } else { + efi::ALLOCATE_ADDRESS + }; + + let r = ((*boot_services).allocate_pages)(alloc_type, efi::LOADER_DATA, pages, &mut addr); + + match r { + efi::Status::SUCCESS => { + protect(addr as *const (), pages * 4096, protection)?; + Ok(addr as *const ()) + } + efi::Status::INVALID_PARAMETER => Err(Error::InvalidParameter( + "Base address or memory type is not valid", + )), + efi::Status::OUT_OF_RESOURCES => Err(Error::InvalidParameter("Out of system resources")), + efi::Status::NOT_FOUND => Err(Error::InvalidParameter("Could not find a page")), + _ => panic!(), + } +} + +pub unsafe fn free(base: *const (), size: usize) -> Result<()> { + let boot_services = env::boot_services().unwrap().as_ptr() as *mut efi::BootServices; + + let pages = (size + 4095) / 4096; + + let r = ((*boot_services).free_pages)(addr_of!(base) as efi::PhysicalAddress, pages); + + match r { + efi::Status::SUCCESS => Ok(()), + efi::Status::INVALID_PARAMETER => Err(Error::InvalidParameter( + "Address it not page aligned or is invalid", + )), + efi::Status::NOT_FOUND => Err(Error::InvalidParameter("Could not find the allocation")), + _ => panic!(), + } +} + +pub fn page_size() -> usize { + 4096 +}