Skip to content

Commit 5165be9

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

File tree

4 files changed

+156
-14
lines changed

4 files changed

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

uefi/src/proto/pci/mod.rs

Lines changed: 15 additions & 11 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
@@ -188,33 +189,36 @@ mod tests {
188189
let addr1_0_0 = PciIoAddress::new(1, 0, 0);
189190

190191
assert_eq!(addr0_0_0.cmp(&addr0_0_0), Ordering::Equal);
191-
assert_eq!(addr0_0_0.cmp(addr0_0_1), Ordering::Less);
192+
assert_eq!(addr0_0_0.cmp(&addr0_0_1), Ordering::Less);
192193
assert_eq!(addr0_0_0.cmp(&addr0_1_0), Ordering::Less);
193194
assert_eq!(addr0_0_0.cmp(&addr1_0_0), Ordering::Less);
194195

195-
assert_eq!(addr0_0_1.cmp(addr0_0_0), Ordering::Greater);
196-
assert_eq!(addr0_0_1.cmp(addr0_0_1), Ordering::Equal);
196+
assert_eq!(addr0_0_1.cmp(&addr0_0_0), Ordering::Greater);
197+
assert_eq!(addr0_0_1.cmp(&addr0_0_1), Ordering::Equal);
197198
assert_eq!(addr0_0_1.cmp(&addr0_1_0), Ordering::Less);
198199
assert_eq!(addr0_0_1.cmp(&addr1_0_0), Ordering::Less);
199200

200-
assert_eq!(addr0_1_0.cmp(addr0_0_0), Ordering::Greater);
201-
assert_eq!(addr0_1_0.cmp(addr0_0_1), Ordering::Greater);
201+
assert_eq!(addr0_1_0.cmp(&addr0_0_0), Ordering::Greater);
202+
assert_eq!(addr0_1_0.cmp(&addr0_0_1), Ordering::Greater);
202203
assert_eq!(addr0_1_0.cmp(&addr0_1_0), Ordering::Equal);
203204
assert_eq!(addr0_1_0.cmp(&addr1_0_0), Ordering::Less);
204205

205-
assert_eq!(addr1_0_0.cmp(addr0_0_0), Ordering::Greater);
206-
assert_eq!(addr1_0_0.cmp(addr0_0_1), Ordering::Greater);
206+
assert_eq!(addr1_0_0.cmp(&addr0_0_0), Ordering::Greater);
207+
assert_eq!(addr1_0_0.cmp(&addr0_0_1), Ordering::Greater);
207208
assert_eq!(addr1_0_0.cmp(&addr0_1_0), Ordering::Greater);
208209
assert_eq!(addr1_0_0.cmp(&addr1_0_0), Ordering::Equal);
209210

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);
211+
assert_eq!(addr0_0_0.cmp(&addr0_0_0.with_register(1)), Ordering::Less);
212212
assert_eq!(
213-
addr0_0_0.cmp(addr0_0_0.with_extended_register(1)),
213+
addr0_0_0.with_register(1).cmp(&addr0_0_0),
214+
Ordering::Greater
215+
);
216+
assert_eq!(
217+
addr0_0_0.cmp(&addr0_0_0.with_extended_register(1)),
214218
Ordering::Less
215219
);
216220
assert_eq!(
217-
addr0_0_0.with_extended_register(1).cmp(addr0_0_0),
221+
addr0_0_0.with_extended_register(1).cmp(&addr0_0_0),
218222
Ordering::Greater
219223
);
220224
}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
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

@@ -52,7 +57,24 @@ impl PciRootBridgeIo {
5257
// TODO: map & unmap & copy memory
5358
// TODO: buffer management
5459
// TODO: get/set attributes
55-
// TODO: configuration / resource settings
60+
61+
/// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors.
62+
///
63+
/// The returned list of descriptors contains information about bus, memory and io ranges that were set up
64+
/// by the firmware.
65+
///
66+
/// # Errors
67+
/// - [`Status::UNSUPPORTED`] The current configuration of this PCI root bridge could not be retrieved.
68+
#[cfg(feature = "alloc")]
69+
pub fn configuration(&self) -> crate::Result<Vec<QwordAddressSpaceDescriptor>> {
70+
// The storage for the resource descriptors is allocated by this function. The caller must treat
71+
// the return buffer as read-only data, and the buffer must not be freed by the caller.
72+
let mut resources: *const c_void = ptr::null();
73+
unsafe {
74+
((self.0.configuration)(&self.0, &mut resources))
75+
.to_result_with_val(|| configuration::parse(resources))
76+
}
77+
}
5678
}
5779

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

0 commit comments

Comments
 (0)