Skip to content

Commit 29e9ec2

Browse files
authored
Merge pull request #1831 from seijikun/mr-pci-device_paths
uefi: Add device path generation for discovered devices in a PciTree
2 parents 2ed6563 + f8444fd commit 29e9ec2

5 files changed

Lines changed: 157 additions & 15 deletions

File tree

uefi-test-runner/src/proto/pci/root_bridge.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

3+
use alloc::collections::btree_set::BTreeSet;
4+
use alloc::string::ToString;
35
use uefi::Handle;
46
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle};
57
use uefi::proto::ProtocolPointer;
8+
use uefi::proto::device_path::DevicePath;
9+
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
610
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
11+
use uefi::proto::scsi::pass_thru::ExtScsiPassThru;
712

813
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
914
const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1;
@@ -14,55 +19,73 @@ const REG_SIZE: u8 = size_of::<u32>() as u8;
1419
pub fn test() {
1520
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
1621

22+
let mut sata_ctrl_cnt = 0;
1723
let mut red_hat_dev_cnt = 0;
1824
let mut mass_storage_ctrl_cnt = 0;
19-
let mut sata_ctrl_cnt = 0;
25+
let mut mass_storage_dev_paths = BTreeSet::new();
2026

2127
for pci_handle in pci_handles {
2228
let mut pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
29+
let root_device_path = get_open_protocol::<DevicePath>(pci_handle);
2330

2431
let pci_tree = pci_proto.enumerate().unwrap();
2532
for addr in pci_tree.iter().cloned() {
26-
let Ok(reg0) = pci_proto.pci().read_one::<u32>(addr.with_register(0)) else {
27-
continue;
28-
};
29-
if reg0 == 0xFFFFFFFF {
30-
continue; // not a valid device
31-
}
33+
let reg0 = pci_proto
34+
.pci()
35+
.read_one::<u32>(addr.with_register(0))
36+
.unwrap();
3237
let reg1 = pci_proto
3338
.pci()
3439
.read_one::<u32>(addr.with_register(2 * REG_SIZE))
3540
.unwrap();
3641

3742
let vendor_id = (reg0 & 0xFFFF) as u16;
3843
let device_id = (reg0 >> 16) as u16;
44+
let class_code = (reg1 >> 24) as u8;
45+
let subclass_code = ((reg1 >> 16) & 0xFF) as u8;
46+
let device_path = pci_tree.device_path(&root_device_path, addr).unwrap();
47+
let device_path_str = device_path
48+
.to_string(DisplayOnly(false), AllowShortcuts(false))
49+
.unwrap()
50+
.to_string();
51+
3952
if vendor_id == RED_HAT_PCI_VENDOR_ID {
4053
red_hat_dev_cnt += 1;
4154
}
42-
43-
let class_code = (reg1 >> 24) as u8;
44-
let subclass_code = ((reg1 >> 16) & 0xFF) as u8;
4555
if class_code == MASS_STORAGE_CTRL_CLASS_CODE {
4656
mass_storage_ctrl_cnt += 1;
47-
4857
if subclass_code == SATA_CTRL_SUBCLASS_CODE {
4958
sata_ctrl_cnt += 1;
5059
}
60+
mass_storage_dev_paths.insert(device_path_str.clone());
5161
}
5262

5363
let (bus, dev, fun) = (addr.bus, addr.dev, addr.fun);
5464
log::info!(
55-
"PCI Device: [{bus:02x}, {dev:02x}, {fun:02x}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X}"
65+
"PCI Device: [{bus:02x}, {dev:02x}, {fun:02x}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X} - {}",
66+
device_path_str
5667
);
5768
for child_bus in pci_tree.child_bus_of_iter(addr) {
58-
log::info!(" |- Bus: {child_bus:02x}");
69+
log::info!(" \\- Bus: {child_bus:02x}");
5970
}
6071
}
6172
}
6273

74+
assert!(sata_ctrl_cnt > 0);
6375
assert!(red_hat_dev_cnt > 0);
6476
assert!(mass_storage_ctrl_cnt > 0);
65-
assert!(sata_ctrl_cnt > 0);
77+
assert_eq!(mass_storage_ctrl_cnt, mass_storage_dev_paths.len());
78+
79+
// Check that all `ExtScsiPassThru` instances' device paths have been found
80+
let scsi_handles = uefi::boot::find_handles::<ExtScsiPassThru>().unwrap();
81+
for scsi_handle in scsi_handles {
82+
let device_path = get_open_protocol::<DevicePath>(scsi_handle);
83+
let device_path = device_path
84+
.to_string(DisplayOnly(false), AllowShortcuts(false))
85+
.unwrap()
86+
.to_string();
87+
assert!(mass_storage_dev_paths.contains(&device_path));
88+
}
6689
}
6790

6891
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {

uefi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
- Added `proto::pci::root_bridge::PciRootBridgeIo::enumerate()`.
88
- Added `proto::nvme::pass_thru::NvmePassThru::broadcast()`.
99
- Added `proto::media::block::BlockIO2`.
10+
- Added `proto::device_path::DevicePath::to_pool()`.
11+
- Added `proto::device_path::DevicePathUtilities::duplicate_path()`.
12+
- Added `proto::pci::enumeration::PciTree::device_path()`.
1013

1114
## Changed
1215
- export all `text::{input, output}::*` types

uefi/src/proto/device_path/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,14 @@ impl DevicePath {
590590
unsafe { mem::transmute(data) }
591591
}
592592

593+
/// Returns an owned pool copy of this path.
594+
#[cfg(feature = "alloc")]
595+
pub fn to_pool(&self) -> Result<PoolDevicePath, DevicePathUtilitiesError> {
596+
open_utility_protocol()?
597+
.duplicate_path(self)
598+
.map_err(|_| DevicePathUtilitiesError::OutOfMemory)
599+
}
600+
593601
/// Transforms the device path to its string representation using the
594602
/// [`DevicePathToText`] protocol.
595603
#[cfg(feature = "alloc")]

uefi/src/proto/device_path/util.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ impl DevicePathUtilities {
2929
unsafe { (self.0.get_device_path_size)(device_path.as_ffi_ptr().cast()) }
3030
}
3131

32+
/// Create a new device path by cloning the given `path` into newly allocated memory.
33+
///
34+
/// # Arguments
35+
/// - `path`: A reference to the device path to clone.
36+
///
37+
/// # Returns
38+
/// A [`PoolDevicePath`] instance created by cloning the given `path`.
39+
pub fn duplicate_path(&self, path: &DevicePath) -> crate::Result<PoolDevicePath> {
40+
unsafe {
41+
let ptr = (self.0.duplicate_device_path)(path.as_ffi_ptr().cast());
42+
NonNull::new(ptr.cast_mut())
43+
.map(|p| PoolDevicePath(PoolAllocation::new(p.cast())))
44+
.ok_or_else(|| Status::OUT_OF_RESOURCES.into())
45+
}
46+
}
47+
3248
/// Creates a new device path by appending the second device path to the first.
3349
///
3450
/// # Arguments

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)