|
2 | 2 |
|
3 | 3 | //! PCI Bus device function and bridge enumeration. |
4 | 4 |
|
5 | | -use core::mem; |
| 5 | +use core::mem::{self, MaybeUninit}; |
6 | 6 |
|
7 | 7 | use alloc::collections::btree_map::BTreeMap; |
8 | 8 | use alloc::collections::btree_set::{self, BTreeSet}; |
9 | 9 |
|
| 10 | +use crate::proto::device_path::build::{BuildError, DevicePathBuilder}; |
| 11 | +use crate::proto::device_path::{self, DevicePath, DevicePathUtilitiesError, PoolDevicePath}; |
| 12 | + |
10 | 13 | use super::PciIoAddress; |
11 | 14 | use super::root_bridge::PciRootBridgeIo; |
12 | 15 |
|
@@ -58,6 +61,29 @@ fn read_device_register_u32<T: Sized + Copy>( |
58 | 61 |
|
59 | 62 | // ########################################################################################## |
60 | 63 |
|
| 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 | + |
61 | 87 | /// Struct representing the tree structure of PCI devices. |
62 | 88 | /// This allows iterating over all valid PCI device addresses in a tree, as well as query |
63 | 89 | /// the tree topology. |
@@ -120,6 +146,54 @@ impl PciTree { |
120 | 146 | .map(|(bus, _)| bus) |
121 | 147 | .cloned() |
122 | 148 | } |
| 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 | + } |
123 | 197 | } |
124 | 198 | impl IntoIterator for PciTree { |
125 | 199 | type Item = PciIoAddress; |
|
0 commit comments