Skip to content

Commit f8a8c23

Browse files
committed
uefi: Add (partial) safe protocol implementation for PCI_ROOT_BRIDGE_IO_PROTOCOL
1 parent 9b9e621 commit f8a8c23

File tree

4 files changed

+339
-0
lines changed

4 files changed

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

0 commit comments

Comments
 (0)