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_DT_FIXUP_PROTOCOL`. 16 17 use crate::efi_call; 18 use crate::protocol::{Protocol, ProtocolInfo}; 19 use efi_types::{EfiDtFixupProtocol, EfiGuid, EFI_DT_APPLY_FIXUPS}; 20 use liberror::Result; 21 22 /// `EFI_DT_FIXUP_PROTOCOL` implementation. 23 pub struct DtFixupProtocol; 24 25 impl ProtocolInfo for DtFixupProtocol { 26 type InterfaceType = EfiDtFixupProtocol; 27 28 const GUID: EfiGuid = 29 EfiGuid::new(0xe617d64c, 0xfe08, 0x46da, [0xf4, 0xdc, 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00]); 30 } 31 32 // Protocol interface wrappers. 33 impl Protocol<'_, DtFixupProtocol> { 34 /// Wraps `EFI_DT_FIXUP_PROTOCOL.fixup()`. fixup(&self, device_tree: &mut [u8]) -> Result<()>35 pub fn fixup(&self, device_tree: &mut [u8]) -> Result<()> { 36 let mut buffer_size = device_tree.len(); 37 38 // SAFETY: 39 // * `self.interface()?` guarantees self.interface is non-null and points to a valid object 40 // established by `Protocol::new()`. 41 // * `device_tree` is non-null buffer available for write, used only within the call. 42 // * `buffer_size` is non-null usize buffer available for write, used only within the call. 43 unsafe { 44 efi_call!( 45 @bufsize buffer_size, 46 self.interface()?.fixup, 47 self.interface, 48 device_tree.as_mut_ptr() as _, 49 &mut buffer_size, 50 EFI_DT_APPLY_FIXUPS 51 )?; 52 } 53 54 Ok(()) 55 } 56 } 57 58 #[cfg(test)] 59 mod test { 60 use super::*; 61 62 use crate::test::run_test_with_mock_protocol; 63 use efi_types::{EfiStatus, EFI_STATUS_BUFFER_TOO_SMALL, EFI_STATUS_SUCCESS}; 64 use liberror::Error; 65 use std::{ffi::c_void, slice}; 66 67 #[test] fixup_device_tree_updated()68 fn fixup_device_tree_updated() { 69 // Don't check actual FDT content for simplicity. 70 const DEVICE_TREE_BUFFER: &[u8] = b"this_is_device_tree"; 71 const UPDATED_DEVICE_TREE_BUFFER: &[u8] = b"this_is_device_trie"; 72 73 // C callback implementation to modify provided FDT to UPDATED_DEVICE_TREE_BUFFER. 74 unsafe extern "C" fn c_modify( 75 _: *mut EfiDtFixupProtocol, 76 device_tree: *mut c_void, 77 buffer_size: *mut usize, 78 flags: u32, 79 ) -> EfiStatus { 80 assert_eq!(flags, EFI_DT_APPLY_FIXUPS); 81 // SAFETY: 82 // * `device_tree` is a valid pointer to the writtable buffer at least `buffer_size` 83 // size. 84 // * `buffer_size` is a valid pointer to usize. 85 let fdt_buffer = 86 unsafe { slice::from_raw_parts_mut(device_tree as *mut u8, *buffer_size) }; 87 assert_eq!(fdt_buffer, DEVICE_TREE_BUFFER); 88 89 fdt_buffer.copy_from_slice(UPDATED_DEVICE_TREE_BUFFER); 90 91 EFI_STATUS_SUCCESS 92 } 93 94 let c_interface = EfiDtFixupProtocol { fixup: Some(c_modify), ..Default::default() }; 95 96 run_test_with_mock_protocol(c_interface, |dt_fixup_protocol| { 97 let mut fdt_buffer: Vec<u8> = DEVICE_TREE_BUFFER.to_vec(); 98 99 assert!(dt_fixup_protocol.fixup(&mut fdt_buffer[..]).is_ok()); 100 assert_eq!(&fdt_buffer[..], UPDATED_DEVICE_TREE_BUFFER); 101 }); 102 } 103 104 #[test] fixup_device_tree_fixup_buffer_too_small()105 fn fixup_device_tree_fixup_buffer_too_small() { 106 const EXPECTED_REQUESTED_FIXUP_SIZE: usize = 256; 107 // C callback implementation to return an error. 108 unsafe extern "C" fn c_error( 109 _: *mut EfiDtFixupProtocol, 110 _: *mut c_void, 111 buffer_size: *mut usize, 112 _: u32, 113 ) -> EfiStatus { 114 // SAFETY: 115 // * `buffer_size` is a valid pointer to writtable usize buffer. 116 unsafe { 117 *buffer_size = EXPECTED_REQUESTED_FIXUP_SIZE; 118 } 119 EFI_STATUS_BUFFER_TOO_SMALL 120 } 121 122 let c_interface = EfiDtFixupProtocol { fixup: Some(c_error), ..Default::default() }; 123 124 run_test_with_mock_protocol(c_interface, |dt_fixup_protocol| { 125 let mut fdt_buffer = [0u8; 128]; 126 127 assert_eq!( 128 dt_fixup_protocol.fixup(&mut fdt_buffer[..]), 129 Err(Error::BufferTooSmall(Some(EXPECTED_REQUESTED_FIXUP_SIZE))), 130 ); 131 }); 132 } 133 } 134