Skip to content

Commit f5b6b95

Browse files
committed
uefi: Implement PciRootBridgeIo::map
1 parent 29683ff commit f5b6b95

File tree

5 files changed

+228
-6
lines changed

5 files changed

+228
-6
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: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, ima
66
use uefi::proto::ProtocolPointer;
77
use uefi::proto::pci::PciIoAddress;
88
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
9-
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttribute;
9+
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation};
1010
use uefi_raw::table::boot::MemoryType;
1111

1212
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
@@ -90,6 +90,32 @@ pub fn test_buffer() {
9090
}
9191
}
9292

93+
pub fn test_mapping() {
94+
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
95+
96+
for pci_handle in pci_handles {
97+
let pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
98+
99+
let mut buffer = pci_proto
100+
.allocate_buffer::<[u8; 4096]>(
101+
MemoryType::BOOT_SERVICES_DATA,
102+
None,
103+
PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE,
104+
)
105+
.unwrap();
106+
let buffer = unsafe {
107+
buffer.assume_init_mut().fill(0);
108+
buffer.assume_init()
109+
};
110+
let mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref());
111+
if mapped.region().0 == buffer.as_ptr().addr() as u64 {
112+
info!("This PCI device uses identity mapping");
113+
} else {
114+
info!("This PCI device uses different mapping from CPU");
115+
}
116+
}
117+
}
118+
93119
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
94120
let open_opts = OpenProtocolParams {
95121
handle,

uefi/src/proto/pci/mapped_region.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Defines wrapper for region mapped by PCI Root Bridge I/O protocol.
4+
5+
use core::ffi::c_void;
6+
use core::ptr;
7+
use log::debug;
8+
use uefi_raw::Status;
9+
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol;
10+
11+
/// Represents a region of memory mapped by PCI Root Bridge I/O protocol.
12+
/// The region will be unmapped automatically when it is dropped.
13+
///
14+
/// # Lifetime
15+
/// `'p` is the lifetime for Protocol.
16+
/// `'r` is the lifetime for Mapped Region.
17+
/// Protocol must outlive the mapped region
18+
/// as unmap function can only be accessed through the protocol.
19+
#[derive(Debug)]
20+
pub struct PciMappedRegion<'p, 'r>
21+
where
22+
'p: 'r,
23+
{
24+
device_address: u64,
25+
length: usize,
26+
_lifetime_holder: &'r (),
27+
key: *const c_void,
28+
proto: &'p PciRootBridgeIoProtocol,
29+
}
30+
31+
impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r {
32+
pub(crate) fn new<T>(
33+
device_address: u64,
34+
length: usize,
35+
key: *const c_void,
36+
to_map: &'r T,
37+
proto: &'p PciRootBridgeIoProtocol,
38+
) -> Self {
39+
let _lifetime_holder: &'r () = unsafe {
40+
let ptr = ptr::from_ref(to_map);
41+
ptr.cast::<()>().as_ref().unwrap()
42+
};
43+
44+
let end = device_address + length as u64;
45+
debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end);
46+
Self {
47+
device_address,
48+
length,
49+
_lifetime_holder,
50+
key,
51+
proto,
52+
}
53+
}
54+
55+
/// Returns mapped address and length of mapped region.
56+
///
57+
/// # Warning
58+
/// **Returned address cannot be used to reference memory from CPU!**
59+
/// **Do not cast it back to pointer or reference**
60+
#[must_use]
61+
pub const fn region(&self) -> (u64, usize) {
62+
(self.device_address, self.length)
63+
}
64+
}
65+
66+
impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> {
67+
fn drop(&mut self) {
68+
let status = unsafe { (self.proto.unmap)(self.proto, self.key) };
69+
match status {
70+
Status::SUCCESS => {
71+
let end = self.device_address + self.length as u64;
72+
debug!("Region [0x{:X}..0x{:X}] was unmapped", self.device_address, end);
73+
}
74+
Status::INVALID_PARAMETER => {
75+
panic!("This region was not mapped using PciRootBridgeIo::map");
76+
}
77+
Status::DEVICE_ERROR => {
78+
panic!("The data was not committed to the target system memory.");
79+
}
80+
_ => unreachable!(),
81+
}
82+
}
83+
}

uefi/src/proto/pci/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use core::cmp::Ordering;
77
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth;
88

99
pub mod buffer;
10+
pub mod mapped_region;
1011
pub mod root_bridge;
1112

1213
/// IO Address for PCI/register IO operations

uefi/src/proto/pci/root_bridge.rs

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
22

