Skip to content

Commit bf18795

Browse files
committed
uefi: Implement memory management and mapping functions
1 parent a6039ce commit bf18795

File tree

4 files changed

+293
-98
lines changed

4 files changed

+293
-98
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: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,29 @@
33
//! PCI Root Bridge protocol.
44
55
use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
6-
#[cfg(doc)]
76
use crate::Status;
87
use crate::StatusExt;
98
#[cfg(feature = "alloc")]
109
use crate::proto::pci::configuration::QwordAddressSpaceDescriptor;
10+
use crate::proto::pci::root_bridge::buffer::PciBuffer;
11+
use crate::proto::pci::root_bridge::region::PciMappedRegion;
1112
#[cfg(feature = "alloc")]
1213
use alloc::vec::Vec;
1314
#[cfg(feature = "alloc")]
1415
use core::ffi::c_void;
16+
use core::mem::MaybeUninit;
17+
use core::num::NonZeroUsize;
1518
use core::ptr;
19+
use core::ptr::{NonNull, null_mut};
20+
use log::debug;
1621
use uefi_macros::unsafe_protocol;
17-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
22+
use uefi_raw::protocol::pci::root_bridge::{
23+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
24+
PciRootBridgeIoProtocolOperation,
25+
};
26+
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
1827

19-
pub mod page;
28+
pub mod buffer;
2029
pub mod region;
2130

2231
/// Protocol that provides access to the PCI Root Bridge I/O protocol.
@@ -53,11 +62,142 @@ impl PciRootBridgeIo {
5362
unsafe { (self.0.flush)(&mut self.0).to_result() }
5463
}
5564

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