Skip to content

uefi: Add (partial) safe protocol implementation for PCI_ROOT_BRIDGE_IO_PROTOCOL #1674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions uefi-test-runner/src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub fn test() {
loaded_image::test();
media::test();
network::test();
pci::test();
pi::test();
rng::test();
shell_params::test();
Expand Down Expand Up @@ -83,6 +84,7 @@ mod media;
mod misc;
mod network;
mod nvme;
mod pci;
mod pi;
mod rng;
mod scsi;
Expand Down
7 changes: 7 additions & 0 deletions uefi-test-runner/src/proto/pci/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

pub mod root_bridge;

pub fn test() {
root_bridge::test();
}
85 changes: 85 additions & 0 deletions uefi-test-runner/src/proto/pci/root_bridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use core::mem;
use uefi::Handle;
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle};
use uefi::proto::ProtocolPointer;
use uefi::proto::pci::PciIoAddress;
use uefi::proto::pci::root_bridge::PciRootBridgeIo;

const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1;
const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6;

const REG_SIZE: u8 = mem::size_of::<u32>() as u8;

pub fn test() {
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();

let mut red_hat_dev_cnt = 0;
let mut mass_storage_ctrl_cnt = 0;
let mut sata_ctrl_cnt = 0;

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

for bus in 0..=255 {
for dev in 0..32 {
for fun in 0..8 {
let addr = PciIoAddress::new(bus, dev, fun);
let Ok(reg0) = pci_proto.pci().read_one::<u32>(addr.with_register(0)) else {
continue;
};
if reg0 == 0xFFFFFFFF {
continue; // not a valid device
}
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;
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;
}
}

log::info!(
"PCI Device: [{}, {}, {}]: vendor={:04X}, device={:04X}, class={:02X}, subclass={:02X}",
bus,
dev,
fun,
vendor_id,
device_id,
class_code,
subclass_code
);
}
}
}
}

assert!(red_hat_dev_cnt > 0);
assert!(mass_storage_ctrl_cnt > 0);
assert!(sata_ctrl_cnt > 0);
}

fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
let open_opts = OpenProtocolParams {
handle,
agent: image_handle(),
controller: None,
};
let open_attrs = OpenProtocolAttributes::GetProtocol;
unsafe { uefi::boot::open_protocol(open_opts, open_attrs).unwrap() }
}
1 change: 1 addition & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Added
- Added `ConfigTableEntry::MEMORY_ATTRIBUTES_GUID` and `ConfigTableEntry::IMAGE_SECURITY_DATABASE_GUID`.
- Added `proto::pci::PciRootBridgeIo`.

## Changed
- **Breaking:** `boot::stall` now take `core::time::Duration` instead of `usize`.
Expand Down
1 change: 1 addition & 0 deletions uefi/src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod misc;
pub mod network;
#[cfg(feature = "alloc")]
pub mod nvme;
pub mod pci;
pub mod pi;
pub mod rng;
#[cfg(feature = "alloc")]
Expand Down
97 changes: 97 additions & 0 deletions uefi/src/proto/pci/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

//! PCI Bus specific protocols.

use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth;

pub mod root_bridge;

/// IO Address for PCI/register IO operations
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PciIoAddress {
/// Register number within the PCI device.
pub reg: u8,
/// Function number within the PCI device.
pub fun: u8,
/// Device number within the PCI bus.
pub dev: u8,
/// Bus number in the PCI hierarchy.
pub bus: u8,
/// Extended register number within the PCI device.
pub ext_reg: u32,
}

impl PciIoAddress {
/// Create address pointing to the device identified by `bus`, `dev` and `fun` ids.
#[must_use]
pub const fn new(bus: u8, dev: u8, fun: u8) -> Self {
Self {
bus,
dev,
fun,
reg: 0,
ext_reg: 0,
}
}

/// Configure the **byte**-offset of the register to access.
#[must_use]
pub const fn with_register(&self, reg: u8) -> Self {
let mut addr = *self;
addr.reg = reg;
addr.ext_reg = 0;
addr
}

/// Configure the **byte**-offset of the extended register to access.
#[must_use]
pub const fn with_extended_register(&self, ext_reg: u32) -> Self {
let mut addr = *self;
addr.reg = 0;
addr.ext_reg = ext_reg;
addr
}
}

impl From<PciIoAddress> for u64 {
fn from(value: PciIoAddress) -> Self {
unsafe { core::mem::transmute(value) }
}
}

/// Trait implemented by all data types that can natively be read from a PCI device.
/// Note: Not all of them have to actually be supported by the hardware at hand.
pub trait PciIoUnit: Sized + Default {}
impl PciIoUnit for u8 {}
impl PciIoUnit for u16 {}
impl PciIoUnit for u32 {}
impl PciIoUnit for u64 {}

#[allow(dead_code)]
enum PciIoMode {
Normal,
Fifo,
Fill,
}

fn encode_io_mode_and_unit<U: PciIoUnit>(mode: PciIoMode) -> PciRootBridgeIoProtocolWidth {
match (mode, core::mem::size_of::<U>()) {
(PciIoMode::Normal, 1) => PciRootBridgeIoProtocolWidth::UINT8,
(PciIoMode::Normal, 2) => PciRootBridgeIoProtocolWidth::UINT16,
(PciIoMode::Normal, 4) => PciRootBridgeIoProtocolWidth::UINT32,
(PciIoMode::Normal, 8) => PciRootBridgeIoProtocolWidth::UINT64,

(PciIoMode::Fifo, 1) => PciRootBridgeIoProtocolWidth::FIFO_UINT8,
(PciIoMode::Fifo, 2) => PciRootBridgeIoProtocolWidth::FIFO_UINT16,
(PciIoMode::Fifo, 4) => PciRootBridgeIoProtocolWidth::FIFO_UINT32,
(PciIoMode::Fifo, 8) => PciRootBridgeIoProtocolWidth::FIFO_UINT64,

(PciIoMode::Fill, 1) => PciRootBridgeIoProtocolWidth::FILL_UINT8,
(PciIoMode::Fill, 2) => PciRootBridgeIoProtocolWidth::FILL_UINT16,
(PciIoMode::Fill, 4) => PciRootBridgeIoProtocolWidth::FILL_UINT32,
(PciIoMode::Fill, 8) => PciRootBridgeIoProtocolWidth::FILL_UINT64,

_ => unreachable!("Illegal PCI IO-Mode / Unit combination"),
}
}
Loading