// Copyright 2024, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This is an example implementation of the libavb rust wrapper backend in the EFI environment. It // is mainly for use by the boot demo. Eventually, these backends will be implemented from the // `GblOps` interface in libgbl, where EFI services will be one level lower as its backend instead. use crate::utils::EfiMultiBlockDevices; use avb::{IoError, IoResult, Ops, PublicKeyForPartitionInfo}; use core::ffi::CStr; use gbl_storage::AsMultiBlockDevices; use uuid::Uuid; extern crate avb_sysdeps; pub struct GblEfiAvbOps<'a, 'b> { gpt_dev: &'b mut EfiMultiBlockDevices<'a>, preloaded_partitions: Option<&'b [(&'b str, &'b [u8])]>, } impl<'a, 'b> GblEfiAvbOps<'a, 'b> { pub fn new( gpt_dev: &'b mut EfiMultiBlockDevices<'a>, preloaded_partitions: Option<&'b [(&'b str, &'b [u8])]>, ) -> Self { Self { gpt_dev, preloaded_partitions } } } /// A helper function for converting CStr to str fn cstr_to_str(s: &CStr, err: E) -> Result<&str, E> { Ok(s.to_str().map_err(|_| err)?) } impl<'b> Ops<'b> for GblEfiAvbOps<'_, 'b> { fn read_from_partition( &mut self, partition: &CStr, offset: i64, buffer: &mut [u8], ) -> IoResult { let part_str = cstr_to_str(partition, IoError::NoSuchPartition)?; let partition_size: u64 = self .gpt_dev .find_partition(part_str) .and_then(|v| v.size()) .map_err(|_| IoError::NoSuchPartition)? .try_into() .map_err(|_| IoError::Oom)?; let read_off: u64 = match offset < 0 { true => { partition_size.checked_sub(offset.abs() as u64).ok_or(IoError::InvalidValueSize)? } _ => offset.try_into().map_err(|_| IoError::InvalidValueSize)?, }; self.gpt_dev.read_gpt_partition(part_str, read_off, buffer).map_err(|_| IoError::Io)?; Ok(buffer.len()) } fn get_preloaded_partition(&mut self, partition: &CStr) -> IoResult<&'b [u8]> { let part_str = cstr_to_str(partition, IoError::NotImplemented)?; Ok(self .preloaded_partitions .ok_or(IoError::NotImplemented)? .iter() .find(|(name, _)| *name == part_str) .ok_or(IoError::NotImplemented)? .1) } fn validate_vbmeta_public_key( &mut self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult { // Not supported until we add our GBL specific EFI protocol that does this. Ok(true) } fn read_rollback_index(&mut self, _rollback_index_location: usize) -> IoResult { // Not supported until we add our GBL specific EFI protocol that does this. Ok(0) } fn write_rollback_index( &mut self, _rollback_index_location: usize, _index: u64, ) -> IoResult<()> { // Not supported until we add our GBL specific EFI protocol that does this. Ok(()) } fn read_is_device_unlocked(&mut self) -> IoResult { // Not supported until we add our GBL specific EFI protocol that does this. // For now always consider unlocked. Ok(true) } fn get_unique_guid_for_partition(&mut self, partition: &CStr) -> IoResult { let part_str = cstr_to_str(partition, IoError::NoSuchPartition)?; let ptn = self.gpt_dev.find_partition(part_str).map_err(|_| IoError::NoSuchPartition)?; Ok(Uuid::from_bytes(ptn.gpt_entry().guid)) } fn get_size_of_partition(&mut self, partition: &CStr) -> IoResult { match self.get_preloaded_partition(partition) { Ok(img) => Ok(img.len().try_into().unwrap()), _ => { let part_str = cstr_to_str(partition, IoError::NoSuchPartition)?; Ok(self .gpt_dev .find_partition(part_str) .and_then(|v| v.size()) .map_err(|_| IoError::NoSuchPartition)? .try_into() .map_err(|_| IoError::NoSuchPartition)?) } } } fn read_persistent_value(&mut self, _name: &CStr, _value: &mut [u8]) -> IoResult { // Not supported until we add our GBL specific EFI protocol that does this. unimplemented!(); } fn write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> IoResult<()> { // Not supported until we add our GBL specific EFI protocol that does this. unimplemented!(); } fn erase_persistent_value(&mut self, _name: &CStr) -> IoResult<()> { // Not supported until we add our GBL specific EFI protocol that does this. unimplemented!(); } fn validate_public_key_for_partition( &mut self, _partition: &CStr, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult { // Not supported until we add our GBL specific EFI protocol that does this. unimplemented!(); } }