Skip to content

Commit 184dd4a

Browse files
committed
uefi: Implement memory management and mapping functions
1 parent 35bfa70 commit 184dd4a

File tree

4 files changed

+295
-101
lines changed

4 files changed

+295
-101
lines changed

uefi-raw/src/protocol/pci/root_bridge.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use crate::table::boot::{AllocateType, MemoryType};
44
use crate::{Handle, PhysicalAddress, Status, newtype_enum};
5+
use bitflags::bitflags;
56
use core::ffi::c_void;
67
use uguid::{Guid, guid};
78

@@ -37,6 +38,29 @@ newtype_enum! {
3738
}
3839
}
3940

41+
bitflags! {
42+
/// Describes PCI I/O Protocol Attribute bitflags specified in UEFI specification.
43+
/// https://uefi.org/specs/UEFI/2.10_A/14_Protocols_PCI_Bus_Support.html
44+
#[repr(transparent)]
45+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
46+
pub struct PciRootBridgeIoProtocolAttribute: u64 {
47+
const PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO = 0x0001;
48+
const PCI_ATTRIBUTE_ISA_IO = 0x0002;
49+
const PCI_ATTRIBUTE_VGA_PALETTE_IO = 0x0004;
50+
const PCI_ATTRIBUTE_VGA_MEMORY = 0x0008;
51+
const PCI_ATTRIBUTE_VGA_IO = 0x0010;
52+
const PCI_ATTRIBUTE_IDE_PRIMARY_IO = 0x0020;
53+
const PCI_ATTRIBUTE_IDE_SECONDARY_IO = 0x0040;
54+
const PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE = 0x0080;
55+
const PCI_ATTRIBUTE_MEMORY_CACHED = 0x0800;
56+
const PCI_ATTRIBUTE_MEMORY_DISABLE = 0x1000;
57+
const PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE = 0x8000;
58+
const PCI_ATTRIBUTE_ISA_IO_16 = 0x10000;
59+
const PCI_ATTRIBUTE_VGA_PALETTE_IO_16 = 0x20000;
60+
const PCI_ATTRIBUTE_VGA_IO_16 = 0x40000;
61+
}
62+
}
63+
4064
#[derive(Debug)]
4165
#[repr(C)]
4266
pub struct PciRootBridgeIoAccess {
Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

33
//! Defines wrapper for pages allocated by PCI Root Bridge protocol.
4+
5+
use crate::StatusExt;
6+
use core::cell::UnsafeCell;
47
use core::fmt::Debug;
58
use core::mem::{ManuallyDrop, MaybeUninit};
69
use core::num::NonZeroUsize;
7-
use core::ops::{Deref, DerefMut};
810
use core::ptr::NonNull;
911
use log::{error, trace};
1012
use uefi_raw::Status;
1113
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocol;
14+
use uefi_raw::table::boot::PAGE_SIZE;
1215

1316
/// Smart pointer for wrapping owned pages allocated by PCI Root Bridge protocol.
17+
/// Value stored in this buffer maybe modified by a PCI device.
1418
///
1519
/// # Lifetime
1620
/// `'p` is the lifetime for Protocol.
21+
///
22+
/// # Invariant
23+
/// Value stored in this memory cannot have a larger alignment requirement than page size
24+
/// which is 4096.
25+
///
26+
/// # Safety
27+
/// Value stored in this memory cannot be larger than the buffer's size, which is 4096 * `pages`
1728
#[derive(Debug)]
1829
pub struct PciBuffer<'p, T> {
19-
pub(super) base: NonNull<T>,
30+
pub(super) base: NonNull<UnsafeCell<T>>,
2031
pub(super) pages: NonZeroUsize,
2132
pub(super) proto: &'p PciRootBridgeIoProtocol,
2233
}
@@ -39,63 +50,56 @@ impl<'p, T> PciBuffer<'p, MaybeUninit<T>> {
3950
}
4051

