xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/mocks/protocol.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 //! Mock protocols.
16 //!
17 //! The structure of these sub-modules must match the libefi structure so that the code can refer
18 //! to either one using the same path.
19 
20 use crate::{DeviceHandle, MOCK_EFI};
21 use core::ffi::CStr;
22 use core::fmt::Write;
23 use efi::protocol::gbl_efi_image_loading::EfiImageBuffer;
24 use efi_types::{
25     EfiInputKey, GblEfiAvbKeyValidationStatus, GblEfiAvbVerificationResult, GblEfiImageInfo,
26     GblEfiPartitionName, GblEfiVerifiedDeviceTree,
27 };
28 use liberror::Result;
29 use mockall::mock;
30 
31 /// Mock device_path module.
32 pub mod device_path {
33     use super::*;
34 
35     mock! {
36         /// Mock [efi::DevicePathProtocol].
37         pub DevicePathProtocol {}
38     }
39     /// Map to the libefi name so code under test can just use one name.
40     pub type DevicePathProtocol = MockDevicePathProtocol;
41 
42     mock! {
43         /// Mock [efi::DevicePathToTextProtocol].
44         pub DevicePathToTextProtocol {
45             /// Returns a [MockDevicePathText].
46             ///
47             /// Lifetimes are a little difficult to mock perfectly, so here we can only allow a
48             /// `'static` return value.
49             pub fn convert_device_path_to_text(
50                 &self,
51                 device_path: &MockDevicePathProtocol,
52                 display_only: bool,
53                 allow_shortcuts: bool,
54             ) -> Result<MockDevicePathText<'static>>;
55         }
56     }
57     /// Map to the libefi name so code under test can just use one name.
58     pub type DevicePathToTextProtocol = MockDevicePathToTextProtocol;
59 
60     mock! {
61         /// Mock [efi::DevicePathText].
62         pub DevicePathText<'a> {
63             /// Returns the text, which is data-only so isn't mocked.
64             pub fn text(&self) -> Option<&'a [u16]>;
65         }
66     }
67     /// Map to the libefi name so code under test can just use one name.
68     pub type DevicePathText<'a> = MockDevicePathText<'a>;
69 }
70 
71 /// Mock loaded_image protocol.
72 pub mod loaded_image {
73     use super::*;
74 
75     mock! {
76         /// Mock [efi::LoadedImageProtocol].
77         pub LoadedImageProtocol {
78             /// Returns a real [efi::DeviceHandle], which is data-only so isn't mocked.
79             pub fn device_handle(&self) -> Result<DeviceHandle>;
80         }
81     }
82     /// Map to the libefi name so code under test can just use one name.
83     pub type LoadedImageProtocol = MockLoadedImageProtocol;
84 }
85 
86 /// Mock simple_text_input module.
87 pub mod simple_text_input {
88     use super::*;
89 
90     mock! {
91         /// Mock [efi::SimpleTextInputProtocol].
92         pub SimpleTextInputProtocol {
93             /// Returns an [EfiInputKey], which is data-only so isn't mocked.
94             pub fn read_key_stroke(&self) -> Result<Option<EfiInputKey>>;
95         }
96     }
97     /// Map to the libefi name so code under test can just use one name.
98     pub type SimpleTextInputProtocol = MockSimpleTextInputProtocol;
99 }
100 
101 /// Mock simple_text_output module.
102 pub mod simple_text_output {
103     use super::*;
104 
105     mock! {
106         /// Mock [efi::SimpleTextOutputProtocol].
107         pub SimpleTextOutputProtocol {}
108 
109         impl Write for SimpleTextOutputProtocol {
110             fn write_str(&mut self, s: &str) -> core::fmt::Result;
111         }
112     }
113     /// Map to the libefi name so code under test can just use one name.
114     pub type SimpleTextOutputProtocol = MockSimpleTextOutputProtocol;
115 
116     /// Returns a [MockSimpleTextOutputProtocol] that forwards all calls to `MOCK_EFI`.
passthrough_con_out() -> MockSimpleTextOutputProtocol117     pub fn passthrough_con_out() -> MockSimpleTextOutputProtocol {
118         let mut con_out = MockSimpleTextOutputProtocol::default();
119         con_out.expect_write_str().returning(|s| {
120             MOCK_EFI.with_borrow_mut(|efi| efi.as_mut().unwrap().con_out.write_str(s))
121         });
122         con_out
123     }
124 
125     /// While this mock itself isn't necessarily thread-local, passing through to the thread-local
126     /// state is our primary use case, so we just disallow [Send] entirely.
127     impl !Send for MockSimpleTextOutputProtocol {}
128 }
129 
130 /// Mock image_loading protocol.
131 pub mod gbl_efi_image_loading {
132     use super::*;
133 
134     mock! {
135         /// Mock [efi::ImageLoadingProtocol].
136         pub GblImageLoadingProtocol {
137             /// Returns [EfiImageBuffer] matching `gbl_image_info`
138             pub fn get_buffer(&self, gbl_image_info: &GblEfiImageInfo) -> Result<EfiImageBuffer>;
139 
140             /// Returns number of partitions to be provided via `get_verify_partitions()`, and thus
141             /// expected size of `partition_name` slice.
142             pub fn get_verify_partitions_count(&self) -> Result<usize>;
143 
144             /// Returns number of partition names written to `partition_name` slice.
145             pub fn get_verify_partitions(
146                 &self,
147                 partition_names: &mut [GblEfiPartitionName]
148             ) -> Result<usize>;
149         }
150     }
151 
152     /// Map to the libefi name so code under test can just use one name.
153     pub type GblImageLoadingProtocol = MockGblImageLoadingProtocol;
154 }
155 
156 /// Mock os_configuration protocol.
157 pub mod gbl_efi_os_configuration {
158     use super::*;
159 
160     mock! {
161         /// Mock [efi::OsConfigurationProtocol].
162         pub GblOsConfigurationProtocol {
163             /// Wraps `GBL_EFI_OS_CONFIGURATION_PROTOCOL.fixup_kernel_commandline()`
164             pub fn fixup_kernel_commandline(
165                 &self,
166                 commandline: &CStr,
167                 fixup: &[u8],
168             ) -> Result<()>;
169 
170             /// Wraps `GBL_EFI_OS_CONFIGURATION_PROTOCOL.fixup_bootconfig()`
171             pub fn fixup_bootconfig(
172                 &self,
173                 bootconfig: &[u8],
174                 fixup: &mut [u8],
175             ) -> Result<usize>;
176 
177             /// Wraps `GBL_EFI_OS_CONFIGURATION_PROTOCOL.select_device_trees()`
178             pub fn select_device_trees(
179                 &self,
180                 components: &mut [GblEfiVerifiedDeviceTree],
181             ) -> Result<()>;
182         }
183     }
184 
185     /// Map to the libefi name so code under test can just use one name.
186     pub type GblOsConfigurationProtocol = MockGblOsConfigurationProtocol;
187 }
188 
189 /// Mock dt_fixup protocol.
190 pub mod dt_fixup {
191     use super::*;
192 
193     mock! {
194         /// Mock [efi::DtFixupProtocol].
195         pub DtFixupProtocol {
196             /// Wraps `EFI_DT_FIXUP_PROTOCOL.fixup()`
197             pub fn fixup(&self, device_tree: &mut [u8]) -> Result<()>;
198         }
199     }
200 
201     /// Map to the libefi name so code under test can just use one name.
202     pub type DtFixupProtocol = MockDtFixupProtocol;
203 }
204 
205 /// Mock avb protocol.
206 pub mod gbl_efi_avb {
207     use super::*;
208 
209     /// Mock implementation of `GBL_EFI_AVB_PROTOCOL`.
210     /// We use a custom mock implementation instead of relying on `mockall` due to its limitations
211     /// regarding argument lifetimes. Specifically, in this case, `mockall` requires the
212     /// `validate_vbmeta_public_key.public_key_metadata` argument to have a `'static` lifetime,
213     /// which is not practical for our use case.
214     #[derive(Clone, Default)]
215     pub struct GblAvbProtocol {
216         /// Expected return value from `validate_vbmeta_public_key`.
217         pub validate_vbmeta_public_key_result: Option<Result<GblEfiAvbKeyValidationStatus>>,
218         /// Expected return value from `read_is_device_unlocked`.
219         pub read_is_device_unlocked_result: Option<Result<bool>>,
220         /// Expected return value from `read_rollback_index`.
221         pub read_rollback_index_result: Option<Result<u64>>,
222         /// Expected return value from `write_rollback_index`.
223         pub write_rollback_index_result: Option<Result<()>>,
224         /// Expected return value from `read_persistent_value`.
225         pub read_persistent_value_result: Option<Result<usize>>,
226         /// Expected return value from `write_persistent_value`.
227         pub write_persistent_value_result: Option<Result<()>>,
228     }
229 
230     impl GblAvbProtocol {
231         /// Wraps `GBL_EFI_AVB_PROTOCOL.validate_vbmeta_public_key()`.
validate_vbmeta_public_key( &self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> Result<GblEfiAvbKeyValidationStatus>232         pub fn validate_vbmeta_public_key(
233             &self,
234             _public_key: &[u8],
235             _public_key_metadata: Option<&[u8]>,
236         ) -> Result<GblEfiAvbKeyValidationStatus> {
237             self.validate_vbmeta_public_key_result.unwrap()
238         }
239 
240         /// Wraps `GBL_EFI_AVB_PROTOCOL.read_is_device_unlocked()`.
read_is_device_unlocked(&self) -> Result<bool>241         pub fn read_is_device_unlocked(&self) -> Result<bool> {
242             self.read_is_device_unlocked_result.unwrap()
243         }
244 
245         /// Wraps `GBL_EFI_AVB_PROTOCOL.read_rollback_index()`.
read_rollback_index(&self, _index_location: usize) -> Result<u64>246         pub fn read_rollback_index(&self, _index_location: usize) -> Result<u64> {
247             self.read_rollback_index_result.unwrap()
248         }
249 
250         /// Wraps `GBL_EFI_AVB_PROTOCOL.write_rollback_index()`.
write_rollback_index( &self, _index_location: usize, _rollback_index: u64, ) -> Result<()>251         pub fn write_rollback_index(
252             &self,
253             _index_location: usize,
254             _rollback_index: u64,
255         ) -> Result<()> {
256             self.write_rollback_index_result.unwrap()
257         }
258 
259         /// Wraps `GBL_EFI_AVB_PROTOCOL.read_persistent_value()`.
read_persistent_value(&self, _name: &CStr, _value: &mut [u8]) -> Result<usize>260         pub fn read_persistent_value(&self, _name: &CStr, _value: &mut [u8]) -> Result<usize> {
261             self.read_persistent_value_result.unwrap()
262         }
263 
264         /// Wraps `GBL_EFI_AVB_PROTOCOL.write_persistent_value()`.
write_persistent_value(&self, _name: &CStr, _value: Option<&[u8]>) -> Result<()>265         pub fn write_persistent_value(&self, _name: &CStr, _value: Option<&[u8]>) -> Result<()> {
266             self.write_persistent_value_result.unwrap()
267         }
268 
269         /// Wraps `GBL_EFI_AVB_PROTOCOL.handle_verification_result()`.
handle_verification_result( &self, _verification_result: &GblEfiAvbVerificationResult, ) -> Result<()>270         pub fn handle_verification_result(
271             &self,
272             _verification_result: &GblEfiAvbVerificationResult,
273         ) -> Result<()> {
274             unimplemented!();
275         }
276     }
277 }
278 
279 /// Mock gbl_efi_fastboot protocol.
280 pub mod gbl_efi_fastboot {
281     use super::*;
282 
283     mock! {
284         /// Mock [efi::protocol::gbl_efi_fastboot::Var].
285         pub Var {
286             /// Get name, arguments and corresponding value.
287             pub fn get<'s>(&self, out: &mut [u8])
288                 -> Result<(&'static str, [&'static str; 1], &'static str)>;
289         }
290     }
291 
292     /// Mock [efi::GblFastbootProtocol].
293     pub struct GblFastbootProtocol {}
294 
295     impl GblFastbootProtocol {
296         /// Protocol<'_, GblFastbootProtocol>::get_var.
get_var<'a>( &self, _: &CStr, _: impl Iterator<Item = &'a CStr> + Clone, _: &mut [u8], ) -> Result<usize>297         pub fn get_var<'a>(
298             &self,
299             _: &CStr,
300             _: impl Iterator<Item = &'a CStr> + Clone,
301             _: &mut [u8],
302         ) -> Result<usize> {
303             unimplemented!()
304         }
305 
306         /// Protocol<'_, GblFastbootProtocol>::get_var_all.
get_var_all(&self, _: impl FnMut(&[&CStr], &CStr)) -> Result<()>307         pub fn get_var_all(&self, _: impl FnMut(&[&CStr], &CStr)) -> Result<()> {
308             unimplemented!()
309         }
310     }
311 
312     /// Map to the libefi name so code under test can just use one name.
313     pub type Var = MockVar;
314 }
315 
316 /// Mock gbl_efi_ab_slot
317 pub mod gbl_efi_ab_slot {
318     use super::*;
319     use efi::protocol::gbl_efi_ab_slot::GblSlot;
320     use efi_types::{GblEfiBootReason, GblEfiSlotMetadataBlock};
321 
322     mock! {
323         /// Mock of [GblSlotProtocol]
324         pub GblSlotProtocol {
325             /// Mock of GblSlotProtocol::get_current_slot.
326             pub fn get_current_slot(&self) -> Result<GblSlot>;
327 
328             /// Mock of GblSlotProtocol::get_next_slot.
329             pub fn get_next_slot(&self, mark_boot_attempt: bool) -> Result<GblSlot>;
330 
331             /// Mock of GblSlotProtocol::load_boot_data.
332             pub fn load_boot_data(&self) -> Result<GblEfiSlotMetadataBlock>;
333 
334             /// Mock of GblSlotProtocol::set_active_slot.
335             pub fn set_active_slot(&self, idx: u8) -> Result<()>;
336 
337             /// Mock of GblSlotProtocol::set_boot_reason.
338             pub fn set_boot_reason(&self, reason: GblEfiBootReason, subreason: &[u8]) -> Result<()>;
339 
340             /// Mock of GblSlotProtocol::get_boot_reason.
341             pub fn get_boot_reason(&self, subreason: &mut [u8]) -> Result<(GblEfiBootReason, usize)>;
342         }
343     }
344 
345     /// Map to the libefi name so code under test can just use one name.
346     pub type GblSlotProtocol = MockGblSlotProtocol;
347 }
348