Skip to content

Commit 36ad85c

Browse files
committed
uefi: Add PciRootBridgeIo::configuration() to query acpi table info
1 parent fe85d8a commit 36ad85c

File tree

4 files changed

+171
-3
lines changed

4 files changed

+171
-3
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Added
44
- Added `proto::ata::AtaRequestBuilder::read_pio()`.
5+
- Added `proto::pci::root_bridge::PciRootBridgeIo::configuration()`.
56

67
## Changed
78
- Changed ordering of `proto::pci::PciIoAddress` to (bus -> dev -> fun -> reg -> ext_reg).
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Pci root bus resource configuration descriptor parsing.
4+
5+
use core::ffi::c_void;
6+
use core::ptr;
7+
8+
use alloc::{slice, vec::Vec};
9+
10+
/// Represents the type of resource described by a QWORD Address Space Descriptor.
11+
/// This corresponds to the `resource_type` field at offset 0x03 in the descriptor.
12+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13+
#[repr(u8)]
14+
pub enum ResourceRangeType {
15+
/// Memory Range (value = 0)
16+
/// Indicates that the descriptor describes a memory-mapped address range.
17+
/// Commonly used for MMIO regions decoded by the PCI root bridge.
18+
Memory = 0,
19+
20+
/// I/O Range (value = 1)
21+
/// Indicates that the descriptor describes a legacy I/O port range.
22+
/// Used for devices that communicate via port-mapped I/O.
23+
Io = 1,
24+
25+
/// Bus Number Range (value = 2)
26+
/// Indicates that the descriptor describes a range of PCI bus numbers.
27+
/// Used to define the bus hierarchy behind a PCI root bridge.
28+
Bus = 2,
29+
30+
/// Unknown or vendor-specific resource type.
31+
/// Captures any unrecognized value for forward compatibility.
32+
Unknown(u8),
33+
}
34+
impl From<u8> for ResourceRangeType {
35+
fn from(value: u8) -> Self {
36+
match value {
37+
0 => Self::Memory,
38+
1 => Self::Io,
39+
2 => Self::Bus,
40+
other => Self::Unknown(other),
41+
}
42+
}
43+
}
44+
45+
/// Represents a parsed QWORD Address Space Descriptor from UEFI.
46+
/// This structure describes a decoded resource range for a PCI root bridge.
47+
#[derive(Clone, Debug)]
48+
pub struct QwordAddressSpaceDescriptor {
49+
/// Type of resource: Memory, I/O, Bus, or Unknown.
50+
pub resource_range_type: ResourceRangeType,
51+
52+
/// General flags that describe decode behavior (e.g., positive decode).
53+
pub general_flags: u8,
54+
55+
/// Type-specific flags (e.g., cacheability for memory).
56+
pub type_specific_flags: u8,
57+
58+
/// Granularity of the address space (typically 32 or 64).
59+
/// Indicates whether the range is 32-bit or 64-bit.
60+
pub granularity: u64,
61+
62+
/// Minimum address of the range (inclusive).
63+
pub address_min: u64,
64+
65+
/// Maximum address of the range (inclusive).
66+
pub address_max: u64,
67+
68+
/// Translation offset to convert host address to PCI address.
69+
/// Usually zero unless the bridge remaps addresses.
70+
pub translation_offset: u64,
71+
72+
/// Length of the address range (in bytes or bus numbers).
73+
pub address_length: u64,
74+
}
75+
76+
const PCI_RESTBL_QWORDADDRSPEC_TAG: u8 = 0x8a;
77+
const PCI_RESTBL_END_TAG: u8 = 0x79;
78+
79+
/// Parses a list of QWORD Address Space Descriptors from a raw memory region.
80+
/// Stops when it encounters an End Tag descriptor (type 0x79).
81+
pub(crate) fn parse(base: *const c_void) -> Vec<QwordAddressSpaceDescriptor> {
82+
let base: *const u8 = base.cast();
83+
84+
// Phase 1: determine total length
85+
let mut offset = 0;
86+
loop {
87+
let tag = unsafe { ptr::read(base.add(offset)) };
88+
offset += match tag {
89+
PCI_RESTBL_QWORDADDRSPEC_TAG => 3 + 0x2B,
90+
PCI_RESTBL_END_TAG => break,
91+
_ => return Vec::new(), // Unknown tag - bailing
92+
};
93+
}
94+
95+
// Phase 2: parse descriptors from resource table
96+
let mut bfr: &[u8] = unsafe { slice::from_raw_parts(base, offset) };
97+
let mut descriptors = Vec::new();
98+
while !bfr.is_empty() {
99+
match bfr[0] {
100+
PCI_RESTBL_QWORDADDRSPEC_TAG => {
101+
let descriptor = QwordAddressSpaceDescriptor {
102+
resource_range_type: ResourceRangeType::from(bfr[0x03]),
103+
general_flags: bfr[0x04],
104+
type_specific_flags: bfr[0x05],
105+
granularity: u64::from_le_bytes(bfr[0x06..0x06 + 8].try_into().unwrap()),
106+
address_min: u64::from_le_bytes(bfr[0x0E..0x0E + 8].try_into().unwrap()),
107+
address_max: u64::from_le_bytes(bfr[0x16..0x16 + 8].try_into().unwrap()),
108+
translation_offset: u64::from_le_bytes(bfr[0x1E..0x1E + 8].try_into().unwrap()),
109+
address_length: u64::from_le_bytes(bfr[0x26..0x26 + 8].try_into().unwrap()),
110+
};
111+
descriptors.push(descriptor);
112+
113+
bfr = &bfr[3 + 0x2B..];
114+
}
115+
_ => break,
116+
}
117+
}
118+
119+
descriptors
120+
}

