Skip to content

Commit 8a96ebf

Browse files
committed
uefi: Add PciRootBridgeIo::configuration() to query acpi table info
1 parent 4583c2f commit 8a96ebf

File tree

4 files changed

+153
-17
lines changed

4 files changed

+153
-17
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: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
/// General flags that describe decode behavior (e.g., positive decode).
52+
pub general_flags: u8,
53+
/// Type-specific flags (e.g., cacheability for memory).
54+
pub type_specific_flags: u8,
55+
/// Granularity of the address space (typically 32 or 64).
56+
/// Indicates whether the range is 32-bit or 64-bit.
57+
pub granularity: u64,
58+
/// Minimum address of the range (inclusive).
59+
pub address_min: u64,
60+
/// Maximum address of the range (inclusive).
61+
pub address_max: u64,
62+
/// Translation offset to convert host address to PCI address.
63+
/// Usually zero unless the bridge remaps addresses.
64+
pub translation_offset: u64,
65+
/// Length of the address range (in bytes or bus numbers).
66+
pub address_length: u64,
67+
}
68+
69+
const PCI_RESTBL_QWORDADDRSPEC_TAG: u8 = 0x8a;
70+
const PCI_RESTBL_END_TAG: u8 = 0x79;
71+
72+
/// Parses a list of QWORD Address Space Descriptors from a raw memory region.
73+
/// Stops when it encounters an End Tag descriptor (type 0x79).
74+
pub(crate) fn parse(base: *const c_void) -> Vec<QwordAddressSpaceDescriptor> {
75+
let base: *const u8 = base.cast();
76+
77+
// Phase 1: determine total length
78+
let mut offset = 0;
79+
loop {
80+
let tag = unsafe { ptr::read(base.add(offset)) };
81+
offset += match tag {
82+
PCI_RESTBL_QWORDADDRSPEC_TAG => 3 + 0x2B,
83+
PCI_RESTBL_END_TAG => break,
84+
_ => return Vec::new(), // Unknown tag - bailing
85+
};
86+
}
87+
88+
// Phase 2: parse descriptors from resource table
89+
let mut bfr: &[u8] = unsafe { slice::from_raw_parts(base, offset) };
90+
let mut descriptors = Vec::new();
91+
while !bfr.is_empty() {
92+
match bfr[0] {
93+
PCI_RESTBL_QWORDADDRSPEC_TAG => {
94+
let descriptor = QwordAddressSpaceDescriptor {
95+
resource_range_type: ResourceRangeType::from(bfr[0x03]),
96+
general_flags: bfr[0x04],
97+
type_specific_flags: bfr[0x05],
98+
granularity: u64::from_le_bytes(bfr[0x06..0x06 + 8].try_into().unwrap()),
99+
address_min: u64::from_le_bytes(bfr[0x0E..0x0E + 8].try_into().unwrap()),
100+
address_max: u64::from_le_bytes(bfr[0x16..0x16 + 8].try_into().unwrap()),
101+
translation_offset: u64::from_le_bytes(bfr[0x1E..0x1E + 8].try_into().unwrap()),
102+
address_length: u64::from_le_bytes(bfr[0x26..0x26 + 8].try_into().unwrap()),
103+
};
104+
descriptors.push(descriptor);
105+
106+
bfr = &bfr[3 + 0x2B..];
107+
}
108+
_ => break,
109+
}
110+
}
111+
112+
descriptors
113+
}

uefi/src/proto/pci/mod.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use core::cmp::Ordering;
66

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

9+
#[cfg(feature = "alloc")]
10+
pub mod configuration;
911
pub mod root_bridge;
1012

