Merge pull request #1728 from PelleKrab/Iommu

Feat: IoMmu protocol
This commit is contained in:
Philipp Schuster 2026-06-25 07:27:35 +00:00 committed by GitHub
commit 838b978c7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 676 additions and 2 deletions

View file

@ -72,7 +72,7 @@ pub const EDKII_IOMMU_PROTOCOL_REVISION: u64 = 0x0001_0000;
bitflags! {
/// EDKII IOMMU attribute flags
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EdkiiIommuAttribute: u64 {
/// Memory is write-combined
const MEMORY_WRITE_COMBINE = 0x0080;
@ -99,7 +99,7 @@ impl EdkiiIommuAttribute {
bitflags! {
/// EDKII IOMMU access flags for SetAttribute
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EdkiiIommuAccess: u64 {
/// Read access
const READ = 0x1;

View file

@ -0,0 +1,358 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use uefi::boot;
use uefi::mem::memory_map::MemoryType;
use uefi::proto::dma::iommu::{EdkiiIommuAccess, EdkiiIommuAttribute, EdkiiIommuOperation, Iommu};
/// Runs the IOMMU protocol integration tests against the firmware-provided
/// protocol instance. These checks cover allocation, buffer access, mapping
/// operations, attributes, and representative buffer sizes.
pub fn test() {
info!("Running IOMMU protocol test");
let handle =
boot::get_handle_for_protocol::<Iommu>().expect("Failed to get IOMMU protocol handle");
let iommu =
boot::open_protocol_exclusive::<Iommu>(handle).expect("Failed to open IOMMU protocol");
let revision = iommu.revision();
info!("Revision: {revision:#x}");
test_allocate_buffer(&iommu);
test_buffer_read_write(&iommu);
test_map_operations(&iommu);
test_map_64bit_operations(&iommu);
test_multiple_mappings(&iommu);
test_different_attributes(&iommu);
test_multiple_buffer_sizes(&iommu);
test_reject_oversized_mapping(&iommu);
}
/// Tests that the IOMMU protocol can allocate a one-page DMA buffer.
/// It verifies both the recorded page count and the byte size exposed by the
/// safe wrapper.
fn test_allocate_buffer(iommu: &Iommu) {
let pages = 1;
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let buffer = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate IOMMU buffer");
assert_eq!(buffer.pages(), pages);
assert_eq!(buffer.size(), 4096);
}
/// Tests that an allocated DMA buffer can be accessed as a mutable byte slice.
fn test_buffer_read_write(iommu: &Iommu) {
let pages = 1;
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate IOMMU buffer");
for (i, byte) in buffer.iter_mut().enumerate() {
*byte = (i & 0xFF) as u8;
}
for (i, byte) in buffer.iter().enumerate() {
assert_eq!(*byte, (i & 0xFF) as u8, "Buffer mismatch at offset {}", i);
}
buffer[0] = 0xAA;
buffer[100] = 0xBB;
buffer[4095] = 0xCC;
assert_eq!(buffer[0], 0xAA);
assert_eq!(buffer[100], 0xBB);
assert_eq!(buffer[4095], 0xCC);
}
/// Tests basic mapping operations on an allocated IOMMU buffer.
/// It verifies that the buffer can be mapped successfully for device access
/// and that the mapped size matches the requested size.
fn test_map_operations(iommu: &Iommu) {
let pages = 1;
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate IOMMU buffer");
let buffer_size = buffer.size();
let (_device_address, mut mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_READ");
assert_eq!(mapped_bytes, buffer_size);
let image_handle = boot::image_handle();
match iommu.set_attribute(image_handle, &mut mapping, EdkiiIommuAccess::READ) {
Ok(()) => {}
Err(e) if e.status() == uefi::Status::UNSUPPORTED => {}
Err(e) => panic!("set_attribute failed with unexpected error: {:?}", e),
}
drop(mapping);
let (_device_address, mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_WRITE,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_WRITE");
assert_eq!(mapped_bytes, buffer_size);
drop(mapping);
let (_device_address, _mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_COMMON_BUFFER,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_COMMON_BUFFER");
assert_eq!(mapped_bytes, buffer_size);
}
/// Tests 64-bit mapping operations on an allocated IOMMU buffer.
/// This ensures the IOMMU correctly handles requests specifically requiring 64-bit device addresses.
fn test_map_64bit_operations(iommu: &Iommu) {
let pages = 1;
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate IOMMU buffer");
let buffer_size = buffer.size();
let (_device_address, mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ64,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_READ64");
assert_eq!(mapped_bytes, buffer_size);
drop(mapping);
let (_device_address, mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_WRITE64,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_WRITE64");
assert_eq!(mapped_bytes, buffer_size);
drop(mapping);
let (_device_address, _mapping, mapped_bytes) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_COMMON_BUFFER64,
&mut buffer,
buffer_size,
)
.expect("Failed to map buffer for BUS_MASTER_COMMON_BUFFER64");
assert_eq!(mapped_bytes, buffer_size);
}
/// Tests that mapping multiple distinct buffers yields unique device addresses.
/// This verifies the IOMMU allocator's ability to handle concurrent mappings
/// without address space collisions.
fn test_multiple_mappings(iommu: &Iommu) {
let pages = 1;
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer1 = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate buffer 1");
let mut buffer2 = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate buffer 2");
let mut buffer3 = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, attributes)
.expect("Failed to allocate buffer 3");
let buffer1_size = buffer1.size();
let buffer2_size = buffer2.size();
let buffer3_size = buffer3.size();
let (addr1, mapping1, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer1,
buffer1_size,
)
.expect("Failed to map buffer 1");
let (addr2, mapping2, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_WRITE,
&mut buffer2,
buffer2_size,
)
.expect("Failed to map buffer 2");
let (addr3, mapping3, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_COMMON_BUFFER,
&mut buffer3,
buffer3_size,
)
.expect("Failed to map buffer 3");
assert_ne!(
addr1, addr2,
"Mapping 1 and 2 should have different addresses"
);
assert_ne!(
addr2, addr3,
"Mapping 2 and 3 should have different addresses"
);
assert_ne!(
addr1, addr3,
"Mapping 1 and 3 should have different addresses"
);
drop(mapping1);
drop(mapping2);
drop(mapping3);
}
/// Tests allocation and mapping with several IOMMU memory attributes.
/// This verifies that cached, write-combine, and combined attributes can pass
/// through the safe API to firmware.
fn test_different_attributes(iommu: &Iommu) {
let pages = 1;
let mut buffer_cached = iommu
.allocate_buffer(
MemoryType::BOOT_SERVICES_DATA,
pages,
EdkiiIommuAttribute::MEMORY_CACHED,
)
.expect("Failed to allocate MEMORY_CACHED buffer");
let mut buffer_wc = iommu
.allocate_buffer(
MemoryType::BOOT_SERVICES_DATA,
pages,
EdkiiIommuAttribute::MEMORY_WRITE_COMBINE,
)
.expect("Failed to allocate MEMORY_WRITE_COMBINE buffer");
let combined_attrs =
EdkiiIommuAttribute::MEMORY_CACHED | EdkiiIommuAttribute::DUAL_ADDRESS_CYCLE;
let mut buffer_combined = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, pages, combined_attrs)
.expect("Failed to allocate buffer with combined attributes");
let buffer_cached_size = buffer_cached.size();
let buffer_wc_size = buffer_wc.size();
let buffer_combined_size = buffer_combined.size();
let (_addr, _mapping, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_cached,
buffer_cached_size,
)
.expect("Failed to map cached buffer");
let (_addr, _mapping, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_wc,
buffer_wc_size,
)
.expect("Failed to map write-combine buffer");
let (_addr, _mapping, _) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_combined,
buffer_combined_size,
)
.expect("Failed to map combined attributes buffer");
}
/// Tests allocation and mapping for representative one-page and multi-page
/// buffers. It verifies that wrapper size calculations stay consistent across
/// different page counts.
fn test_multiple_buffer_sizes(iommu: &Iommu) {
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer_1pg = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, 1, attributes)
.expect("Failed to allocate 1-page buffer");
assert_eq!(buffer_1pg.pages(), 1);
assert_eq!(buffer_1pg.size(), 4096);
let mut buffer_4pg = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, 4, attributes)
.expect("Failed to allocate 4-page buffer");
assert_eq!(buffer_4pg.pages(), 4);
assert_eq!(buffer_4pg.size(), 16384);
let mut buffer_16pg = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, 16, attributes)
.expect("Failed to allocate 16-page buffer");
assert_eq!(buffer_16pg.pages(), 16);
assert_eq!(buffer_16pg.size(), 65536);
let buffer_1pg_size = buffer_1pg.size();
let buffer_4pg_size = buffer_4pg.size();
let buffer_16pg_size = buffer_16pg.size();
let (_, _mapping, mapped) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_1pg,
buffer_1pg_size,
)
.expect("Failed to map 1-page buffer");
assert_eq!(mapped, buffer_1pg_size);
let (_, _mapping, mapped) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_4pg,
buffer_4pg_size,
)
.expect("Failed to map 4-page buffer");
assert_eq!(mapped, buffer_4pg_size);
let (_, _mapping, mapped) = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer_16pg,
buffer_16pg_size,
)
.expect("Failed to map 16-page buffer");
assert_eq!(mapped, buffer_16pg_size);
}
/// Tests that the safe mapping API rejects lengths larger than the DMA buffer.
fn test_reject_oversized_mapping(iommu: &Iommu) {
let attributes = EdkiiIommuAttribute::MEMORY_CACHED;
let mut buffer = iommu
.allocate_buffer(MemoryType::BOOT_SERVICES_DATA, 1, attributes)
.expect("Failed to allocate IOMMU buffer");
let oversized_len = buffer.size() + 1;
let err = iommu
.map(
EdkiiIommuOperation::BUS_MASTER_READ,
&mut buffer,
oversized_len,
)
.expect_err("IOMMU map accepted an oversized buffer length");
assert_eq!(err.status(), uefi::Status::BAD_BUFFER_SIZE);
}

