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, 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 impl Protocol<'_, GblFastbootProtocol> { 46 /// 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>47 pub fn get_var<'a>( 48 &self, 49 var: &CStr, 50 args: impl Iterator<Item = &'a CStr> + Clone, 51 out: &mut [u8], 52 ) -> Result<usize> { 53 let mut args_arr = [null(); MAX_ARGS]; 54 let num_args = safemath::SafeNum::from(1) + args.clone().count(); 55 let args_arr = args_arr.get_mut(..num_args.try_into()?).ok_or(Error::InvalidInput)?; 56 args_arr[0] = var.as_ptr(); 57 args_arr[1..].iter_mut().zip(args).for_each(|(l, r)| *l = r.as_ptr()); 58 let mut bufsize = out.len(); 59 // SAFETY: 60 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 61 // established by `Protocol::new()`. 62 // No parameters are retained, all parameters outlive the call, and no pointers are Null. 63 unsafe { 64 efi_call!( 65 @bufsize bufsize, 66 self.interface()?.get_var, 67 self.interface, 68 args_arr.as_ptr(), 69 args_arr.len(), 70 out.as_mut_ptr(), 71 &mut bufsize 72 )? 73 }; 74 Ok(bufsize) 75 } 76 77 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_var_all` get_var_all(&self, mut cb: impl FnMut(&[&CStr], &CStr)) -> Result<()>78 pub fn get_var_all(&self, mut cb: impl FnMut(&[&CStr], &CStr)) -> Result<()> { 79 struct Callback<'a>(&'a mut dyn FnMut(&[&CStr], &CStr)); 80 81 /// Callback C function to be passed to the `get_var_all` function. 82 /// 83 /// # Safety 84 /// 85 /// * Caller must guarantee that `ctx` points to a valid instance of `Callback`, outlives 86 /// the call, and not being referenced elsewhere. 87 /// * Caller must guarantee that `args` points to an array of NULL-terminated strings with 88 /// size `len` and outlives the call. 89 /// * Caller must guarantee that `val` points to valid NULL-terminated strings and outlives 90 /// the call. 91 unsafe extern "C" fn get_var_all_cb( 92 ctx: *mut c_void, 93 args: *const *const c_char, 94 len: usize, 95 val: *const c_char, 96 ) { 97 // SAFETY: By safety requirement of this function, `args` points to an array of 98 // NULL-terminated strings of length `len`. 99 let args = 100 unsafe { from_raw_parts(args, len) }.iter().map(|v| unsafe { CStr::from_ptr(*v) }); 101 // SAFETY: By requirement of this function, `ctx` points to a `Callback`. 102 let cb = unsafe { (ctx as *mut Callback).as_mut() }.unwrap(); 103 // Checks number of arguments and stores them in an array. 104 let mut args_arr = [c""; MAX_ARGS]; 105 match args_arr.get_mut(..len) { 106 Some(v) => { 107 v.iter_mut().zip(args).for_each(|(l, r)| *l = r); 108 // SAFETY: By safety requirement of this function `val` points to a 109 // NULL-terminated string. 110 (cb.0)(&v, unsafe { CStr::from_ptr(val) }) 111 } 112 _ => (cb.0)(&[c"<Number of arguments exceeds limit>"], c""), 113 } 114 } 115 116 // SAFETY: 117 // *`self.interface()?` guarantees self.interface is non-null and points to a valid object 118 // * established by `Protocol::new()`. 119 // * The `ctx` parameter is a valid `Callback` object, outlives the call and not being 120 // referenced elsewhere(declared inline at the parameter site). 121 // * By UEFI interface requirement, vendor firmware passes array of C strings to 122 // `get_var_all_cb` that remains valid for the call. 123 unsafe { 124 efi_call!( 125 self.interface()?.get_var_all, 126 self.interface, 127 &mut Callback(&mut cb) as *mut _ as _, 128 Some(get_var_all_cb), 129 )? 130 }; 131 132 Ok(()) 133 } 134 135 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.run_oem_function()` run_oem_function(&self, cmd: &str, buffer: &mut [u8]) -> Result<usize>136 pub fn run_oem_function(&self, cmd: &str, buffer: &mut [u8]) -> Result<usize> { 137 let mut bufsize = buffer.len(); 138 if !buffer.is_empty() { 139 buffer[0] = 0; 140 } 141 142 // SAFETY: 143 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 144 // established by `Protocol::new()`. 145 // No parameter is retained, all parameters outlive the call, 146 // and no pointers are Null. 147 unsafe { 148 efi_call!( 149 @bufsize bufsize, 150 self.interface()?.run_oem_function, 151 self.interface, 152 cmd.as_ptr(), 153 cmd.len(), 154 buffer.as_mut_ptr(), 155 &mut bufsize, 156 )? 157 }; 158 Ok(core::cmp::min(bufsize, buffer.iter().position(|c| *c == 0).unwrap_or(buffer.len()))) 159 } 160 161 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_policy()` get_policy(&self) -> Result<GblEfiFastbootPolicy>162 pub fn get_policy(&self) -> Result<GblEfiFastbootPolicy> { 163 let mut policy: GblEfiFastbootPolicy = Default::default(); 164 165 // SAFETY: 166 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 167 // established by `Protocol::new()`. 168 // No parameters are retained, all parameters outlive the call, and no pointers are Null. 169 unsafe { efi_call!(self.interface()?.get_policy, self.interface, &mut policy)? }; 170 171 Ok(policy) 172 } 173 174 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.set_lock()` set_lock(&self, flags: u64) -> Result<()>175 pub fn set_lock(&self, flags: u64) -> Result<()> { 176 // SAFETY: 177 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 178 // established by `Protocol::new()`. 179 // `self.interface` is an input parameter and will not be retained. It outlives the call. 180 unsafe { efi_call!(self.interface()?.set_lock, self.interface, flags) } 181 } 182 183 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.clear_lock()` clear_lock(&self, flags: u64) -> Result<()>184 pub fn clear_lock(&self, flags: u64) -> Result<()> { 185 // SAFETY: 186 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 187 // established by `Protocol::new()`. 188 // `self.interface` is an input parameter and will not be retained. It outlives the call. 189 unsafe { efi_call!(self.interface()?.clear_lock, self.interface, flags) } 190 } 191 192 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.get_partition_permissions()` get_partition_permissions(&self, part_name: &str) -> Result<u64>193 pub fn get_partition_permissions(&self, part_name: &str) -> Result<u64> { 194 let mut permissions = 0u64; 195 196 // SAFETY: 197 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 198 // established by `Protocol::new()`. 199 // No parameters are retained, all parameters outlive the call, and no pointers are Null. 200 unsafe { 201 efi_call!( 202 self.interface()?.get_partition_permissions, 203 self.interface, 204 part_name.as_ptr(), 205 part_name.len(), 206 &mut permissions 207 )? 208 }; 209 Ok(permissions) 210 } 211 212 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.wipe_user_data()` wipe_user_data(&self) -> Result<()>213 pub fn wipe_user_data(&self) -> Result<()> { 214 // SAFETY: 215 // `self.interface()?` guarantees self.interface is non-null and points to a valid object 216 // established by `Protocol::new()`. 217 // `self.interface` is an input parameter and will not be retained. It outlives the call. 218 unsafe { efi_call!(self.interface()?.wipe_user_data, self.interface) } 219 } 220 221 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.serial_number` serial_number(&self) -> Result<&str>222 pub fn serial_number(&self) -> Result<&str> { 223 let serial_number = &self.interface()?.serial_number; 224 let null_idx = serial_number.iter().position(|c| *c == 0).unwrap_or(serial_number.len()); 225 Ok(from_utf8(&serial_number[..null_idx])?) 226 } 227 228 /// Wrapper of `GBL_EFI_FASTBOOT_PROTOCOL.version` version(&self) -> Result<u32>229 pub fn version(&self) -> Result<u32> { 230 Ok(self.interface()?.version) 231 } 232 } 233 234 #[cfg(test)] 235 mod test { 236 use super::*; 237 use crate::{ 238 protocol::GetVarAllCallback, 239 test::{generate_protocol, run_test}, 240 EfiEntry, 241 }; 242 use core::{ 243 ffi::{c_void, CStr}, 244 slice::from_raw_parts_mut, 245 }; 246 use efi_types::{EfiStatus, EFI_STATUS_SUCCESS}; 247 248 #[test] test_serial_number()249 fn test_serial_number() { 250 run_test(|image_handle, systab_ptr| { 251 // Serial number is shorter than max length and contains non-ASCII unicode. 252 let austria = "Österreich"; 253 254 let mut fb = GblEfiFastbootProtocol { ..Default::default() }; 255 fb.serial_number.as_mut_slice()[..austria.len()].copy_from_slice(austria.as_bytes()); 256 let efi_entry = EfiEntry { image_handle, systab_ptr }; 257 258 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 259 260 // Don't include trailing Null terminators. 261 assert_eq!(protocol.serial_number().unwrap().len(), 11); 262 assert_eq!(protocol.serial_number().unwrap(), austria); 263 }); 264 } 265 266 #[test] test_serial_number_max_length()267 fn test_serial_number_max_length() { 268 run_test(|image_handle, systab_ptr| { 269 let mut fb = GblEfiFastbootProtocol { serial_number: [71u8; 32], ..Default::default() }; 270 let efi_entry = EfiEntry { image_handle, systab_ptr }; 271 272 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 273 274 assert_eq!(protocol.serial_number().unwrap().len(), 32); 275 assert_eq!(protocol.serial_number().unwrap(), "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"); 276 }); 277 } 278 279 #[test] test_serial_number_invalid_utf8()280 fn test_serial_number_invalid_utf8() { 281 run_test(|image_handle, systab_ptr| { 282 let mut fb = GblEfiFastbootProtocol { serial_number: [0xF8; 32], ..Default::default() }; 283 let efi_entry = EfiEntry { image_handle, systab_ptr }; 284 285 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 286 287 assert_eq!(protocol.serial_number(), Err(Error::InvalidInput)); 288 }); 289 } 290 291 #[test] test_get_var()292 fn test_get_var() { 293 /// # Safety 294 /// 295 /// * Caller must guarantee that `args` points to an array of NULL-terminated strings with 296 /// size `num_args`. 297 /// * Caller must guarantee that `out` points to a `[u8]` 298 /// * Caller must guarantee that `out_size` points to a `usize` 299 unsafe extern "C" fn get_var_test( 300 _: *mut GblEfiFastbootProtocol, 301 args: *const *const c_char, 302 num_args: usize, 303 out: *mut u8, 304 out_size: *mut usize, 305 ) -> EfiStatus { 306 // SAFETY: By safety requirement of this function, `args` points to an array of 307 // NULL-terminated strings with length `num_args`. 308 let args = unsafe { from_raw_parts(args, num_args) } 309 .iter() 310 .map(|v| unsafe { CStr::from_ptr(*v) }) 311 .collect::<Vec<_>>(); 312 assert_eq!(args, [c"var", c"arg1", c"arg2"]); 313 // SAFETY: By safety requirement of this function, `out_size` points to a `usize`; 314 let out_size = &mut unsafe { *out_size }; 315 // SAFETY: By safety requirement of this function, `out` points to a `[u8]`; 316 let out = unsafe { from_raw_parts_mut(out, *out_size) }; 317 out.clone_from_slice(c"val".to_bytes()); 318 *out_size = c"val".to_bytes().len(); 319 EFI_STATUS_SUCCESS 320 } 321 322 run_test(|image_handle, systab_ptr| { 323 let mut fb = 324 GblEfiFastbootProtocol { get_var: Some(get_var_test), ..Default::default() }; 325 let efi_entry = EfiEntry { image_handle, systab_ptr }; 326 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 327 let mut out = [0u8; 3]; 328 let args = [c"arg1", c"arg2"]; 329 assert_eq!(protocol.get_var(c"var", args.iter().copied(), &mut out[..]), Ok(3)); 330 assert_eq!(&out, b"val"); 331 }); 332 } 333 334 #[test] test_get_var_all()335 fn test_get_var_all() { 336 /// # Safety 337 /// 338 /// * Caller must guarantee that `ctx` points to data needed by function pointer `cb`. 339 unsafe extern "C" fn test_get_var_all( 340 _: *mut GblEfiFastbootProtocol, 341 ctx: *mut c_void, 342 cb: GetVarAllCallback, 343 ) -> EfiStatus { 344 for (args, val) in [ 345 ([c"foo", c"foo_arg1", c"foo_arg2"], c"foo_val"), 346 ([c"bar", c"bar_arg1", c"bar_arg2"], c"bar_val"), 347 ] { 348 let args = args.map(|v| v.as_ptr()); 349 // SAFETY: 350 // * `args` is an array of NULL-terminated strings. `val` is a NULL-terminated 351 // string. 352 // * By safety requirement of this function, `ctx` points to a valid type of data 353 // needed by `cb`. 354 unsafe { (cb.unwrap())(ctx, args.as_ptr(), args.len(), val.as_ptr()) }; 355 } 356 EFI_STATUS_SUCCESS 357 } 358 run_test(|image_handle, systab_ptr| { 359 let mut fb = GblEfiFastbootProtocol { 360 get_var_all: Some(test_get_var_all), 361 ..Default::default() 362 }; 363 let efi_entry = EfiEntry { image_handle, systab_ptr }; 364 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 365 let mut out = vec![]; 366 protocol 367 .get_var_all(|args, val| { 368 let args_str = 369 args.iter().map(|v| v.to_str().unwrap()).collect::<Vec<_>>().join(":"); 370 out.push(format!("{args_str}: {}", val.to_str().unwrap())) 371 }) 372 .unwrap(); 373 assert_eq!(out, ["foo:foo_arg1:foo_arg2: foo_val", "bar:bar_arg1:bar_arg2: bar_val",]) 374 }); 375 } 376 377 #[test] test_get_var_all_exceeds_max_arguments()378 fn test_get_var_all_exceeds_max_arguments() { 379 /// # Safety 380 /// 381 /// * Caller must guarantee that `ctx` points to data needed by function pointer `cb`. 382 unsafe extern "C" fn test_get_var_all( 383 _: *mut GblEfiFastbootProtocol, 384 ctx: *mut c_void, 385 cb: GetVarAllCallback, 386 ) -> EfiStatus { 387 let args = [c"".as_ptr(); MAX_ARGS + 1]; 388 // SAFETY: 389 // * `args` is an array of NULL-terminated strings. `val` is a NULL-terminated 390 // string. 391 // * By safety requirement of this function, `ctx` points to a valid type of data 392 // needed by `cb`. 393 unsafe { (cb.unwrap())(ctx, args.as_ptr(), args.len(), c"".as_ptr()) }; 394 EFI_STATUS_SUCCESS 395 } 396 run_test(|image_handle, systab_ptr| { 397 let mut fb = GblEfiFastbootProtocol { 398 get_var_all: Some(test_get_var_all), 399 ..Default::default() 400 }; 401 let efi_entry = EfiEntry { image_handle, systab_ptr }; 402 let protocol = generate_protocol::<GblFastbootProtocol>(&efi_entry, &mut fb); 403 let mut out = vec![]; 404 protocol 405 .get_var_all(|args, val| { 406 let args_str = 407 args.iter().map(|v| v.to_str().unwrap()).collect::<Vec<_>>().join(":"); 408 out.push(format!("{args_str}: {}", val.to_str().unwrap())) 409 }) 410 .unwrap(); 411 assert_eq!(out, ["<Number of arguments exceeds limit>: "]) 412 }); 413 } 414 } 415