Skip to content

Commit dce1ba3

Browse files
committed
uefi: Implement memory management and mapping functions
1 parent 3bfded1 commit dce1ba3

File tree

4 files changed

+295
-100
lines changed

4 files changed

+295
-100
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 & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@
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;
1313
#[cfg(feature = "alloc")]
1414
use core::ffi::c_void;
15+
use core::mem::MaybeUninit;
16+
use core::num::NonZeroUsize;
1517
use core::ptr;
18+
use core::ptr::{NonNull, null_mut};
19+
use log::debug;
1620
use uefi_macros::unsafe_protocol;
17-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
21+
use uefi_raw::protocol::pci::root_bridge::{
22+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
23+
PciRootBridgeIoProtocolOperation,
24+
};
25+
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
1826

19-
pub mod page;
27+
pub mod buffer;
2028
pub mod region;
2129

2230
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
@@ -53,11 +61,143 @@ impl PciRootBridgeIo {
5361
unsafe { (self.0.flush)(&mut self.0).to_result() }
5462
}
5563

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

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