1 // Copyright 2024, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Rust wrapper for `EFI_IMAGE_LOADING_PROTOCOL`. 16 17 use crate::efi_call; 18 use crate::protocol::{Protocol, ProtocolInfo}; 19 use arrayvec::ArrayVec; 20 use core::mem::{size_of, MaybeUninit}; 21 use efi_types::{ 22 EfiGuid, GblEfiImageBuffer, GblEfiImageInfo, GblEfiImageLoadingProtocol, GblEfiPartitionName, 23 PARTITION_NAME_LEN_U16, 24 }; 25 use liberror::{Error, Result}; 26 use spin::Mutex; 27 28 /// GBL_IMAGE_LOADING_PROTOCOL 29 pub struct GblImageLoadingProtocol; 30 31 impl ProtocolInfo for GblImageLoadingProtocol { 32 type InterfaceType = GblEfiImageLoadingProtocol; 33 34 const GUID: EfiGuid = 35 EfiGuid::new(0xdb84b4fa, 0x53bd, 0x4436, [0x98, 0xa7, 0x4e, 0x02, 0x71, 0x42, 0x8b, 0xa8]); 36 } 37 38 /// Max length of partition name in UTF8 in bytes. 39 pub const PARTITION_NAME_LEN_U8: usize = size_of::<char>() * PARTITION_NAME_LEN_U16; 40 41 const MAX_ARRAY_SIZE: usize = 100; 42 static RETURNED_BUFFERS: Mutex<ArrayVec<usize, MAX_ARRAY_SIZE>> = Mutex::new(ArrayVec::new_const()); 43 44 /// Wrapper class for buffer received with [get_buffer] function. 45 /// 46 /// Helps to keep track of allocated memory and avoid getting same buffer more than once. 47 #[derive(Debug)] 48 pub struct EfiImageBuffer { 49 buffer: Option<&'static mut [MaybeUninit<u8>]>, 50 } 51 52 /// Represents either static reserved memory space or memory to be allocated dynamically. 53 #[derive(Debug)] 54 pub enum EfiImageBufferInfo { 55 /// Static memory space returned from UEFI firmware. 56 Buffer(EfiImageBuffer), 57 /// Target buffer should be dynamically allocated by the given size. 58 AllocSize(usize), 59 } 60 61 impl EfiImageBufferInfo { 62 /// Gets as EfiImageBuffer::Buffer; buffer(&mut self) -> Option<&mut [MaybeUninit<u8>]>63 pub fn buffer(&mut self) -> Option<&mut [MaybeUninit<u8>]> { 64 match self { 65 Self::Buffer(EfiImageBuffer { buffer: Some(v) }) => Some(v), 66 _ => None, 67 } 68 } 69 70 /// Move buffer ownership out of EfiImageBuffer, and consume it. take(self) -> Option<&'static mut [MaybeUninit<u8>]>71 pub fn take(self) -> Option<&'static mut [MaybeUninit<u8>]> { 72 match self { 73 Self::Buffer(mut v) => Some(v.take()), 74 _ => None, 75 } 76 } 77 } 78 79 impl EfiImageBuffer { 80 // # Safety 81 // 82 // `gbl_buffer` must represent valid buffer. 83 // 84 // # Return 85 // 86 // Err(EFI_STATUS_INVALID_PARAMETER) - If `gbl_buffer.Memory` == NULL 87 // Err(EFI_STATUS_ALREADY_STARTED) - Requested buffer was already returned and is still in use. 88 // Err(err) - on error 89 // Ok(_) - on success new(gbl_buffer: GblEfiImageBuffer) -> Result<EfiImageBuffer>90 unsafe fn new(gbl_buffer: GblEfiImageBuffer) -> Result<EfiImageBuffer> { 91 if gbl_buffer.Memory.is_null() { 92 return Err(Error::InvalidInput); 93 } 94 95 let addr = gbl_buffer.Memory as usize; 96 let mut returned_buffers = RETURNED_BUFFERS.lock(); 97 if returned_buffers.contains(&addr) { 98 return Err(Error::AlreadyStarted); 99 } 100 returned_buffers.push(addr); 101 102 // SAFETY: 103 // `gbl_buffer.Memory` is guaranteed to be not null 104 // This code is relying on EFI protocol implementation to provide valid buffer pointer 105 // to memory region of size `gbl_buffer.SizeBytes`. 106 Ok(EfiImageBuffer { 107 buffer: Some(unsafe { 108 core::slice::from_raw_parts_mut( 109 gbl_buffer.Memory as *mut MaybeUninit<u8>, 110 gbl_buffer.SizeBytes, 111 ) 112 }), 113 }) 114 } 115 116 /// Move buffer ownership out of EfiImageBuffer, and consume it. take(&mut self) -> &'static mut [MaybeUninit<u8>]117 pub fn take(&mut self) -> &'static mut [MaybeUninit<u8>] { 118 self.buffer.take().unwrap() 119 } 120 121 // Removes address from `RETURNED_BUFFERS`. 122 // 123 // # Safety 124 // 125 // Caller must guarantee that address is not referenced anymore. release(address: usize)126 unsafe fn release(address: usize) { 127 let mut returned_buffers = RETURNED_BUFFERS.lock(); 128 let res = returned_buffers.iter().position(|&val| val == address); 129 debug_assert!( 130 res.is_some(), 131 "EfiImageBuffer::release trying to release address ({address}) that is not tracked" 132 ); 133 if let Some(pos) = res { 134 returned_buffers.swap_remove(pos); 135 } 136 } 137 } 138 139 impl Drop for EfiImageBuffer { drop(&mut self)140 fn drop(&mut self) { 141 if self.buffer.is_none() { 142 return; 143 } 144 145 // SAFETY: 146 // EfiIMageBuffer is the only owner of the buffer. The only way to get address for it is to 147 // call `take()` which consumes `self.buffer`, which we check above. 148 unsafe { EfiImageBuffer::release((*self.buffer.as_ref().unwrap()).as_ptr() as usize) }; 149 } 150 } 151 152 // Protocol interface wrappers. 153 impl Protocol<'_, GblImageLoadingProtocol> { 154 /// Wrapper of `GBL_IMAGE_LOADING_PROTOCOL.get_buffer()` 155 /// 156 /// # Return 157 /// Ok(Some(EfiImageBuffer)) if buffer was successfully provided, 158 /// Ok(None) if buffer was not provided 159 /// Err(Error::EFI_STATUS_BUFFER_TOO_SMALL) if provided buffer is too small 160 /// Err(Error::EFI_STATUS_INVALID_PARAMETER) if received buffer is NULL 161 /// Err(Error::EFI_STATUS_ALREADY_STARTED) buffer was already returned and is still in use. 162 /// Err(err) if `err` occurred get_buffer(&self, gbl_image_info: &GblEfiImageInfo) -> Result<EfiImageBufferInfo>163 pub fn get_buffer(&self, gbl_image_info: &GblEfiImageInfo) -> Result<EfiImageBufferInfo> { 164 let mut gbl_buffer: GblEfiImageBuffer = Default::default(); 165 // SAFETY: 166 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 167 // established by `Protocol::new()`. 168 // `self.interface` and `gbl_buffer` are input/output parameters, outlive the call and 169 // will not be retained. 170 // `gbl_buffer` returned by this call must not overlap, and will be checked by 171 // `EfiImageBuffer` 172 unsafe { 173 efi_call!( 174 @bufsize gbl_image_info.SizeBytes, 175 self.interface()?.get_buffer, 176 self.interface, 177 gbl_image_info, 178 &mut gbl_buffer 179 )?; 180 } 181 182 if gbl_buffer.SizeBytes < gbl_image_info.SizeBytes { 183 return Err(Error::BufferTooSmall(Some(gbl_image_info.SizeBytes))); 184 } else if gbl_buffer.Memory.is_null() { 185 return Ok(EfiImageBufferInfo::AllocSize(gbl_buffer.SizeBytes)); 186 } 187 188 // SAFETY: 189 // `gbl_buffer.Memory` must be not null. This checked in `new()` call 190 // `gbl_buffer.Size` must be valid size of the buffer. 191 // This protocol is relying on EFI protocol implementation to provide valid buffer pointer 192 // to memory region of size `gbl_buffer.SizeBytes`. 193 let image_buffer = EfiImageBufferInfo::Buffer(unsafe { EfiImageBuffer::new(gbl_buffer)? }); 194 195 Ok(image_buffer) 196 } 197 198 /// Wrapper of `GBL_IMAGE_LOADING_PROTOCOL.get_verify_partitions()` 199 /// 200 /// # Result 201 /// Err(BufferTooSmall(Some(size))) - when provided `partition_names` is less than expected 202 /// `size` 203 /// Err(err) - if error occurred. 204 /// Ok(len) - will return number of `GblEfiPartitionName`s copied to `partition_names` slice. get_verify_partitions( &self, partition_names: &mut [GblEfiPartitionName], ) -> Result<usize>205 pub fn get_verify_partitions( 206 &self, 207 partition_names: &mut [GblEfiPartitionName], 208 ) -> Result<usize> { 209 let partition_count_in: usize = partition_names.len(); 210 let mut partition_count: usize = partition_count_in; 211 212 // SAFETY: 213 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 214 // established by `Protocol::new()`. 215 // `self.interface` is input parameter, outlives the call, and will not be retained. 216 // `partition_count` must be set to valid length of `partition_names` array after the call. 217 // `partition_names` must be valid array of length `partition_count` after the call. 218 unsafe { 219 efi_call!( 220 @bufsize partition_count, 221 self.interface()?.get_verify_partitions, 222 self.interface, 223 &mut partition_count, 224 partition_names.as_mut_ptr(), 225 )?; 226 } 227 228 Ok(partition_count) 229 } 230 } 231 232 #[cfg(test)] 233 mod test { 234 use super::*; 235 use crate::{ 236 protocol::gbl_efi_image_loading::GblImageLoadingProtocol, test::run_test, DeviceHandle, 237 EfiEntry, 238 }; 239 use core::{ffi::c_void, iter::zip, ptr::null_mut}; 240 use efi_types::{ 241 EfiStatus, EFI_STATUS_BAD_BUFFER_SIZE, EFI_STATUS_BUFFER_TOO_SMALL, 242 EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_SUCCESS, 243 }; 244 use spin::MutexGuard; 245 use std::cell::RefCell; 246 use std::collections::HashSet; 247 248 const UCS2_STR: [u16; 8] = [0x2603, 0x0073, 0x006e, 0x006f, 0x0077, 0x006d, 0x0061, 0x006e]; 249 const UTF8_STR: &str = "☃snowman"; 250 const PARTITIONS_MAX: usize = 128; 251 get_buffer_utf8() -> [[u8; PARTITION_NAME_LEN_U8]; PARTITIONS_MAX]252 fn get_buffer_utf8() -> [[u8; PARTITION_NAME_LEN_U8]; PARTITIONS_MAX] { 253 [[0; PARTITION_NAME_LEN_U8]; PARTITIONS_MAX] 254 } 255 get_printable_utf16() -> Vec<u16>256 fn get_printable_utf16() -> Vec<u16> { 257 (0x0021..0x007e).collect::<Vec<u16>>() 258 } 259 get_printable_string() -> String260 fn get_printable_string() -> String { 261 String::from_utf8((0x21..0x7e).collect::<Vec<u8>>()).unwrap() 262 } 263 264 #[test] test_partition_name_get_str()265 fn test_partition_name_get_str() { 266 let mut buffer = [0u8; 100]; 267 // empty string 268 assert_eq!(GblEfiPartitionName::from([0u16]).get_str(&mut buffer).unwrap(), ""); 269 assert_eq!(GblEfiPartitionName::from([0u16]).get_str(&mut buffer).unwrap(), ""); 270 assert_eq!(GblEfiPartitionName::from([0x0000]).get_str(&mut buffer[..0]).unwrap(), ""); 271 272 // Special characters 273 assert_eq!(GblEfiPartitionName::from(UCS2_STR).get_str(&mut buffer).unwrap(), UTF8_STR); 274 275 // Null character in the middle 276 assert_eq!( 277 GblEfiPartitionName::from([0x006d, 0x0075, 0x0000, 0x0073, 0x0069, 0x0063]) 278 .get_str(&mut buffer), 279 Ok("mu") 280 ); 281 282 // Null character at the end 283 assert_eq!( 284 GblEfiPartitionName::from([0x006d, 0x0075, 0x0073, 0x0069, 0x0063, 0x0000]) 285 .get_str(&mut buffer), 286 Ok("music") 287 ); 288 289 // exact buffer size 290 assert_eq!( 291 GblEfiPartitionName::from([0x006d, 0x0075, 0x0073, 0x0069, 0x0063]) 292 .get_str(&mut buffer[..5]), 293 Ok("music") 294 ); 295 assert_eq!( 296 GblEfiPartitionName::from([0x006d, 0x0075, 0x0000, 0x0073, 0x0069, 0x0063]) 297 .get_str(&mut buffer[..2]), 298 Ok("mu") 299 ); 300 } 301 302 #[test] test_partition_name_get_str_small_buffer()303 fn test_partition_name_get_str_small_buffer() { 304 let mut buffer = [0u8; 8]; 305 let partition_name: GblEfiPartitionName = UCS2_STR.into(); 306 assert_eq!(partition_name.get_str(&mut buffer), Err(10usize)); 307 } 308 generate_protocol<'a, P: ProtocolInfo>( efi_entry: &'a EfiEntry, proto: &'a mut P::InterfaceType, ) -> Protocol<'a, P>309 fn generate_protocol<'a, P: ProtocolInfo>( 310 efi_entry: &'a EfiEntry, 311 proto: &'a mut P::InterfaceType, 312 ) -> Protocol<'a, P> { 313 // SAFETY: 314 // proto is a valid pointer and lasts at least as long as efi_entry. 315 unsafe { Protocol::<'a, P>::new(DeviceHandle::new(null_mut()), proto, efi_entry) } 316 } 317 318 #[test] test_proto_get_partitions_count()319 fn test_proto_get_partitions_count() { 320 const EXPECTED_PARTITIONS_NUM: usize = 2; 321 unsafe extern "C" fn get_verify_partitions( 322 _: *mut GblEfiImageLoadingProtocol, 323 number_of_partitions: *mut usize, 324 _: *mut GblEfiPartitionName, 325 ) -> EfiStatus { 326 assert!(!number_of_partitions.is_null()); 327 // SAFETY 328 // `number_of_partitions` must be valid pointer to usize 329 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 330 331 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 332 EFI_STATUS_BUFFER_TOO_SMALL 333 } 334 335 run_test(|image_handle, systab_ptr| { 336 let mut image_loading = GblEfiImageLoadingProtocol { 337 get_verify_partitions: Some(get_verify_partitions), 338 ..Default::default() 339 }; 340 let efi_entry = EfiEntry { image_handle, systab_ptr }; 341 let protocol = 342 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 343 344 let mut partitions: [GblEfiPartitionName; 0] = Default::default(); 345 assert_eq!( 346 protocol.get_verify_partitions(&mut partitions).unwrap_err(), 347 Error::BufferTooSmall(Some(EXPECTED_PARTITIONS_NUM)) 348 ); 349 }); 350 } 351 352 #[test] test_proto_get_partitions_count_error()353 fn test_proto_get_partitions_count_error() { 354 unsafe extern "C" fn get_verify_partitions( 355 _: *mut GblEfiImageLoadingProtocol, 356 _: *mut usize, 357 _: *mut GblEfiPartitionName, 358 ) -> EfiStatus { 359 EFI_STATUS_INVALID_PARAMETER 360 } 361 362 run_test(|image_handle, systab_ptr| { 363 let mut image_loading = GblEfiImageLoadingProtocol { 364 get_verify_partitions: Some(get_verify_partitions), 365 ..Default::default() 366 }; 367 let efi_entry = EfiEntry { image_handle, systab_ptr }; 368 let protocol = 369 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 370 371 let mut partitions: [GblEfiPartitionName; 0] = Default::default(); 372 assert_eq!( 373 protocol.get_verify_partitions(&mut partitions).unwrap_err(), 374 Error::InvalidInput 375 ); 376 }); 377 } 378 379 #[test] test_proto_get_partitions_len_and_value()380 fn test_proto_get_partitions_len_and_value() { 381 const EXPECTED_PARTITIONS_NUM: usize = 1; 382 unsafe extern "C" fn get_verify_partitions( 383 _: *mut GblEfiImageLoadingProtocol, 384 number_of_partitions: *mut usize, 385 partitions: *mut GblEfiPartitionName, 386 ) -> EfiStatus { 387 // SAFETY 388 // `number_of_partitions` must be valid pointer to usize 389 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 390 391 match *number_of_partitions { 392 n if n < EXPECTED_PARTITIONS_NUM => { 393 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 394 EFI_STATUS_BUFFER_TOO_SMALL 395 } 396 _ => { 397 // SAFETY 398 // `partitions` must be valid array of size `number_of_partitions` 399 let partitions = unsafe { 400 core::slice::from_raw_parts_mut(partitions, *number_of_partitions) 401 }; 402 *number_of_partitions = 1; 403 partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR); 404 EFI_STATUS_SUCCESS 405 } 406 } 407 } 408 409 run_test(|image_handle, systab_ptr| { 410 let mut buffer_utf8 = get_buffer_utf8(); 411 let mut image_loading = GblEfiImageLoadingProtocol { 412 get_verify_partitions: Some(get_verify_partitions), 413 ..Default::default() 414 }; 415 let efi_entry = EfiEntry { image_handle, systab_ptr }; 416 let protocol = 417 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 418 let mut partitions: [GblEfiPartitionName; 2] = Default::default(); 419 420 assert_eq!( 421 protocol.get_verify_partitions(&mut partitions[..0]).unwrap_err(), 422 Error::BufferTooSmall(Some(EXPECTED_PARTITIONS_NUM)) 423 ); 424 let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap(); 425 assert_eq!(verify_partitions_len, 1); 426 assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR)); 427 }); 428 } 429 430 #[test] test_proto_get_partitions_zero_len()431 fn test_proto_get_partitions_zero_len() { 432 const EXPECTED_PARTITIONS_NUM: usize = 1; 433 unsafe extern "C" fn get_verify_partitions( 434 _: *mut GblEfiImageLoadingProtocol, 435 number_of_partitions: *mut usize, 436 _: *mut GblEfiPartitionName, 437 ) -> EfiStatus { 438 // SAFETY 439 // `number_of_partitions` must be valid pointer to usize 440 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 441 assert_eq!(*number_of_partitions, 0); 442 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 443 EFI_STATUS_BUFFER_TOO_SMALL 444 } 445 446 run_test(|image_handle, systab_ptr| { 447 let mut image_loading = GblEfiImageLoadingProtocol { 448 get_verify_partitions: Some(get_verify_partitions), 449 ..Default::default() 450 }; 451 let efi_entry = EfiEntry { image_handle, systab_ptr }; 452 let protocol = 453 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 454 let mut partitions: [GblEfiPartitionName; 0] = Default::default(); 455 456 let verify_partitions_res = protocol.get_verify_partitions(&mut partitions); 457 assert_eq!( 458 verify_partitions_res.unwrap_err(), 459 Error::BufferTooSmall(Some(EXPECTED_PARTITIONS_NUM)) 460 ); 461 }); 462 } 463 464 #[test] test_proto_get_partitions_less_than_buffer()465 fn test_proto_get_partitions_less_than_buffer() { 466 const EXPECTED_PARTITIONS_NUM: usize = 1; 467 unsafe extern "C" fn get_verify_partitions( 468 _: *mut GblEfiImageLoadingProtocol, 469 number_of_partitions: *mut usize, 470 partitions: *mut GblEfiPartitionName, 471 ) -> EfiStatus { 472 // SAFETY 473 // `number_of_partitions` must be valid pointer to usize 474 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 475 476 assert!(!partitions.is_null()); 477 assert!(*number_of_partitions > 0); 478 479 // SAFETY 480 // `partitions` must be valid array of size `number_of_partitions` 481 let partitions = 482 unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) }; 483 partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR); 484 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 485 EFI_STATUS_SUCCESS 486 } 487 488 run_test(|image_handle, systab_ptr| { 489 let mut buffer_utf8 = get_buffer_utf8(); 490 let mut image_loading = GblEfiImageLoadingProtocol { 491 get_verify_partitions: Some(get_verify_partitions), 492 ..Default::default() 493 }; 494 let efi_entry = EfiEntry { image_handle, systab_ptr }; 495 let protocol = 496 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 497 let mut partitions: [GblEfiPartitionName; 2] = Default::default(); 498 499 let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap(); 500 assert_eq!(verify_partitions_len, EXPECTED_PARTITIONS_NUM); 501 assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR)); 502 }); 503 } 504 505 #[test] test_proto_get_partitions_name_max()506 fn test_proto_get_partitions_name_max() { 507 unsafe extern "C" fn get_verify_partitions( 508 _: *mut GblEfiImageLoadingProtocol, 509 number_of_partitions: *mut usize, 510 partitions: *mut GblEfiPartitionName, 511 ) -> EfiStatus { 512 let printable_utf16 = get_printable_utf16(); 513 // SAFETY 514 // `number_of_partitions` must be valid pointer to usize 515 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 516 517 assert!(!partitions.is_null()); 518 assert!(*number_of_partitions > 0); 519 520 // SAFETY 521 // `partitions` must be valid array of size `number_of_partitions` 522 let partitions = 523 unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) }; 524 525 let partition_names: [GblEfiPartitionName; PARTITIONS_MAX] = (0 526 ..PARTITION_NAME_LEN_U16) 527 .cycle() 528 .take(PARTITIONS_MAX) 529 .map(|i| printable_utf16[i..i + PARTITION_NAME_LEN_U16].into()) 530 .collect::<Vec<_>>() 531 .try_into() 532 .unwrap(); 533 534 *number_of_partitions = partition_names.len(); 535 536 for (p_out, p_gen) in zip(partitions.iter_mut(), partition_names.iter()) { 537 *p_out = *p_gen; 538 } 539 540 EFI_STATUS_SUCCESS 541 } 542 543 run_test(|image_handle, systab_ptr| { 544 let mut buffer_utf8 = get_buffer_utf8(); 545 let printable_str = get_printable_string(); 546 let mut image_loading = GblEfiImageLoadingProtocol { 547 get_verify_partitions: Some(get_verify_partitions), 548 ..Default::default() 549 }; 550 let efi_entry = EfiEntry { image_handle, systab_ptr }; 551 let protocol = 552 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 553 let mut partitions = [GblEfiPartitionName::default(); PARTITIONS_MAX]; 554 let expected_strs: Vec<&str> = (0..PARTITION_NAME_LEN_U16) 555 .cycle() 556 .take(PARTITIONS_MAX) 557 .map(|i| &printable_str[i..i + PARTITION_NAME_LEN_U16]) 558 .collect(); 559 560 let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap(); 561 assert_eq!(verify_partitions_len, PARTITIONS_MAX); 562 563 assert!(zip(partitions.iter(), expected_strs.iter()) 564 .all(|(p, expected_str)| { p.get_str(&mut buffer_utf8[0]) == Ok(*expected_str) })); 565 }); 566 } 567 568 #[test] test_proto_get_partitions()569 fn test_proto_get_partitions() { 570 const EXPECTED_PARTITIONS_NUM: usize = 2; 571 unsafe extern "C" fn get_verify_partitions( 572 _: *mut GblEfiImageLoadingProtocol, 573 number_of_partitions: *mut usize, 574 partitions: *mut GblEfiPartitionName, 575 ) -> EfiStatus { 576 // SAFETY 577 // `number_of_partitions` must be valid pointer to usize 578 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 579 580 assert!(!partitions.is_null()); 581 assert!(*number_of_partitions > 0); 582 583 // SAFETY 584 // `partitions` must be valid array of size `number_of_partitions` 585 let partitions = 586 unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) }; 587 partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR); 588 partitions[1].StrUtf16[..UCS2_STR.len() - 1].copy_from_slice(&UCS2_STR[1..]); 589 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 590 EFI_STATUS_SUCCESS 591 } 592 593 run_test(|image_handle, systab_ptr| { 594 let mut buffer_utf8 = get_buffer_utf8(); 595 let mut image_loading = GblEfiImageLoadingProtocol { 596 get_verify_partitions: Some(get_verify_partitions), 597 ..Default::default() 598 }; 599 let efi_entry = EfiEntry { image_handle, systab_ptr }; 600 let protocol = 601 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 602 let mut partitions: [GblEfiPartitionName; EXPECTED_PARTITIONS_NUM] = Default::default(); 603 604 let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap(); 605 assert_eq!(verify_partitions_len, EXPECTED_PARTITIONS_NUM); 606 607 let mut char_idx = UTF8_STR.char_indices(); 608 char_idx.next(); 609 let (next_char_pos, _) = char_idx.next().unwrap(); 610 611 assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR)); 612 assert_eq!(partitions[1].get_str(&mut buffer_utf8[1]), Ok(&UTF8_STR[next_char_pos..])); 613 }); 614 } 615 616 #[test] test_proto_get_partitions_empty()617 fn test_proto_get_partitions_empty() { 618 const EXPECTED_PARTITIONS_NUM: usize = 0; 619 unsafe extern "C" fn get_verify_partitions( 620 _: *mut GblEfiImageLoadingProtocol, 621 number_of_partitions: *mut usize, 622 partitions: *mut GblEfiPartitionName, 623 ) -> EfiStatus { 624 // SAFETY 625 // `number_of_partitions` must be valid pointer to usize 626 let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap(); 627 assert!(!partitions.is_null()); 628 *number_of_partitions = EXPECTED_PARTITIONS_NUM; 629 EFI_STATUS_SUCCESS 630 } 631 632 run_test(|image_handle, systab_ptr| { 633 let mut image_loading = GblEfiImageLoadingProtocol { 634 get_verify_partitions: Some(get_verify_partitions), 635 ..Default::default() 636 }; 637 let efi_entry = EfiEntry { image_handle, systab_ptr }; 638 let protocol = 639 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 640 let mut partitions: [GblEfiPartitionName; 2] = Default::default(); 641 642 let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap(); 643 assert_eq!(verify_partitions_len, EXPECTED_PARTITIONS_NUM); 644 }); 645 } 646 647 #[test] test_proto_get_partitions_error()648 fn test_proto_get_partitions_error() { 649 unsafe extern "C" fn get_verify_partitions( 650 _: *mut GblEfiImageLoadingProtocol, 651 _: *mut usize, 652 _: *mut GblEfiPartitionName, 653 ) -> EfiStatus { 654 EFI_STATUS_BAD_BUFFER_SIZE 655 } 656 657 run_test(|image_handle, systab_ptr| { 658 let mut image_loading = GblEfiImageLoadingProtocol { 659 get_verify_partitions: Some(get_verify_partitions), 660 ..Default::default() 661 }; 662 let efi_entry = EfiEntry { image_handle, systab_ptr }; 663 let protocol = 664 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 665 let mut partitions: [GblEfiPartitionName; 1] = Default::default(); 666 667 assert!(protocol.get_verify_partitions(&mut partitions).is_err()); 668 }); 669 } 670 671 // Mutex to make sure tests that use `static RETURNED_BUFFERS` do not run in parallel to avoid 672 // unexpected results since this is global static that would be shared between tests. And can 673 // overflow due to amount of tests. 674 // 675 // See MEMORY_TEST thread local variable that should be used for convenience. 676 static GET_BUFFER_MUTEX: Mutex<()> = Mutex::new(()); 677 678 // Size of MEMORY_TEST buffers 679 const MEMORY_TEST_BUF_SIZE: usize = 100; 680 681 // Helper struct for safe acquisition of the memory and releasing it on exit 682 struct MemoryTest<'a> { 683 // Tracking if test guard was acquired with `start()` 684 init: bool, 685 // Keep track of all buffers returned 686 returned_buffers: HashSet<*mut [u8; MEMORY_TEST_BUF_SIZE]>, 687 // Store same buffer value for `get_memory_same()` calls. 688 same_buffer: Option<*mut c_void>, 689 // It is necessary to run 1 test at a time that uses UEFI `get_buffer()`. 690 // Because it is uses static size array to track returned values to prevent reusing same 691 // buffer. With current number of test if they run simultaneously there are situations when 692 // array limit is reached and unlucky test will fail. To prevent this flakiness this guard 693 // is used. 694 _get_buffer_guard: MutexGuard<'a, ()>, 695 } 696 697 thread_local! { 698 static MEMORY_TEST: RefCell<MemoryTest<'static>> = RefCell::new(MemoryTest::new()); 699 } 700 struct MemoryTestInitGuard {} 701 702 impl Drop for MemoryTestInitGuard { drop(&mut self)703 fn drop(&mut self) { 704 MEMORY_TEST.with_borrow_mut(|v| v.stop()); 705 } 706 } 707 708 // Helper implementation for getting raw buffers for `get_buffer()` calls. 709 // And cleanly releasing buffers at the end of the test to prevent memory leaks. 710 // 711 // Use `thread_local` static MEMORY_TEST variable. 712 // 713 // ``` 714 // let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 715 // ... 716 // buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 717 // ... 718 // ``` 719 // _memory_guard will make sure to cleanup all memory that was retrieved by `get_memory()` call 720 // 721 // Note: 722 // If using raw EfiImageBuffer, there is no need for this helper. Since the structure does 723 // cleaning on its own. 724 // Except when using `EfiImageBuffer::take()` then manual `EfiImageBuffer::release()` must be 725 // used. 726 impl MemoryTest<'_> { new() -> Self727 fn new() -> Self { 728 MemoryTest { 729 init: false, 730 returned_buffers: HashSet::new(), 731 same_buffer: None, 732 _get_buffer_guard: GET_BUFFER_MUTEX.lock(), 733 } 734 } 735 start(&mut self) -> MemoryTestInitGuard736 fn start(&mut self) -> MemoryTestInitGuard { 737 assert!(!self.init); 738 self.init = true; 739 MemoryTestInitGuard {} 740 } 741 742 // Return heap allocated buffer, and keep track of its address 743 // To verify it was properly released 744 // 745 // # Safety 746 // 747 // Returned pointers must not be used after guard returned by `start()` 748 // is destroyed. get_memory(&mut self) -> *mut c_void749 unsafe fn get_memory(&mut self) -> *mut c_void { 750 assert!(self.init); 751 let ptr = Box::into_raw(Box::new([0u8; MEMORY_TEST_BUF_SIZE])); 752 assert!(self.returned_buffers.insert(ptr)); 753 ptr as *mut c_void 754 } 755 756 // Return same buffer for all calls, allocating and tracking it only for first call. 757 // 758 // # Safety 759 // 760 // Returned pointers must not be used after guard returned by `start()` 761 // is destroyed. get_memory_same(&mut self) -> *mut c_void762 unsafe fn get_memory_same(&mut self) -> *mut c_void { 763 if self.same_buffer.is_none() { 764 // SAFETY: 765 // This function has same requirements as `get_memory()` 766 let address = unsafe { self.get_memory() }; 767 768 self.same_buffer = Some(address); 769 } 770 771 *self.same_buffer.as_mut().unwrap() 772 } 773 774 // Clear address from buffers returned list 775 // Which allows to reuse it in other tests. stop(&mut self)776 fn stop(&mut self) { 777 assert!(self.init); 778 self.init = false; 779 self.same_buffer = None; 780 for ptr in self.returned_buffers.drain() { 781 // SAFETY: 782 // `ptr` is valid since was created by `Box::into_raw()`. 783 // Double free is covered by safety requirements for this function. (`release_memory()` 784 // must be called only on buffer holding the only reference to buffer.) 785 // As well as tracking `returned_buffers` and asserting remove in the line above. 786 unsafe { 787 let _restore_box = Box::from_raw(ptr); 788 } 789 } 790 } 791 } 792 793 #[test] test_proto_get_buffer_error()794 fn test_proto_get_buffer_error() { 795 unsafe extern "C" fn get_buffer( 796 _: *mut GblEfiImageLoadingProtocol, 797 _: *const GblEfiImageInfo, 798 _: *mut GblEfiImageBuffer, 799 ) -> EfiStatus { 800 EFI_STATUS_INVALID_PARAMETER 801 } 802 803 run_test(|image_handle, systab_ptr| { 804 let gbl_image_info: GblEfiImageInfo = Default::default(); 805 let mut image_loading = 806 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 807 let efi_entry = EfiEntry { image_handle, systab_ptr }; 808 let protocol = 809 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 810 811 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 812 assert!(protocol.get_buffer(&gbl_image_info).is_err()); 813 }); 814 } 815 816 #[test] test_proto_get_buffer_return_alloc_size()817 fn test_proto_get_buffer_return_alloc_size() { 818 // SAFETY: 819 // * Caler must guarantee that `buffer` points to a valid instance of `GblEfiImageBuffer`. 820 unsafe extern "C" fn get_buffer( 821 _: *mut GblEfiImageLoadingProtocol, 822 _: *const GblEfiImageInfo, 823 buffer: *mut GblEfiImageBuffer, 824 ) -> EfiStatus { 825 // SAFETY 826 // By safety requirement of this function, `buffer` points to a valid instance of 827 // `GblEfiImageBuffer`. 828 let buffer = unsafe { buffer.as_mut() }.unwrap(); 829 buffer.Memory = null_mut(); 830 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 831 EFI_STATUS_SUCCESS 832 } 833 834 run_test(|image_handle, systab_ptr| { 835 let gbl_image_info: GblEfiImageInfo = 836 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 }; 837 let mut image_loading = 838 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 839 let efi_entry = EfiEntry { image_handle, systab_ptr }; 840 let protocol = 841 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 842 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 843 assert!(matches!( 844 protocol.get_buffer(&gbl_image_info), 845 Ok(EfiImageBufferInfo::AllocSize(MEMORY_TEST_BUF_SIZE)) 846 )); 847 }); 848 } 849 850 #[test] test_proto_get_buffer_zero_size()851 fn test_proto_get_buffer_zero_size() { 852 unsafe extern "C" fn get_buffer( 853 _: *mut GblEfiImageLoadingProtocol, 854 image_info: *const GblEfiImageInfo, 855 buffer: *mut GblEfiImageBuffer, 856 ) -> EfiStatus { 857 assert!(!image_info.is_null()); 858 assert!(!buffer.is_null()); 859 // SAFETY 860 // `buffer` must be valid pointer to `GblEfiImageBuffer` 861 let buffer = unsafe { buffer.as_mut() }.unwrap(); 862 863 // SAFETY: 864 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 865 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 866 // Same function where `start()` guard is acquired, so it will not outlive guard. 867 unsafe { 868 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 869 } 870 buffer.SizeBytes = 0; 871 872 EFI_STATUS_SUCCESS 873 } 874 875 run_test(|image_handle, systab_ptr| { 876 let gbl_image_info: GblEfiImageInfo = Default::default(); 877 let mut image_loading = 878 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 879 let efi_entry = EfiEntry { image_handle, systab_ptr }; 880 let protocol = 881 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 882 883 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 884 let mut res = protocol.get_buffer(&gbl_image_info).unwrap(); 885 assert!(res.buffer().as_ref().unwrap().is_empty()); 886 }); 887 } 888 889 #[test] test_proto_get_buffer_small()890 fn test_proto_get_buffer_small() { 891 unsafe extern "C" fn get_buffer( 892 _: *mut GblEfiImageLoadingProtocol, 893 image_info: *const GblEfiImageInfo, 894 buffer: *mut GblEfiImageBuffer, 895 ) -> EfiStatus { 896 assert!(!image_info.is_null()); 897 // SAFETY 898 // `image_info` must be valid pointer to `GblEfiImageInfo` 899 let image_info = unsafe { image_info.as_ref() }.unwrap(); 900 assert!(!buffer.is_null()); 901 // SAFETY 902 // `buffer` must be valid pointer to `GblEfiImageBuffer` 903 let buffer = unsafe { buffer.as_mut() }.unwrap(); 904 905 // SAFETY: 906 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 907 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 908 // Same function where `start()` guard is acquired, so it will not outlive guard. 909 unsafe { 910 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 911 } 912 buffer.SizeBytes = image_info.SizeBytes - 1; 913 914 EFI_STATUS_SUCCESS 915 } 916 917 run_test(|image_handle, systab_ptr| { 918 let gbl_image_info: GblEfiImageInfo = 919 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 10 }; 920 let mut image_loading = 921 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 922 let efi_entry = EfiEntry { image_handle, systab_ptr }; 923 let protocol = 924 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 925 926 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 927 let res = protocol.get_buffer(&gbl_image_info); 928 assert_eq!(res.unwrap_err(), Error::BufferTooSmall(Some(10))); 929 }); 930 } 931 932 #[test] test_proto_get_buffer()933 fn test_proto_get_buffer() { 934 unsafe extern "C" fn get_buffer( 935 _: *mut GblEfiImageLoadingProtocol, 936 image_info: *const GblEfiImageInfo, 937 buffer: *mut GblEfiImageBuffer, 938 ) -> EfiStatus { 939 assert!(!image_info.is_null()); 940 assert!(!buffer.is_null()); 941 // SAFETY 942 // `buffer` must be valid pointer to `GblEfiImageBuffer` 943 let buffer = unsafe { buffer.as_mut() }.unwrap(); 944 945 // SAFETY: 946 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 947 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 948 // Same function where `start()` guard is acquired, so it will not outlive guard. 949 unsafe { 950 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 951 } 952 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 953 954 EFI_STATUS_SUCCESS 955 } 956 957 run_test(|image_handle, systab_ptr| { 958 let gbl_image_info: GblEfiImageInfo = 959 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 }; 960 let mut image_loading = 961 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 962 let efi_entry = EfiEntry { image_handle, systab_ptr }; 963 let protocol = 964 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 965 966 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 967 let mut buf = protocol.get_buffer(&gbl_image_info).unwrap(); 968 assert_ne!(buf.buffer().as_ref().unwrap().as_ptr(), null_mut()); 969 assert_eq!(buf.buffer().as_ref().unwrap().len(), 100); 970 }); 971 } 972 973 #[test] test_proto_get_buffer_image_type()974 fn test_proto_get_buffer_image_type() { 975 const IMAGE_TYPE_STR: &'static str = "test"; 976 unsafe extern "C" fn get_buffer( 977 _: *mut GblEfiImageLoadingProtocol, 978 image_info: *const GblEfiImageInfo, 979 buffer: *mut GblEfiImageBuffer, 980 ) -> EfiStatus { 981 assert!(!image_info.is_null()); 982 // SAFETY 983 // `image_info` must be valid pointer to `GblEfiImageInfo` 984 let image_info = unsafe { image_info.as_ref() }.unwrap(); 985 assert!(!buffer.is_null()); 986 // SAFETY 987 // `buffer` must be valid pointer to `GblEfiImageBuffer` 988 let buffer = unsafe { buffer.as_mut() }.unwrap(); 989 990 let mut buffer_utf8 = [0u8; 100]; 991 assert_eq!( 992 GblEfiPartitionName::from(image_info.ImageType).get_str(&mut buffer_utf8).unwrap(), 993 IMAGE_TYPE_STR 994 ); 995 996 // SAFETY: 997 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 998 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 999 // Same function where `start()` guard is acquired, so it will not outlive guard. 1000 unsafe { 1001 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 1002 } 1003 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 1004 1005 EFI_STATUS_SUCCESS 1006 } 1007 1008 run_test(|image_handle, systab_ptr| { 1009 let mut image_type = [0u16; PARTITION_NAME_LEN_U16]; 1010 image_type[..4].copy_from_slice(&IMAGE_TYPE_STR.encode_utf16().collect::<Vec<u16>>()); 1011 let gbl_image_info: GblEfiImageInfo = 1012 GblEfiImageInfo { ImageType: image_type, SizeBytes: 100 }; 1013 let mut image_loading = 1014 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 1015 let efi_entry = EfiEntry { image_handle, systab_ptr }; 1016 let protocol = 1017 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 1018 1019 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1020 assert!(protocol.get_buffer(&gbl_image_info).is_ok()); 1021 }); 1022 } 1023 1024 #[test] test_proto_get_buffer_double_call()1025 fn test_proto_get_buffer_double_call() { 1026 unsafe extern "C" fn get_buffer( 1027 _: *mut GblEfiImageLoadingProtocol, 1028 image_info: *const GblEfiImageInfo, 1029 buffer: *mut GblEfiImageBuffer, 1030 ) -> EfiStatus { 1031 assert!(!image_info.is_null()); 1032 assert!(!buffer.is_null()); 1033 // SAFETY 1034 // `buffer` must be valid pointer to `GblEfiImageBuffer` 1035 let buffer = unsafe { buffer.as_mut() }.unwrap(); 1036 1037 // SAFETY: 1038 // `get_memory_same()` results are returned in `buffer` in `get_buffer()` function. 1039 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 1040 // Same function where `start()` guard is acquired, so it will not outlive guard. 1041 unsafe { 1042 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory_same()); 1043 } 1044 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 1045 1046 EFI_STATUS_SUCCESS 1047 } 1048 1049 run_test(|image_handle, systab_ptr| { 1050 let gbl_image_info: GblEfiImageInfo = 1051 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 }; 1052 let mut image_loading = 1053 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 1054 let efi_entry = EfiEntry { image_handle, systab_ptr }; 1055 let protocol = 1056 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 1057 1058 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1059 let _buf = protocol.get_buffer(&gbl_image_info).unwrap(); 1060 assert_eq!(protocol.get_buffer(&gbl_image_info).unwrap_err(), Error::AlreadyStarted); 1061 }); 1062 } 1063 1064 #[test] test_proto_get_buffer_double_call_after_drop()1065 fn test_proto_get_buffer_double_call_after_drop() { 1066 unsafe extern "C" fn get_buffer( 1067 _: *mut GblEfiImageLoadingProtocol, 1068 image_info: *const GblEfiImageInfo, 1069 buffer: *mut GblEfiImageBuffer, 1070 ) -> EfiStatus { 1071 assert!(!image_info.is_null()); 1072 assert!(!buffer.is_null()); 1073 // SAFETY 1074 // `buffer` must be valid pointer to `GblEfiImageBuffer` 1075 let buffer = unsafe { buffer.as_mut() }.unwrap(); 1076 1077 // SAFETY: 1078 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 1079 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 1080 // Same function where `start()` guard is acquired, so it will not outlive guard. 1081 unsafe { 1082 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory_same()); 1083 } 1084 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 1085 1086 EFI_STATUS_SUCCESS 1087 } 1088 1089 run_test(|image_handle, systab_ptr| { 1090 let gbl_image_info: GblEfiImageInfo = 1091 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 }; 1092 let mut image_loading = 1093 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 1094 let efi_entry = EfiEntry { image_handle, systab_ptr }; 1095 let protocol = 1096 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 1097 1098 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1099 protocol.get_buffer(&gbl_image_info).unwrap(); 1100 protocol.get_buffer(&gbl_image_info).unwrap(); 1101 }); 1102 } 1103 1104 #[test] 1105 #[should_panic] test_proto_get_buffer_too_many_times()1106 fn test_proto_get_buffer_too_many_times() { 1107 unsafe extern "C" fn get_buffer( 1108 _: *mut GblEfiImageLoadingProtocol, 1109 image_info: *const GblEfiImageInfo, 1110 buffer: *mut GblEfiImageBuffer, 1111 ) -> EfiStatus { 1112 assert!(!image_info.is_null()); 1113 assert!(!buffer.is_null()); 1114 // SAFETY 1115 // `buffer` must be valid pointer to `GblEfiImageBuffer` 1116 let buffer = unsafe { buffer.as_mut() }.unwrap(); 1117 1118 // SAFETY: 1119 // `get_memory()` results are returned in `buffer` in `get_buffer()` function. 1120 // All usage of `get_buffer()` results are not leaving `run_test()` scope. 1121 // Same function where `start()` guard is acquired, so it will not outlive guard. 1122 unsafe { 1123 buffer.Memory = MEMORY_TEST.with_borrow_mut(|v| v.get_memory()); 1124 } 1125 buffer.SizeBytes = MEMORY_TEST_BUF_SIZE; 1126 1127 EFI_STATUS_SUCCESS 1128 } 1129 1130 run_test(|image_handle, systab_ptr| { 1131 let gbl_image_info: GblEfiImageInfo = 1132 GblEfiImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 }; 1133 let mut image_loading = 1134 GblEfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() }; 1135 let efi_entry = EfiEntry { image_handle, systab_ptr }; 1136 let protocol = 1137 generate_protocol::<GblImageLoadingProtocol>(&efi_entry, &mut image_loading); 1138 1139 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1140 let mut keep_alive: Vec<EfiImageBufferInfo> = vec![]; 1141 for _ in 1..=MAX_ARRAY_SIZE + 1 { 1142 keep_alive.push(protocol.get_buffer(&gbl_image_info).unwrap()); 1143 } 1144 }); 1145 } 1146 1147 #[test] test_efi_image_buffer()1148 fn test_efi_image_buffer() { 1149 let mut v = vec![0u8; 1]; 1150 let gbl_buffer = 1151 GblEfiImageBuffer { Memory: v.as_mut_ptr() as *mut c_void, SizeBytes: v.len() }; 1152 1153 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1154 // SAFETY: 1155 // 'gbl_buffer` represents valid buffer created by vector. 1156 let res = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1157 assert!(res.is_ok()); 1158 } 1159 1160 #[test] test_efi_image_buffer_null()1161 fn test_efi_image_buffer_null() { 1162 let gbl_buffer = GblEfiImageBuffer { Memory: null_mut(), SizeBytes: 1 }; 1163 1164 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1165 // SAFETY: 1166 // 'gbl_buffer` contains Memory == NULL, which is valid input value. And we expect Error as 1167 // a result 1168 let res = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1169 assert_eq!(res.unwrap_err(), Error::InvalidInput); 1170 } 1171 1172 #[test] test_efi_image_buffer_same_buffer()1173 fn test_efi_image_buffer_same_buffer() { 1174 let mut v = vec![0u8; 1]; 1175 let gbl_buffer = 1176 GblEfiImageBuffer { Memory: v.as_mut_ptr() as *mut c_void, SizeBytes: v.len() }; 1177 1178 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1179 // SAFETY: 1180 // 'gbl_buffer` represents valid buffer created by vector. 1181 let res1 = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1182 assert!(res1.is_ok()); 1183 1184 // Since we keep `res1`, second return of same buffer should fail 1185 // SAFETY: 1186 // 'gbl_buffer` represents valid buffer created by vector. 1187 let res2 = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1188 assert_eq!(res2.unwrap_err(), Error::AlreadyStarted); 1189 } 1190 1191 #[test] test_efi_image_buffer_same_buffer_after_drop()1192 fn test_efi_image_buffer_same_buffer_after_drop() { 1193 let mut v = vec![0u8; 1]; 1194 let gbl_buffer = 1195 GblEfiImageBuffer { Memory: v.as_mut_ptr() as *mut c_void, SizeBytes: v.len() }; 1196 1197 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1198 // SAFETY: 1199 // 'gbl_buffer` represents valid buffer created by vector. 1200 let res1 = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1201 drop(res1); 1202 1203 // Since `res1` was dropped same buffer can be returned. 1204 // SAFETY: 1205 // 'gbl_buffer` represents valid buffer created by vector. 1206 let res2 = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1207 assert!(res2.is_ok()); 1208 } 1209 1210 #[test] test_efi_image_buffer_take()1211 fn test_efi_image_buffer_take() { 1212 let mut v = vec![0u8; 1]; 1213 let gbl_buffer = 1214 GblEfiImageBuffer { Memory: v.as_mut_ptr() as *mut c_void, SizeBytes: v.len() }; 1215 1216 let _memory_guard = MEMORY_TEST.with_borrow_mut(|v| v.start()); 1217 // SAFETY: 1218 // 'gbl_buffer` represents valid buffer created by vector. 1219 let mut res1 = unsafe { EfiImageBuffer::new(gbl_buffer) }.unwrap(); 1220 let buf_no_owner = res1.take(); 1221 1222 // Since `res1` was taken, we can't reuse same buffer. 1223 // SAFETY: 1224 // 'gbl_buffer` represents valid buffer created by vector. 1225 let res2 = unsafe { EfiImageBuffer::new(gbl_buffer) }; 1226 assert_eq!(res2.unwrap_err(), Error::AlreadyStarted); 1227 1228 // Make sure to clean tracking 1229 // SAFETY: 1230 // `buf_no_owner` is the only reference to buffer 1231 unsafe { 1232 EfiImageBuffer::release(buf_no_owner.as_ptr() as usize); 1233 } 1234 } 1235 } 1236