Skip to content

Commit cece9b1

Browse files
committed
uefi: Add pci device path generation to PciTree
1 parent b2e4de3 commit cece9b1

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Added `proto::pci::root_bridge::PciRootBridgeIo::enumerate()`.
88
- Added `proto::device_path::DevicePath::to_pool()`.
99
- Added `proto::device_path::DevicePathUtilities::duplicate_path()`.
10+
- Added `proto::pci::enumeration::PciTree::device_path()`.
1011

1112
## Changed
1213
- Changed ordering of `proto::pci::PciIoAddress` to (bus -> dev -> fun -> reg -> ext_reg).

uefi/src/proto/pci/enumeration.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
//! PCI Bus device function and bridge enumeration.
44
5-
use core::mem;
5+
use core::mem::{self, MaybeUninit};
66

77
use alloc::collections::btree_map::BTreeMap;
88
use alloc::collections::btree_set::{self, BTreeSet};
99

10+
use crate::proto::device_path::build::{BuildError, DevicePathBuilder};
11+
use crate::proto::device_path::{self, DevicePath, DevicePathUtilitiesError, PoolDevicePath};
12+
1013
use super::PciIoAddress;
1114
use super::root_bridge::PciRootBridgeIo;
1215

@@ -58,6 +61,29 @@ fn read_device_register_u32<T: Sized + Copy>(
5861

5962
// ##########################################################################################
6063

64+
/// Error type used by the device path construction of [`PciTree`].
65+
#[derive(Debug)]
66+
pub enum PciDevicePathBuildError {
67+
/// The given [`PciIoAddress`] was invalid or not path of the enumeration.
68+
InvalidAddress,
69+
/// Error while constructing the pci device DevicePath.
70+
PathBuildError(BuildError),
71+
/// Error while
72+
DevicePathUtilitiesError(DevicePathUtilitiesError),
73+
}
74+
impl From<BuildError> for PciDevicePathBuildError {
75+
fn from(value: BuildError) -> Self {
76+
Self::PathBuildError(value)
77+
}
78+
}
79+
impl From<DevicePathUtilitiesError> for PciDevicePathBuildError {
80+
fn from(value: DevicePathUtilitiesError) -> Self {
81+
Self::DevicePathUtilitiesError(value)
82+
}
83+
}
84+
85+
// ------------------------------------------------------------------------------------------
86+
6187
/// Struct representing the tree structure of PCI devices.
6288
///
6389
/// This allows iterating over all valid PCI device addresses in a tree, as well as querying
@@ -125,6 +151,54 @@ impl PciTree {
125151
.map(|(bus, _)| bus)
126152
.cloned()
127153
}
154+
155+
/// Construct a device path for the given PCI `addr` and append it to the given `root_path`.
156+
///
157+
/// # Arguments
158+
/// - `root_path`: The [`DevicePath`] instance corresponding to the [`PciRootBridgeIo`] instance that
159+
/// produced this [`PciTree`]. This path is prepended to the generated device paths.
160+
/// - `addr`: [`PciIoAddress`] of the device
161+
pub fn device_path(
162+
&self,
163+
root_path: &DevicePath,
164+
addr: PciIoAddress,
165+
) -> Result<PoolDevicePath, PciDevicePathBuildError> {
166+
use device_path::build;
167+
168+
if !self.devices.contains(&addr) {
169+
return Err(PciDevicePathBuildError::InvalidAddress);
170+
}
171+
172+
// A PCI [`DevicePath`] can have max. 255 PCI segments, each of which is 6 bytes in size.
173+
// These are prepended by the given `root_path`. A construction buffer of 2048 bytes
174+
// should thus suffice for all realistic scenarios.
175+
let mut bfr = [MaybeUninit::uninit(); 2048];
176+
let mut builder = DevicePathBuilder::with_buf(&mut bfr);
177+
for node in root_path.node_iter() {
178+
builder = builder.push(&node)?;
179+
}
180+
181+
// A pci device path is built by appending segments of `dev` and `fun` address byte pairs
182+
// starting from a pci root bus to the specified address. Since the child <-> parent
183+
// relationship is stored from child to parent, we start at the address and recurse back
184+
// to the parent for path generation.
185+
fn inner<'a>(
186+
root: &PciTree,
187+
mut builder: DevicePathBuilder<'a>,
188+
addr: PciIoAddress,
189+
) -> Result<DevicePathBuilder<'a>, BuildError> {
190+
if let Some(parent) = root.parent_for(addr) {
191+
builder = inner(root, builder, parent)?;
192+
}
193+
builder.push(&build::hardware::Pci {
194+
function: addr.fun,
195+
device: addr.dev,
196+
})
197+
}
198+
199+
builder = inner(self, builder, addr)?;
200+
Ok(builder.finalize()?.to_pool()?)
201+
}
128202
}
129203
impl IntoIterator for PciTree {
130204
type Item = PciIoAddress;

0 commit comments

Comments
 (0)