xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/src/protocol/dt_fixup.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 `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