Skip to content

Commit f4c1b4c

Browse files
committed
feat: IoMmu protocol
fix
1 parent a4b852f commit f4c1b4c

File tree

5 files changed

+336
-0
lines changed

5 files changed

+336
-0
lines changed

uefi-raw/src/protocol/iommu.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
use crate::{Guid, Handle, Status, guid, table::boot::MemoryType};
4+
use bitflags::bitflags;
5+
use core::ffi::c_void;
6+
7+
use crate::newtype_enum;
8+
9+
/// EDKII IOMMU Protocol GUID
10+
impl EdkiiIommuProtocol {
11+
pub const GUID: Guid = guid!("4e939de9-d948-4b0f-88ed-e6e1ce517c1e");
12+
}
13+
14+
#[derive(Debug)]
15+
#[repr(C)]
16+
pub struct EdkiiIommuProtocol {
17+
pub revision: u64,
18+
pub set_attribute: unsafe extern "efiapi" fn(
19+
this: &Self,
20+
device_handle: Handle,
21+
mapping: *mut c_void,
22+
iommu_access: u64,
23+
) -> Status,
24+
pub map: unsafe extern "efiapi" fn(
25+
this: &Self,
26+
operation: EdkiiIommuOperation,
27+
host_address: *mut c_void,
28+
number_of_bytes: *mut usize,
29+
device_address: *mut u64,
30+
mapping: *mut *mut c_void,
31+
) -> Status,
32+
pub unmap: unsafe extern "efiapi" fn(this: &Self, mapping: *mut c_void) -> Status,
33+
pub allocate_buffer: unsafe extern "efiapi" fn(
34+
this: &Self,
35+
allocate_type: u32,
36+
memory_type: MemoryType,
37+
pages: usize,
38+
host_address: *mut *mut c_void,
39+
attributes: u64,
40+
) -> Status,
41+
pub free_buffer:
42+
unsafe extern "efiapi" fn(this: &Self, pages: usize, host_address: *mut c_void) -> Status,
43+
}
44+
45+
newtype_enum! {
46+
/// IOMMU Operation for Map (matches EDKII_IOMMU_OPERATION)
47+
pub enum EdkiiIommuOperation: u32 => {
48+
/// A read operation from system memory by a bus master that is not capable of producing PCI dual address cycles.
49+
BUS_MASTER_READ = 0,
50+
/// A write operation to system memory by a bus master that is not capable of producing PCI dual address cycles.
51+
BUS_MASTER_WRITE = 1,
52+
/// 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.
53+
BUS_MASTER_COMMON_BUFFER = 2,
54+
/// A read operation from system memory by a bus master that is capable of producing PCI dual address cycles.
55+
BUS_MASTER_READ64 = 3,
56+
/// A write operation to system memory by a bus master that is capable of producing PCI dual address cycles.
57+
BUS_MASTER_WRITE64 = 4,
58+
/// 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.
59+
BUS_MASTER_COMMON_BUFFER64 = 5,
60+
/// Maximum value (not a valid operation, for bounds checking)
61+
MAXIMUM = 6,
62+
}
63+
}
64+
65+
/// EDKII IOMMU protocol revision constant
66+
pub const EDKII_IOMMU_PROTOCOL_REVISION: u64 = 0x0001_0000;
67+
68+
bitflags! {
69+
/// EDKII IOMMU attribute flags
70+
#[derive(Default)]
71+
pub struct EdkiiIommuAttribute: u64 {
72+
/// Memory is write-combined
73+
const MEMORY_WRITE_COMBINE = 0x0080;
74+
/// Memory is cached
75+
const MEMORY_CACHED = 0x0800;
76+
/// Dual address cycle supported
77+
const DUAL_ADDRESS_CYCLE = 0x8000;
78+
}
79+
}
80+
81+
impl EdkiiIommuAttribute {
82+
/// Valid attributes for allocate_buffer
83+
pub const VALID_FOR_ALLOCATE_BUFFER: Self = Self::from_bits_truncate(
84+
Self::MEMORY_WRITE_COMBINE.bits()
85+
| Self::MEMORY_CACHED.bits()
86+
| Self::DUAL_ADDRESS_CYCLE.bits(),
87+
);
88+
89+
/// Invalid attributes for allocate_buffer (all bits except valid)
90+
pub const INVALID_FOR_ALLOCATE_BUFFER: Self =
91+
Self::from_bits_truncate(!Self::VALID_FOR_ALLOCATE_BUFFER.bits());
92+
}
93+
94+
bitflags! {
95+
/// EDKII IOMMU access flags for SetAttribute
96+
#[derive(Default)]
97+
pub struct EdkiiIommuAccess: u64 {
98+
/// Read access
99+
const READ = 0x1;
100+
/// Write access
101+
const WRITE = 0x2;
102+
}
103+
}

