Skip to content

Commit 895ae1f

Browse files
committed
uefi: Add (partial) safe protocol implementation for PCI_ROOT_BRIDGE_IO_PROTOCOL
1 parent 6d8185f commit 895ae1f

File tree

4 files changed

+338
-0
lines changed

4 files changed

+338
-0
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 `ConfigTableEntry::MEMORY_ATTRIBUTES_GUID` and `ConfigTableEntry::IMAGE_SECURITY_DATABASE_GUID`.
5+
- Added `proto::pci::PciRootBridgeIo`.
56

67
## Changed
78
- **Breaking:** `boot::stall` now take `core::time::Duration` instead of `usize`.

uefi/src/proto/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ pub mod console;
1616
pub mod debug;
1717
pub mod device_path;
1818
pub mod driver;
19+
pub mod hii;
1920
pub mod loaded_image;
2021
pub mod media;
2122
pub mod misc;
2223
pub mod network;
2324
#[cfg(feature = "alloc")]
2425
pub mod nvme;
26+
pub mod pci;
2527
pub mod pi;
2628
pub mod rng;
2729
#[cfg(feature = "alloc")]

uefi/src/proto/pci/mod.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! PCI Bus specific protocols.
4+
5+
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth;
6+
7+
pub mod root_bridge;
8+
9+
/// IO Address for PCI/register IO operations
10+
#[repr(C, packed)]
11+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12+
pub struct PciIoAddress {
13+
/// Register number within the PCI device.
14+
pub reg: u8,
15+
/// Function number within the PCI device.
16+
pub fun: u8,
17+
/// Device number within the PCI bus.
18+
pub dev: u8,
19+
/// Bus number in the PCI hierarchy.
20+
pub bus: u8,
21+
/// Extended register number within the PCI device.
22+
pub ext_reg: u32,
23+
}
24+
25+
impl PciIoAddress {
26+
/// Create address pointing to the device identified by `bus`, `dev` and `fun` ids.
27+
#[must_use]
28+
pub const fn new(bus: u8, dev: u8, fun: u8) -> Self {
29+
Self {
30+
bus,
31+
dev,
32+
fun,
33+
reg: 0,
34+
ext_reg: 0,
35+
}
36+
}
37+
38+
/// Configure the **byte**-offset of the register to access.
39+
#[must_use]
40+
pub const fn with_register(&self, reg: u8) -> Self {
41+
let mut addr = *self;
42+
addr.reg = reg;
43+
addr.ext_reg = 0;
44+
addr
45+
}
46+
47+
/// Configure the **byte**-offset of the extended register to access.
48+
#[must_use]
49+
pub const fn with_extended_register(&self, ext_reg: u32) -> Self {
50+
let mut addr = *self;
51+
addr.reg = 0;
52+
addr.ext_reg = ext_reg;
53+
addr
54+
}
55+
}
56+
57+
impl From<PciIoAddress> for u64 {
58+
fn from(value: PciIoAddress) -> Self {
59+
unsafe { core::mem::transmute(value) }
60+
}
61+
}
62+
63+
/// Trait implemented by all data types that can natively be read from a PCI device.
64+
/// Note: Not all of them have to actually be supported by the hardware at hand.
65+
pub trait PciIoUnit: Sized + Default {}
66+
impl PciIoUnit for u8 {}
67+
impl PciIoUnit for u16 {}
68+
impl PciIoUnit for u32 {}
69+
impl PciIoUnit for u64 {}
70+
71+
#[allow(dead_code)]
72+
enum PciIoMode {
73+
Normal,
74+
Fifo,
75+
Fill,
76+
}
77+
78+
fn encode_io_mode_and_unit<U: PciIoUnit>(mode: PciIoMode) -> PciRootBridgeIoProtocolWidth {
79+
match (mode, core::mem::size_of::<U>()) {
80+
(PciIoMode::Normal, 1) => PciRootBridgeIoProtocolWidth::UINT8,
81+
(PciIoMode::Normal, 2) => PciRootBridgeIoProtocolWidth::UINT16,
82+
(PciIoMode::Normal, 4) => PciRootBridgeIoProtocolWidth::UINT32,
83+
(PciIoMode::Normal, 8) => PciRootBridgeIoProtocolWidth::UINT64,
84+
85+
(PciIoMode::Fifo, 1) => PciRootBridgeIoProtocolWidth::FIFO_UINT8,
86+
(PciIoMode::Fifo, 2) => PciRootBridgeIoProtocolWidth::FIFO_UINT16,
87+
(PciIoMode::Fifo, 4) => PciRootBridgeIoProtocolWidth::FIFO_UINT32,
88+
(PciIoMode::Fifo, 8) => PciRootBridgeIoProtocolWidth::FIFO_UINT64,
89+
90+
(PciIoMode::Fill, 1) => PciRootBridgeIoProtocolWidth::FILL_UINT8,
91+
(PciIoMode::Fill, 2) => PciRootBridgeIoProtocolWidth::FILL_UINT16,
92+
(PciIoMode::Fill, 4) => PciRootBridgeIoProtocolWidth::FILL_UINT32,
93+
(PciIoMode::Fill, 8) => PciRootBridgeIoProtocolWidth::FILL_UINT64,
94+
95+
_ => unreachable!("Illegal PCI IO-Mode / Unit combination"),
96+
}
97+
}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! PCI Root Bridge protocol.
4+
5+
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
6+
use crate::StatusExt;
7+
use uefi_macros::unsafe_protocol;
8+
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
9+
10+
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
11+
///
12+
/// # UEFI Spec Description
13+
/// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are
14+
/// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller.
15+
#[derive(Debug)]
16+
#[repr(transparent)]
17+
#[unsafe_protocol(PciRootBridgeIoProtocol::GUID)]
18+
pub struct PciRootBridgeIo(PciRootBridgeIoProtocol);
19+
20+
impl PciRootBridgeIo {
21+
/// Get the segment number where this PCI root bridge resides.
22+
#[must_use]
23+
pub const fn segment_nr(&self) -> u32 {
24+
self.0.segment_number
25+
}
26+
27+
/// Access PCI I/O operations on this root bridge.
28+
pub fn pci(&mut self) -> PciIoAccessPci<'_> {
29+
PciIoAccessPci {
30+
proto: &mut self.0,
31+
io_access: &mut self.0.pci,
32+
}
33+
}
34+
35+
/// Flush all PCI posted write transactions from a PCI host bridge to system memory.
36+
///
37+
/// # Errors
38+
/// - [`crate::Status::DEVICE_ERROR`] The PCI posted write transactions were not flushed from the PCI host bridge
39+
/// due to a hardware error.
40+
pub fn flush(&mut self) -> crate::Result<()> {
41+
unsafe { (self.0.flush)(&mut self.0).to_result() }
42+
}
43+
44+
// TODO: poll I/O
45+
// TODO: mem I/O access
46+
// TODO: io I/O access
47+
// TODO: map & unmap & copy memory
48+
// TODO: buffer management
49+
// TODO: get/set attributes
50+
// TODO: configuration / resource settings
51+
}
52+
53+
/// Struct for performing PCI I/O operations on a root bridge.
54+
#[derive(Debug)]
55+
pub struct PciIoAccessPci<'a> {
56+
proto: *mut PciRootBridgeIoProtocol,
57+
io_access: &'a mut PciRootBridgeIoAccess,
58+
}
59+
60+
impl<'a> PciIoAccessPci<'a> {
61+
/// Reads a single value of type `U` from the specified PCI address.
62+
///
63+
/// # Arguments
64+
/// - `addr` - The PCI address to read from.
65+
///
66+
/// # Returns
67+
/// - The read value of type `U`.
68+
///
69+
/// # Errors
70+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
71+
/// - [`crate::Status::OUT_OF_RESOURCES`] The read request could not be completed due to a lack of resources.
72+
pub fn read_one<U: PciIoUnit>(&self, addr: PciIoAddress) -> crate::Result<U> {
73+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
74+
let mut result = U::default();
75+
unsafe {
76+
(self.io_access.read)(
77+
self.proto,
78+
width_mode,
79+
addr.into(),
80+
1,
81+
(&mut result as *mut U).cast(),
82+
)
83+
.to_result_with_val(|| result)
84+
}
85+
}
86+
87+
/// Writes a single value of type `U` to the specified PCI address.
88+
///
89+
/// # Arguments
90+
/// - `addr` - The PCI address to write to.
91+
/// - `data` - The value to write.
92+
///
93+
/// # Errors
94+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
95+
/// - [`crate::Status::OUT_OF_RESOURCES`] The write request could not be completed due to a lack of resources.
96+
pub fn write_one<U: PciIoUnit>(&self, addr: PciIoAddress, data: U) -> crate::Result<()> {
97+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
98+
unsafe {
99+
(self.io_access.write)(
100+
self.proto,
101+
width_mode,
102+
addr.into(),
103+
1,
104+
(&data as *const U).cast(),
105+
)
106+
.to_result()
107+
}
108+
}
109+
110+
/// Reads multiple values from the specified PCI address range.
111+
///
112+
/// # Arguments
113+
/// - `addr` - The starting PCI address to read from.
114+
/// - `data` - A mutable slice to store the read values.
115+
///
116+
/// # Errors
117+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
118+
/// - [`crate::Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources.
119+
pub fn read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
120+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
121+
unsafe {
122+
(self.io_access.read)(
123+
self.proto,
124+
width_mode,
125+
addr.into(),
126+
data.len(),
127+
data.as_mut_ptr().cast(),
128+
)
129+
.to_result()
130+
}
131+
}
132+
133+
/// Writes multiple values to the specified PCI address range.
134+
///
135+
/// # Arguments
136+
/// - `addr` - The starting PCI address to write to.
137+
/// - `data` - A slice containing the values to write.
138+
///
139+
/// # Errors
140+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
141+
/// - [`crate::Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources.
142+
pub fn write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
143+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
144+
unsafe {
145+
(self.io_access.write)(
146+
self.proto,
147+
width_mode,
148+
addr.into(),
149+
data.len(),
150+
data.as_ptr().cast(),
151+
)
152+
.to_result()
153+
}
154+
}
155+
156+
/// Fills a PCI address range with the specified value.
157+
///
158+
/// # Arguments
159+
/// - `addr` - The starting PCI address to fill.
160+
/// - `count` - The number of units to write.
161+
/// - `data` - The value to fill the address range with.
162+
///
163+
/// # Errors
164+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
165+
/// - [`crate::Status::OUT_OF_RESOURCES`] The operation could not be completed due to a lack of resources.
166+
pub fn fill_write<U: PciIoUnit>(
167+
&self,
168+
addr: PciIoAddress,
169+
count: usize,
170+
data: U,
171+
) -> crate::Result<()> {
172+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fill);
173+
unsafe {
174+
(self.io_access.write)(
175+
self.proto,
176+
width_mode,
177+
addr.into(),
178+
count,
179+
(&data as *const U).cast(),
180+
)
181+
.to_result()
182+
}
183+
}
184+
185+
/// Reads a sequence of values of type `U` from the specified PCI address by repeatedly accessing it.
186+
///
187+
/// # Arguments
188+
/// - `addr` - The PCI address to read from.
189+
/// - `data` - A mutable slice to store the read values.
190+
///
191+
/// # Behavior
192+
/// This reads from the same memory region (starting at `addr` and ending at `addr + size_of::<U>()`) repeatedly.
193+
/// The resulting `data` buffer will contain the elements returned by reading the same address multiple times sequentially.
194+
///
195+
/// # Errors
196+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
197+
/// - [`crate::Status::OUT_OF_RESOURCES`] The read operation could not be completed due to a lack of resources.
198+
pub fn fifo_read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
199+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
200+
unsafe {
201+
(self.io_access.read)(
202+
self.proto,
203+
width_mode,
204+
addr.into(),
205+
data.len(),
206+
data.as_mut_ptr().cast(),
207+
)
208+
.to_result()
209+
}
210+
}
211+
212+
/// Writes a sequence of values of type `U` to the specified PCI address repeatedly.
213+
///
214+
/// # Arguments
215+
/// - `addr` - The PCI address to write to.
216+
/// - `data` - A slice containing the values to write.
217+
///
218+
/// # Behavior
219+
/// This sequentially writes all elements within the given `data` buffer to the same memory region
220+
/// (starting at `addr` and ending at `addr + size_of::<U>()`) sequentially.
221+
///
222+
/// # Errors
223+
/// - [`crate::Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
224+
/// - [`crate::Status::OUT_OF_RESOURCES`] The write operation could not be completed due to a lack of resources.
225+
pub fn fifo_write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
226+
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
227+
unsafe {
228+
(self.io_access.write)(
229+
self.proto,
230+
width_mode,
231+
addr.into(),
232+
data.len(),
233+
data.as_ptr().cast(),
234+
)
235+
.to_result()
236+
}
237+
}
238+
}

0 commit comments

Comments
 (0)