View file

@ -30,6 +30,9 @@ pub fn test() {
usb::test();
misc::test();
#[cfg(target_arch = "x86_64")]
iommu::test();
// disable the ATA test on aarch64 for now. The aarch64 UEFI Firmware does not yet seem
// to support SATA controllers (and providing an AtaPassThru protocol instance for them).
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@ -111,6 +114,8 @@ mod console;
mod debug;
mod device_path;
mod driver;
#[cfg(target_arch = "x86_64")]
mod iommu;
mod load;
mod loaded_image;
mod media;

View file

@ -17,6 +17,7 @@
set_attributes(), set_attributes_with_range()}`
- Added `memory()` and `io()` address space access to `PciRootBridgeIo`
protocol.
- Added `proto::dma::iommu::Iommu` for IOMMU-based DMA buffer management.
## Changed
- MSRV increased from 1.88 to 1.91.

162
uefi/src/proto/dma/iommu.rs Normal file
View file

@ -0,0 +1,162 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//! EDKII IOMMU protocol.
use crate::data_types::PhysicalAddress;
use crate::mem::memory_map::MemoryType;
use crate::proto::unsafe_protocol;
use crate::{Handle, Result, Status, StatusExt};
use core::ffi::c_void;
use uefi_raw::table::boot::AllocateType;
pub use crate::proto::dma::{DmaBuffer, Mapping};
pub use 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.
///
/// # Errors
///
/// * [`crate::Status::INVALID_PARAMETER`]: invalid device handle, mapping, or access flags
/// * [`crate::Status::UNSUPPORTED`]: operation not supported by this IOMMU
/// * [`crate::Status::OUT_OF_RESOURCES`]: insufficient resources to modify IOMMU access
/// * [`crate::Status::DEVICE_ERROR`]: IOMMU device reported an error
pub fn set_attribute(
&self,
device_handle: Handle,
mapping: &mut Mapping<'_, '_>,
iommu_access: EdkiiIommuAccess,
) -> Result {
let mapping_raw = mapping.as_mut_ptr();
// SAFETY: `mapping_raw` comes from a live `Mapping`, and `device_handle`
// is an opaque firmware handle passed through unchanged.
let status = unsafe {
(self.0.set_attribute)(&self.0, device_handle.as_ptr(), mapping_raw, iommu_access)
};
status.to_result()
}
/// Map a buffer for DMA operations.
///
/// Returns the device address, mapping handle, and actual number of bytes mapped.
/// The mapping is tied to `host_buffer` and will be automatically unmapped when
/// dropped.
///
/// # Errors
///
/// * [`crate::Status::INVALID_PARAMETER`]: invalid operation or buffer
/// * [`crate::Status::BAD_BUFFER_SIZE`]: `number_of_bytes` is larger than `host_buffer`
/// * [`crate::Status::UNSUPPORTED`]: host address cannot be mapped as a common buffer
/// * [`crate::Status::OUT_OF_RESOURCES`]: insufficient resources
/// * [`crate::Status::DEVICE_ERROR`]: system hardware could not map the requested address
pub fn map<'iommu, 'buf>(
&'iommu self,
operation: EdkiiIommuOperation,
host_buffer: &'buf mut DmaBuffer<'iommu>,
number_of_bytes: usize,
) -> Result<(PhysicalAddress, Mapping<'iommu, 'buf>, usize)> {
if number_of_bytes > host_buffer.size() {
return Err(Status::BAD_BUFFER_SIZE.into());
}
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_mut_ptr();
// SAFETY: `host_address` points into `host_buffer`, which is valid for
// `number_of_bytes` because oversized lengths were rejected above.
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(|| {
// SAFETY: `mapping_raw` was returned by this IOMMU protocol, and
// `host_buffer` remains mutably borrowed for the mapping lifetime.
let mapping = unsafe { Mapping::from_raw(mapping_raw, self, host_buffer) };
(device_address, mapping, number_of_bytes)
})
}
/// Unmap a previously mapped buffer
pub(crate) fn unmap_raw(&self, mapping: *mut c_void) -> Result {
// SAFETY: The safe `Mapping` API only stores active mapping
// pointers returned by this protocol, and `Drop` calls this once.
let status = unsafe { (self.0.unmap)(&self.0, mapping) };
status.to_result()
}
/// Allocate a buffer suitable for DMA operations.
///
/// The buffer will be automatically freed when dropped.
///
/// # Errors
///
/// * [`crate::Status::INVALID_PARAMETER`]: invalid memory type or attributes
/// * [`crate::Status::UNSUPPORTED`]: unsupported attributes
/// * [`crate::Status::OUT_OF_RESOURCES`]: memory pages could not be allocated
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();
// Per spec, AllocateType is ignored by the IOMMU allocate_buffer implementation.
let allocate_type = AllocateType::ANY_PAGES;
// SAFETY: `host_address` is a valid out-pointer, and all other
// arguments are plain values forwarded to firmware.
let status = unsafe {
(self.0.allocate_buffer)(
&self.0,
allocate_type,
memory_type,
pages,
&mut host_address,
attributes,
)
};
status.to_result_with_val(|| {
// SAFETY: On success, firmware initialized `host_address` with a
// buffer allocated by this IOMMU protocol for `pages` pages.
unsafe { DmaBuffer::from_raw(host_address, pages, self) }
})
}
/// Free a buffer allocated with allocate_buffer
pub(crate) fn free_buffer_raw(&self, ptr: *mut c_void, pages: usize) -> Result {
// SAFETY: `DmaBuffer` calls this only for buffers allocated by this
// protocol, preserving the original page count.
let status = unsafe { (self.0.free_buffer)(&self.0, pages, ptr) };
status.to_result()
}
}

144
uefi/src/proto/dma/mod.rs Normal file
View file

@ -0,0 +1,144 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//! EDK2 IOMMU protocol.
use core::ffi::c_void;
use core::marker::PhantomData;
use core::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 allocated through the IOMMU protocol.
///
/// The buffer can be accessed as a byte slice and is returned to firmware when
/// dropped.
#[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 valid for `pages * PAGE_SIZE` bytes allocated by the IOMMU protocol.
/// - `pages` correctly represents the number of pages allocated.
/// - `iommu` is the protocol instance that allocated `ptr`.
/// - This `DmaBuffer` is the unique owner responsible for freeing `ptr`.
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) -> *const c_void {
self.ptr.cast_const()
}
/// Get the raw mutable pointer to the buffer.
#[must_use]
pub const fn as_mut_ptr(&mut 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] {
// SAFETY: `DmaBuffer::from_raw` requires `ptr` to be valid for
// `pages * PAGE_SIZE` bytes for this buffer's lifetime.
unsafe { core::slice::from_raw_parts(self.ptr.cast(), self.pages * PAGE_SIZE) }
}
}
impl<'a> DerefMut for DmaBuffer<'a> {
fn deref_mut(&mut self) -> &mut [u8] {
// SAFETY: `&mut self` guarantees unique access to the owned DMA buffer,
// whose raw memory is valid for `pages * PAGE_SIZE` bytes.
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) {
if let Err(e) = self.iommu.free_buffer_raw(self.ptr, self.pages) {
log::error!("IOMMU free_buffer failed: {e:?}");
}
}
}
/// A smart pointer for active DMA buffer mappings.
///
/// The mapping keeps the firmware mapping alive and unmaps it when
/// dropped.
#[must_use]
#[derive(Debug)]
pub struct Mapping<'a, 'buf> {
ptr: *mut c_void,
iommu: &'a Iommu,
_buffer: PhantomData<&'buf mut DmaBuffer<'a>>,
}
impl<'a, 'buf> Mapping<'a, 'buf> {
/// 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.
/// - `iommu` is the protocol instance that created `ptr`.
/// - `_buffer` is the `DmaBuffer` used to create this mapping and remains
/// exclusively borrowed for the returned mapping's lifetime.
pub const unsafe fn from_raw(
ptr: *mut c_void,
iommu: &'a Iommu,
_buffer: &'buf mut DmaBuffer<'a>,
) -> Self {
Self {
ptr,
iommu,
_buffer: PhantomData,
}
}
/// Get the raw mapping pointer.
#[must_use]
pub const fn as_ptr(&self) -> *const c_void {
self.ptr.cast_const()
}
/// Get the raw mutable mapping pointer.
#[must_use]
pub const fn as_mut_ptr(&mut self) -> *mut c_void {
self.ptr
}
}
impl<'a, 'buf> Drop for Mapping<'a, 'buf> {
fn drop(&mut self) {
if let Err(e) = self.iommu.unmap_raw(self.as_mut_ptr()) {
log::error!("IOMMU unmap failed: {e:?}");
}
}
}

View file

@ -37,6 +37,7 @@ pub mod ata;
pub mod console;
pub mod debug;
pub mod device_path;
pub mod dma;
pub mod driver;
pub mod hii;
pub mod loaded_image;

View file

@ -425,6 +425,9 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
UefiArch::IA32 | UefiArch::X86_64 => {
// Use a modern machine.
cmd.args(["-machine", "q35"]);
if arch == UefiArch::X86_64 {
cmd.args(["-device", "intel-iommu"]);
}
// Multi-processor services protocol test needs exactly 4 CPUs.
cmd.args(["-smp", "4"]);