Skip to content

Commit 21c0f7c

Browse files
committed
uefi: Add wrapper for pages allocated by PCI protocol
1 parent 180e3a9 commit 21c0f7c

File tree

4 files changed

+233
-7
lines changed

4 files changed

+233
-7
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
pub mod root_bridge;
44

55
pub fn test() {
6-
root_bridge::test();
6+
root_bridge::test_io();
7+
root_bridge::test_buffer();
78
}

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ use uefi::proto::device_path::DevicePath;
99
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
1010
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
1111
use uefi::proto::scsi::pass_thru::ExtScsiPassThru;
12+
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolAttribute;
13+
use uefi_raw::table::boot::MemoryType;
1214

1315
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
1416
const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1;
1517
const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6;
1618

1719
const REG_SIZE: u8 = size_of::<u32>() as u8;
1820

19-
pub fn test() {
21+
pub fn test_io() {
2022
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
2123

2224
let mut sata_ctrl_cnt = 0;
@@ -88,6 +90,31 @@ pub fn test() {
8890
}
8991
}
9092

93+
pub fn test_buffer() {
94+
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
95+
96+
for pci_handle in pci_handles {
97+
let pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
98+
99+
let buffer = pci_proto
100+
.allocate_buffer::<[u8; 4096]>(
101+
MemoryType::BOOT_SERVICES_DATA,
102+
None,
103+
PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE,
104+
)
105+
.unwrap();
106+
let buffer = unsafe {
107+
let buffer = buffer.assume_init();
108+
buffer.base_ptr().as_mut().unwrap().fill(0);
109+
buffer
110+
};
111+
assert_eq!(buffer.base_ptr().addr() % 4096, 0);
112+
unsafe {
113+
assert!(buffer.base_ptr().as_mut().unwrap().iter().all(|v| *v == 0));
114+
}
115+
}
116+
}
117+
91118
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
92119
let open_opts = OpenProtocolParams {
93120
handle,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Defines wrapper for pages allocated by PCI Root Bridge protocol.
4+
5+
use crate::StatusExt;
6+
use core::cell::UnsafeCell;
7+
use core::fmt::Debug;
8+
use core::mem::{ManuallyDrop, MaybeUninit};
9+
use core::num::NonZeroUsize;
10+
use core::ptr::NonNull;
11+
use log::{error, trace};
12+
use uefi_raw::Status;
13+
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol;
14+
use uefi_raw::table::boot::PAGE_SIZE;
15+
16+
/// Smart pointer for wrapping owned pages allocated by PCI Root Bridge protocol.
17+
/// Value stored in this buffer maybe modified by a PCI device.
18+
///
19+
/// # Lifetime
20+
/// `'p` is the lifetime for Protocol.
21+
///
22+
/// # Invariant
23+
/// * Value stored in this memory cannot have a larger alignment requirement
24+
/// than page size, which is 4096.
25+
/// * Value stored in this memory cannot be larger than the buffer's size, which is 4096 * `pages`
26+
#[derive(Debug)]
27+
pub struct PciBuffer<'p, T> {
28+
pub(super) base: NonNull<UnsafeCell<T>>,
29+
pub(super) pages: NonZeroUsize,
30+
pub(super) proto: &'p PciRootBridgeIoProtocol,
31+
}
32+
33+
impl<'p, T> PciBuffer<'p, MaybeUninit<T>> {
34+
/// Assumes the contents of this buffer have been initialized.
35+
///
36+
/// # Safety
37+
/// Callers of this function must guarantee that the value stored is valid.
38+
#[must_use]
39+
pub const unsafe fn assume_init(self) -> PciBuffer<'p, T> {
40+
let initialized = PciBuffer {
41+
base: self.base.cast(),
42+
pages: self.pages,
43+
proto: self.proto,
44+
};
45+
let _ = ManuallyDrop::new(self);
46+
initialized
47+
}
48+
}
49+
50+
impl<'p, T> PciBuffer<'p, T> {
51+
/// Returns the base pointer of this buffer
52+
#[must_use]
53+
pub const fn base_ptr(&self) -> *mut T {
54+
self.base.as_ptr().cast()
55+
}
56+
57+
/// Returns the number of pages this buffer uses
58+
#[must_use]
59+
pub const fn pages(&self) -> NonZeroUsize {
60+
self.pages
61+
}
62+
63+
/// Returns the size of this buffer in bytes
64+
#[must_use]
65+
pub const fn bytes_size(&self) -> NonZeroUsize {
66+
self.pages
67+
.checked_mul(NonZeroUsize::new(PAGE_SIZE).unwrap())
68+
.expect("Memory size Overflow")
69+
}
70+
71+
/// Frees underlying memory of this buffer.
72+
/// It is recommended to use this over drop implementation.
73+
pub fn free(self) -> crate::Result {
74+
self.free_inner()
75+
}
76+
77+
fn free_inner(&self) -> crate::Result {
78+
unsafe { (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) }
79+
.to_result_with_val(|| {
80+
trace!(
81+
"Freed {} pages at 0x{:X}",
82+
self.pages,
83+
self.base.as_ptr().addr()
84+
)
85+
})
86+
}
87+
}
88+
89+
impl<T> Drop for PciBuffer<'_, T> {
90+
fn drop(&mut self) {
91+
let Err(status) = self.free_inner() else {
92+
return;
93+
};
94+
match status.status() {
95+
Status::SUCCESS => {}
96+
Status::INVALID_PARAMETER => {
97+
error!("PciBuffer was not created through valid protocol usage!")
98+
}
99+
etc => {
100+
error!("Unexpected error occurred when freeing memory: {:?}", etc);
101+
}
102+
}
103+
}
104+
}
Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,26 @@
33
//! PCI Root Bridge protocol.
44
55
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
6-
use crate::StatusExt;
76
#[cfg(feature = "alloc")]
87
use crate::proto::pci::configuration::QwordAddressSpaceDescriptor;
8+
use crate::proto::pci::root_bridge::buffer::PciBuffer;
9+
use crate::{Status, StatusExt};
910
#[cfg(feature = "alloc")]
1011
use alloc::vec::Vec;
1112
#[cfg(feature = "alloc")]
1213
use core::ffi::c_void;
14+
use core::mem::MaybeUninit;
15+
use core::num::NonZeroUsize;
1316
use core::ptr;
17+
use core::ptr::{NonNull, null_mut};
18+
use log::debug;
1419
use uefi_macros::unsafe_protocol;
15-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
20+
use uefi_raw::protocol::pci::root_bridge::{
21+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
22+
};
23+
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
1624

