Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 41 additions & 16 deletions uefi-test-runner/src/proto/pci/root_bridge.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use alloc::collections::btree_set::BTreeSet;
use alloc::string::ToString;
use uefi::Handle;
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle};
use uefi::proto::ProtocolPointer;
use uefi::proto::device_path::DevicePath;
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
use uefi::proto::scsi::pass_thru::ExtScsiPassThru;

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

let mut sata_ctrl_cnt = 0;
let mut red_hat_dev_cnt = 0;
let mut mass_storage_ctrl_cnt = 0;
let mut sata_ctrl_cnt = 0;
let mut mass_storage_dev_paths = BTreeSet::new();

for pci_handle in pci_handles {
let mut pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
let root_device_path = get_open_protocol::<DevicePath>(pci_handle);

let devices = pci_proto.enumerate().unwrap();
for fqaddr in devices {
let addr = fqaddr.addr();
let Ok(reg0) = pci_proto.pci().read_one::<u32>(addr.with_register(0)) else {
continue;
};
if reg0 == 0xFFFFFFFF {
continue; // not a valid device
}
let pci_tree = pci_proto.enumerate().unwrap();
for addr in pci_tree.iter().cloned() {
let reg0 = pci_proto
.pci()
.read_one::<u32>(addr.with_register(0))
.unwrap();
let reg1 = pci_proto
.pci()
.read_one::<u32>(addr.with_register(2 * REG_SIZE))
.unwrap();

let vendor_id = (reg0 & 0xFFFF) as u16;
let device_id = (reg0 >> 16) as u16;
let class_code = (reg1 >> 24) as u8;
let subclass_code = ((reg1 >> 16) & 0xFF) as u8;
let device_path = pci_tree.device_path(&root_device_path, addr).unwrap();
let device_path_str = device_path
.to_string(DisplayOnly(false), AllowShortcuts(false))
.unwrap()
.to_string();

if vendor_id == RED_HAT_PCI_VENDOR_ID {
red_hat_dev_cnt += 1;
}

let class_code = (reg1 >> 24) as u8;
let subclass_code = ((reg1 >> 16) & 0xFF) as u8;
if class_code == MASS_STORAGE_CTRL_CLASS_CODE {
mass_storage_ctrl_cnt += 1;

if subclass_code == SATA_CTRL_SUBCLASS_CODE {
sata_ctrl_cnt += 1;
}
mass_storage_dev_paths.insert(device_path_str.clone());
}

let (bus, dev, fun) = (addr.bus, addr.dev, addr.fun);
log::info!(
"PCI Device: [{bus}, {dev}, {fun}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X}"
"PCI Device: [{bus:02x}, {dev:02x}, {fun:02x}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X} - {}",
device_path_str
);
for child_bus in pci_tree.iter_subsequent_busses_for(addr) {
log::info!(" \\- Bus: {child_bus:02x}");
}
}
}

assert!(sata_ctrl_cnt > 0);
assert!(red_hat_dev_cnt > 0);
assert!(mass_storage_ctrl_cnt > 0);
assert!(sata_ctrl_cnt > 0);
assert_eq!(mass_storage_ctrl_cnt, mass_storage_dev_paths.len());

// Check that all `ExtScsiPassThru` instances' device paths have been found
let scsi_handles = uefi::boot::find_handles::<ExtScsiPassThru>().unwrap();
for scsi_handle in scsi_handles {
let device_path = get_open_protocol::<DevicePath>(scsi_handle);
let device_path = device_path
.to_string(DisplayOnly(false), AllowShortcuts(false))
.unwrap()
.to_string();
assert!(mass_storage_dev_paths.contains(&device_path));
}
}

fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
Expand Down
4 changes: 2 additions & 2 deletions uefi-test-runner/src/proto/scsi/pass_thru.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ fn test_allocating_api() {
// by default respectively. We manually configure an additional SCSI controller.
// Thus, we should see two controllers with support for EXT_SCSI_PASS_THRU on this platform
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
assert_eq!(scsi_ctrl_handles.len(), 2);
assert_eq!(scsi_ctrl_handles.len(), 4);
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
assert_eq!(scsi_ctrl_handles.len(), 1);
assert_eq!(scsi_ctrl_handles.len(), 3);

let mut found_drive = false;
for handle in scsi_ctrl_handles {
Expand Down
3 changes: 3 additions & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
- Added `proto::shell::Shell::{var(), set_var(), vars()}`
- Added `proto::pci::root_bridge::PciRootBridgeIo::configuration()`.
- Added `proto::pci::root_bridge::PciRootBridgeIo::enumerate()`.
- Added `proto::device_path::DevicePath::to_pool()`.
- Added `proto::device_path::DevicePathUtilities::duplicate_path()`.
- Added `proto::pci::enumeration::PciTree::device_path()`.

## Changed
- Changed ordering of `proto::pci::PciIoAddress` to (bus -> dev -> fun -> reg -> ext_reg).
Expand Down
8 changes: 8 additions & 0 deletions uefi/src/proto/device_path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,14 @@ impl DevicePath {
unsafe { mem::transmute(data) }
}

/// Returns an owned pool copy of this path.
#[cfg(feature = "alloc")]
pub fn to_pool(&self) -> Result<PoolDevicePath, DevicePathUtilitiesError> {
open_utility_protocol()?
.duplicate_path(self)
.map_err(|_| DevicePathUtilitiesError::OutOfMemory)
}

/// Transforms the device path to its string representation using the
/// [`DevicePathToText`] protocol.
#[cfg(feature = "alloc")]
Expand Down
16 changes: 16 additions & 0 deletions uefi/src/proto/device_path/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ impl DevicePathUtilities {
unsafe { (self.0.get_device_path_size)(device_path.as_ffi_ptr().cast()) }
}

/// Create a new device path by cloning the given `path` into newly allocated memory.
///
/// # Arguments
/// - `path`: A reference to the device path to clone.
///
/// # Returns
/// A [`PoolDevicePath`] instance created by cloning the given `path`.
pub fn duplicate_path(&self, path: &DevicePath) -> crate::Result<PoolDevicePath> {
unsafe {
let ptr = (self.0.duplicate_device_path)(path.as_ffi_ptr().cast());
NonNull::new(ptr.cast_mut())
.map(|p| PoolDevicePath(PoolAllocation::new(p.cast())))
.ok_or_else(|| Status::OUT_OF_RESOURCES.into())
}
}

/// Creates a new device path by appending the second device path to the first.
///
/// # Arguments
Expand Down
Loading
Loading