Skip to content

Commit 949ad3c

Browse files
authored
Merge pull request #1958 from JarlEvanson/pci-root-bridge
uefi: Add PciRootBridgeIo memory and I/O space access
2 parents a24d931 + 4b63af8 commit 949ad3c

3 files changed

Lines changed: 172 additions & 18 deletions

File tree

uefi-test-runner/src/proto/pci/root_bridge.rs

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use uefi::proto::scsi::pass_thru::ExtScsiPassThru;
1212
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttributes;
1313

1414
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
15+
const VIRTIO_RNG_DEVICE_ID: u16 = 0x1005;
1516
const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1;
1617
const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6;
1718

@@ -40,15 +41,20 @@ fn test_enumeration_and_address_space_access() {
4041
.pci()
4142
.read_one::<u32>(addr.with_register(0))
4243
.unwrap();
43-
let reg1 = pci_proto
44+
let reg2 = pci_proto
4445
.pci()
4546
.read_one::<u32>(addr.with_register(2 * REG_SIZE))
4647
.unwrap();
48+
let reg3 = pci_proto
49+
.pci()
50+
.read_one::<u32>(addr.with_register(3 * REG_SIZE))
51+
.unwrap();
4752

4853
let vendor_id = (reg0 & 0xFFFF) as u16;
4954
let device_id = (reg0 >> 16) as u16;
50-
let class_code = (reg1 >> 24) as u8;
51-
let subclass_code = ((reg1 >> 16) & 0xFF) as u8;
55+
let class_code = (reg2 >> 24) as u8;
56+
let subclass_code = ((reg2 >> 16) & 0xFF) as u8;
57+
let header_type = ((reg3 >> 16) & 0x7F) as u8;
5258
let device_path = pci_tree.device_path(&root_device_path, addr).unwrap();
5359
let device_path_str = device_path
5460
.to_string16(DisplayOnly(false), AllowShortcuts(false))
@@ -66,6 +72,63 @@ fn test_enumeration_and_address_space_access() {
6672
mass_storage_dev_paths.insert(device_path_str.clone());
6773
}
6874

75+
if vendor_id == RED_HAT_PCI_VENDOR_ID && device_id == VIRTIO_RNG_DEVICE_ID {
76+
// This PCI device has well-defined IO port and MMIO address spaces
77+
// suitable for testing their respective access APIs These address
78+
// spaces are suitable because they feature idempotent reads.
79+
assert_eq!(
80+
header_type, 0x00,
81+
"unexpected header type for PCI virtio rng device"
82+
);
83+
84+
let mut bars = [0; 6];
85+
pci_proto
86+
.pci()
87+
.read::<u32>(addr.with_register(4 * REG_SIZE), &mut bars)
88+
.unwrap();
89+
log::info!("BARS: {bars:#x?}");
90+
91+
let mut bars = bars.into_iter();
92+
let mut next_bar = bars.next();
93+
while let Some(bar) = next_bar {
94+
next_bar = bars.next();
95+
96+
let bar = decode_bar(bar, next_bar);
97+
match bar {
98+
Bar::Io(base) => {
99+
// Virtio RNG devices have a device features register that is safe to
100+
// read at the start of the I/O space.
101+
let device_features = pci_proto.io().read_one::<u32>(base).unwrap();
102+
log::info!("Device Features: {device_features:#0b}");
103+
}
104+
Bar::Mem32 {
105+
base,
106+
prefetchable: true,
107+
} => {
108+
// Reading from a prefetchable MMIO region is always non-destructive.
109+
pci_proto.memory().read_one::<u32>(u64::from(base)).unwrap();
110+
}
111+
Bar::Mem64 {
112+
base,
113+
prefetchable: true,
114+
} => {
115+
// Reading from a prefetchable MMIO region is always non-destructive.
116+
pci_proto.memory().read_one::<u32>(base).unwrap();
117+
}
118+
_ => {}
119+
}
120+
121+
log::info!("BAR: {bar:x?}");
122+
if let Bar::Mem64 {
123+
base: _,
124+
prefetchable: _,
125+
} = bar
126+
{
127+
next_bar = bars.next();
128+
}
129+
}
130+
}
131+
69132
let (bus, dev, fun) = (addr.bus, addr.dev, addr.fun);
70133
log::info!(
71134
"PCI Device: [{bus:02x}, {dev:02x}, {fun:02x}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X} - {}",
@@ -124,3 +187,34 @@ fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProto
124187
let open_attrs = OpenProtocolAttributes::GetProtocol;
125188
unsafe { uefi::boot::open_protocol(open_opts, open_attrs).unwrap() }
126189
}
190+
191+
fn decode_bar(bar: u32, next_bar: Option<u32>) -> Bar {
192+
if bar & 0b1 == 0b0 {
193+
match (bar & 0b110) >> 1 {
194+
0b00 => Bar::Mem32 {
195+
base: bar & !0b1111,
196+
prefetchable: bar & 0b1000 != 0,
197+
},
198+
0b10 => {
199+
if let Some(next_bar) = next_bar {
200+
Bar::Mem64 {
201+
base: u64::from(bar & !0b1111) | (u64::from(next_bar) << 32),
202+
prefetchable: bar & 0b1000 != 0,
203+
}
204+
} else {
205+
unreachable!("PCI hardware error")
206+
}
207+
}
208+
_ => unimplemented!(),
209+
}
210+
} else {
211+
Bar::Io(bar & !0b11)
212+
}
213+
}
214+
215+
#[derive(Debug)]
216+
enum Bar {
217+
Mem32 { base: u32, prefetchable: bool },
218+
Mem64 { base: u64, prefetchable: bool },
219+
Io(u32),
220+
}

uefi/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
- Added `proto::console::text::InputEx`.
55
- Added `proto::pci::PciRootBridgeIo::{supported_attributes(), attributes(),
66
set_attributes(), set_attributes_with_range()}`
7+
- Added `memory()` and `io()` address space access to `PciRootBridgeIo`
8+
protocol.
79

810
## Changed
911
- MSRV increased from 1.88 to 1.91.
@@ -13,6 +15,8 @@
1315
argument should pass in `&[]` instead.
1416
- **Breaking:** Corrected function signature of `boot::exit` to enable handling
1517
errors during exit.
18+
- **Breaking:** Renamed `PciIoAccessPci` to `PciIoAccess` and added a generic
19+
parameter to handle the PCI configuration, IO port, and MMIO address spaces.
1620

1721
## Removed
1822
- **Breaking:** Removed the deprecated `table::cfg::*_GUID` constants. Use

uefi/src/proto/pci/root_bridge.rs

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,30 @@ impl PciRootBridgeIo {
3636
self.0.segment_number
3737
}
3838

39-
/// Access PCI I/O operations on this root bridge.
40-
pub const fn pci(&mut self) -> PciIoAccessPci<'_> {
41-
PciIoAccessPci {
39+
/// Access PCI controller registers in the configuration space on this root bridge.
40+
pub const fn pci(&mut self) -> PciIoAccess<'_, PciConfigurationSpace> {
41+
PciIoAccess {
4242
proto: &mut self.0,
4343
io_access: &mut self.0.pci,
44+
_address_space: PciConfigurationSpace,
45+
}
46+
}
47+
48+
/// Access PCI controller registers in the memory space on this root bridge.
49+
pub const fn memory(&mut self) -> PciIoAccess<'_, PciMemorySpace> {
50+
PciIoAccess {
51+
proto: &mut self.0,
52+
io_access: &mut self.0.mem,
53+
_address_space: PciMemorySpace,
54+
}
55+
}
56+
57+
/// Access PCI controller registers in the I/O space on this root bridge.
58+
pub const fn io(&mut self) -> PciIoAccess<'_, PciIoSpace> {
59+
PciIoAccess {
60+
proto: &mut self.0,
61+
io_access: &mut self.0.io,
62+
_address_space: PciIoSpace,
4463
}
4564
}
4665

