Skip to content

uefi-raw: Add binding for EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL #1658

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
4 changes: 4 additions & 0 deletions uefi-raw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# uefi-raw - [Unreleased]

## Added
- Added `AllocateType`.
- Added `PciRootBridgeIoProtocol`.


# uefi-raw - 0.11.0 (2025-05-04)

Expand Down
1 change: 1 addition & 0 deletions uefi-raw/src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod memory_protection;
pub mod misc;
pub mod network;
pub mod nvme;
pub mod pci;
pub mod rng;
pub mod scsi;
pub mod shell_params;
Expand Down
3 changes: 3 additions & 0 deletions uefi-raw/src/protocol/pci/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

pub mod root_bridge;
171 changes: 171 additions & 0 deletions uefi-raw/src/protocol/pci/root_bridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::table::boot::{AllocateType, MemoryType};
use crate::{Handle, PhysicalAddress, Status};
use core::ffi::c_void;
use uguid::{guid, Guid};

newtype_enum! {
/// Corresponds to the `EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH` enum.
pub enum PciRootBridgeIoProtocolWidth: u32 => {
UINT8 = 0,
UINT16 = 1,
UINT32 = 2,
UINT64 = 3,
FIFO_UINT8 = 4,
FIFO_UINT16 = 5,
FIFO_UINT32 = 6,
FIFO_UINT64 = 7,
FILL_UINT8 = 8,
FILL_UINT16 = 9,
FILL_UINT32 = 10,
FILL_UINT64 = 11,
MAXIMUM = 12,
}
}

newtype_enum! {
/// Corresponds to the `EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION` enum.
pub enum PciRootBridgeIoProtocolOperation: u32 => {
BUS_MASTER_READ = 0,
BUS_MASTER_WRITE = 1,
BUS_MASTER_COMMON_BUFFER = 2,
BUS_MASTER_READ64 = 3,
BUS_MASTER_WRITE64 = 4,
BUS_MASTER_COMMON_BUFFER64 = 5,
MAXIMUM = 6,
}
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PciIoAddress {
pub reg: u8,
pub fun: u8,
pub dev: u8,
pub bus: u8,
pub ext_reg: u32,
}

impl PciIoAddress {
#[must_use]
pub const fn new(bus: u8, dev: u8, fun: u8) -> Self {
Self {
bus,
dev,
fun,
reg: 0,
ext_reg: 0,
}
}

#[must_use]
pub const fn with_register(&self, reg: u8) -> Self {
let mut addr = *self;
addr.reg = reg;
addr.ext_reg = 0;
addr
}

#[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
}
}

#[derive(Debug)]
#[repr(C)]
pub struct PciRootBridgeIoAccess<TAddr> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uefi-raw crate doesn't normally use generics. I think in the spec the address is treated as u64 in all cases, even though for for pci specifically the u64 is broken down into subfields, so let's do that and leave higher-level abstractions to the uefi crate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the interface itself in the spec accepts a u64, because they use the same struct for pci, memory and io.
But the pci one is supposed to be filled by something like this:

#define EFI_PCI_ADDRESS(bus, dev, func, reg) \
  (UINT64) ( \
  (((UINTN) bus) << 24) | \
  (((UINTN) dev) << 16) | \
  (((UINTN) func) << 8) | \
  (((UINTN) (reg)) < 256 ? ((UINTN) (reg)) : (UINT64) (LShiftU64 ((UINT64) (reg), 32))))

My thought process was: If they could have used templates/generics in their interface, they would have done it there.

At least I would like to have the PCI addressing somehow as part of the unsafe API, because otherwise you can't use it without reading the UEFI spec. Do you have an alternative suggestion? The only alternative I was able to come up with, was a union and that's IMHO ugly to use.

pub read: unsafe extern "efiapi" fn(
this: *mut PciRootBridgeIoProtocol,
width: PciRootBridgeIoProtocolWidth,
address: TAddr,
count: usize,
buffer: *mut c_void,
) -> Status,
pub write: unsafe extern "efiapi" fn(
this: *mut Self,
width: PciRootBridgeIoProtocolWidth,
address: TAddr,
count: usize,
buffer: *const c_void,
) -> Status,
}

