xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/src/protocol/gbl_efi_avb.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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 `GBL_EFI_AVB_PROTOCOL`.
16 
17 use crate::efi_call;
18 use crate::protocol::{Protocol, ProtocolInfo};
19 use core::ffi::CStr;
20 use core::ptr::null;
21 use efi_types::{
22     EfiGuid, GblEfiAvbKeyValidationStatus, GblEfiAvbProtocol, GblEfiAvbVerificationResult,
23 };
24 use liberror::Result;
25 
26 /// `GBL_EFI_AVB_PROTOCOL` implementation.
27 pub struct GblAvbProtocol;
28 
29 impl ProtocolInfo for GblAvbProtocol {
30     type InterfaceType = GblEfiAvbProtocol;
31 
32     const GUID: EfiGuid =
33         EfiGuid::new(0x6bc66b9a, 0xd5c9, 0x4c02, [0x9d, 0xa9, 0x50, 0xaf, 0x19, 0x8d, 0x91, 0x2c]);
34 }
35 
36 // Protocol interface wrappers.
37 impl Protocol<'_, GblAvbProtocol> {
38     /// Wraps `GBL_EFI_AVB_PROTOCOL.validate_vbmeta_public_key()`.
validate_vbmeta_public_key( &self, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> Result<GblEfiAvbKeyValidationStatus>39     pub fn validate_vbmeta_public_key(
40         &self,
41         public_key: &[u8],
42         public_key_metadata: Option<&[u8]>,
43     ) -> Result<GblEfiAvbKeyValidationStatus> {
44         let mut validation_status: GblEfiAvbKeyValidationStatus =
45             efi_types::GBL_EFI_AVB_KEY_VALIDATION_STATUS_INVALID;
46 
47         // SAFETY:
48         // * `self.interface()?` guarantees self.interface is non-null and points to a valid object
49         //   established by `Protocol::new()`
50         // * `public_key` pointer is not-null and used only within the call
51         // * `public_key_metadata` pointer (can be null), used only within the call
52         // * `validation_status` non-null pointer available to write
53         unsafe {
54             efi_call!(
55                 self.interface()?.validate_vbmeta_public_key,
56                 self.interface,
57                 public_key.as_ptr() as *const _,
58                 public_key.len(),
59                 public_key_metadata.map_or(null(), |m| m.as_ptr() as *const _),
60                 public_key_metadata.map_or(0, |m| m.len()),
61                 &mut validation_status,
62             )?
63         }
64 
65         Ok(validation_status)
66     }
67 
68     /// Wraps `GBL_EFI_AVB_PROTOCOL.read_is_device_unlocked()`.
read_is_device_unlocked(&self) -> Result<bool>69     pub fn read_is_device_unlocked(&self) -> Result<bool> {
70         let mut is_unlocked: bool = false;
71 
72         // SAFETY:
73         // * `self.interface()?` guarantees `self.interface` is non-null and points to a valid
74         // object established by `Protocol::new()`.
75         // * `is_unlocked` is a non-null pointer to a `bool` available for write.
76         unsafe {
77             efi_call!(self.interface()?.read_is_device_unlocked, self.interface, &mut is_unlocked)?
78         }
79 
80         Ok(is_unlocked)
81     }
82 
83     /// Wraps `GBL_EFI_AVB_PROTOCOL.read_rollback_index()`.
read_rollback_index(&self, index_location: usize) -> Result<u64>84     pub fn read_rollback_index(&self, index_location: usize) -> Result<u64> {
85         let mut rollback_index: u64 = 0;
86 
87         // SAFETY:
88         // * `self.interface()?` guarantees `self.interface` is non-null and points to a valid
89         //   object established by `Protocol::new()`.
90         // * `rollback_index` is a valid pointer to a `u64` available for write.
91         unsafe {
92             efi_call!(
93                 self.interface()?.read_rollback_index,
94                 self.interface,
95                 index_location,
96                 &mut rollback_index,
97             )?
98         }
99 
100         Ok(rollback_index)
101     }
102 
103     /// Wraps `GBL_EFI_AVB_PROTOCOL.write_rollback_index()`.
write_rollback_index(&self, index_location: usize, rollback_index: u64) -> Result<()>104     pub fn write_rollback_index(&self, index_location: usize, rollback_index: u64) -> Result<()> {
105         // SAFETY:
106         // * `self.interface()?` guarantees `self.interface` is non-null and points to a valid
107         //   object established by `Protocol::new()`.
108         unsafe {
109             efi_call!(
110                 self.interface()?.write_rollback_index,
111                 self.interface,
112                 index_location,
113                 rollback_index,
114             )?
115         }
116 
117         Ok(())
118     }
119 
120     /// Wraps `GBL_EFI_AVB_PROTOCOL.read_persistent_value()`.
read_persistent_value(&self, name: &CStr, value: &mut [u8]) -> Result<usize>121     pub fn read_persistent_value(&self, name: &CStr, value: &mut [u8]) -> Result<usize> {
122         let mut value_buffer_size = value.len();
123 
124         let value_ptr = match value.is_empty() {
125             true => core::ptr::null_mut(),
126             false => value.as_mut_ptr(),
127         };
128 
129         // SAFETY:
130         // * `self.interface()?` guarantees `self.interface` is non-null and points to a valid
131         //   object established by `Protocol::new()`.
132         // * `name` is a valid pointer to a null-terminated string used only within the call.
133         // * `value_ptr` is either a valid pointer to a writable buffer or a null pointer, used only
134         //   within the call
135         // * `value_buffer_size` holds a mutable reference to `usize`, used only within the call.
136         unsafe {
137             efi_call!(
138                 @bufsize value_buffer_size,
139                 self.interface()?.read_persistent_value,
140                 self.interface,
141                 name.as_ptr(),
142                 value_ptr,
143                 &mut value_buffer_size,
144             )?
145         }
146 
147         Ok(value_buffer_size)
148     }
149 
150     /// Wraps `GBL_EFI_AVB_PROTOCOL.write_persistent_value()`.
write_persistent_value(&self, name: &CStr, value: Option<&[u8]>) -> Result<()>151     pub fn write_persistent_value(&self, name: &CStr, value: Option<&[u8]>) -> Result<()> {
152         let (value_ptr, value_len) = match value {
153             Some(v) => (v.as_ptr(), v.len()),
154             None => (core::ptr::null(), 0),
155         };
156 
157         // SAFETY:
158         // * `self.interface()?` guarantees `self.interface` is non-null and points to a valid
159         //   object established by `Protocol::new()`.
160         // * `name` is a valid pointer to a null-terminated string used only within the call.
161         // * `value_ptr` is a valid pointer to `value_len` sized buffer or null, used only within
162         //   the call.
163         unsafe {
164             efi_call!(
165                 self.interface()?.write_persistent_value,
166                 self.interface,
167                 name.as_ptr(),
168                 value_ptr,
169                 value_len,
170             )?
171         }
172 
173         Ok(())
174     }
175 
176     /// Wraps `GBL_EFI_AVB_PROTOCOL.handle_verification_result()`.
handle_verification_result( &self, verification_result: &GblEfiAvbVerificationResult, ) -> Result<()>177     pub fn handle_verification_result(
178         &self,
179         verification_result: &GblEfiAvbVerificationResult,
180     ) -> Result<()> {
181         // SAFETY:
182         // * `self.interface()?` guarantees self.interface is non-null and points to a valid object
183         //   established by `Protocol::new()`.
184         // * `verification_result` pointer is not-null and used only within the call.
185         unsafe {
186             efi_call!(
187                 self.interface()?.handle_verification_result,
188                 self.interface,
189                 verification_result as *const _
190             )
191         }
192     }
193 }
194 
195 #[cfg(test)]
196 mod test {
197     use super::*;
198     use crate::{protocol::EFI_STATUS_BUFFER_TOO_SMALL, test::run_test_with_mock_protocol, Error};
199     use efi_types::{EfiStatus, EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_SUCCESS};
200     use std::{ffi::c_char, ptr, slice};
201 
202     #[test]
validate_vbmeta_public_key_status_provided()203     fn validate_vbmeta_public_key_status_provided() {
204         const EXPECTED_PUBLIC_KEY: &[u8] = b"test_key";
205         const EXPECTED_STATUS: GblEfiAvbKeyValidationStatus =
206             efi_types::GBL_EFI_AVB_KEY_VALIDATION_STATUS_VALID_CUSTOM_KEY;
207 
208         // C callback implementation that returns an error
209         unsafe extern "C" fn c_return_error(
210             _: *mut GblEfiAvbProtocol,
211             public_key_ptr: *const u8,
212             public_key_len: usize,
213             _metadata_ptr: *const u8,
214             _metadata_len: usize,
215             validation_status_ptr: *mut GblEfiAvbKeyValidationStatus,
216         ) -> EfiStatus {
217             // SAFETY:
218             // * `public_key_ptr` is a non-null pointer to the buffer at least `public_key_len`
219             // size.
220             let public_key_buffer =
221                 unsafe { slice::from_raw_parts(public_key_ptr, public_key_len) };
222 
223             assert_eq!(public_key_buffer, EXPECTED_PUBLIC_KEY);
224 
225             // SAFETY:
226             // * `validation_status_ptr` is a non-null pointer to GblEfiAvbKeyValidationStatus
227             // available to write.
228             unsafe { *validation_status_ptr = EXPECTED_STATUS };
229 
230             EFI_STATUS_SUCCESS
231         }
232 
233         let c_interface = GblEfiAvbProtocol {
234             validate_vbmeta_public_key: Some(c_return_error),
235             ..Default::default()
236         };
237 
238         run_test_with_mock_protocol(c_interface, |avb_protocol| {
239             assert_eq!(
240                 avb_protocol.validate_vbmeta_public_key(EXPECTED_PUBLIC_KEY, None),
241                 Ok(EXPECTED_STATUS)
242             );
243         });
244     }
245 
246     #[test]
validate_vbmeta_public_key_error_handled()247     fn validate_vbmeta_public_key_error_handled() {
248         // C callback implementation that returns an error
249         unsafe extern "C" fn c_return_error(
250             _: *mut GblEfiAvbProtocol,
251             _public_key_ptr: *const u8,
252             _public_key_len: usize,
253             _metadata_ptr: *const u8,
254             _metadata_len: usize,
255             _validation_status_ptr: *mut GblEfiAvbKeyValidationStatus,
256         ) -> EfiStatus {
257             EFI_STATUS_INVALID_PARAMETER
258         }
259 
260         let c_interface = GblEfiAvbProtocol {
261             validate_vbmeta_public_key: Some(c_return_error),
262             ..Default::default()
263         };
264 
265         run_test_with_mock_protocol(c_interface, |avb_protocol| {
266             assert!(avb_protocol.validate_vbmeta_public_key(b"test_key", None).is_err());
267         });
268     }
269 
270     #[test]
handle_verification_result_data_provided()271     fn handle_verification_result_data_provided() {
272         const COLOR: u32 = efi_types::GBL_EFI_AVB_BOOT_STATE_COLOR_RED;
273 
274         // C callback implementation that returns success.
275         unsafe extern "C" fn c_return_success(
276             _: *mut GblEfiAvbProtocol,
277             result: *const GblEfiAvbVerificationResult,
278         ) -> EfiStatus {
279             // SAFETY:
280             // * `result` is non-null.
281             assert_eq!(unsafe { (*result).color }, COLOR);
282             EFI_STATUS_SUCCESS
283         }
284 
285         let c_interface = GblEfiAvbProtocol {
286             handle_verification_result: Some(c_return_success),
287             ..Default::default()
288         };
289 
290         run_test_with_mock_protocol(c_interface, |avb_protocol| {
291             let verification_result =
292                 GblEfiAvbVerificationResult { color: COLOR, ..Default::default() };
293 
294             assert!(avb_protocol.handle_verification_result(&verification_result).is_ok());
295         });
296     }
297 
298     #[test]
handle_verification_result_error()299     fn handle_verification_result_error() {
300         // C callback implementation that returns an error.
301         unsafe extern "C" fn c_return_error(
302             _: *mut GblEfiAvbProtocol,
303             _: *const GblEfiAvbVerificationResult,
304         ) -> EfiStatus {
305             EFI_STATUS_INVALID_PARAMETER
306         }
307 
308         let c_interface = GblEfiAvbProtocol {
309             handle_verification_result: Some(c_return_error),
310             ..Default::default()
311         };
312 
313         run_test_with_mock_protocol(c_interface, |avb_protocol| {
314             let verification_result = GblEfiAvbVerificationResult::default();
315 
316             assert!(avb_protocol.handle_verification_result(&verification_result).is_err());
317         });
318     }
319 
320     #[test]
read_is_device_unlocked_returns_true()321     fn read_is_device_unlocked_returns_true() {
322         /// C callback implementation that sets is_unlocked to true.
323         ///
324         /// # Safety:
325         /// Caller must guaranteed that `is_unlocked_ptr` points to a valid bool variable available
326         /// for write.
327         unsafe extern "C" fn c_return_true(
328             _: *mut GblEfiAvbProtocol,
329             is_unlocked_ptr: *mut bool,
330         ) -> EfiStatus {
331             // SAFETY: By safety requirement of this function, is_unlocked_ptr is a valid pointer.
332             unsafe { *is_unlocked_ptr = true };
333             EFI_STATUS_SUCCESS
334         }
335 
336         let c_interface = GblEfiAvbProtocol {
337             read_is_device_unlocked: Some(c_return_true),
338             ..Default::default()
339         };
340 
341         run_test_with_mock_protocol(c_interface, |avb_protocol| {
342             assert_eq!(avb_protocol.read_is_device_unlocked(), Ok(true));
343         });
344     }
345 
346     #[test]
read_is_device_unlocked_returns_false()347     fn read_is_device_unlocked_returns_false() {
348         /// C callback implementation that sets is_unlocked to false.
349         ///
350         /// # Safety:
351         /// Caller must guaranteed that `is_unlocked_ptr` points to a valid bool variable available
352         /// for write.
353         unsafe extern "C" fn c_return_false(
354             _: *mut GblEfiAvbProtocol,
355             is_unlocked_ptr: *mut bool,
356         ) -> EfiStatus {
357             // SAFETY: By safety requirement of this function, is_unlocked_ptr is a valid pointer.
358             unsafe { *is_unlocked_ptr = false };
359             EFI_STATUS_SUCCESS
360         }
361 
362         let c_interface = GblEfiAvbProtocol {
363             read_is_device_unlocked: Some(c_return_false),
364             ..Default::default()
365         };
366 
367         run_test_with_mock_protocol(c_interface, |avb_protocol| {
368             assert_eq!(avb_protocol.read_is_device_unlocked(), Ok(false));
369         });
370     }
371 
372     #[test]
read_is_device_unlocked_error_handled()373     fn read_is_device_unlocked_error_handled() {
374         /// C callback implementation that returns an error.
375         unsafe extern "C" fn c_return_error(_: *mut GblEfiAvbProtocol, _: *mut bool) -> EfiStatus {
376             EFI_STATUS_INVALID_PARAMETER
377         }
378 
379         let c_interface = GblEfiAvbProtocol {
380             read_is_device_unlocked: Some(c_return_error),
381             ..Default::default()
382         };
383 
384         run_test_with_mock_protocol(c_interface, |avb_protocol| {
385             assert!(avb_protocol.read_is_device_unlocked().is_err());
386         });
387     }
388 
389     #[test]
read_rollback_index_returns_value()390     fn read_rollback_index_returns_value() {
391         const EXPECTED_INDEX_LOCATION: usize = 1;
392         const EXPECTED_ROLLBACK_INDEX: u64 = 42;
393 
394         /// C callback implementation that sets rollback_index to EXPECTED_ROLLBACK_INDEX.
395         ///
396         /// # Safety:
397         /// Caller must guaranteed that `rollback_index_ptr` points to a valid u64 variable
398         /// available for write.
399         unsafe extern "C" fn c_return_value(
400             _: *mut GblEfiAvbProtocol,
401             index_location: usize,
402             rollback_index_ptr: *mut u64,
403         ) -> EfiStatus {
404             assert_eq!(index_location, EXPECTED_INDEX_LOCATION);
405 
406             // SAFETY: By safety requirement of this function, `rollback_index_ptr` is a valid
407             // pointer.
408             unsafe { *rollback_index_ptr = EXPECTED_ROLLBACK_INDEX };
409             EFI_STATUS_SUCCESS
410         }
411 
412         let c_interface =
413             GblEfiAvbProtocol { read_rollback_index: Some(c_return_value), ..Default::default() };
414 
415         run_test_with_mock_protocol(c_interface, |avb_protocol| {
416             assert_eq!(
417                 avb_protocol.read_rollback_index(EXPECTED_INDEX_LOCATION),
418                 Ok(EXPECTED_ROLLBACK_INDEX)
419             );
420         });
421     }
422 
423     #[test]
read_rollback_index_error_handled()424     fn read_rollback_index_error_handled() {
425         /// C callback implementation that returns an error.
426         unsafe extern "C" fn c_return_error(
427             _: *mut GblEfiAvbProtocol,
428             _: usize,
429             _: *mut u64,
430         ) -> EfiStatus {
431             EFI_STATUS_INVALID_PARAMETER
432         }
433 
434         let c_interface =
435             GblEfiAvbProtocol { read_rollback_index: Some(c_return_error), ..Default::default() };
436 
437         run_test_with_mock_protocol(c_interface, |avb_protocol| {
438             assert!(avb_protocol.read_rollback_index(0).is_err());
439         });
440     }
441 
442     #[test]
write_rollback_index_success()443     fn write_rollback_index_success() {
444         const EXPECTED_INDEX_LOCATION: usize = 1;
445         const EXPECTED_ROLLBACK_INDEX: u64 = 42;
446 
447         /// C callback implementation that checks the passed parameters and returns success.
448         unsafe extern "C" fn c_return_success(
449             _: *mut GblEfiAvbProtocol,
450             index_location: usize,
451             rollback_index: u64,
452         ) -> EfiStatus {
453             assert_eq!(index_location, EXPECTED_INDEX_LOCATION);
454             assert_eq!(rollback_index, EXPECTED_ROLLBACK_INDEX);
455             EFI_STATUS_SUCCESS
456         }
457 
458         let c_interface = GblEfiAvbProtocol {
459             write_rollback_index: Some(c_return_success),
460             ..Default::default()
461         };
462 
463         run_test_with_mock_protocol(c_interface, |avb_protocol| {
464             assert!(avb_protocol
465                 .write_rollback_index(EXPECTED_INDEX_LOCATION, EXPECTED_ROLLBACK_INDEX)
466                 .is_ok());
467         });
468     }
469 
470     #[test]
write_rollback_index_error_handled()471     fn write_rollback_index_error_handled() {
472         /// C callback implementation that returns an error.
473         unsafe extern "C" fn c_return_error(
474             _: *mut GblEfiAvbProtocol,
475             _: usize,
476             _: u64,
477         ) -> EfiStatus {
478             EFI_STATUS_INVALID_PARAMETER
479         }
480 
481         let c_interface =
482             GblEfiAvbProtocol { write_rollback_index: Some(c_return_error), ..Default::default() };
483 
484         run_test_with_mock_protocol(c_interface, |avb_protocol| {
485             assert!(avb_protocol.write_rollback_index(0, 0).is_err());
486         });
487     }
488 
489     #[test]
read_persistent_value_success()490     fn read_persistent_value_success() {
491         const EXPECTED_NAME: &CStr = c"test_key";
492         const EXPECTED_VALUE: &[u8] = b"test_value";
493 
494         /// C callback implementation.
495         ///
496         /// # Safety:
497         /// * Caller must guaranteed that `name` points to a valid null-terminated string.
498         /// * Caller must guaranteed that `value` points to non-null `value_size` sized bytes
499         ///   buffer.
500         /// * Caller must guaranteed that `value_size` points to a valid usize available to write
501         ///   value buffer.
502         unsafe extern "C" fn c_read_persistent_value_success(
503             _: *mut GblEfiAvbProtocol,
504             name: *const c_char,
505             value: *mut u8,
506             value_size: *mut usize,
507         ) -> EfiStatus {
508             assert_eq!(
509                 // SAFETY:
510                 // * `name` is a valid pointer to null-terminated string.
511                 unsafe { CStr::from_ptr(name) },
512                 EXPECTED_NAME
513             );
514             assert_eq!(
515                 // SAFETY:
516                 // * `value_size` is a valid non-null pointer to `usize` value.
517                 unsafe { ptr::read(value_size) },
518                 EXPECTED_VALUE.len()
519             );
520 
521             // SAFETY:
522             // * `value` is non-null pointer available for write.
523             let value_buffer = unsafe { slice::from_raw_parts_mut(value, EXPECTED_VALUE.len()) };
524             value_buffer.copy_from_slice(EXPECTED_VALUE);
525 
526             return EFI_STATUS_SUCCESS;
527         }
528 
529         let c_interface = GblEfiAvbProtocol {
530             read_persistent_value: Some(c_read_persistent_value_success),
531             ..Default::default()
532         };
533 
534         run_test_with_mock_protocol(c_interface, |avb_protocol| {
535             let mut buffer = [0u8; EXPECTED_VALUE.len()];
536 
537             assert_eq!(
538                 avb_protocol.read_persistent_value(EXPECTED_NAME, &mut buffer),
539                 Ok(EXPECTED_VALUE.len())
540             );
541             assert_eq!(&buffer, EXPECTED_VALUE);
542         });
543     }
544 
545     #[test]
read_persistent_value_buffer_too_small()546     fn read_persistent_value_buffer_too_small() {
547         const EXPECTED_BUFFER_SIZE: usize = 12;
548 
549         /// C callback implementation.
550         ///
551         /// # Safety:
552         /// * Caller must guaranteed that `value_size` points to a valid usize available to write
553         ///   value buffer.
554         unsafe extern "C" fn c_read_persistent_value_buffer_too_small(
555             _: *mut GblEfiAvbProtocol,
556             _: *const c_char,
557             _: *mut u8,
558             value_size: *mut usize,
559         ) -> EfiStatus {
560             // SAFETY:
561             // * `value_size` is a valid non-null pointer to `usize` value.
562             unsafe { ptr::write(value_size, EXPECTED_BUFFER_SIZE) };
563 
564             return EFI_STATUS_BUFFER_TOO_SMALL;
565         }
566 
567         let c_interface = GblEfiAvbProtocol {
568             read_persistent_value: Some(c_read_persistent_value_buffer_too_small),
569             ..Default::default()
570         };
571 
572         run_test_with_mock_protocol(c_interface, |avb_protocol| {
573             let mut buffer = [0u8; 0];
574 
575             assert_eq!(
576                 avb_protocol.read_persistent_value(c"name", &mut buffer),
577                 Err(Error::BufferTooSmall(Some(EXPECTED_BUFFER_SIZE)))
578             );
579         });
580     }
581 
582     #[test]
write_persistent_value_success()583     fn write_persistent_value_success() {
584         const EXPECTED_NAME: &CStr = c"test_key";
585         const EXPECTED_VALUE: &[u8] = b"test_value";
586 
587         /// C callback implementation.
588         ///
589         /// # Safety:
590         /// * Caller must guarantee that `name` points to a valid null-terminated string.
591         /// * Caller must guarantee that `value` points to a valid `value_size` sized bytes buffer.
592         unsafe extern "C" fn c_write_persistent_value_success(
593             _: *mut GblEfiAvbProtocol,
594             name: *const c_char,
595             value: *const u8,
596             value_size: usize,
597         ) -> EfiStatus {
598             assert_eq!(
599                 // SAFETY:
600                 // * `name` is a valid pointer to null-terminated string.
601                 unsafe { CStr::from_ptr(name) },
602                 EXPECTED_NAME
603             );
604             assert_eq!(value_size, EXPECTED_VALUE.len());
605 
606             // SAFETY:
607             // * `value` is a valid pointer to `value_size` bytes.
608             let value_buffer = unsafe { slice::from_raw_parts(value, value_size) };
609             assert_eq!(value_buffer, EXPECTED_VALUE);
610 
611             return EFI_STATUS_SUCCESS;
612         }
613 
614         let c_interface = GblEfiAvbProtocol {
615             write_persistent_value: Some(c_write_persistent_value_success),
616             ..Default::default()
617         };
618 
619         run_test_with_mock_protocol(c_interface, |avb_protocol| {
620             assert_eq!(
621                 avb_protocol.write_persistent_value(EXPECTED_NAME, Some(EXPECTED_VALUE)),
622                 Ok(())
623             );
624         });
625     }
626 
627     #[test]
write_persistent_value_delete()628     fn write_persistent_value_delete() {
629         const EXPECTED_NAME: &CStr = c"test_key";
630 
631         /// C callback implementation for deleting a persistent value.
632         ///
633         /// # Safety:
634         /// * Caller must guarantee that `name` points to a valid null-terminated string.
635         unsafe extern "C" fn c_write_persistent_value_delete(
636             _: *mut GblEfiAvbProtocol,
637             name: *const c_char,
638             value: *const u8,
639             value_size: usize,
640         ) -> EfiStatus {
641             assert_eq!(
642                 // SAFETY:
643                 // * `name` is a valid pointer to null-terminated string.
644                 unsafe { CStr::from_ptr(name) },
645                 EXPECTED_NAME
646             );
647             assert!(value.is_null());
648             assert_eq!(value_size, 0);
649 
650             return EFI_STATUS_SUCCESS;
651         }
652 
653         let c_interface = GblEfiAvbProtocol {
654             write_persistent_value: Some(c_write_persistent_value_delete),
655             ..Default::default()
656         };
657 
658         run_test_with_mock_protocol(c_interface, |avb_protocol| {
659             assert_eq!(avb_protocol.write_persistent_value(EXPECTED_NAME, None), Ok(()));
660         });
661     }
662 
663     #[test]
write_persistent_value_error_handled()664     fn write_persistent_value_error_handled() {
665         const EXPECTED_NAME: &CStr = c"test_key";
666         const EXPECTED_VALUE: &[u8] = b"test_value";
667 
668         /// C callback implementation that returns an error.
669         ///
670         /// # Safety:
671         /// * Caller must guarantee that `name` points to a valid null-terminated string.
672         unsafe extern "C" fn c_write_persistent_value_error(
673             _: *mut GblEfiAvbProtocol,
674             name: *const c_char,
675             _: *const u8,
676             _: usize,
677         ) -> EfiStatus {
678             assert_eq!(
679                 // SAFETY:
680                 // * `name` is a valid pointer to null-terminated string.
681                 unsafe { CStr::from_ptr(name) },
682                 EXPECTED_NAME
683             );
684 
685             return EFI_STATUS_INVALID_PARAMETER;
686         }
687 
688         let c_interface = GblEfiAvbProtocol {
689             write_persistent_value: Some(c_write_persistent_value_error),
690             ..Default::default()
691         };
692 
693         run_test_with_mock_protocol(c_interface, |avb_protocol| {
694             assert_eq!(
695                 avb_protocol.write_persistent_value(EXPECTED_NAME, Some(EXPECTED_VALUE)),
696                 Err(Error::InvalidInput),
697             );
698         });
699     }
700 }
701