• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_FASTBOOT_PROTOCOL`
16 
17 use crate::{
18     efi_call,
19     protocol::{Protocol, ProtocolInfo},
20 };
21 use core::{
22     ffi::{c_char, c_void, CStr},
23     ptr::{null, null_mut},
24     slice::from_raw_parts,
25     str::from_utf8,
26 };
27 use efi_types::{EfiGuid, GblEfiFastbootPolicy, GblEfiFastbootProtocol};
28 use liberror::{Error, Result};
29 
30 /// GBL_EFI_FASTBOOT_PROTOCOL
31 pub struct GblFastbootProtocol;
32 
33 // Note: this is an internal limitation due to the need to allocate
34 // fixed sized buffers for storing args in the iterator
35 // and in the wrapper for `GblEfiFastbootProtocol.get_var`.
36 const MAX_ARGS: usize = 16;
37 
38 impl ProtocolInfo for GblFastbootProtocol {
39     type InterfaceType = GblEfiFastbootProtocol;
40 
41     const GUID: EfiGuid =
42         EfiGuid::new(0xc67e48a0, 0x5eb8, 0x4127, [0xbe, 0x89, 0xdf, 0x2e, 0xd9, 0x3d, 0x8a, 0x9a]);
43 }
44 
45 /// Wrapper type for context parameter used in a fastboot local session.
46 pub struct LocalSessionContext(*mut c_void);
47 
48 impl Protocol<'_, GblFastbootProtocol> {
49     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_var`
get_var<'a>( &self, var: &CStr, args: impl Iterator<Item = &'a CStr> + Clone, out: &mut [u8], ) -> Result<usize>50     pub fn get_var<'a>(
51         &self,
52         var: &CStr,
53         args: impl Iterator<Item = &'a CStr> + Clone,
54         out: &mut [u8],
55     ) -> Result<usize> {
56         let mut args_arr = [null(); MAX_ARGS];
57         let num_args = safemath::SafeNum::from(1) + args.clone().count();
58         let args_arr = args_arr.get_mut(..num_args.try_into()?).ok_or(Error::InvalidInput)?;
59         args_arr[0] = var.as_ptr();
60         args_arr[1..].iter_mut().zip(args).for_each(|(l, r)| *l = r.as_ptr());
61         let mut bufsize = out.len();
62         // SAFETY:
63         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
64         // established by `Protocol::new()`.
65         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
66         unsafe {
67             efi_call!(
68                 @bufsize bufsize,
69                 self.interface()?.get_var,
70                 self.interface,
71                 args_arr.as_ptr(),
72                 args_arr.len(),
73                 out.as_mut_ptr(),
74                 &mut bufsize
75             )?
76         };
77         Ok(bufsize)
78     }
79 
80     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_var_all`
get_var_all(&self, mut cb: impl FnMut(&[&CStr], &CStr)) -> Result<()>81     pub fn get_var_all(&self, mut cb: impl FnMut(&[&CStr], &CStr)) -> Result<()> {
82         struct Callback<'a>(&'a mut dyn FnMut(&[&CStr], &CStr));
83 
84         /// Callback C function to be passed to the `get_var_all` function.
85         ///
86         /// # Safety
87         ///
88         /// * Caller must guarantee that `ctx` points to a valid instance of `Callback`, outlives
89         ///   the call, and not being referenced elsewhere.
90         /// * Caller must guarantee that `args` points to an array of NULL-terminated strings with
91         ///   size `len` and outlives the call.
92         /// * Caller must guarantee that `val` points to valid NULL-terminated strings and outlives
93         ///   the call.
94         unsafe extern "C" fn get_var_all_cb(
95             ctx: *mut c_void,
96             args: *const *const c_char,
97             len: usize,
98             val: *const c_char,
99         ) {
100             // SAFETY: By safety requirement of this function, `args` points to an array of
101             // NULL-terminated strings of length `len`.
102             let args =
103                 unsafe { from_raw_parts(args, len) }.iter().map(|v| unsafe { CStr::from_ptr(*v) });
104             // SAFETY: By requirement of this function, `ctx` points to a `Callback`.
105             let cb = unsafe { (ctx as *mut Callback).as_mut() }.unwrap();
106             // Checks number of arguments and stores them in an array.
107             let mut args_arr = [c""; MAX_ARGS];
108             match args_arr.get_mut(..len) {
109                 Some(v) => {
110                     v.iter_mut().zip(args).for_each(|(l, r)| *l = r);
111                     // SAFETY: By safety requirement of this function `val` points to a
112                     // NULL-terminated string.
113                     (cb.0)(&v, unsafe { CStr::from_ptr(val) })
114                 }
115                 _ => (cb.0)(&[c"<Number of arguments exceeds limit>"], c""),
116             }
117         }
118 
119         // SAFETY:
120         // *`self.interface()?` guarantees self.interface is non-null and points to a valid object
121         // * established by `Protocol::new()`.
122         // * The `ctx` parameter is a valid `Callback` object, outlives the call and not being
123         //   referenced elsewhere(declared inline at the parameter site).
124         // * By UEFI interface requirement, vendor firmware passes array of C strings to
125         //   `get_var_all_cb` that remains valid for the call.
126         unsafe {
127             efi_call!(
128                 self.interface()?.get_var_all,
129                 self.interface,
130                 &mut Callback(&mut cb) as *mut _ as _,
131                 Some(get_var_all_cb),
132             )?
133         };
134 
135         Ok(())
136     }
137 
138     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.run_oem_function()`
run_oem_function(&self, cmd: &str, buffer: &mut [u8]) -> Result<usize>139     pub fn run_oem_function(&self, cmd: &str, buffer: &mut [u8]) -> Result<usize> {
140         let mut bufsize = buffer.len();
141         if !buffer.is_empty() {
142             buffer[0] = 0;
143         }
144 
145         // SAFETY:
146         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
147         // established by `Protocol::new()`.
148         // No parameter is retained, all parameters outlive the call,
149         // and no pointers are Null.
150         unsafe {
151             efi_call!(
152                 @bufsize bufsize,
153                 self.interface()?.run_oem_function,
154                 self.interface,
155                 cmd.as_ptr(),
156                 cmd.len(),
157                 buffer.as_mut_ptr(),
158                 &mut bufsize,
159             )?
160         };
161         Ok(core::cmp::min(bufsize, buffer.iter().position(|c| *c == 0).unwrap_or(buffer.len())))
162     }
163 
164     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_policy()`
get_policy(&self) -> Result<GblEfiFastbootPolicy>165     pub fn get_policy(&self) -> Result<GblEfiFastbootPolicy> {
166         let mut policy: GblEfiFastbootPolicy = Default::default();
167 
168         // SAFETY:
169         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
170         // established by `Protocol::new()`.
171         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
172         unsafe { efi_call!(self.interface()?.get_policy, self.interface, &mut policy)? };
173 
174         Ok(policy)
175     }
176 
177     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.set_lock()`
set_lock(&self, flags: u64) -> Result<()>178     pub fn set_lock(&self, flags: u64) -> Result<()> {
179         // SAFETY:
180         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
181         // established by `Protocol::new()`.
182         // `self.interface` is an input parameter and will not be retained. It outlives the call.
183         unsafe { efi_call!(self.interface()?.set_lock, self.interface, flags) }
184     }
185 
186     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.clear_lock()`
clear_lock(&self, flags: u64) -> Result<()>187     pub fn clear_lock(&self, flags: u64) -> Result<()> {
188         // SAFETY:
189         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
190         // established by `Protocol::new()`.
191         // `self.interface` is an input parameter and will not be retained. It outlives the call.
192         unsafe { efi_call!(self.interface()?.clear_lock, self.interface, flags) }
193     }
194 
195     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_partition_permissions()`
get_partition_permissions(&self, part_name: &str) -> Result<u64>196     pub fn get_partition_permissions(&self, part_name: &str) -> Result<u64> {
197         let mut permissions = 0u64;
198 
199         // SAFETY:
200         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
201         // established by `Protocol::new()`.
202         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
203         unsafe {
204             efi_call!(
205                 self.interface()?.get_partition_permissions,
206                 self.interface,
207                 part_name.as_ptr(),
208                 part_name.len(),
209                 &mut permissions
210             )?
211         };
212         Ok(permissions)
213     }
214 
215     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.start_local_session()`
start_local_session(&self) -> Result<LocalSessionContext>216     pub fn start_local_session(&self) -> Result<LocalSessionContext> {
217         let mut ctx = null_mut();
218         // SAFETY:
219         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
220         // established by `Protocol::new()`.
221         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
222         unsafe { efi_call!(self.interface()?.start_local_session, self.interface, &mut ctx)? };
223         Ok(LocalSessionContext(ctx))
224     }
225 
226     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.update_local_session()`
update_local_session(&self, ctx: &LocalSessionContext, out: &mut [u8]) -> Result<usize>227     pub fn update_local_session(&self, ctx: &LocalSessionContext, out: &mut [u8]) -> Result<usize> {
228         let mut bufsize = out.len();
229 
230         // SAFETY:
231         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
232         // established by `Protocol::new()`.
233         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
234         unsafe {
235             efi_call!(
236                 @bufsize bufsize,
237                 self.interface()?.update_local_session,
238                 self.interface,
239                 ctx.0, out.as_mut_ptr(),
240                 &mut bufsize)?
241         };
242         Ok(bufsize)
243     }
244 
245     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.close_local_session()`
close_local_session(&self, ctx: &LocalSessionContext) -> Result<()>246     pub fn close_local_session(&self, ctx: &LocalSessionContext) -> Result<()> {
247         // SAFETY:
248         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
249         // established by `Protocol::new()`.
250         // No parameters are retained, all parameters outlive the call, and no pointers are Null.
251         unsafe { efi_call!(self.interface()?.close_local_session, self.interface, ctx.0) }
252     }
253 
254     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.wipe_user_data()`
wipe_user_data(&self) -> Result<()>255     pub fn wipe_user_data(&self) -> Result<()> {
256         // SAFETY:
257         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
258         // established by `Protocol::new()`.
259         // `self.interface` is an input parameter and will not be retained. It outlives the call.
260         unsafe { efi_call!(self.interface()?.wipe_user_data, self.interface) }
261     }
262 
263     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.should_stop_in_fastboot()`
should_stop_in_fastboot(&self) -> bool264     pub fn should_stop_in_fastboot(&self) -> bool {
265         let Ok(interface) = self.interface() else { return false };
266 
267         let Some(should_stop_in_fastboot) = interface.should_stop_in_fastboot else { return false };
268         // SAFETY:
269         // `self.interface` is non-null due to check above.
270         // `self.interface` is an input parameter and will not be retained. It outlives the call.
271         // `should_stop_in_fastboot` is non-null due to check above.
272         // `should_stop_in_fastboot` is responsible for validating its input.
273         unsafe { should_stop_in_fastboot(self.interface) }
274     }
275 
276     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.serial_number`
serial_number(&self) -> Result<&str>277     pub fn serial_number(&self) -> Result<&str> {
278         let serial_number = &self.interface()?.serial_number;
279         let null_idx = serial_number.iter().position(|c| *c == 0).unwrap_or(serial_number.len());
280         Ok(from_utf8(&serial_number[..null_idx])?)
281     }
282 
283     /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.version`
version(&self) -> Result<u32>284     pub fn version(&self) -> Result<u32> {
285         Ok(self.interface()?.version)
286     }
287 }
288 
289 #[cfg(test)]
290 mod test {
291     use super::*;
292     use crate::{
293         protocol::GetVarAllCallback,
294         test::{generate_protocol, run_test},
295         DeviceHandle, EfiEntry,
296     };
297     use core::{
298         ffi::{c_void, CStr},
299         ptr::null_mut,
300         slice::from_raw_parts_mut,
301     };
302     use efi_types::{EfiStatus, EFI_STATUS_SUCCESS};
303 
304     #[test]
test_serial_number()305     fn test_serial_number() {
306         run_test(|image_handle, systab_ptr| {
307             // Serial number is shorter than max length and contains non-ASCII unicode.
308             let austria = "Österreich";
309 
310             let mut fb = GblEfiFastbootProtocol { ..Default::default() };
311             fb.serial_number.as_mut_slice()[..austria.len()].copy_from_slice(austria.as_bytes());
312             let efi_entry = EfiEntry { image_handle, systab_ptr };
313 
314             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
315 
316             // Don't include trailing Null terminators.
317             assert_eq!(protocol.serial_number().unwrap().len(), 11);
318             assert_eq!(protocol.serial_number().unwrap(), austria);
319         });
320     }
321 
322     #[test]
test_serial_number_max_length()323     fn test_serial_number_max_length() {
324         run_test(|image_handle, systab_ptr| {
325             let mut fb = GblEfiFastbootProtocol { serial_number: [71u8; 32], ..Default::default() };
326             let efi_entry = EfiEntry { image_handle, systab_ptr };
327 
328             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
329 
330             assert_eq!(protocol.serial_number().unwrap().len(), 32);
331             assert_eq!(protocol.serial_number().unwrap(), "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
332         });
333     }
334 
335     #[test]
test_serial_number_invalid_utf8()336     fn test_serial_number_invalid_utf8() {
337         run_test(|image_handle, systab_ptr| {
338             let mut fb = GblEfiFastbootProtocol { serial_number: [0xF8; 32], ..Default::default() };
339             let efi_entry = EfiEntry { image_handle, systab_ptr };
340 
341             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
342 
343             assert_eq!(protocol.serial_number(), Err(Error::InvalidInput));
344         });
345     }
346 
347     #[test]
test_get_var()348     fn test_get_var() {
349         /// # Safety
350         ///
351         /// * Caller must guarantee that `args` points to an array of NULL-terminated strings with
352         ///   size `num_args`.
353         /// * Caller must guarantee that `out` points to a `[u8]`
354         /// * Caller must guarantee that `out_size` points to a `usize`
355         unsafe extern "C" fn get_var_test(
356             _: *mut GblEfiFastbootProtocol,
357             args: *const *const c_char,
358             num_args: usize,
359             out: *mut u8,
360             out_size: *mut usize,
361         ) -> EfiStatus {
362             // SAFETY: By safety requirement of this function, `args` points to an array of
363             // NULL-terminated strings with length `num_args`.
364             let args = unsafe { from_raw_parts(args, num_args) }
365                 .iter()
366                 .map(|v| unsafe { CStr::from_ptr(*v) })
367                 .collect::<Vec<_>>();
368             assert_eq!(args, [c"var", c"arg1", c"arg2"]);
369             // SAFETY: By safety requirement of this function, `out_size` points to a `usize`;
370             let out_size = &mut unsafe { *out_size };
371             // SAFETY: By safety requirement of this function, `out` points to a `[u8]`;
372             let out = unsafe { from_raw_parts_mut(out, *out_size) };
373             out.clone_from_slice(c"val".to_bytes());
374             *out_size = c"val".to_bytes().len();
375             EFI_STATUS_SUCCESS
376         }
377 
378         run_test(|image_handle, systab_ptr| {
379             let mut fb =
380                 GblEfiFastbootProtocol { get_var: Some(get_var_test), ..Default::default() };
381             let efi_entry = EfiEntry { image_handle, systab_ptr };
382             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
383             let mut out = [0u8; 3];
384             let args = [c"arg1", c"arg2"];
385             assert_eq!(protocol.get_var(c"var", args.iter().copied(), &mut out[..]), Ok(3));
386             assert_eq!(&out, b"val");
387         });
388     }
389 
390     #[test]
test_get_var_all()391     fn test_get_var_all() {
392         /// # Safety
393         ///
394         /// * Caller must guarantee that `ctx` points to data needed by function pointer `cb`.
395         unsafe extern "C" fn test_get_var_all(
396             _: *mut GblEfiFastbootProtocol,
397             ctx: *mut c_void,
398             cb: GetVarAllCallback,
399         ) -> EfiStatus {
400             for (args, val) in [
401                 ([c"foo", c"foo_arg1", c"foo_arg2"], c"foo_val"),
402                 ([c"bar", c"bar_arg1", c"bar_arg2"], c"bar_val"),
403             ] {
404                 let args = args.map(|v| v.as_ptr());
405                 // SAFETY:
406                 // * `args` is an array of NULL-terminated strings. `val` is a NULL-terminated
407                 //   string.
408                 // * By safety requirement of this function, `ctx` points to a valid type of data
409                 //   needed by `cb`.
410                 unsafe { (cb.unwrap())(ctx, args.as_ptr(), args.len(), val.as_ptr()) };
411             }
412             EFI_STATUS_SUCCESS
413         }
414         run_test(|image_handle, systab_ptr| {
415             let mut fb = GblEfiFastbootProtocol {
416                 get_var_all: Some(test_get_var_all),
417                 ..Default::default()
418             };
419             let efi_entry = EfiEntry { image_handle, systab_ptr };
420             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
421             let mut out = vec![];
422             protocol
423                 .get_var_all(|args, val| {
424                     let args_str =
425                         args.iter().map(|v| v.to_str().unwrap()).collect::<Vec<_>>().join(":");
426                     out.push(format!("{args_str}: {}", val.to_str().unwrap()))
427                 })
428                 .unwrap();
429             assert_eq!(out, ["foo:foo_arg1:foo_arg2: foo_val", "bar:bar_arg1:bar_arg2: bar_val",])
430         });
431     }
432 
433     #[test]
test_get_var_all_exceeds_max_arguments()434     fn test_get_var_all_exceeds_max_arguments() {
435         /// # Safety
436         ///
437         /// * Caller must guarantee that `ctx` points to data needed by function pointer `cb`.
438         unsafe extern "C" fn test_get_var_all(
439             _: *mut GblEfiFastbootProtocol,
440             ctx: *mut c_void,
441             cb: GetVarAllCallback,
442         ) -> EfiStatus {
443             let args = [c"".as_ptr(); MAX_ARGS + 1];
444             // SAFETY:
445             // * `args` is an array of NULL-terminated strings. `val` is a NULL-terminated
446             //   string.
447             // * By safety requirement of this function, `ctx` points to a valid type of data
448             //   needed by `cb`.
449             unsafe { (cb.unwrap())(ctx, args.as_ptr(), args.len(), c"".as_ptr()) };
450             EFI_STATUS_SUCCESS
451         }
452         run_test(|image_handle, systab_ptr| {
453             let mut fb = GblEfiFastbootProtocol {
454                 get_var_all: Some(test_get_var_all),
455                 ..Default::default()
456             };
457             let efi_entry = EfiEntry { image_handle, systab_ptr };
458             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
459             let mut out = vec![];
460             protocol
461                 .get_var_all(|args, val| {
462                     let args_str =
463                         args.iter().map(|v| v.to_str().unwrap()).collect::<Vec<_>>().join(":");
464                     out.push(format!("{args_str}: {}", val.to_str().unwrap()))
465                 })
466                 .unwrap();
467             assert_eq!(out, ["<Number of arguments exceeds limit>: "])
468         });
469     }
470 
471     #[test]
test_should_stop_in_fastboot()472     fn test_should_stop_in_fastboot() {
473         unsafe extern "C" fn test_should_stop_in_fastboot(_: *mut GblEfiFastbootProtocol) -> bool {
474             true
475         }
476         run_test(|image_handle, systab_ptr| {
477             let mut fb = GblEfiFastbootProtocol {
478                 should_stop_in_fastboot: Some(test_should_stop_in_fastboot),
479                 ..Default::default()
480             };
481             let efi_entry = EfiEntry { image_handle, systab_ptr };
482             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
483             assert!(protocol.should_stop_in_fastboot());
484         });
485     }
486 
487     #[test]
test_should_stop_in_fastboot_no_interface()488     fn test_should_stop_in_fastboot_no_interface() {
489         run_test(|image_handle, systab_ptr| {
490             let efi_entry = EfiEntry { image_handle, systab_ptr };
491             // SAFETY: `protocol.interface` is explicitly null for testing.
492             let protocol = unsafe {
493                 Protocol::<GblFastbootProtocol>::new(
494                     DeviceHandle::new(null_mut()),
495                     null_mut(),
496                     &efi_entry,
497                 )
498             };
499             assert!(!protocol.should_stop_in_fastboot());
500         });
501     }
502 
503     #[test]
test_should_stop_in_fastboot_no_method()504     fn test_should_stop_in_fastboot_no_method() {
505         run_test(|image_handle, systab_ptr| {
506             let mut fb: GblEfiFastbootProtocol = Default::default();
507             let efi_entry = EfiEntry { image_handle, systab_ptr };
508             let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb);
509             assert!(!protocol.should_stop_in_fastboot());
510         });
511     }
512 }
513