|
2 | 2 |
|
3 | 3 | //! PCI Bus device function and bridge enumeration. |
4 | 4 |
|
5 | | -use core::mem; |
| 5 | +use core::fmt::{Display, Formatter}; |
| 6 | +use core::mem::{self, MaybeUninit}; |
6 | 7 |
|
7 | 8 | use alloc::collections::btree_map::BTreeMap; |
8 | 9 | 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}; |
9 | 14 |
|
10 | 15 | use super::PciIoAddress; |
11 | 16 | use super::root_bridge::PciRootBridgeIo; |
@@ -58,6 +63,45 @@ fn read_device_register_u32<T: Sized + Copy>( |
58 | 63 |
|
59 | 64 | // ########################################################################################## |
60 | 65 |
|
| 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 | + |
61 | 105 | /// Struct representing the tree structure of PCI devices. |
62 | 106 | /// |
63 | 107 | /// This allows iterating over all valid PCI device addresses in a tree, as well as querying |
@@ -125,6 +169,54 @@ impl PciTree { |
125 | 169 | .map(|(bus, _)| bus) |
126 | 170 | .cloned() |
127 | 171 | } |
| 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 | + } |
128 | 220 | } |
129 | 221 | impl IntoIterator for PciTree { |
130 | 222 | type Item = PciIoAddress; |
|
0 commit comments