4152
impl<'p, T> PciBuffer<'p, T> {
42-
/// Returns the base address of this buffer
53+
/// Returns the base pointer of this buffer
4354
#[must_use]
44-
pub fn base(&self) -> NonZeroUsize {
45-
self.base.addr()
55+
pub const fn base_ptr(&self) -> *mut T {
56+
self.base.as_ptr().cast()
4657
}
4758

4859
/// Returns the number of pages this buffer uses
4960
#[must_use]
5061
pub const fn pages(&self) -> NonZeroUsize {
5162
self.pages
5263
}
53-
}
5464

55-
impl<T> AsRef<T> for PciBuffer<'_, T> {
56-
fn as_ref(&self) -> &T {
57-
unsafe { self.base.as_ref() }
65+
/// Returns the size of this buffer in bytes
66+
#[must_use]
67+
pub const fn bytes_size(&self) -> NonZeroUsize {
68+
self.pages
69+
.checked_mul(NonZeroUsize::new(PAGE_SIZE).unwrap())
70+
.expect("Memory size Overflow")
5871
}
59-
}
6072

61-
impl<T> AsMut<T> for PciBuffer<'_, T> {
62-
fn as_mut(&mut self) -> &mut T {
63-
unsafe { self.base.as_mut() }
73+
/// Frees underlying memory of this buffer.
74+
/// It is recommended to use this over drop implementation.
75+
pub fn free(self) -> crate::Result {
76+
self.free_inner()
6477
}
65-
}
6678

67-
impl<T> Deref for PciBuffer<'_, T> {
68-
type Target = T;
69-
70-
fn deref(&self) -> &Self::Target {
71-
self.as_ref()
72-
}
73-
}
74-
75-
impl<T> DerefMut for PciBuffer<'_, T> {
76-
fn deref_mut(&mut self) -> &mut Self::Target {
77-
self.as_mut()
79+
fn free_inner(&self) -> crate::Result {
80+
unsafe { (self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast()) }
81+
.to_result_with_val(|| {
82+
trace!(
83+
"Freed {} pages at 0x{:X}",
84+
self.pages,
85+
self.base.as_ptr().addr()
86+
)
87+
})
7888
}
7989
}
8090