1113
/// IO Address for PCI/register IO operations
@@ -188,33 +190,36 @@ mod tests {
188190
let addr1_0_0 = PciIoAddress::new(1, 0, 0);
189191

190192
assert_eq!(addr0_0_0.cmp(&addr0_0_0), Ordering::Equal);
191-
assert_eq!(addr0_0_0.cmp(addr0_0_1), Ordering::Less);
193+
assert_eq!(addr0_0_0.cmp(&addr0_0_1), Ordering::Less);
192194
assert_eq!(addr0_0_0.cmp(&addr0_1_0), Ordering::Less);
193195
assert_eq!(addr0_0_0.cmp(&addr1_0_0), Ordering::Less);
194196

195-
assert_eq!(addr0_0_1.cmp(addr0_0_0), Ordering::Greater);
196-
assert_eq!(addr0_0_1.cmp(addr0_0_1), Ordering::Equal);
197+
assert_eq!(addr0_0_1.cmp(&addr0_0_0), Ordering::Greater);
198+
assert_eq!(addr0_0_1.cmp(&addr0_0_1), Ordering::Equal);
197199
assert_eq!(addr0_0_1.cmp(&addr0_1_0), Ordering::Less);
198200
assert_eq!(addr0_0_1.cmp(&addr1_0_0), Ordering::Less);
199201

200-
assert_eq!(addr0_1_0.cmp(addr0_0_0), Ordering::Greater);
201-
assert_eq!(addr0_1_0.cmp(addr0_0_1), Ordering::Greater);
202+
assert_eq!(addr0_1_0.cmp(&addr0_0_0), Ordering::Greater);
203+
assert_eq!(addr0_1_0.cmp(&addr0_0_1), Ordering::Greater);
202204
assert_eq!(addr0_1_0.cmp(&addr0_1_0), Ordering::Equal);
203205
assert_eq!(addr0_1_0.cmp(&addr1_0_0), Ordering::Less);
204206

205-
assert_eq!(addr1_0_0.cmp(addr0_0_0), Ordering::Greater);
206-
assert_eq!(addr1_0_0.cmp(addr0_0_1), Ordering::Greater);
207+
assert_eq!(addr1_0_0.cmp(&addr0_0_0), Ordering::Greater);
208+
assert_eq!(addr1_0_0.cmp(&addr0_0_1), Ordering::Greater);
207209
assert_eq!(addr1_0_0.cmp(&addr0_1_0), Ordering::Greater);
208210
assert_eq!(addr1_0_0.cmp(&addr1_0_0), Ordering::Equal);
209211

210-
assert_eq!(addr0_0_0.cmp(addr0_0_0.with_register(1)), Ordering::Less);
211-
assert_eq!(addr0_0_0.with_register(1).cmp(addr0_0_0), Ordering::Greater);
212+
assert_eq!(addr0_0_0.cmp(&addr0_0_0.with_register(1)), Ordering::Less);
212213
assert_eq!(
213-
addr0_0_0.cmp(addr0_0_0.with_extended_register(1)),
214+
addr0_0_0.with_register(1).cmp(&addr0_0_0),
215+
Ordering::Greater
216+
);
217+
assert_eq!(
218+
addr0_0_0.cmp(&addr0_0_0.with_extended_register(1)),
214219
Ordering::Less
215220
);
216221
assert_eq!(
217-
addr0_0_0.with_extended_register(1).cmp(addr0_0_0),
222+
addr0_0_0.with_extended_register(1).cmp(&addr0_0_0),
218223
Ordering::Greater
219224
);
220225
}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

33
//! PCI Root Bridge protocol.
44
5-
use core::ptr;
6-
75
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
86
use crate::StatusExt;
7+
#[cfg(feature = "alloc")]
8+
use crate::proto::pci::configuration::{self, QwordAddressSpaceDescriptor};
9+
#[cfg(feature = "alloc")]
10+
use alloc::vec::Vec;
11+
#[cfg(feature = "alloc")]
12+
use core::ffi::c_void;
13+
use core::ptr;
914
use uefi_macros::unsafe_protocol;
1015
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
1116

12-
#[cfg(doc)]
13-
use crate::Status;
14-
1517
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
1618
///
1719
/// # UEFI Spec Description
@@ -52,7 +54,22 @@ impl PciRootBridgeIo {
5254
// TODO: map & unmap & copy memory
5355
// TODO: buffer management
5456
// TODO: get/set attributes
55-
// TODO: configuration / resource settings
57+
58+
/// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors.
59+
///
60+
/// The returned list of descriptors contains information about bus, memory and io ranges that were set up
61+
/// by the firmware.
62+
///
63+
/// # Errors
64+
/// - [`Status::UNSUPPORTED`] The current configuration of this PCI root bridge could not be retrieved.
65+
#[cfg(feature = "alloc")]
66+
pub fn configuration(&self) -> crate::Result<Vec<QwordAddressSpaceDescriptor>> {
67+
let mut resources: *const c_void = ptr::null();
68+
unsafe {
69+
((self.0.configuration)(&self.0, &mut resources))
70+
.to_result_with_val(|| configuration::parse(resources))
71+
}
72+
}
5673
}
5774

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

0 commit comments

Comments
 (0)