Skip to content

Commit 7bd8a9a

Browse files
committed
uefi: Use RefCell for allocate_buffer and map
1 parent f5b6b95 commit 7bd8a9a

File tree

5 files changed

+137
-30
lines changed

5 files changed

+137
-30
lines changed

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

33
use core::mem;
4+
use qcell::{QCell, QCellOwner};
45
use uefi::Handle;
56
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle};
67
use uefi::proto::ProtocolPointer;
78
use uefi::proto::pci::PciIoAddress;
89
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
9-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation};
10-
use uefi_raw::table::boot::MemoryType;
10+
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth};
11+
use uefi_raw::table::boot::{MemoryType, PAGE_SIZE};
1112

1213
const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4;
1314
const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1;
@@ -108,14 +109,54 @@ pub fn test_mapping() {
108109
buffer.assume_init()
109110
};
110111
let mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_COMMON_BUFFER64, buffer.as_ref());
111-
if mapped.region().0 == buffer.as_ptr().addr() as u64 {
112+
if mapped.region().device_address == buffer.as_ptr().addr() as u64 {
112113
info!("This PCI device uses identity mapping");
113114
} else {
114115
info!("This PCI device uses different mapping from CPU");
115116
}
116117
}
117118
}
118119

120+
pub fn test_copy() {
121+
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
122+
123+
for pci_handle in pci_handles {
124+
let mut owner = QCellOwner::new();
125+
let item = QCell::new(&owner, get_open_protocol::<PciRootBridgeIo>(pci_handle));
126+
let pci_proto = owner.rw(&item);
127+
128+
let mut src = pci_proto
129+
.allocate_buffer::<[u32; 4096 / 4]>(
130+
MemoryType::BOOT_SERVICES_DATA,
131+
None,
132+
PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE,
133+
)
134+
.unwrap();
135+
assert_eq!(size_of_val(src.as_ref()), size_of::<[u8; PAGE_SIZE]>());
136+
let src = unsafe {
137+
src.assume_init_mut().fill(0xDEADBEEF);
138+
src.assume_init()
139+
};
140+
let src_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_READ, src.as_ref());
141+
142+
let dst = pci_proto
143+
.allocate_buffer::<[u32; 4096 / 4]>(
144+
MemoryType::BOOT_SERVICES_DATA,
145+
None,
146+
PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE,
147+
)
148+
.unwrap();
149+
assert_eq!(size_of_val(dst.as_ref()), size_of::<[u8; PAGE_SIZE]>());
150+
let dst_mapped = pci_proto.map(PciRootBridgeIoProtocolOperation::BUS_MASTER_WRITE, dst.as_ref());
151+
152+
pci_proto.copy(PciRootBridgeIoProtocolWidth::UINT32, dst_mapped.region(), src_mapped.region()).unwrap();
153+
drop(dst_mapped);
154+
let dst = unsafe { dst.assume_init() };
155+
156+
assert!(dst.iter().all(|&b| b == 0xDEADBEEF));
157+
}
158+
}
159+
119160
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
120161
let open_opts = OpenProtocolParams {
121162
handle,

uefi/src/proto/pci/buffer.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use core::mem::{ManuallyDrop, MaybeUninit};
66
use core::num::NonZeroUsize;
77
use core::ops::{Deref, DerefMut};
8+
use core::ptr;
89
use core::ptr::NonNull;
910
use log::debug;
1011
use uefi_raw::Status;
@@ -13,12 +14,27 @@ use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol;
1314
/// Smart pointer for wrapping owned buffer allocated by PCI Root Bridge protocol.
1415
#[derive(Debug)]
1516
pub struct PciBuffer<'p, T> {
16-
pub(crate) base: NonNull<T>,
17-
pub(crate) pages: NonZeroUsize,
18-
pub(crate) proto: &'p PciRootBridgeIoProtocol,
17+
base: NonNull<T>,
18+
pages: NonZeroUsize,
19+
proto_lifetime: &'p (),
20+
proto: *const PciRootBridgeIoProtocol,
1921
}
2022

