Skip to content

Feat: IoMmu protocol #1728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions uefi/src/proto/dma/iommu.rs
Original file line number Diff line number Diff line change
@@ -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<DmaBuffer> {
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()
}
}
108 changes: 108 additions & 0 deletions uefi/src/proto/dma/mod.rs
Original file line number Diff line number Diff line change
@@ -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::<u8>(), 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);
}
}
1 change: 1 addition & 0 deletions uefi/src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading