Skip to content

Commit dda6680

Browse files
committed
uefi: Add wrapper for the mapped region of memory
1 parent 21c0f7c commit dda6680

File tree

4 files changed

+237
-3
lines changed

4 files changed

+237
-3
lines changed

uefi-test-runner/src/proto/pci/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ pub mod root_bridge;
55
pub fn test() {
66
root_bridge::test_io();
77
root_bridge::test_buffer();
8+
root_bridge::test_mapping();
89
}

uefi-test-runner/src/proto/pci/root_bridge.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use uefi::proto::device_path::DevicePath;
99
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
1010
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
1111
use uefi::proto::scsi::pass_thru::ExtScsiPassThru;
12-
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttribute;
12+
use uefi_raw::protocol::pci::root_bridge::{
13+
PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation,
14+
};
1315
use uefi_raw::table::boot::MemoryType;
1416

1517
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
@@ -115,6 +117,45 @@ pub fn test_buffer() {
115117
}
116118
}
117119

120+
pub fn test_mapping() {
121+
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
122+
const BUFFER_SIZE: usize = 12342;
123+
124+
for pci_handle in pci_handles {
125+
let pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
126+
127+
let buffer = pci_proto
128+
.allocate_buffer::<[u8; BUFFER_SIZE]>(
129+
MemoryType::BOOT_SERVICES_DATA,
130+
None,
131+
PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE,
132+
)
133+
.unwrap();
134+
let buffer = unsafe {
135+
let buffer = buffer.assume_init();
136+
buffer.base_ptr().as_mut().unwrap().fill(0);
137+
buffer
138+
};
139+
140+
let mut mapped_regions = vec![];
141+
let mut offset = 0;
142+
loop {
143+
let (mapped, mapped_size) = pci_proto
144+
.map(
145+
PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64,
146+
&buffer,
147+
offset,
148+
)
149+
.unwrap();
150+
mapped_regions.push(mapped);
151+
offset += mapped_size;
152+
if offset == size_of::<[u8; BUFFER_SIZE]>() {
153+
break;
154+
}
155+
}
156+
}
157+
}
158+
118159
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
119160
let open_opts = OpenProtocolParams {
120161
handle,

uefi/src/proto/pci/root_bridge/mod.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
66
#[cfg(feature = "alloc")]
77
use crate::proto::pci::configuration::QwordAddressSpaceDescriptor;
88
use crate::proto::pci::root_bridge::buffer::PciBuffer;
9+
use crate::proto::pci::root_bridge::region::PciMappedRegion;
910
use crate::{Status, StatusExt};
1011
#[cfg(feature = "alloc")]
1112
use alloc::vec::Vec;
12-
#[cfg(feature = "alloc")]
1313
use core::ffi::c_void;
1414
use core::mem::MaybeUninit;
1515
use core::num::NonZeroUsize;
@@ -19,10 +19,12 @@ use log::debug;
1919
use uefi_macros::unsafe_protocol;
2020
use uefi_raw::protocol::pci::root_bridge::{
2121
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
22+
PciRootBridgeIoProtocolOperation,
2223
};
2324
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
2425

2526
pub mod buffer;
27+
pub mod region;
2628

2729
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
2830
///
@@ -146,10 +148,62 @@ impl PciRootBridgeIo {
146148
}
147149
}
148150

151+
/// Map the given buffer into a PCI Controller-specific address
152+
/// so that devices can read system memory through it.
153+
/// This will not map all bytes in pages allocated in the buffer. Instead it will only
154+
/// map bytse actually occupied by type `T`.
155+
///
156+
/// # Arguments
157+
/// - `operation` - Indicates if bus master is going to read, write, or do both to the buffer.
158+
/// - `to_map` - Buffer to map.
159+
/// - 'offset' - Offset to add to the base address of buffer.
160+
///
161+
/// # Returns
162+
/// A mapped region and mapped bytes. It can map up to one DMA operation. Meaning large values
163+
/// will require multiple calls to this function.
164+
pub fn map<'p: 'r, 'r, T>(
165+
&'p self,
166+
operation: PciRootBridgeIoProtocolOperation,
167+
to_map: &'r PciBuffer<'p, T>,
168+
offset: usize,
169+
) -> crate::Result<(PciMappedRegion<'p, 'r, T>, usize)> {
170+
let host_address = unsafe {
171+
if to_map.pages.get() * PAGE_SIZE < offset {
172+
return Err(Status::INVALID_PARAMETER.into());
173+
}
174+
to_map.base_ptr().byte_offset(offset as isize)
175+
};
176+
let mut bytes = size_of::<T>() - offset;
177+
let mut device_address = 0usize;
178+
let mut mapping: *mut c_void = null_mut();
179+
180+
let status = unsafe {
181+
(self.0.map)(
182+
&self.0,
183+
operation,
184+
host_address.cast(),
185+
ptr::from_mut(&mut bytes),
186+
ptr::from_mut(&mut device_address).cast(),
187+
ptr::from_mut(&mut mapping),
188+
)
189+
};
190+
191+
status.to_result_with_val(|| {
192+
let region = PciMappedRegion {
193+
host: to_map,
194+
length: 0,
195+
device_address,
196+
key: mapping,
197+
proto: &self.0,
198+
};
199+
(region, bytes)
200+
})
201+
}
202+
149203
// TODO: poll I/O
150204
// TODO: mem I/O access
151205
// TODO: io I/O access
152-
// TODO: map & unmap & copy memory
206+
// TODO: copy memory
153207
// TODO: get/set attributes
154208