17-
#[cfg(doc)]
18-
use crate::Status;
25+
pub mod buffer;
1926

2027
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
2128
///
@@ -51,11 +58,98 @@ impl PciRootBridgeIo {
5158
unsafe { (self.0.flush)(&mut self.0).to_result() }
5259
}
5360

61+
/// Allocates pages suitable for communicating with PCI devices.
62+
///
63+
/// # Errors
64+
/// - [`Status::INVALID_PARAMETER`] MemoryType is invalid.
65+
/// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
66+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
67+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
68+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
69+
/// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
70+
pub fn allocate_buffer<T>(
71+
&self,
72+
memory_type: MemoryType,
73+
pages: Option<NonZeroUsize>,
74+
attributes: PciRootBridgeIoProtocolAttribute,
75+
) -> crate::Result<PciBuffer<'_, MaybeUninit<T>>> {
76+
let original_alignment = align_of::<T>();
77+
// TODO switch to const block once it lands on stable. These checks should be done in compile time.
78+
assert_ne!(original_alignment, 0);
79+
assert!(PAGE_SIZE >= original_alignment);
80+
assert_eq!(PAGE_SIZE % original_alignment, 0);
81+
82+
let alignment = PAGE_SIZE;
83+
84+
let pages = if let Some(pages) = pages {
85+
pages
86+
} else {
87+
let size = size_of::<T>();
88+
assert_ne!(size, 0);
89+
90+
NonZeroUsize::new(size.div_ceil(alignment)).unwrap()
91+
};
92+
let size = size_of::<T>();
93+
// TODO switch to const block once it lands on stable.
94+
assert!(pages.get() * PAGE_SIZE >= size);
95+
96+
let mut address: *mut T = null_mut();
97+
let status = unsafe {
98+
(self.0.allocate_buffer)(
99+
&self.0,
100+
AllocateType(0),
101+
memory_type,
102+
pages.get(),
103+
ptr::from_mut(&mut address).cast(),
104+
attributes.bits(),
105+
)
106+
};
107+
108+
match status {
109+
Status::SUCCESS => {
110+
let base = NonNull::new(address.cast()).unwrap();
111+
debug!("Allocated {} pages at 0x{:X}", pages.get(), address.addr());
112+
Ok(PciBuffer {
113+
base,
114+
pages,
115+
proto: &self.0,
116+
})
117+
}
118+
error
119+
@ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => {
120+
Err(error.into())
121+
}
122+
_ => unreachable!(),
123+
}
124+
}
125+
126+
/// Allocates pages suitable for communicating with PCI devices and initialize it right away.
127+
///
128+
/// # Errors
129+
/// Same as [`Self::allocate_buffer`]
130+
/// - [`Status::INVALID_PARAMETER`] MemoryType is invalid.
131+
/// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
132+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
133+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
134+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
135+
/// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
136+
pub fn allocate_buffer_init<T>(
137+
&self,
138+
memory_type: MemoryType,
139+
value: T,
140+
attributes: PciRootBridgeIoProtocolAttribute,
141+
) -> crate::Result<PciBuffer<'_, T>> {
142+
let buffer = self.allocate_buffer(memory_type, None, attributes)?;
143+
unsafe {
144+
buffer.base_ptr().write(MaybeUninit::new(value));
145+
Ok(buffer.assume_init())
146+
}
147+
}
148+
54149
// TODO: poll I/O
55150
// TODO: mem I/O access
56151
// TODO: io I/O access
57152
// TODO: map & unmap & copy memory
58-
// TODO: buffer management
59153
// TODO: get/set attributes
60154

61155
/// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors.

0 commit comments

Comments
 (0)