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