155209
/// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Defines wrapper for a region mapped by PCI Root Bridge I/O protocol.
4+
5+
use crate::StatusExt;
6+
use crate::proto::pci::root_bridge::buffer::PciBuffer;
7+
use core::ffi::c_void;
8+
use core::fmt::Debug;
9+
use core::mem::ManuallyDrop;
10+
use core::ptr;
11+
use log::{error, trace};
12+
use uefi_raw::Status;
13+
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol;
14+
15+
/// Represents a region of memory mapped and made visible to devices
16+
/// by PCI Root Bridge I/O protocol.
17+
/// The region will be unmapped automatically when it is dropped.
18+
///
19+
/// # Lifetime
20+
/// `'p` is the lifetime for Protocol.
21+
/// Protocol must be available for the entire lifetime of this struct
22+
/// as it provides its unmap function.
23+
///
24+
/// # Invariant
25+
/// Value stored in its internal buffer cannot have a larger alignment requirement than page size,
26+
/// which is 4096.
27+
///
28+
/// # Safety
29+
/// Value stored in its internal buffer cannot be larger than the buffer's size,
30+
/// which is 4096 * `pages`
31+
#[derive(Debug)]
32+
pub struct PciMappedRegion<'p: 'r, 'r, T> {
33+
pub(super) host: &'r PciBuffer<'p, T>,
34+
/// Bytes size of the mapped region.
35+
pub(super) length: usize,
36+
pub(super) device_address: usize,
37+
pub(super) key: *const c_void,
38+
pub(super) proto: &'p PciRootBridgeIoProtocol,
39+
}
40+
41+
/// Represents a region of memory that a PCI device can use.
42+
/// CPU cannot use address in this struct to deference memory.
43+
/// This is effectively the same as rust's slice type.
44+
/// This type only exists to prevent users from accidentally dereferencing it.
45+
#[derive(Debug, Copy, Clone)]
46+
pub struct DeviceRegion {
47+
/// Starting address of the memory region
48+
pub device_address: usize,
49+
50+
/// Byte length of the memory region.
51+
pub length: usize,
52+
}
53+
54+
impl<'p: 'r, 'r, T> PciMappedRegion<'p, 'r, T> {
55+
/// Returns access to the underlying buffer.
56+
#[must_use]
57+
pub const fn host(&'r self) -> &'r PciBuffer<'p, T> {
58+
self.host
59+
}
60+
61+
/// Returns mapped address and length of a region.
62+
///
63+
/// # Safety
64+
/// **Returned address cannot be used to reference memory from CPU!**
65+
/// **Do not cast it back to pointer or reference**
66+
#[must_use]
67+
pub const fn region(&self) -> DeviceRegion {
68+
DeviceRegion {
69+
device_address: self.device_address,
70+
length: self.length,
71+
}
72+
}
73+
74+
/// Unmaps underlying memory region.
75+
/// It is recommended to use this over its Drop implementation, which will only log an error
76+
/// if unmapping fails.
77+
pub fn unmap(self) -> crate::Result<PciBuffer<'p, T>> {
78+
let region = ManuallyDrop::new(self);
79+
match region.unmap_inner() {
80+
// SAFETY:
81+
// This technically creates an alias to its underlying ExclusivePtr value,
82+
// but we don't do any read/writes through it.
83+
// And the original is discarded right away.
84+
Ok(_) => unsafe { Ok(ptr::read(region.host)) },
85+
Err(e) => Err(e),
86+
}
87+
}
88+
89+
fn unmap_inner(&self) -> crate::Result {
90+
unsafe { (self.proto.unmap)(self.proto, self.key) }.to_result_with_val(|| {
91+
let host_start = self.host.base_ptr().addr();
92+
let host_end = host_start + self.length;
93+
let device_start = self.device_address;
94+
let device_end = device_start + self.length;
95+
trace!(
96+
"Region [Host 0x{:X}..0x{:X}] -> [Device 0x{:}..0x{:X}] was unmapped",
97+
host_start, host_end, device_start, device_end
98+
);
99+
})
100+
}
101+
}
102+
103+
impl<'p: 'r, 'r, T> Drop for PciMappedRegion<'p, 'r, T> {
104+
fn drop(&mut self) {
105+
let Err(status) = self.unmap_inner() else {
106+
return;
107+
};
108+
match status.status() {
109+
// Effectively unreachable path
110+
Status::SUCCESS => {}
111+
112+
Status::INVALID_PARAMETER => {
113+
error!("This region was not mapped using PciRootBridgeIo::map");
114+
}
115+
Status::DEVICE_ERROR => {
116+
error!("The data was not committed to the target system memory.");
117+
}
118+
etc => {
119+
error!(
120+
"Unexpected error occurred when unmapping device memory: {:?}",
121+
etc
122+
);
123+
}
124+
}
125+
}
126+
}
127+
128+
impl DeviceRegion {
129+
/// Changes length of a given region.
130+
/// The new region must have a shorter length to ensure
131+
/// it won't contain invalid memory address.
132+
#[must_use]
133+
pub fn with_length(mut self, new_length: usize) -> Self {
134+
assert!(new_length <= self.length);
135+
self.length = new_length;
136+
self
137+
}
138+
}

0 commit comments

Comments
 (0)