uefi/src/proto/pci/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use core::cmp::Ordering;
66

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

9+
pub mod configuration;
910
pub mod root_bridge;
1011

1112
/// IO Address for PCI/register IO operations
@@ -121,6 +122,34 @@ impl Ord for PciIoAddress {
121122
}
122123
}
123124

125+
// --------------------------------------------------------------------------------------------
126+
127+
/// Fully qualified pci address. This address is valid across root bridges.
128+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
129+
pub struct FullPciIoAddress {
130+
/// PCI segment number
131+
segment: u32,
132+
/// Subsequent PCI address
133+
addr: PciIoAddress,
134+
}
135+
impl FullPciIoAddress {
136+
pub(crate) fn new(segment: u32, addr: PciIoAddress) -> Self {
137+
Self { segment, addr }
138+
}
139+
140+
/// Get the segment number this address belongs to.
141+
pub fn segment(&self) -> u32 {
142+
self.segment
143+
}
144+
145+
/// Get the internal RootBridge-specific portion of the address.
146+
pub fn addr(&self) -> PciIoAddress {
147+
self.addr
148+
}
149+
}
150+
151+
// ############################################################################################
152+
124153
/// Trait implemented by all data types that can natively be read from a PCI device.
125154
/// Note: Not all of them have to actually be supported by the hardware at hand.
126155
pub trait PciIoUnit: Sized + Default {}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
//! PCI Root Bridge protocol.
44
5-
use core::ptr;
5+
use core::{ffi::c_void, ptr};
66

77
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
8-
use crate::StatusExt;
8+
use crate::{
9+
StatusExt,
10+
proto::pci::configuration::{self, QwordAddressSpaceDescriptor},
11+
};
12+
use alloc::vec::Vec;
913
use uefi_macros::unsafe_protocol;
1014
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
1115

@@ -52,7 +56,21 @@ impl PciRootBridgeIo {
5256
// TODO: map & unmap & copy memory
5357
// TODO: buffer management
5458
// TODO: get/set attributes
55-
// TODO: configuration / resource settings
59+
60+
/// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors.
61+
///
62+
/// The returned list of descriptors contains information about bus, memory and io ranges that were set up
63+
/// by the firmware.
64+
///
65+
/// # Errors
66+
/// - [`Status::UNSUPPORTED`] The current configuration of this PCI root bridge could not be retrieved.
67+
pub fn configuration(&self) -> crate::Result<Vec<QwordAddressSpaceDescriptor>> {
68+
let mut resources: *const c_void = ptr::null();
69+
unsafe {
70+
((self.0.configuration)(&self.0, &mut resources))
71+
.to_result_with_val(|| configuration::parse(resources))
72+
}
73+
}
5674
}
5775

5876
/// Struct for performing PCI I/O operations on a root bridge.

0 commit comments

Comments
 (0)