uefi-raw/src/protocol/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod driver;
3232
pub mod file_system;
3333
pub mod firmware_volume;
3434
pub mod hii;
35+
pub mod iommu;
3536
pub mod loaded_image;
3637
pub mod media;
3738
pub mod memory_protection;

uefi/src/proto/dma/iommu.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! EDK2 IoMmu protocol.
4+
5+
use core::ffi::c_void;
6+
use uefi::{
7+
Handle, Result, StatusExt, data_types::PhysicalAddress, mem::memory_map::MemoryType,
8+
proto::unsafe_protocol,
9+
};
10+
11+
pub use crate::{
12+
proto::dma::{DmaBuffer, Mapping},
13+
uefi_raw::protocol::iommu::{
14+
EdkiiIommuAccess, EdkiiIommuAttribute, EdkiiIommuOperation, EdkiiIommuProtocol,
15+
},
16+
};
17+
18+
/// EDK2 IoMmu [`Protocol`].
19+
///
20+
/// [`Protocol`]: uefi::proto::Protocol
21+
#[derive(Debug)]
22+
#[repr(transparent)]
23+
#[unsafe_protocol(EdkiiIommuProtocol::GUID)]
24+
pub struct Iommu(EdkiiIommuProtocol);
25+
26+
impl Iommu {
27+
/// Get the IOMMU protocol revision
28+
#[must_use]
29+
pub const fn revision(&self) -> u64 {
30+
self.0.revision
31+
}
32+
33+
/// Set access attributes for a mapping
34+
pub fn set_attribute(
35+
&self,
36+
device_handle: Handle,
37+
mapping: &Mapping,
38+
iommu_access: EdkiiIommuAccess,
39+
) -> Result {
40+
let mapping_raw = mapping.as_ptr();
41+
let status = unsafe {
42+
(self.0.set_attribute)(
43+
&self.0,
44+
device_handle.as_ptr(),
45+
mapping_raw,
46+
iommu_access.bits(),
47+
)
48+
};
49+
50+
status.to_result()
51+
}
52+
53+
/// Map a buffer for DMA operations
54+
pub fn map(
55+
&self,
56+
operation: EdkiiIommuOperation,
57+
host_buffer: &DmaBuffer,
58+
number_of_bytes: usize,
59+
) -> Result<(PhysicalAddress, Mapping, usize)> {
60+
let mut number_of_bytes = number_of_bytes;
61+
62+
let mut mapping_raw: *mut c_void = core::ptr::null_mut();
63+
let mut device_address: u64 = 0;
64+
65+
let host_address: *mut c_void = host_buffer.as_ptr();
66+
67+
let status = unsafe {
68+
(self.0.map)(
69+
&self.0,
70+
operation,
71+
host_address,
72+
&mut number_of_bytes,
73+
&mut device_address,
74+
&mut mapping_raw,
75+
)
76+
};
77+
78+
status.to_result_with_val(|| {
79+
let mapping = unsafe { Mapping::from_raw(mapping_raw, self) };
80+
(device_address, mapping, number_of_bytes)
81+
})
82+
}
83+
84+
/// Unmap a previously mapped buffer
85+
pub(crate) fn unmap_raw(&self, mapping: *mut c_void) -> Result {
86+
let status = unsafe { (self.0.unmap)(&self.0, mapping) };
87+
status.to_result()
88+
}
89+
90+
/// Allocate a buffer suitable for DMA operations
91+
pub fn allocate_buffer(
92+
&self,
93+
memory_type: MemoryType,
94+
pages: usize,
95+
attributes: EdkiiIommuAttribute,
96+
) -> Result<DmaBuffer> {
97+
let mut host_address: *mut c_void = core::ptr::null_mut();
98+
99+
// Must be ignored
100+
let allocate_type = 0u32;
101+
102+
let status = unsafe {
103+
(self.0.allocate_buffer)(
104+
&self.0,
105+
allocate_type,
106+
memory_type,
107+
pages,
108+
&mut host_address,
109+
attributes.bits(),
110+
)
111+
};
112+
113+
let dma_buffer = unsafe { DmaBuffer::from_raw(host_address, pages, self) };
114+
115+
status.to_result_with_val(|| dma_buffer)
116+
}
117+
118+
/// Free a buffer allocated with allocate_buffer
119+
pub(crate) fn free_buffer_raw(&self, ptr: *mut c_void, pages: usize) -> Result {
120+
let status = unsafe { (self.0.free_buffer)(&self.0, pages, ptr) };
121+
status.to_result()
122+
}
123+
}