33
//! PCI Root Bridge protocol.
44
5-
use core::ptr;
6-
75
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
86
use crate::StatusExt;
7+
use crate::proto::pci::buffer::PciBuffer;
8+
use crate::proto::pci::mapped_region::PciMappedRegion;
9+
use core::ffi::c_void;
10+
use core::mem::MaybeUninit;
11+
use core::num::NonZeroUsize;
12+
use core::ptr;
13+
use core::ptr::NonNull;
14+
use log::debug;
915
use uefi_macros::unsafe_protocol;
10-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
16+
use uefi_raw::Status;
17+
use uefi_raw::protocol::pci::root_bridge::{
18+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
19+
PciRootBridgeIoProtocolOperation,
20+
};
21+
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
1122

1223
#[cfg(doc)]
1324
use crate::Status;
@@ -46,11 +57,111 @@ impl PciRootBridgeIo {
4657
unsafe { (self.0.flush)(&mut self.0).to_result() }
4758
}
4859

60+
/// Allocates pages suitable for communicating with PCI devices.
61+
///
62+
/// # Errors
63+
/// - [`crate::Status::INVALID_PARAMETER`] MemoryType is invalid.
64+
/// - [`crate::Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
65+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
66+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
67+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
68+
/// - [`crate::Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
69+
pub fn allocate_buffer<T>(
70+
&self,
71+
memory_type: MemoryType,
72+
pages: Option<NonZeroUsize>,
73+
attributes: PciRootBridgeIoProtocolAttribute,
74+
) -> crate::Result<PciBuffer<MaybeUninit<T>>> {
75+
let mut address = 0usize;
76+
let original_alignment = align_of::<T>();
77+
assert_ne!(original_alignment, 0);
78+
assert!(PAGE_SIZE >= original_alignment);
79+
assert_eq!(PAGE_SIZE % original_alignment, 0);
80+
81+
let alignment = PAGE_SIZE;
82+
83+
let pages = if let Some(pages) = pages {
84+
pages
85+
} else {
86+
let size = size_of::<T>();
87+
assert_ne!(size, 0);
88+
89+
NonZeroUsize::new(size.div_ceil(alignment)).unwrap()
90+
};
91+
92+
let status = unsafe {
93+
(self.0.allocate_buffer)(
94+
&self.0,
95+
AllocateType(0),
96+
memory_type,
97+
pages.get(),
98+
ptr::from_mut(&mut address).cast(),
99+
attributes.bits(),
100+
)
101+
};
102+
103+
match status {
104+
Status::SUCCESS => {
105+
let base = NonNull::new(address as *mut MaybeUninit<T>).unwrap();
106+
debug!("Allocated {} pages at 0x{:X}", pages.get(), address);
107+
Ok(PciBuffer {
108+
base,
109+
pages,
110+
proto: &self.0,
111+
})
112+
}
113+
error
114+
@ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => {
115+
Err(error.into())
116+
}
117+
_ => unreachable!(),
118+
}
119+
}
120+
121+
/// Map given variable's address into PCI Controller-specific address
122+
/// required to access it from a DMA bus master.
123+
/// # Arguments
124+
/// - `operation` - Indicates if bus master is going to read, write or do both to given variable.
125+
/// - `to_map` - Variable to map.
126+
///
127+
/// # Returns
128+
/// - PciMappedRegion capturing lifetime of passed variable
129+
pub fn map<'p, 'r, T>(
130+
&'p self,
131+
operation: PciRootBridgeIoProtocolOperation,
132+
to_map: &'r T,
133+
) -> PciMappedRegion<'p, 'r>
134+
where
135+
'p: 'r,
136+
{
137+
let host_address = ptr::from_ref(to_map);
138+
let mut bytes = size_of_val(to_map);
139+
let mut mapped_address = 0u64;
140+
let mut mapping: *mut c_void = ptr::null_mut();
141+
142+
let status = unsafe {
143+
(self.0.map)(
144+
&self.0,
145+
operation,
146+
host_address.cast(),
147+
ptr::from_mut(&mut bytes),
148+
ptr::from_mut(&mut mapped_address).cast(),
149+
ptr::from_mut(&mut mapping),
150+
)
151+
};
152+
153+
match status {
154+
Status::SUCCESS => {
155+
PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0)
156+
}
157+
_ => unreachable!(),
158+
}
159+
}
160+
49161
// TODO: poll I/O
50162
// TODO: mem I/O access
51163
// TODO: io I/O access
52-
// TODO: map & unmap & copy memory
53-
// TODO: buffer management
164+
// TODO: copy memory
54165
// TODO: get/set attributes
55166
// TODO: configuration / resource settings
56167
}

0 commit comments

Comments
 (0)