8191
impl<T> Drop for PciBuffer<'_, T> {
8292
fn drop(&mut self) {
83-
let status = unsafe {
84-
(self.proto.free_buffer)(self.proto, self.pages.get(), self.base.as_ptr().cast())
93+
let Err(status) = self.free_inner() else {
94+
return;
8595
};
86-
match status {
87-
Status::SUCCESS => {
88-
trace!(
89-
"Freed {} pages at 0x{:X}",
90-
self.pages.get(),
91-
self.base.as_ptr().addr()
92-
);
93-
}
96+
match status.status() {
97+
Status::SUCCESS => {}
9498
Status::INVALID_PARAMETER => {
9599
error!("PciBuffer was not created through valid protocol usage!")
96100
}
97101
etc => {
98-
error!("Failed to free PciBuffer: {:?}", etc);
102+
error!("Unexpected error occurred when freeing memory: {:?}", etc);
99103
}
100104
}
101105
}

uefi/src/proto/pci/root_bridge/mod.rs

Lines changed: 147 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,27 @@
33
//! PCI Root Bridge protocol.
44
55
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
6-
#[cfg(doc)]
7-
use crate::Status;
8-
use crate::StatusExt;
96
#[cfg(feature = "alloc")]
107
use crate::proto::pci::configuration::QwordAddressSpaceDescriptor;
8+
use crate::proto::pci::root_bridge::buffer::PciBuffer;
9+
use crate::proto::pci::root_bridge::region::PciMappedRegion;
10+
use crate::{Status, StatusExt};
1111
#[cfg(feature = "alloc")]
1212
use alloc::vec::Vec;
13-
#[cfg(feature = "alloc")]
1413
use core::ffi::c_void;
14+
use core::mem::MaybeUninit;
15+
use core::num::NonZeroUsize;
1516
use core::ptr;
17+
use core::ptr::{NonNull, null_mut};
18+
use log::debug;
1619
use uefi_macros::unsafe_protocol;
17-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
20+
use uefi_raw::protocol::pci::root_bridge::{
21+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
22+
PciRootBridgeIoProtocolOperation,
23+
};
24+
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
1825

19-
pub mod page;
26+
pub mod buffer;
2027
pub mod region;
2128

2229
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
@@ -53,11 +60,143 @@ impl PciRootBridgeIo {
5360
unsafe { (self.0.flush)(&mut self.0).to_result() }
5461
}
5562

63+
/// Allocates pages suitable for communicating with PCI devices.
64+
///
65+
/// # Errors
66+
/// - [`Status::INVALID_PARAMETER`] MemoryType is invalid.
67+
/// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
68+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
69+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
70+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
71+
/// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
72+
pub fn allocate_buffer<T>(
73+
&self,
74+
memory_type: MemoryType,
75+
pages: Option<NonZeroUsize>,
76+
attributes: PciRootBridgeIoProtocolAttribute,
77+
) -> crate::Result<PciBuffer<'_, MaybeUninit<T>>> {
78+
let original_alignment = align_of::<T>();
79+
// TODO switch to const block once it lands on stable. These checks should be done in compile time.
80+
assert_ne!(original_alignment, 0);
81+
assert!(PAGE_SIZE >= original_alignment);
82+
assert_eq!(PAGE_SIZE % original_alignment, 0);
83+
84+
let alignment = PAGE_SIZE;
85+
86+
let pages = if let Some(pages) = pages {
87+
pages
88+
} else {
89+
let size = size_of::<T>();
90+
assert_ne!(size, 0);
91+
92+
NonZeroUsize::new(size.div_ceil(alignment)).unwrap()
93+
};
94+
let size = size_of::<T>();
95+
// TODO switch to const block once it lands on stable.
96+
assert!(pages.get() * PAGE_SIZE >= size);
97+
98+
let mut address: *mut T = null_mut();
99+
let status = unsafe {
100+
(self.0.allocate_buffer)(
101+
&self.0,
102+
AllocateType(0),
103+
memory_type,
104+
pages.get(),
105+
ptr::from_mut(&mut address).cast(),
106+
attributes.bits(),
107+
)
108+
};
109+
110+
match status {
111+
Status::SUCCESS => {
112+
let base = NonNull::new(address.cast()).unwrap();
113+
debug!("Allocated {} pages at 0x{:X}", pages.get(), address.addr());
114+
Ok(PciBuffer {
115+
base,
116+
pages,
117+
proto: &self.0,
118+
})
119+
}
120+
error
121+
@ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => {
122+
Err(error.into())
123+
}
124+
_ => unreachable!(),
125+
}
126+
}
127+
128+
/// Allocates pages suitable for communicating with PCI devices and initialize it right away.
129+
///
130+
/// # Errors
131+
/// Same as [`Self::allocate_buffer`]
132+
/// - [`Status::INVALID_PARAMETER`] MemoryType is invalid.
133+
/// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are:
134+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`]
135+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`]
136+
/// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`]
137+
/// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated.
138+
pub fn allocate_buffer_init<T>(
139+
&self,
140+
memory_type: MemoryType,
141+
value: T,
142+
attributes: PciRootBridgeIoProtocolAttribute,
143+
) -> crate::Result<PciBuffer<'_, T>> {
144+
let buffer = self.allocate_buffer(memory_type, None, attributes)?;
145+
unsafe {
146+
buffer.base_ptr().write(MaybeUninit::new(value));
147+
Ok(buffer.assume_init())
148+
}
149+
}
150+
151+
/// Map the given buffer into a PCI Controller-specific address
152+
/// so that devices can read system memory through it.
153+
///
154+
/// # Arguments
155+
/// - `operation` - Indicates if bus master is going to read, write, or do both to the buffer.
156+
/// - `to_map` - Buffer to map.
157+
///
158+
/// # Returns
159+
/// An mapped region and unmapped bytes. It can map up to one DMA operation. Meaning large values
160+
/// will require multiple calls to this function.
161+
pub fn map<'p: 'r, 'r, T>(
162+
&'p self,
163+
operation: PciRootBridgeIoProtocolOperation,
164+
to_map: &'r PciBuffer<'p, T>,
165+
) -> crate::Result<(PciMappedRegion<'p, 'r, T>, usize)> {
166+
let host_address = to_map.base_ptr();
167+
let mut bytes = size_of::<T>();
168+
let requested_bytes = bytes;
169+
let mut device_address = 0usize;
170+
let mut mapping: *mut c_void = null_mut();
171+
172+
let status = unsafe {
173+
(self.0.map)(
174+
&self.0,
175+
operation,
176+
host_address.cast(),
177+
ptr::from_mut(&mut bytes),
178+
ptr::from_mut(&mut device_address).cast(),
179+
ptr::from_mut(&mut mapping),
180+
)
181+
};
182+
183+
status.to_result_with_val(|| {
184+
let left_over = requested_bytes - bytes;
185+
let region = PciMappedRegion {
186+
host: to_map,
187+
length: 0,
188+
device_address,
189+
key: mapping,
190+
proto: &self.0,
191+
};
192+
(region, left_over)
193+
})
194+
}
195+
56196
// TODO: poll I/O
57197
// TODO: mem I/O access
58198
// TODO: io I/O access
59-
// TODO: map & unmap & copy memory
60-
// TODO: buffer management
199+
// TODO: copy memory
61200
// TODO: get/set attributes
62201

63202
/// 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)