2123
impl<'p, T> PciBuffer<'p, MaybeUninit<T>> {
24+
25+
/// Creates wrapper for buffer allocated by PCI Root Bridge protocol.
26+
/// Passed protocol is stored as a pointer along with its lifetime so that it doesn't
27+
/// block others from using its mutable functions.
28+
#[must_use]
29+
pub const fn new(base: NonNull<MaybeUninit<T>>, pages: NonZeroUsize, proto: &'p PciRootBridgeIoProtocol) -> Self {
30+
Self {
31+
base,
32+
pages,
33+
proto_lifetime: &(),
34+
proto: ptr::from_ref(proto),
35+
}
36+
}
37+
2238
/// Assumes the contents of this buffer have been initialized.
2339
///
2440
/// # Safety
@@ -29,6 +45,7 @@ impl<'p, T> PciBuffer<'p, MaybeUninit<T>> {
2945
PciBuffer {
3046
base: old.base.cast(),
3147
pages: old.pages,
48+
proto_lifetime: old.proto_lifetime,
3249
proto: old.proto,
3350
}
3451
}
@@ -63,7 +80,8 @@ impl<'p, T> DerefMut for PciBuffer<'p, T> {
6380
impl<'p, T> Drop for PciBuffer<'p, T> {
6481
fn drop(&mut self) {
6582
let status = unsafe {
66-
(self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast())
83+
let proto = self.proto.as_ref().unwrap();
84+
(proto.free_buffer)(proto, self.pages.get(), self.base.as_ptr().cast())
6785
};
6886
match status {
6987
Status::SUCCESS => {

uefi/src/proto/pci/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use core::cmp::Ordering;
77
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth;
88

99
pub mod buffer;
10-
pub mod mapped_region;
10+
pub mod region;
1111
pub mod root_bridge;
1212

1313
/// IO Address for PCI/register IO operations

uefi/src/proto/pci/mapped_region.rs renamed to uefi/src/proto/pci/region.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,25 @@ pub struct PciMappedRegion<'p, 'r>
2121
where
2222
'p: 'r,
2323
{
24-
device_address: u64,
25-
length: usize,
24+
region: PciRegion,
2625
_lifetime_holder: &'r (),
2726
key: *const c_void,
2827
proto: &'p PciRootBridgeIoProtocol,
2928
}
3029

30+
/// Represents a region of memory in PCI root bridge memory space.
31+
/// CPU cannot use address in this struct to deference memory.
32+
/// This is effectively the same as rust's slice type.
33+
/// This type only exists to prevent users from accidentally dereferencing it.
34+
#[derive(Debug, Copy, Clone)]
35+
pub struct PciRegion {
36+
/// Starting address of the memory region
37+
pub device_address: u64,
38+
39+
/// Byte length of the memory region.
40+
pub length: usize
41+
}
42+
3143
impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r {
3244
pub(crate) fn new<T>(
3345
device_address: u64,
@@ -44,8 +56,10 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r {
4456
let end = device_address + length as u64;
4557
debug!("Mapped new region [0x{:X}..0x{:X}]", device_address, end);
4658
Self {
47-
device_address,
48-
length,
59+
region: PciRegion {
60+
device_address,
61+
length,
62+
},
4963
_lifetime_holder,
5064
key,
5165
proto,
@@ -58,8 +72,8 @@ impl<'p, 'r> PciMappedRegion<'p, 'r> where 'p: 'r {
5872
/// **Returned address cannot be used to reference memory from CPU!**
5973
/// **Do not cast it back to pointer or reference**
6074
#[must_use]
61-
pub const fn region(&self) -> (u64, usize) {
62-
(self.device_address, self.length)
75+
pub const fn region(&self) -> PciRegion {
76+
self.region
6377
}
6478
}
6579

@@ -68,8 +82,8 @@ impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> {
6882
let status = unsafe { (self.proto.unmap)(self.proto, self.key) };
6983
match status {
7084
Status::SUCCESS => {
71-
let end = self.device_address + self.length as u64;
72-
debug!("Region [0x{:X}..0x{:X}] was unmapped", self.device_address, end);
85+
let end = self.region.device_address + self.region.length as u64;
86+
debug!("Region [0x{:X}..0x{:X}] was unmapped", self.region.device_address, end);
7387
}
7488
Status::INVALID_PARAMETER => {
7589
panic!("This region was not mapped using PciRootBridgeIo::map");
@@ -81,3 +95,17 @@ impl<'p, 'r> Drop for PciMappedRegion<'p, 'r> {
8195
}
8296
}
8397
}
98+
99+
impl PciRegion {
100+
/// Creates a new region of memory with different length.
101+
/// The new region must have shorter length to ensure
102+
/// it won't contain invalid memory address.
103+
#[must_use]
104+
pub fn with_length(self, new_length: usize) -> Self {
105+
assert!(new_length <= self.length);
106+
Self {
107+
device_address: self.device_address,
108+
length: new_length,
109+
}
110+
}
111+
}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
66
use crate::StatusExt;
77
use crate::proto::pci::buffer::PciBuffer;
8-
use crate::proto::pci::mapped_region::PciMappedRegion;
8+
use crate::proto::pci::region::{PciMappedRegion, PciRegion};
99
use core::ffi::c_void;
1010
use core::mem::MaybeUninit;
1111
use core::num::NonZeroUsize;
@@ -14,10 +14,7 @@ use core::ptr::NonNull;
1414
use log::debug;
1515
use uefi_macros::unsafe_protocol;
1616
use uefi_raw::Status;
17-
use uefi_raw::protocol::pci::root_bridge::{
18-
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
19-
PciRootBridgeIoProtocolOperation,
20-
};
17+
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth};
2118
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
2219

2320
#[cfg(doc)]
@@ -60,12 +57,12 @@ impl PciRootBridgeIo {
6057
/// Allocates pages suitable for communicating with PCI devices.
6158
///
6259
/// # Errors
63-
/// - [`crate::Status::INVALID_PARAMETER`] MemoryType is invalid.
64-
/// - [`crate::Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
60+
/// - [`Status::INVALID_PARAMETER`] MemoryType is invalid.
61+
/// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
6562
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
6663
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
6764
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
68-
/// - [`crate::Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
65+
/// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
6966
pub fn allocate_buffer<T>(
7067
&self,
7168
memory_type: MemoryType,
@@ -104,11 +101,7 @@ impl PciRootBridgeIo {
104101
Status::SUCCESS => {
105102
let base = NonNull::new(address as *mut MaybeUninit<T>).unwrap();
106103
debug!("Allocated {} pages at 0x{:X}", pages.get(), address);
107-
Ok(PciBuffer {
108-
base,
109-
pages,
110-
proto: &self.0,
111-
})
104+
Ok(PciBuffer::new(base, pages, &self.0))
112105
}
113106
error
114107
@ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => {
@@ -158,10 +151,37 @@ impl PciRootBridgeIo {
158151
}
159152
}
160153

154+
/// Copies a region in PCI root bridge memory space onto the other.
155+
/// Two regions must have same length. Functionally, this is the same as
156+
/// `<[T]>::copy_from_slice` which is effectively memcpy.
157+
/// And the same safety requirements as the above method apply.
158+
pub fn copy(
159+
&mut self,
160+
width: PciRootBridgeIoProtocolWidth,
161+
destination: PciRegion,
162+
source: PciRegion,
163+
) -> crate::Result<()> {
164+
assert_eq!(destination.length, source.length);
165+
166+
let status = unsafe {
167+
(self.0.copy_mem)(
168+
&mut self.0,
169+
width,
170+
destination.device_address,
171+
source.device_address,
172+
destination.length,
173+
)
174+
};
175+
176+
match status {
177+
Status::SUCCESS => Ok(()),
178+
error => Err(error.into()),
179+
}
180+
}
181+
161182
// TODO: poll I/O
162183
// TODO: mem I/O access
163184
// TODO: io I/O access
164-
// TODO: copy memory
165185
// TODO: get/set attributes
166186
// TODO: configuration / resource settings
167187
}

0 commit comments

Comments
 (0)