uefi/src/proto/dma/mod.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! EDK2 IoMmu protocol.
4+
5+
use core::{
6+
ffi::c_void,
7+
ops::{Deref, DerefMut},
8+
};
9+
10+
use uefi_raw::table::boot::PAGE_SIZE;
11+
12+
use crate::proto::dma::iommu::Iommu;
13+
14+
pub mod iommu;
15+
16+
/// A smart pointer for DMA buffers
17+
#[must_use]
18+
#[derive(Debug)]
19+
pub struct DmaBuffer<'a> {
20+
ptr: *mut c_void,
21+
pages: usize,
22+
iommu: &'a Iommu,
23+
}
24+
25+
impl<'a> DmaBuffer<'a> {
26+
/// Create a new DmaBuffer from a raw pointer and page count
27+
///
28+
/// # Safety
29+
/// The caller must ensure that:
30+
/// - `ptr` is a valid pointer to memory allocated by the IOMMU protocol
31+
/// - `pages` correctly represents the number of pages allocated
32+
pub const unsafe fn from_raw(ptr: *mut c_void, pages: usize, iommu: &'a Iommu) -> Self {
33+
Self { ptr, pages, iommu }
34+
}
35+
36+
/// Get the raw pointer to the buffer
37+
#[must_use]
38+
pub const fn as_ptr(&self) -> *mut c_void {
39+
self.ptr
40+
}
41+
42+
/// Get the number of pages in the buffer
43+
#[must_use]
44+
pub const fn pages(&self) -> usize {
45+
self.pages
46+
}
47+
48+
/// Get the size of the buffer in bytes
49+
#[must_use]
50+
pub const fn size(&self) -> usize {
51+
self.pages * PAGE_SIZE
52+
}
53+
}
54+
55+
impl<'a> Deref for DmaBuffer<'a> {
56+
type Target = [u8];
57+
58+
fn deref(&self) -> &[u8] {
59+
unsafe { core::slice::from_raw_parts(self.ptr as *const u8, self.pages * PAGE_SIZE) }
60+
}
61+
}
62+
63+
impl<'a> DerefMut for DmaBuffer<'a> {
64+
fn deref_mut(&mut self) -> &mut [u8] {
65+
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast::<u8>(), self.pages * PAGE_SIZE) }
66+
}
67+
}
68+
69+
impl<'a> Drop for DmaBuffer<'a> {
70+
fn drop(&mut self) {
71+
let ptr = self.ptr;
72+
let pages = self.pages;
73+
let _ = self.iommu.free_buffer_raw(ptr, pages);
74+
}
75+
}
76+
77+
/// A smart pointer for IOMMU mappings
78+
#[must_use]
79+
#[derive(Debug)]
80+
pub struct Mapping<'a> {
81+
ptr: *mut c_void,
82+
iommu: &'a Iommu,
83+
}
84+
85+
impl<'a> Mapping<'a> {
86+
/// Create a new Mapping from a raw pointer
87+
///
88+
/// # Safety
89+
/// The caller must ensure that:
90+
/// - `ptr` is a valid mapping pointer returned by the IOMMU protocol
91+
/// - The mapping is currently active and valid
92+
pub const unsafe fn from_raw(ptr: *mut c_void, iommu: &'a Iommu) -> Self {
93+
Self { ptr, iommu }
94+
}
95+
96+
/// Get the raw mapping pointer
97+
#[must_use]
98+
pub const fn as_ptr(&self) -> *mut c_void {
99+
self.ptr
100+
}
101+
}
102+
103+
impl<'a> Drop for Mapping<'a> {
104+
fn drop(&mut self) {
105+
let ptr = self.ptr;
106+
let _ = self.iommu.unmap_raw(ptr);
107+
}
108+
}

uefi/src/proto/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod debug;
3838
pub mod device_path;
3939
pub mod driver;
4040
pub mod hii;
41+
pub mod dma;
4142
pub mod loaded_image;
4243
pub mod media;
4344
pub mod misc;

0 commit comments

Comments
 (0)