#[derive(Debug)]
#[repr(C)]
pub struct PciRootBridgeIoProtocol {
pub parent_handle: Handle,
pub poll_mem: unsafe extern "efiapi" fn(
this: *mut Self,
width: PciRootBridgeIoProtocolWidth,
address: u64,
mask: u64,
value: u64,
delay: u64,
result: *mut u64,
) -> Status,
pub poll_io: unsafe extern "efiapi" fn(
this: *mut Self,
width: PciRootBridgeIoProtocolWidth,
address: u64,
mask: u64,
value: u64,
delay: u64,
result: *mut u64,
) -> Status,
pub mem: PciRootBridgeIoAccess<u64>,
pub io: PciRootBridgeIoAccess<u64>,
pub pci: PciRootBridgeIoAccess<PciIoAddress>,
pub copy_mem: unsafe extern "efiapi" fn(
this: *mut Self,
width: PciRootBridgeIoProtocolWidth,
dest_addr: u64,
src_addr: u64,
count: usize,
) -> Status,
pub map: unsafe extern "efiapi" fn(
this: *const Self,
operation: PciRootBridgeIoProtocolOperation,
host_addr: *const c_void,
num_bytes: *mut usize,
device_addr: *mut PhysicalAddress,
mapping: *mut *mut c_void,
) -> Status,
pub unmap: unsafe extern "efiapi" fn(this: *const Self, mapping: *const c_void) -> Status,
pub allocate_buffer: unsafe extern "efiapi" fn(
this: *const Self,
alloc_ty: AllocateType,
memory_ty: MemoryType,
pages: usize,
host_addr: *mut *const c_void,
attributes: u64,
) -> Status,
pub free_buffer: unsafe extern "efiapi" fn(
this: *const Self,
pages: usize,
host_addr: *const c_void,
) -> Status,
pub flush: unsafe extern "efiapi" fn(this: *mut Self) -> Status,
pub get_attributes: unsafe extern "efiapi" fn(
this: *const Self,
supports: *mut u64,
attributes: *mut u64,
) -> Status,
pub set_attributes: unsafe extern "efiapi" fn(
this: *mut Self,
attributes: u64,
resource_base: *mut u64,
resource_length: *mut u64,
) -> Status,
pub configuration:
unsafe extern "efiapi" fn(this: *const Self, resources: *mut *const c_void) -> Status,
pub segment_number: u32,
}

impl PciRootBridgeIoProtocol {
pub const GUID: Guid = guid!("2f707ebb-4a1a-11d4-9a38-0090273fc14d");
}
11 changes: 10 additions & 1 deletion uefi-raw/src/table/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ use bitflags::bitflags;
use core::ffi::c_void;
use core::ops::RangeInclusive;

newtype_enum! {
pub enum AllocateType: u32 => {
ANY_PAGES = 0,
MAX_ADDRESS = 1,
ADDRESS = 2,
MAX_ALLOCATE_TYPE = 3,
}
}

/// Table of pointers to all the boot services.
#[derive(Debug)]
#[repr(C)]
Expand All @@ -21,7 +30,7 @@ pub struct BootServices {

// Memory allocation functions
pub allocate_pages: unsafe extern "efiapi" fn(
alloc_ty: u32,
alloc_ty: AllocateType,
mem_ty: MemoryType,
count: usize,
addr: *mut PhysicalAddress,
Expand Down
8 changes: 4 additions & 4 deletions uefi/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use core::ptr::{self, NonNull};
use core::sync::atomic::{AtomicPtr, Ordering};
use core::time::Duration;
use core::{mem, slice};
use uefi_raw::table::boot::{InterfaceType, TimerDelay};
use uefi_raw::table::boot::{AllocateType as RawAllocateType, InterfaceType, TimerDelay};
#[cfg(feature = "alloc")]
use {alloc::vec::Vec, uefi::ResultExt};

Expand Down Expand Up @@ -136,9 +136,9 @@ pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Res
let bt = unsafe { bt.as_ref() };

let (ty, initial_addr) = match ty {
AllocateType::AnyPages => (0, 0),
AllocateType::MaxAddress(addr) => (1, addr),
AllocateType::Address(addr) => (2, addr),
AllocateType::AnyPages => (RawAllocateType::ANY_PAGES, 0),
AllocateType::MaxAddress(addr) => (RawAllocateType::MAX_ADDRESS, addr),
AllocateType::Address(addr) => (RawAllocateType::ADDRESS, addr),
};

let mut addr1 = initial_addr;
Expand Down