• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Bundles all relevant types and helpers to work with the UEFI memory map.
2 //!
3 //! To work with the memory map, you should use one of the structs
4 //! [`MemoryMapOwned`], [`MemoryMapRef`], or [`MemoryMapRefMut`] - depending on
5 //! your use-case. The  traits [`MemoryMap`] and [`MemoryMapMut`] mainly exist
6 //! to guarantee a streamlined API across these types. We recommend to work with
7 //! the specific implementation.
8 //!
9 //! # Usecase: Obtain UEFI Memory Map
10 //!
11 //! You can use [`boot::exit_boot_services`] or
12 //! [`boot::memory_map`], which returns an properly initialized
13 //! [`MemoryMapOwned`].
14 //!
15 //! # Usecase: Parse Memory Slice as UEFI Memory Map
16 //!
17 //! If you have a chunk of memory and want to parse it as UEFI memory map, which
18 //! might be the case if a bootloader such as GRUB or Limine passes its boot
19 //! information, you can use [`MemoryMapRef`] or [`MemoryMapRefMut`].
20 //!
21 //! # All relevant exports:
22 //!
23 //! - the traits [`MemoryMap`] and [`MemoryMapMut`],
24 //! - the trait implementations [`MemoryMapOwned`], [`MemoryMapRef`], and
25 //!   [`MemoryMapRefMut`],
26 //! - the iterator [`MemoryMapIter`]
27 //! - various associated helper types, such as [`MemoryMapKey`] and
28 //!   [`MemoryMapMeta`],
29 //! - re-exports [`MemoryDescriptor`], [`MemoryType`], and [`MemoryAttribute`].
30 //!
31 //! [`boot::exit_boot_services`]: crate::boot::exit_boot_services
32 //! [`boot::memory_map`]: crate::boot::memory_map
33 
34 mod api;
35 mod impl_;
36 mod iter;
37 
38 pub use api::*;
39 pub use impl_::*;
40 pub use iter::*;
41 pub use uefi_raw::table::boot::{MemoryAttribute, MemoryDescriptor, MemoryType};
42 
43 use crate::data_types::Align;
44 use core::mem;
45 
46 impl Align for MemoryDescriptor {
alignment() -> usize47     fn alignment() -> usize {
48         mem::align_of::<Self>()
49     }
50 }
51 
52 /// A unique identifier of a UEFI memory map, used to tell the firmware that one
53 /// has the latest valid memory map when exiting boot services.
54 ///
55 /// If the memory map changes, due to any allocation or deallocation, this value
56 /// is no longer valid, and exiting boot services will fail.
57 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
58 #[repr(C)]
59 pub struct MemoryMapKey(pub(crate) usize);
60 
61 /// A structure containing the meta attributes associated with a call to
62 /// `GetMemoryMap` of UEFI. Note that all values refer to the time this was
63 /// called. All following invocations (hidden, subtle, and asynchronous ones)
64 /// will likely invalidate this.
65 #[derive(Copy, Clone, Debug)]
66 pub struct MemoryMapMeta {
67     /// The actual size of the map.
68     pub map_size: usize,
69     /// The reported memory descriptor size. Note that this is the reference
70     /// and never `size_of::<MemoryDescriptor>()`!
71     pub desc_size: usize,
72     /// A unique memory key bound to a specific memory map version/state.
73     pub map_key: MemoryMapKey,
74     /// The version of the descriptor struct.
75     pub desc_version: u32,
76 }
77 
78 impl MemoryMapMeta {
79     /// Returns the amount of entries in the map.
80     #[must_use]
entry_count(&self) -> usize81     pub fn entry_count(&self) -> usize {
82         assert_eq!(self.map_size % self.desc_size, 0);
83         self.map_size / self.desc_size
84     }
85 
86     /// Runs some sanity assertions.
assert_sanity_checks(&self)87     pub fn assert_sanity_checks(&self) {
88         assert!(self.desc_size > 0);
89         // Although very unlikely, this might fail if the memory descriptor is
90         // extended by a future UEFI revision by a significant amount, we
91         // update the struct, but an old UEFI implementation reports a small
92         // size.
93         assert!(self.desc_size >= mem::size_of::<MemoryDescriptor>());
94         assert!(self.map_size > 0);
95 
96         // Ensure the mmap size is (somehow) sane.
97         const ONE_GB: usize = 1024 * 1024 * 1024;
98         assert!(self.map_size <= ONE_GB);
99     }
100 }
101 
102 /// Comprehensive unit test of the memory map functionality with the simplified
103 /// data. Here, `desc_size` equals `size_of::<MemoryDescriptor`.
104 #[cfg(test)]
105 mod tests_mmap_artificial {
106     use super::*;
107     use core::mem::{size_of, size_of_val};
108 
buffer_to_map(buffer: &mut [MemoryDescriptor]) -> MemoryMapRefMut109     fn buffer_to_map(buffer: &mut [MemoryDescriptor]) -> MemoryMapRefMut {
110         let mmap_len = size_of_val(buffer);
111         let mmap = {
112             unsafe { core::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, mmap_len) }
113         };
114 
115         MemoryMapRefMut::new(
116             mmap,
117             MemoryMapMeta {
118                 map_size: mmap_len,
119                 desc_size: size_of::<MemoryDescriptor>(),
120                 map_key: Default::default(),
121                 desc_version: MemoryDescriptor::VERSION,
122             },
123         )
124         .unwrap()
125     }
126 
127     #[test]
mem_map_sorting()128     fn mem_map_sorting() {
129         // Doesn't matter what type it is.
130         const TY: MemoryType = MemoryType::RESERVED;
131 
132         const BASE: MemoryDescriptor = MemoryDescriptor {
133             ty: TY,
134             phys_start: 0,
135             virt_start: 0,
136             page_count: 0,
137             att: MemoryAttribute::empty(),
138         };
139 
140         let mut buffer = [
141             MemoryDescriptor {
142                 phys_start: 2000,
143                 ..BASE
144             },
145             MemoryDescriptor {
146                 phys_start: 3000,
147                 ..BASE
148             },
149             BASE,
150             MemoryDescriptor {
151                 phys_start: 1000,
152                 ..BASE
153             },
154         ];
155 
156         let mut mem_map = buffer_to_map(&mut buffer);
157 
158         mem_map.sort();
159 
160         if !is_sorted(&mem_map.entries()) {
161             panic!("mem_map is not sorted: {:?}", mem_map);
162         }
163     }
164 
165     #[test]
mem_map_get()166     fn mem_map_get() {
167         // Doesn't matter what type it is.
168         const TY: MemoryType = MemoryType::RESERVED;
169 
170         const BASE: MemoryDescriptor = MemoryDescriptor {
171             ty: TY,
172             phys_start: 0,
173             virt_start: 0,
174             page_count: 0,
175             att: MemoryAttribute::empty(),
176         };
177 
178         const BUFFER: [MemoryDescriptor; 4] = [
179             MemoryDescriptor {
180                 phys_start: 2000,
181                 ..BASE
182             },
183             MemoryDescriptor {
184                 phys_start: 3000,
185                 ..BASE
186             },
187             BASE,
188             MemoryDescriptor {
189                 phys_start: 1000,
190                 ..BASE
191             },
192         ];
193 
194         let mut buffer = BUFFER;
195 
196         let mut mem_map = buffer_to_map(&mut buffer);
197 
198         for index in 0..3 {
199             assert_eq!(mem_map.get(index), BUFFER.get(index));
200 
201             // Test Index impl
202             assert_eq!(Some(&mem_map[index]), BUFFER.get(index));
203         }
204 
205         let mut_desc = mem_map.get_mut(2).unwrap();
206 
207         mut_desc.phys_start = 300;
208 
209         let desc = mem_map.get(2).unwrap();
210 
211         assert_ne!(*desc, BUFFER[2]);
212     }
213 
is_sorted(iter: &MemoryMapIter) -> bool214     fn is_sorted(iter: &MemoryMapIter) -> bool {
215         let mut iter = iter.clone();
216         let mut curr_start;
217 
218         if let Some(val) = iter.next() {
219             curr_start = val.phys_start;
220         } else {
221             return true;
222         }
223 
224         for desc in iter {
225             if desc.phys_start <= curr_start {
226                 return false;
227             }
228             curr_start = desc.phys_start
229         }
230         true
231     }
232 }
233 
234 /// Comprehensive unit test of the memory map functionality with the data from a
235 /// real UEFI memory map. The important property that we test here is that
236 /// the reported `desc_size` doesn't equal `size_of::<MemoryDescriptor`.
237 #[cfg(test)]
238 mod tests_mmap_real {
239     use super::*;
240     use alloc::vec::Vec;
241     use core::mem::size_of;
242     use core::slice;
243 
244     const MMAP_META: MemoryMapMeta = MemoryMapMeta {
245         map_size: MMAP_RAW.len() * size_of::<u64>(),
246         desc_size: 48,
247         map_key: MemoryMapKey(0),
248         desc_version: 1,
249     };
250     /// Sample with 10 entries of a real UEFI memory map extracted from our
251     /// UEFI test runner.
252     const MMAP_RAW: [u64; 60] = [
253         3, 0, 0, 1, 15, 0, 7, 4096, 0, 134, 15, 0, 4, 552960, 0, 1, 15, 0, 7, 557056, 0, 24, 15, 0,
254         7, 1048576, 0, 1792, 15, 0, 10, 8388608, 0, 8, 15, 0, 7, 8421376, 0, 3, 15, 0, 10, 8433664,
255         0, 1, 15, 0, 7, 8437760, 0, 4, 15, 0, 10, 8454144, 0, 240, 15, 0,
256     ];
257 
258     #[test]
basic_functionality()259     fn basic_functionality() {
260         let mut buf = MMAP_RAW;
261         let buf =
262             unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<u8>(), MMAP_META.map_size) };
263         let mut mmap = MemoryMapRefMut::new(buf, MMAP_META).unwrap();
264 
265         assert!(mmap.is_sorted());
266         mmap.sort();
267         assert!(mmap.is_sorted());
268 
269         let entries = mmap.entries().copied().collect::<Vec<_>>();
270 
271         let expected = [
272             MemoryDescriptor {
273                 ty: MemoryType::BOOT_SERVICES_CODE,
274                 phys_start: 0x0,
275                 virt_start: 0x0,
276                 page_count: 0x1,
277                 att: MemoryAttribute::UNCACHEABLE
278                     | MemoryAttribute::WRITE_COMBINE
279                     | MemoryAttribute::WRITE_THROUGH
280                     | MemoryAttribute::WRITE_BACK,
281             },
282             MemoryDescriptor {
283                 ty: MemoryType::CONVENTIONAL,
284                 phys_start: 0x1000,
285                 virt_start: 0x0,
286                 page_count: 0x86,
287                 att: MemoryAttribute::UNCACHEABLE
288                     | MemoryAttribute::WRITE_COMBINE
289                     | MemoryAttribute::WRITE_THROUGH
290                     | MemoryAttribute::WRITE_BACK,
291             },
292             MemoryDescriptor {
293                 ty: MemoryType::BOOT_SERVICES_DATA,
294                 phys_start: 0x87000,
295                 virt_start: 0x0,
296                 page_count: 0x1,
297                 att: MemoryAttribute::UNCACHEABLE
298                     | MemoryAttribute::WRITE_COMBINE
299                     | MemoryAttribute::WRITE_THROUGH
300                     | MemoryAttribute::WRITE_BACK,
301             },
302             MemoryDescriptor {
303                 ty: MemoryType::CONVENTIONAL,
304                 phys_start: 0x88000,
305                 virt_start: 0x0,
306                 page_count: 0x18,
307                 att: MemoryAttribute::UNCACHEABLE
308                     | MemoryAttribute::WRITE_COMBINE
309                     | MemoryAttribute::WRITE_THROUGH
310                     | MemoryAttribute::WRITE_BACK,
311             },
312             MemoryDescriptor {
313                 ty: MemoryType::CONVENTIONAL,
314                 phys_start: 0x100000,
315                 virt_start: 0x0,
316                 page_count: 0x700,
317                 att: MemoryAttribute::UNCACHEABLE
318                     | MemoryAttribute::WRITE_COMBINE
319                     | MemoryAttribute::WRITE_THROUGH
320                     | MemoryAttribute::WRITE_BACK,
321             },
322             MemoryDescriptor {
323                 ty: MemoryType::ACPI_NON_VOLATILE,
324                 phys_start: 0x800000,
325                 virt_start: 0x0,
326                 page_count: 0x8,
327                 att: MemoryAttribute::UNCACHEABLE
328                     | MemoryAttribute::WRITE_COMBINE
329                     | MemoryAttribute::WRITE_THROUGH
330                     | MemoryAttribute::WRITE_BACK,
331             },
332             MemoryDescriptor {
333                 ty: MemoryType::CONVENTIONAL,
334                 phys_start: 0x808000,
335                 virt_start: 0x0,
336                 page_count: 0x3,
337                 att: MemoryAttribute::UNCACHEABLE
338                     | MemoryAttribute::WRITE_COMBINE
339                     | MemoryAttribute::WRITE_THROUGH
340                     | MemoryAttribute::WRITE_BACK,
341             },
342             MemoryDescriptor {
343                 ty: MemoryType::ACPI_NON_VOLATILE,
344                 phys_start: 0x80b000,
345                 virt_start: 0x0,
346                 page_count: 0x1,
347                 att: MemoryAttribute::UNCACHEABLE
348                     | MemoryAttribute::WRITE_COMBINE
349                     | MemoryAttribute::WRITE_THROUGH
350                     | MemoryAttribute::WRITE_BACK,
351             },
352             MemoryDescriptor {
353                 ty: MemoryType::CONVENTIONAL,
354                 phys_start: 0x80c000,
355                 virt_start: 0x0,
356                 page_count: 0x4,
357                 att: MemoryAttribute::UNCACHEABLE
358                     | MemoryAttribute::WRITE_COMBINE
359                     | MemoryAttribute::WRITE_THROUGH
360                     | MemoryAttribute::WRITE_BACK,
361             },
362             MemoryDescriptor {
363                 ty: MemoryType::ACPI_NON_VOLATILE,
364                 phys_start: 0x810000,
365                 virt_start: 0x0,
366                 page_count: 0xf0,
367                 att: MemoryAttribute::UNCACHEABLE
368                     | MemoryAttribute::WRITE_COMBINE
369                     | MemoryAttribute::WRITE_THROUGH
370                     | MemoryAttribute::WRITE_BACK,
371             },
372         ];
373         assert_eq!(entries.as_slice(), &expected);
374     }
375 }
376