From f4c1b4c9b708d9936ccc433929ac5fd70bb98432 Mon Sep 17 00:00:00 2001 From: Pelle Krab <78560773+PelleKrab@users.noreply.github.com> Date: Fri, 18 Jul 2025 10:12:34 -0700 Subject: [PATCH 1/3] feat: IoMmu protocol fix --- uefi-raw/src/protocol/iommu.rs | 103 +++++++++++++++++++++++++++ uefi-raw/src/protocol/mod.rs | 1 + uefi/src/proto/dma/iommu.rs | 123 +++++++++++++++++++++++++++++++++ uefi/src/proto/dma/mod.rs | 108 +++++++++++++++++++++++++++++ uefi/src/proto/mod.rs | 1 + 5 files changed, 336 insertions(+) create mode 100644 uefi-raw/src/protocol/iommu.rs create mode 100644 uefi/src/proto/dma/iommu.rs create mode 100644 uefi/src/proto/dma/mod.rs diff --git a/uefi-raw/src/protocol/iommu.rs b/uefi-raw/src/protocol/iommu.rs new file mode 100644 index 000000000..269d5ce82 --- /dev/null +++ b/uefi-raw/src/protocol/iommu.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{Guid, Handle, Status, guid, table::boot::MemoryType}; +use bitflags::bitflags; +use core::ffi::c_void; + +use crate::newtype_enum; + +/// EDKII IOMMU Protocol GUID +impl EdkiiIommuProtocol { + pub const GUID: Guid = guid!("4e939de9-d948-4b0f-88ed-e6e1ce517c1e"); +} + +#[derive(Debug)] +#[repr(C)] +pub struct EdkiiIommuProtocol { + pub revision: u64, + pub set_attribute: unsafe extern "efiapi" fn( + this: &Self, + device_handle: Handle, + mapping: *mut c_void, + iommu_access: u64, + ) -> Status, + pub map: unsafe extern "efiapi" fn( + this: &Self, + operation: EdkiiIommuOperation, + host_address: *mut c_void, + number_of_bytes: *mut usize, + device_address: *mut u64, + mapping: *mut *mut c_void, + ) -> Status, + pub unmap: unsafe extern "efiapi" fn(this: &Self, mapping: *mut c_void) -> Status, + pub allocate_buffer: unsafe extern "efiapi" fn( + this: &Self, + allocate_type: u32, + memory_type: MemoryType, + pages: usize, + host_address: *mut *mut c_void, + attributes: u64, + ) -> Status, + pub free_buffer: + unsafe extern "efiapi" fn(this: &Self, pages: usize, host_address: *mut c_void) -> Status, +} + +newtype_enum! { + /// IOMMU Operation for Map (matches EDKII_IOMMU_OPERATION) + pub enum EdkiiIommuOperation: u32 => { + /// A read operation from system memory by a bus master that is not capable of producing PCI dual address cycles. + BUS_MASTER_READ = 0, + /// A write operation to system memory by a bus master that is not capable of producing PCI dual address cycles. + BUS_MASTER_WRITE = 1, + /// Provides both read and write access to system memory by both the processor and a bus master that is not capable of producing PCI dual address cycles. + BUS_MASTER_COMMON_BUFFER = 2, + /// A read operation from system memory by a bus master that is capable of producing PCI dual address cycles. + BUS_MASTER_READ64 = 3, + /// A write operation to system memory by a bus master that is capable of producing PCI dual address cycles. + BUS_MASTER_WRITE64 = 4, + /// Provides both read and write access to system memory by both the processor and a bus master that is capable of producing PCI dual address cycles. + BUS_MASTER_COMMON_BUFFER64 = 5, + /// Maximum value (not a valid operation, for bounds checking) + MAXIMUM = 6, + } +} + +/// EDKII IOMMU protocol revision constant +pub const EDKII_IOMMU_PROTOCOL_REVISION: u64 = 0x0001_0000; + +bitflags! { + /// EDKII IOMMU attribute flags + #[derive(Default)] + pub struct EdkiiIommuAttribute: u64 { + /// Memory is write-combined + const MEMORY_WRITE_COMBINE = 0x0080; + /// Memory is cached + const MEMORY_CACHED = 0x0800; + /// Dual address cycle supported + const DUAL_ADDRESS_CYCLE = 0x8000; + } +} + +impl EdkiiIommuAttribute { + /// Valid attributes for allocate_buffer + pub const VALID_FOR_ALLOCATE_BUFFER: Self = Self::from_bits_truncate( + Self::MEMORY_WRITE_COMBINE.bits() + | Self::MEMORY_CACHED.bits() + | Self::DUAL_ADDRESS_CYCLE.bits(), + ); + + /// Invalid attributes for allocate_buffer (all bits except valid) + pub const INVALID_FOR_ALLOCATE_BUFFER: Self = + Self::from_bits_truncate(!Self::VALID_FOR_ALLOCATE_BUFFER.bits()); +} + +bitflags! { + /// EDKII IOMMU access flags for SetAttribute + #[derive(Default)] + pub struct EdkiiIommuAccess: u64 { + /// Read access + const READ = 0x1; + /// Write access + const WRITE = 0x2; + } +} diff --git a/uefi-raw/src/protocol/mod.rs b/uefi-raw/src/protocol/mod.rs index e95966275..b7940648c 100644 --- a/uefi-raw/src/protocol/mod.rs +++ b/uefi-raw/src/protocol/mod.rs @@ -32,6 +32,7 @@ pub mod driver; pub mod file_system; pub mod firmware_volume; pub mod hii; +pub mod iommu; pub mod loaded_image; pub mod media; pub mod memory_protection; diff --git a/uefi/src/proto/dma/iommu.rs b/uefi/src/proto/dma/iommu.rs new file mode 100644 index 000000000..c7a1110ab --- /dev/null +++ b/uefi/src/proto/dma/iommu.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! EDK2 IoMmu protocol. + +use core::ffi::c_void; +use uefi::{ + Handle, Result, StatusExt, data_types::PhysicalAddress, mem::memory_map::MemoryType, + proto::unsafe_protocol, +}; + +pub use crate::{ + proto::dma::{DmaBuffer, Mapping}, + uefi_raw::protocol::iommu::{ + EdkiiIommuAccess, EdkiiIommuAttribute, EdkiiIommuOperation, EdkiiIommuProtocol, + }, +}; + +/// EDK2 IoMmu [`Protocol`]. +/// +/// [`Protocol`]: uefi::proto::Protocol +#[derive(Debug)] +#[repr(transparent)] +#[unsafe_protocol(EdkiiIommuProtocol::GUID)] +pub struct Iommu(EdkiiIommuProtocol); + +impl Iommu { + /// Get the IOMMU protocol revision + #[must_use] + pub const fn revision(&self) -> u64 { + self.0.revision + } + + /// Set access attributes for a mapping + pub fn set_attribute( + &self, + device_handle: Handle, + mapping: &Mapping, + iommu_access: EdkiiIommuAccess, + ) -> Result { + let mapping_raw = mapping.as_ptr(); + let status = unsafe { + (self.0.set_attribute)( + &self.0, + device_handle.as_ptr(), + mapping_raw, + iommu_access.bits(), + ) + }; + + status.to_result() + } + + /// Map a buffer for DMA operations + pub fn map( + &self, + operation: EdkiiIommuOperation, + host_buffer: &DmaBuffer, + number_of_bytes: usize, + ) -> Result<(PhysicalAddress, Mapping, usize)> { + let mut number_of_bytes = number_of_bytes; + + let mut mapping_raw: *mut c_void = core::ptr::null_mut(); + let mut device_address: u64 = 0; + + let host_address: *mut c_void = host_buffer.as_ptr(); + + let status = unsafe { + (self.0.map)( + &self.0, + operation, + host_address, + &mut number_of_bytes, + &mut device_address, + &mut mapping_raw, + ) + }; + + status.to_result_with_val(|| { + let mapping = unsafe { Mapping::from_raw(mapping_raw, self) }; + (device_address, mapping, number_of_bytes) + }) + } + + /// Unmap a previously mapped buffer + pub(crate) fn unmap_raw(&self, mapping: *mut c_void) -> Result { + let status = unsafe { (self.0.unmap)(&self.0, mapping) }; + status.to_result() + } + + /// Allocate a buffer suitable for DMA operations + pub fn allocate_buffer( + &self, + memory_type: MemoryType, + pages: usize, + attributes: EdkiiIommuAttribute, + ) -> Result { + let mut host_address: *mut c_void = core::ptr::null_mut(); + + // Must be ignored + let allocate_type = 0u32; + + let status = unsafe { + (self.0.allocate_buffer)( + &self.0, + allocate_type, + memory_type, + pages, + &mut host_address, + attributes.bits(), + ) + }; + + let dma_buffer = unsafe { DmaBuffer::from_raw(host_address, pages, self) }; + + status.to_result_with_val(|| dma_buffer) + } + + /// Free a buffer allocated with allocate_buffer + pub(crate) fn free_buffer_raw(&self, ptr: *mut c_void, pages: usize) -> Result { + let status = unsafe { (self.0.free_buffer)(&self.0, pages, ptr) }; + status.to_result() + } +} diff --git a/uefi/src/proto/dma/mod.rs b/uefi/src/proto/dma/mod.rs new file mode 100644 index 000000000..13ef950c8 --- /dev/null +++ b/uefi/src/proto/dma/mod.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! EDK2 IoMmu protocol. + +use core::{ + ffi::c_void, + ops::{Deref, DerefMut}, +}; + +use uefi_raw::table::boot::PAGE_SIZE; + +use crate::proto::dma::iommu::Iommu; + +pub mod iommu; + +/// A smart pointer for DMA buffers +#[must_use] +#[derive(Debug)] +pub struct DmaBuffer<'a> { + ptr: *mut c_void, + pages: usize, + iommu: &'a Iommu, +} + +impl<'a> DmaBuffer<'a> { + /// Create a new DmaBuffer from a raw pointer and page count + /// + /// # Safety + /// The caller must ensure that: + /// - `ptr` is a valid pointer to memory allocated by the IOMMU protocol + /// - `pages` correctly represents the number of pages allocated + pub const unsafe fn from_raw(ptr: *mut c_void, pages: usize, iommu: &'a Iommu) -> Self { + Self { ptr, pages, iommu } + } + + /// Get the raw pointer to the buffer + #[must_use] + pub const fn as_ptr(&self) -> *mut c_void { + self.ptr + } + + /// Get the number of pages in the buffer + #[must_use] + pub const fn pages(&self) -> usize { + self.pages + } + + /// Get the size of the buffer in bytes + #[must_use] + pub const fn size(&self) -> usize { + self.pages * PAGE_SIZE + } +} + +impl<'a> Deref for DmaBuffer<'a> { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.ptr as *const u8, self.pages * PAGE_SIZE) } + } +} + +impl<'a> DerefMut for DmaBuffer<'a> { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { core::slice::from_raw_parts_mut(self.ptr.cast::(), self.pages * PAGE_SIZE) } + } +} + +impl<'a> Drop for DmaBuffer<'a> { + fn drop(&mut self) { + let ptr = self.ptr; + let pages = self.pages; + let _ = self.iommu.free_buffer_raw(ptr, pages); + } +} + +/// A smart pointer for IOMMU mappings +#[must_use] +#[derive(Debug)] +pub struct Mapping<'a> { + ptr: *mut c_void, + iommu: &'a Iommu, +} + +impl<'a> Mapping<'a> { + /// Create a new Mapping from a raw pointer + /// + /// # Safety + /// The caller must ensure that: + /// - `ptr` is a valid mapping pointer returned by the IOMMU protocol + /// - The mapping is currently active and valid + pub const unsafe fn from_raw(ptr: *mut c_void, iommu: &'a Iommu) -> Self { + Self { ptr, iommu } + } + + /// Get the raw mapping pointer + #[must_use] + pub const fn as_ptr(&self) -> *mut c_void { + self.ptr + } +} + +impl<'a> Drop for Mapping<'a> { + fn drop(&mut self) { + let ptr = self.ptr; + let _ = self.iommu.unmap_raw(ptr); + } +} diff --git a/uefi/src/proto/mod.rs b/uefi/src/proto/mod.rs index 565f8f73e..a13cf9bb8 100644 --- a/uefi/src/proto/mod.rs +++ b/uefi/src/proto/mod.rs @@ -38,6 +38,7 @@ pub mod debug; pub mod device_path; pub mod driver; pub mod hii; +pub mod dma; pub mod loaded_image; pub mod media; pub mod misc; From 06eb3235bd9464eae971b9c49fb01824d18c9abf Mon Sep 17 00:00:00 2001 From: pellekrab <78560773+PelleKrab@users.noreply.github.com> Date: Wed, 30 Jul 2025 11:43:43 -0700 Subject: [PATCH 2/3] Removed uefi-raw impl --- uefi-raw/src/protocol/iommu.rs | 103 --------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 uefi-raw/src/protocol/iommu.rs diff --git a/uefi-raw/src/protocol/iommu.rs b/uefi-raw/src/protocol/iommu.rs deleted file mode 100644 index 269d5ce82..000000000 --- a/uefi-raw/src/protocol/iommu.rs +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 - -use crate::{Guid, Handle, Status, guid, table::boot::MemoryType}; -use bitflags::bitflags; -use core::ffi::c_void; - -use crate::newtype_enum; - -/// EDKII IOMMU Protocol GUID -impl EdkiiIommuProtocol { - pub const GUID: Guid = guid!("4e939de9-d948-4b0f-88ed-e6e1ce517c1e"); -} - -#[derive(Debug)] -#[repr(C)] -pub struct EdkiiIommuProtocol { - pub revision: u64, - pub set_attribute: unsafe extern "efiapi" fn( - this: &Self, - device_handle: Handle, - mapping: *mut c_void, - iommu_access: u64, - ) -> Status, - pub map: unsafe extern "efiapi" fn( - this: &Self, - operation: EdkiiIommuOperation, - host_address: *mut c_void, - number_of_bytes: *mut usize, - device_address: *mut u64, - mapping: *mut *mut c_void, - ) -> Status, - pub unmap: unsafe extern "efiapi" fn(this: &Self, mapping: *mut c_void) -> Status, - pub allocate_buffer: unsafe extern "efiapi" fn( - this: &Self, - allocate_type: u32, - memory_type: MemoryType, - pages: usize, - host_address: *mut *mut c_void, - attributes: u64, - ) -> Status, - pub free_buffer: - unsafe extern "efiapi" fn(this: &Self, pages: usize, host_address: *mut c_void) -> Status, -} - -newtype_enum! { - /// IOMMU Operation for Map (matches EDKII_IOMMU_OPERATION) - pub enum EdkiiIommuOperation: u32 => { - /// A read operation from system memory by a bus master that is not capable of producing PCI dual address cycles. - BUS_MASTER_READ = 0, - /// A write operation to system memory by a bus master that is not capable of producing PCI dual address cycles. - BUS_MASTER_WRITE = 1, - /// Provides both read and write access to system memory by both the processor and a bus master that is not capable of producing PCI dual address cycles. - BUS_MASTER_COMMON_BUFFER = 2, - /// A read operation from system memory by a bus master that is capable of producing PCI dual address cycles. - BUS_MASTER_READ64 = 3, - /// A write operation to system memory by a bus master that is capable of producing PCI dual address cycles. - BUS_MASTER_WRITE64 = 4, - /// Provides both read and write access to system memory by both the processor and a bus master that is capable of producing PCI dual address cycles. - BUS_MASTER_COMMON_BUFFER64 = 5, - /// Maximum value (not a valid operation, for bounds checking) - MAXIMUM = 6, - } -} - -/// EDKII IOMMU protocol revision constant -pub const EDKII_IOMMU_PROTOCOL_REVISION: u64 = 0x0001_0000; - -bitflags! { - /// EDKII IOMMU attribute flags - #[derive(Default)] - pub struct EdkiiIommuAttribute: u64 { - /// Memory is write-combined - const MEMORY_WRITE_COMBINE = 0x0080; - /// Memory is cached - const MEMORY_CACHED = 0x0800; - /// Dual address cycle supported - const DUAL_ADDRESS_CYCLE = 0x8000; - } -} - -impl EdkiiIommuAttribute { - /// Valid attributes for allocate_buffer - pub const VALID_FOR_ALLOCATE_BUFFER: Self = Self::from_bits_truncate( - Self::MEMORY_WRITE_COMBINE.bits() - | Self::MEMORY_CACHED.bits() - | Self::DUAL_ADDRESS_CYCLE.bits(), - ); - - /// Invalid attributes for allocate_buffer (all bits except valid) - pub const INVALID_FOR_ALLOCATE_BUFFER: Self = - Self::from_bits_truncate(!Self::VALID_FOR_ALLOCATE_BUFFER.bits()); -} - -bitflags! { - /// EDKII IOMMU access flags for SetAttribute - #[derive(Default)] - pub struct EdkiiIommuAccess: u64 { - /// Read access - const READ = 0x1; - /// Write access - const WRITE = 0x2; - } -} From 7df462ed1d1b3d184eb86ff1fdd4f58c3d9dec96 Mon Sep 17 00:00:00 2001 From: pellekrab <78560773+PelleKrab@users.noreply.github.com> Date: Wed, 30 Jul 2025 11:45:50 -0700 Subject: [PATCH 3/3] Removed iommu from uefi-raw mod.rs --- uefi-raw/src/protocol/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/uefi-raw/src/protocol/mod.rs b/uefi-raw/src/protocol/mod.rs index b7940648c..e95966275 100644 --- a/uefi-raw/src/protocol/mod.rs +++ b/uefi-raw/src/protocol/mod.rs @@ -32,7 +32,6 @@ pub mod driver; pub mod file_system; pub mod firmware_volume; pub mod hii; -pub mod iommu; pub mod loaded_image; pub mod media; pub mod memory_protection;