Skip to content

Commit f0d295c

Browse files
committed
uefi: Add pci device path generation to PciTree
1 parent 625d3ec commit f0d295c

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
/// This allows iterating over all valid PCI device addresses in a tree, as well as query
6389
/// the tree topology.
@@ -120,6 +146,54 @@ impl PciTree {
120146
.map(|(bus, _)| bus)
121147
.cloned()
122148
}
149+
150+
/// Construct a device path for the given PCI `addr` and append it to the given `root_path`.
151+
///
152+
/// # Arguments
153+
/// - `root_path`: The [`DevicePath`] instance corresponding to the [`PciRootBridgeIo`] instance that
154+
/// produced this [`PciTree`]. This path is prepended to the generated device paths.
155+
/// - `addr`: [`PciIoAddress`] of the device
156+
pub fn device_path(
157+
&self,
158+
root_path: &DevicePath,
159+
addr: PciIoAddress,
160+
) -> Result<PoolDevicePath, PciDevicePathBuildError> {
161+
use device_path::build;
162+
163+
if !self.devices.contains(&addr) {
164+
return Err(PciDevicePathBuildError::InvalidAddress);
165+
}
166+
167+
// A PCI [`DevicePath`] can have max. 255 PCI segments, each of which is 6 bytes in size.
168+
// These are prepended by the given `root_path`. A construction buffer of 2048 bytes
169+
// should thus suffice for all realistic scenarios.
170+
let mut bfr = [MaybeUninit::uninit(); 2048];
171+
let mut builder = DevicePathBuilder::with_buf(&mut bfr);
172+
for node in root_path.node_iter() {
173+
builder = builder.push(&node)?;
174+
}
175+
176+
// A pci device path is built by appending segments of `dev` and `fun` address byte pairs
177+
// starting from a pci root bus to the specified address. Since the child <-> parent
178+
// relationship is stored from child to parent, we start at the address and recurse back
179+
// to the parent for path generation.
180+
fn inner<'a>(
181+
root: &PciTree,
182+
mut builder: DevicePathBuilder<'a>,
183+
addr: PciIoAddress,
184+
) -> Result<DevicePathBuilder<'a>, BuildError> {
185+
if let Some(parent) = root.parent_for(addr) {
186+
builder = inner(root, builder, parent)?;
187+
}
188+
builder.push(&build::hardware::Pci {
189+
function: addr.fun,
190+
device: addr.dev,
191+
})
192+
}
193+
194+
builder = inner(self, builder, addr)?;
195+
Ok(builder.finalize()?.to_pool()?)
196+
}
123197
}
124198
impl IntoIterator for PciTree {
125199
type Item = PciIoAddress;

0 commit comments

Comments
 (0)