Skip to content

Commit 81da7a4

Browse files
committed
uefi: Implement PciRootBridgeIo::map
1 parent c4a1231 commit 81da7a4

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
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
1324
///
@@ -43,11 +54,111 @@ impl PciRootBridgeIo {
4354
unsafe { (self.0.flush)(&mut self.0).to_result() }
4455
}
4556

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

0 commit comments

Comments
 (0)