• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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