Skip to content

Commit b07649a

Browse files
committed
uefi: Add pci device path generation to PciTree
1 parent 26a49f2 commit b07649a

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Added `proto::media::block::BlockIO2`.
1010
- Added `proto::device_path::DevicePath::to_pool()`.
1111
- Added `proto::device_path::DevicePathUtilities::duplicate_path()`.
12+
- Added `proto::pci::enumeration::PciTree::device_path()`.
1213

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

uefi/src/proto/pci/enumeration.rs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

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

78
use alloc::collections::btree_map::BTreeMap;
89
use alloc::collections::btree_set::{self, BTreeSet};
10+
use alloc::fmt;
11+
12+
use crate::proto::device_path::build::{BuildError, DevicePathBuilder};
13+
use crate::proto::device_path::{self, DevicePath, DevicePathUtilitiesError, PoolDevicePath};
914

1015
use super::PciIoAddress;
1116
use super::root_bridge::PciRootBridgeIo;
@@ -58,6 +63,45 @@ fn read_device_register_u32<T: Sized + Copy>(
5863

5964
// ##########################################################################################
6065

66+
/// Error type used by the device path construction of [`PciTree`].
67+
#[derive(Debug)]
68+
pub enum PciDevicePathBuildError {
69+
/// The given [`PciIoAddress`] was invalid or not path of the enumeration.
70+
InvalidAddress,
71+
/// Error while constructing the pci device DevicePath.
72+
PathBuildError(BuildError),
73+
/// Error while
74+
DevicePathUtilitiesError(DevicePathUtilitiesError),
75+
}
76+
impl From<BuildError> for PciDevicePathBuildError {
77+
fn from(value: BuildError) -> Self {
78+
Self::PathBuildError(value)
79+
}
80+
}
81+
impl From<DevicePathUtilitiesError> for PciDevicePathBuildError {
82+
fn from(value: DevicePathUtilitiesError) -> Self {
83+
Self::DevicePathUtilitiesError(value)
84+
}
85+
}
86+
87+
impl Display for PciDevicePathBuildError {
88+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
89+
write!(f, "{self:?}")
90+
}
91+
}
92+
93+
impl core::error::Error for PciDevicePathBuildError {
94+
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
95+
match self {
96+
Self::PathBuildError(e) => Some(e),
97+
Self::DevicePathUtilitiesError(e) => Some(e),
98+
_ => None,
99+
}
100+
}
101+
}
102+
103+
// ------------------------------------------------------------------------------------------
104+
61105
/// Struct representing the tree structure of PCI devices.
62106
///
63107
/// This allows iterating over all valid PCI device addresses in a tree, as well as querying
@@ -125,6 +169,54 @@ impl PciTree {
125169
.map(|(bus, _)| bus)
126170
.cloned()
127171
}
172+
173+
/// Construct a device path for the given PCI `addr` and append it to the given `root_path`.
174+
///
175+
/// # Arguments
176+
/// - `root_path`: The [`DevicePath`] instance corresponding to the [`PciRootBridgeIo`] instance that
177+
/// produced this [`PciTree`]. This path is prepended to the generated device paths.
178+
/// - `addr`: [`PciIoAddress`] of the device
179+
pub fn device_path(
180+
&self,
181+
root_path: &DevicePath,
182+
addr: PciIoAddress,
183+
) -> Result<PoolDevicePath, PciDevicePathBuildError> {
184+
use device_path::build;
185+
186+
if !self.devices.contains(&addr) {
187+
return Err(PciDevicePathBuildError::InvalidAddress);
188+
}
189+
190+
// A PCI [`DevicePath`] can have max. 255 PCI segments, each of which is 6 bytes in size.
191+
// These are prepended by the given `root_path`. A construction buffer of 2048 bytes
192+
// should thus suffice for all realistic scenarios.
193+
let mut bfr = [MaybeUninit::uninit(); 2048];
194+
let mut builder = DevicePathBuilder::with_buf(&mut bfr);
195+
for node in root_path.node_iter() {
196+
builder = builder.push(&node)?;
197+
}
198+
199+
// A pci device path is built by appending segments of `dev` and `fun` address byte pairs
200+
// starting from a pci root bus to the specified address. Since the child <-> parent
201+
// relationship is stored from child to parent, we start at the address and recurse back
202+
// to the parent for path generation.
203+
fn inner<'a>(
204+
root: &PciTree,
205+
mut builder: DevicePathBuilder<'a>,
206+
addr: PciIoAddress,
207+
) -> Result<DevicePathBuilder<'a>, BuildError> {
208+
if let Some(parent) = root.parent_for(addr) {
209+
builder = inner(root, builder, parent)?;
210+
}
211+
builder.push(&build::hardware::Pci {
212+
function: addr.fun,
213+
device: addr.dev,
214+
})
215+
}
216+
217+
builder = inner(self, builder, addr)?;
218+
Ok(builder.finalize()?.to_pool()?)
219+
}
128220
}
129221
impl IntoIterator for PciTree {
130222
type Item = PciIoAddress;

0 commit comments

Comments
 (0)