Skip to content

Commit c801389

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

File tree

4 files changed

+154
-17
lines changed

4 files changed

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

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)