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