@@ -117,8 +136,6 @@ impl PciRootBridgeIo {
117136
}
118137

119138
// TODO: poll I/O
120-
// TODO: mem I/O access
121-
// TODO: io I/O access
122139
// TODO: map & unmap & copy memory
123140
// TODO: buffer management
124141

@@ -154,7 +171,7 @@ impl PciRootBridgeIo {
154171
/// An ordered list of addresses containing all present devices below this RootBridge.
155172
///
156173
/// # Errors
157-
/// This can basically fail with all the IO errors found in [`PciIoAccessPci`] methods.
174+
/// This can basically fail with all the IO errors found in [`PciIoAccess`] methods.
158175
#[cfg(feature = "alloc")]
159176
pub fn enumerate(&mut self) -> crate::Result<super::enumeration::PciTree> {
160177
use super::enumeration::{self, PciTree};
@@ -182,12 +199,13 @@ impl PciRootBridgeIo {
182199

183200
/// Struct for performing PCI I/O operations on a root bridge.
184201
#[derive(Debug)]
185-
pub struct PciIoAccessPci<'a> {
202+
pub struct PciIoAccess<'a, S: PciIoAddressSpace> {
186203
proto: *mut PciRootBridgeIoProtocol,
187204
io_access: &'a mut PciRootBridgeIoAccess,
205+
_address_space: S,
188206
}
189207

190-
impl PciIoAccessPci<'_> {
208+
impl<S: PciIoAddressSpace> PciIoAccess<'_, S> {
191209
/// Reads a single value of type `U` from the specified PCI address.
192210
///
193211
/// # Arguments
@@ -199,7 +217,7 @@ impl PciIoAccessPci<'_> {
199217
/// # Errors
200218
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
201219
/// - [`Status::OUT_OF_RESOURCES`] The read request could not be completed due to a lack of resources.
202-
pub fn read_one<U: PciIoUnit>(&self, addr: PciIoAddress) -> crate::Result<U> {
220+
pub fn read_one<U: PciIoUnit>(&self, addr: S::Address) -> crate::Result<U> {
203221
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
204222
let mut result = U::default();
205223
unsafe {
@@ -223,7 +241,7 @@ impl PciIoAccessPci<'_> {
223241
/// # Errors
224242
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
225243
/// - [`Status::OUT_OF_RESOURCES`] The write request could not be completed due to a lack of resources.
226-
pub fn write_one<U: PciIoUnit>(&self, addr: PciIoAddress, data: U) -> crate::Result<()> {
244+
pub fn write_one<U: PciIoUnit>(&self, addr: S::Address, data: U) -> crate::Result<()> {
227245
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
228246
unsafe {
229247
(self.io_access.write)(
@@ -246,7 +264,7 @@ impl PciIoAccessPci<'_> {
246264
/// # Errors
247265
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
248266
/// - [`Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources.
249-
pub fn read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
267+
pub fn read<U: PciIoUnit>(&self, addr: S::Address, data: &mut [U]) -> crate::Result<()> {
250268
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
251269
unsafe {
252270
(self.io_access.read)(
@@ -269,7 +287,7 @@ impl PciIoAccessPci<'_> {
269287
/// # Errors
270288
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
271289
/// - [`Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources.
272-
pub fn write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
290+
pub fn write<U: PciIoUnit>(&self, addr: S::Address, data: &[U]) -> crate::Result<()> {
273291
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
274292
unsafe {
275293
(self.io_access.write)(
@@ -295,7 +313,7 @@ impl PciIoAccessPci<'_> {
295313
/// - [`Status::OUT_OF_RESOURCES`] The operation could not be completed due to a lack of resources.
296314
pub fn fill_write<U: PciIoUnit>(
297315
&self,
298-
addr: PciIoAddress,
316+
addr: S::Address,
299317
count: usize,
300318
data: U,
301319
) -> crate::Result<()> {
@@ -325,7 +343,7 @@ impl PciIoAccessPci<'_> {
325343
/// # Errors
326344
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
327345
/// - [`Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources.
328-
pub fn fifo_read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
346+
pub fn fifo_read<U: PciIoUnit>(&self, addr: S::Address, data: &mut [U]) -> crate::Result<()> {
329347
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
330348
unsafe {
331349
(self.io_access.read)(
@@ -352,7 +370,7 @@ impl PciIoAccessPci<'_> {
352370
/// # Errors
353371
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
354372
/// - [`Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources.
355-
pub fn fifo_write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
373+
pub fn fifo_write<U: PciIoUnit>(&self, addr: S::Address, data: &[U]) -> crate::Result<()> {
356374
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
357375
unsafe {
358376
(self.io_access.write)(
@@ -366,3 +384,41 @@ impl PciIoAccessPci<'_> {
366384
}
367385
}
368386
}
387+
388+
/// Marker struct for the PCI memory space.
389+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
390+
pub struct PciMemorySpace;
391+
392+
impl private::Sealed for PciMemorySpace {}
393+
impl PciIoAddressSpace for PciMemorySpace {
394+
type Address = u64;
395+
}
396+
397+
/// Marker struct for the PCI I/O space.
398+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
399+
pub struct PciIoSpace;
400+
401+
impl private::Sealed for PciIoSpace {}
402+
impl PciIoAddressSpace for PciIoSpace {
403+
type Address = u32;
404+
}
405+
406+
/// Marker struct for the PCI configuration space.
407+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
408+
pub struct PciConfigurationSpace;
409+
410+
impl private::Sealed for PciConfigurationSpace {}
411+
impl PciIoAddressSpace for PciConfigurationSpace {
412+
type Address = PciIoAddress;
413+
}
414+
415+
/// Trait representing how to convert from the address type expected for the address space and the
416+
/// raw address space.
417+
pub trait PciIoAddressSpace: private::Sealed {
418+
/// Specifies the type of the address space addresses.
419+
type Address: Into<u64>;
420+
}
421+
422+
mod private {
423+
pub trait Sealed {}
424+
}

0 commit comments

Comments
 (0)