Skip to content

Commit 6b730d5

Browse files
authored
Merge pull request rust-osdev#662 from JarlEvanson/issue#661
Add Sorted Iterator for the UEFI Memory Map (Issue#661)
2 parents 4728db0 + bbc030a commit 6b730d5

File tree

3 files changed

+224
-20
lines changed

3 files changed

+224
-20
lines changed

uefi-test-runner/src/boot/memory.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,28 @@ fn memory_map(bt: &BootServices) {
9191
// We will use vectors for convenience.
9292
let mut buffer = vec![0_u8; buf_sz];
9393

94-
let (_key, desc_iter) = bt
94+
let mut memory_map = bt
9595
.memory_map(&mut buffer)
9696
.expect("Failed to retrieve UEFI memory map");
9797

98+
memory_map.sort();
99+
98100
// Collect the descriptors into a vector
99-
let descriptors = desc_iter.copied().collect::<Vec<_>>();
101+
let descriptors = memory_map.entries().copied().collect::<Vec<_>>();
100102

101103
// Ensured we have at least one entry.
102104
// Real memory maps usually have dozens of entries.
103105
assert!(!descriptors.is_empty(), "Memory map is empty");
104106

107+
let mut curr_value = descriptors[0];
108+
109+
for value in descriptors.iter().skip(1) {
110+
if value.phys_start <= curr_value.phys_start {
111+
panic!("memory map sorting failed");
112+
}
113+
curr_value = *value;
114+
}
115+
105116
// This is pretty much a sanity test to ensure returned memory isn't filled with random values.
106117
let first_desc = descriptors[0];
107118

uefi/src/table/boot.rs

+205-12
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,7 @@ impl BootServices {
425425
///
426426
/// * [`uefi::Status::BUFFER_TOO_SMALL`]
427427
/// * [`uefi::Status::INVALID_PARAMETER`]
428-
pub fn memory_map<'buf>(
429-
&self,
430-
buffer: &'buf mut [u8],
431-
) -> Result<(MemoryMapKey, MemoryMapIter<'buf>)> {
428+
pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result<MemoryMap<'buf>> {
432429
let mut map_size = buffer.len();
433430
MemoryDescriptor::assert_aligned(buffer);
434431
let map_buffer = buffer.as_mut_ptr().cast::<MemoryDescriptor>();
@@ -453,13 +450,13 @@ impl BootServices {
453450
}
454451
.into_with_val(move || {
455452
let len = map_size / entry_size;
456-
let iter = MemoryMapIter {
457-
buffer,
453+
454+
MemoryMap {
455+
key: map_key,
456+
buf: buffer,
458457
entry_size,
459-
index: 0,
460458
len,
461-
};
462-
(map_key, iter)
459+
}
463460
})
464461
}
465462

@@ -1993,6 +1990,108 @@ pub struct MemoryMapSize {
19931990
pub map_size: usize,
19941991
}
19951992

1993+
/// An iterator of [`MemoryDescriptor`] that is always associated with the
1994+
/// unique [`MemoryMapKey`] contained in the struct.
1995+
///
1996+
/// To iterate over the entries, call [`MemoryMap::entries`]. To get a sorted
1997+
/// map, you manually have to call [`MemoryMap::sort`] first.
1998+
pub struct MemoryMap<'buf> {
1999+
key: MemoryMapKey,
2000+
buf: &'buf mut [u8],
2001+
entry_size: usize,
2002+
len: usize,
2003+
}
2004+
2005+
impl<'buf> MemoryMap<'buf> {
2006+
#[must_use]
2007+
/// Returns the unique [`MemoryMapKey`] associated with the memory map.
2008+
pub fn key(&self) -> MemoryMapKey {
2009+
self.key
2010+
}
2011+
2012+
/// Sorts the memory map by physical address in place.
2013+
/// This operation is optional and should be invoked only once.
2014+
pub fn sort(&mut self) {
2015+
unsafe {
2016+
self.qsort(0, self.len - 1);
2017+
}
2018+
}
2019+
2020+
/// Hoare partition scheme for quicksort.
2021+
/// Must be called with `low` and `high` being indices within bounds.
2022+
unsafe fn qsort(&mut self, low: usize, high: usize) {
2023+
if low >= high {
2024+
return;
2025+
}
2026+
2027+
let p = self.partition(low, high);
2028+
self.qsort(low, p);
2029+
self.qsort(p + 1, high);
2030+
}
2031+
2032+
unsafe fn partition(&mut self, low: usize, high: usize) -> usize {
2033+
let pivot = self.get_element_phys_addr(low + (high - low) / 2);
2034+
2035+
let mut left_index = low.wrapping_sub(1);
2036+
let mut right_index = high.wrapping_add(1);
2037+
2038+
loop {
2039+
while {
2040+
left_index = left_index.wrapping_add(1);
2041+
2042+
self.get_element_phys_addr(left_index) < pivot
2043+
} {}
2044+
2045+
while {
2046+
right_index = right_index.wrapping_sub(1);
2047+
2048+
self.get_element_phys_addr(right_index) > pivot
2049+
} {}
2050+
2051+
if left_index >= right_index {
2052+
return right_index;
2053+
}
2054+
2055+
self.swap(left_index, right_index);
2056+
}
2057+
}
2058+
2059+
/// Indices must be smaller than len.
2060+
unsafe fn swap(&mut self, index1: usize, index2: usize) {
2061+
if index1 == index2 {
2062+
return;
2063+
}
2064+
2065+
let base = self.buf.as_mut_ptr();
2066+
2067+
unsafe {
2068+
ptr::swap_nonoverlapping(
2069+
base.add(index1 * self.entry_size),
2070+
base.add(index2 * self.entry_size),
2071+
self.entry_size,
2072+
);
2073+
}
2074+
}
2075+
2076+
fn get_element_phys_addr(&self, index: usize) -> PhysicalAddress {
2077+
let offset = index.checked_mul(self.entry_size).unwrap();
2078+
let elem = unsafe { &*self.buf.as_ptr().add(offset).cast::<MemoryDescriptor>() };
2079+
elem.phys_start
2080+
}
2081+
2082+
/// Returns an iterator over the contained memory map. To get a sorted map,
2083+
/// call [`MemoryMap::sort`] first.
2084+
#[must_use]
2085+
pub fn entries(&self) -> MemoryMapIter {
2086+
MemoryMapIter {
2087+
buffer: self.buf,
2088+
entry_size: self.entry_size,
2089+
index: 0,
2090+
len: self.len,
2091+
}
2092+
}
2093+
}
2094+
19962095
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19972096
/// associated with a unique [`MemoryMapKey`].
19982097
#[derive(Debug, Clone)]
@@ -2014,12 +2113,16 @@ impl<'buf> Iterator for MemoryMapIter<'buf> {
20142113

20152114
fn next(&mut self) -> Option<Self::Item> {
20162115
if self.index < self.len {
2017-
let ptr = self.buffer.as_ptr() as usize + self.entry_size * self.index;
2116+
let descriptor = unsafe {
2117+
&*self
2118+
.buffer
2119+
.as_ptr()
2120+
.add(self.entry_size * self.index)
2121+
.cast::<MemoryDescriptor>()
2122+
};
20182123

20192124
self.index += 1;
20202125

2021-
let descriptor = unsafe { &*(ptr as *const MemoryDescriptor) };
2022-
20232126
Some(descriptor)
20242127
} else {
20252128
None
@@ -2197,3 +2300,93 @@ pub enum InterfaceType: i32 => {
21972300
#[derive(Debug, Clone, Copy)]
21982301
#[repr(transparent)]
21992302
pub struct ProtocolSearchKey(NonNull<c_void>);
2303+
2304+
#[cfg(test)]
2305+
mod tests {
2306+
use core::mem::size_of;
2307+
2308+
use crate::table::boot::{MemoryAttribute, MemoryMap, MemoryMapKey, MemoryType};
2309+
2310+
use super::{MemoryDescriptor, MemoryMapIter};
2311+
2312+
#[test]
2313+
fn mem_map_sorting() {
2314+
// Doesn't matter what type it is.
2315+
const TY: MemoryType = MemoryType::RESERVED;
2316+
2317+
const BASE: MemoryDescriptor = MemoryDescriptor {
2318+
ty: TY,
2319+
phys_start: 0,
2320+
virt_start: 0,
2321+
page_count: 0,
2322+
att: MemoryAttribute::empty(),
2323+
};
2324+
2325+
let mut buffer = [
2326+
MemoryDescriptor {
2327+
phys_start: 2000,
2328+
..BASE
2329+
},
2330+
MemoryDescriptor {
2331+
phys_start: 3000,
2332+
..BASE
2333+
},
2334+
BASE,
2335+
MemoryDescriptor {
2336+
phys_start: 1000,
2337+
..BASE
2338+
},
2339+
];
2340+
2341+
let desc_count = buffer.len();
2342+
2343+
let byte_buffer = {
2344+
let size = desc_count * size_of::<MemoryDescriptor>();
2345+
unsafe { core::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, size) }
2346+
};
2347+
2348+
let mut mem_map = MemoryMap {
2349+
// Key doesn't matter
2350+
key: MemoryMapKey(0),
2351+
len: desc_count,
2352+
buf: byte_buffer,
2353+
entry_size: size_of::<MemoryDescriptor>(),
2354+
};
2355+
2356+
mem_map.sort();
2357+
2358+
if !is_sorted(&mem_map.entries()) {
2359+
panic!("mem_map is not sorted: {}", mem_map);
2360+
}
2361+
}
2362+
2363+
// Added for debug purposes on test failure
2364+
impl core::fmt::Display for MemoryMap<'_> {
2365+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2366+
writeln!(f)?;
2367+
for desc in self.entries() {
2368+
writeln!(f, "{:?}", desc)?;
2369+
}
2370+
Ok(())
2371+
}
2372+
}
2373+
2374+
fn is_sorted(iter: &MemoryMapIter) -> bool {
2375+
let mut iter = iter.clone();
2376+
let mut curr_start;
2377+
2378+
if let Some(val) = iter.next() {
2379+
curr_start = val.phys_start;
2380+
} else {
2381+
return true;
2382+
}
2383+
2384+
for desc in iter {
2385+
if desc.phys_start <= curr_start {
2386+
return false;
2387+
}
2388+
curr_start = desc.phys_start
2389+
}
2390+
true
2391+
}
2392+
}

uefi/src/table/system.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use core::{ptr, slice};
77
use crate::proto::console::text;
88
use crate::{CStr16, Char16, Handle, Result, Status};
99

10-
use super::boot::{BootServices, MemoryDescriptor, MemoryMapIter, MemoryType};
10+
use super::boot::{BootServices, MemoryDescriptor, MemoryMap, MemoryType};
1111
use super::runtime::{ResetType, RuntimeServices};
1212
use super::{cfg, Header, Revision};
1313

@@ -158,20 +158,20 @@ impl SystemTable<Boot> {
158158
unsafe fn get_memory_map_and_exit_boot_services(
159159
&self,
160160
buf: &'static mut [u8],
161-
) -> Result<MemoryMapIter<'static>> {
161+
) -> Result<MemoryMap<'static>> {
162162
let boot_services = self.boot_services();
163163

164164
// Get the memory map.
165-
let (memory_map_key, memory_map_iter) = boot_services.memory_map(buf)?;
165+
let memory_map = boot_services.memory_map(buf)?;
166166

167167
// Try to exit boot services using the memory map key. Note that after
168168
// the first call to `exit_boot_services`, there are restrictions on
169169
// what boot services functions can be called. In UEFI 2.8 and earlier,
170170
// only `get_memory_map` and `exit_boot_services` are allowed. Starting
171171
// in UEFI 2.9 other memory allocation functions may also be called.
172172
boot_services
173-
.exit_boot_services(boot_services.image_handle(), memory_map_key)
174-
.map(move |()| memory_map_iter)
173+
.exit_boot_services(boot_services.image_handle(), memory_map.key())
174+
.map(move |()| memory_map)
175175
}
176176

177177
/// Exit the UEFI boot services.
@@ -212,7 +212,7 @@ impl SystemTable<Boot> {
212212
/// [`Logger::disable`]: crate::logger::Logger::disable
213213
/// [`uefi_services::init`]: https://docs.rs/uefi-services/latest/uefi_services/fn.init.html
214214
#[must_use]
215-
pub fn exit_boot_services(self) -> (SystemTable<Runtime>, MemoryMapIter<'static>) {
215+
pub fn exit_boot_services(self) -> (SystemTable<Runtime>, MemoryMap<'static>) {
216216
let boot_services = self.boot_services();
217217

218218
// Reboot the device.

0 commit comments

Comments
 (0)