xref: /aosp_15_r20/bootable/libbootloader/gbl/libutils/src/lib.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1  // Copyright (C) 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  //! Low-level utilities shared across multiple GBL libraries.
16  
17  #![cfg_attr(not(test), no_std)]
18  
19  use liberror::{Error, Result};
20  use safemath::SafeNum;
21  
22  /// Returns the largest aligned subslice.
23  ///
24  /// This function drops as many bytes as needed from the front of the given slice to ensure the
25  /// result is properly-aligned. It does not truncate bytes from the end, so the resulting size may
26  /// not be a multiple of `alignment`.
27  ///
28  /// If the next `alignment` boundary would be directly following the last byte, this returns the
29  /// 0-length slice at that alignment rather than an error, to match standard slicing behavior.
30  ///
31  /// # Arguments
32  /// * `bytes`: the byte slice to align
33  /// * `alignment`: the desired starting alignment
34  ///
35  /// # Returns
36  /// * The subslice on success
37  /// * [Error::ArithmeticOverflow] if `bytes` overflows when finding the next `alignment`
38  /// * [Error::BufferTooSmall] if `bytes` is not large enough to reach the next `alignment`. The
39  ///   error will contain the size that would have been needed to reach `alignment`.
aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]> where T: Copy + Into<SafeNum>,40  pub fn aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]>
41  where
42      T: Copy + Into<SafeNum>,
43  {
44      let addr = bytes.as_ptr() as usize;
45      let aligned_offset = (SafeNum::from(addr).round_up(alignment) - addr).try_into()?;
46      Ok(bytes.get_mut(aligned_offset..).ok_or(Error::BufferTooSmall(Some(aligned_offset)))?)
47  }
48  
49  /// A helper for getting the offset of the first byte with and aligned address.
50  ///
51  /// # Arguments
52  /// * `bytes`: the byte slice
53  /// * `alignment`: the desired starting alignment.
54  ///
55  /// # Returns
56  ///
57  /// * Returns Ok(offset) on success, Err() on integer overflow.
aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize> where T: Copy + Into<SafeNum>,58  pub fn aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize>
59  where
60      T: Copy + Into<SafeNum>,
61  {
62      let addr = SafeNum::from(buffer.as_ptr() as usize);
63      (addr.round_up(alignment) - addr).try_into().map_err(From::from)
64  }
65  
66  #[cfg(test)]
67  mod test {
68      use super::*;
69  
70      // A byte array that's always at least 8-byte aligned for testing.
71      #[repr(align(8))]
72      struct AlignedBytes<const N: usize>([u8; N]);
73  
74      #[test]
aligned_subslice_already_aligned()75      fn aligned_subslice_already_aligned() {
76          let mut bytes = AlignedBytes([0u8; 16]);
77          let bytes = &mut bytes.0;
78  
79          // AlignedBytes is `align(8)`, so must be 1/2/4/8-aligned.
80          assert_eq!(aligned_subslice(bytes, 1).unwrap().as_ptr_range(), bytes.as_ptr_range());
81          assert_eq!(aligned_subslice(bytes, 2).unwrap().as_ptr_range(), bytes.as_ptr_range());
82          assert_eq!(aligned_subslice(bytes, 4).unwrap().as_ptr_range(), bytes.as_ptr_range());
83          assert_eq!(aligned_subslice(bytes, 8).unwrap().as_ptr_range(), bytes.as_ptr_range());
84      }
85  
86      #[test]
aligned_subslice_unaligned()87      fn aligned_subslice_unaligned() {
88          let mut bytes = AlignedBytes([0u8; 16]);
89          let bytes = &mut bytes.0;
90  
91          // AlignedBytes is 8-aligned, so offsetting by <8 should snap to the next 8-alignment.
92          assert_eq!(
93              aligned_subslice(&mut bytes[1..], 8).unwrap().as_ptr_range(),
94              bytes[8..].as_ptr_range()
95          );
96          assert_eq!(
97              aligned_subslice(&mut bytes[4..], 8).unwrap().as_ptr_range(),
98              bytes[8..].as_ptr_range()
99          );
100          assert_eq!(
101              aligned_subslice(&mut bytes[7..], 8).unwrap().as_ptr_range(),
102              bytes[8..].as_ptr_range()
103          );
104      }
105  
106      #[test]
aligned_subslice_empty_slice()107      fn aligned_subslice_empty_slice() {
108          let mut bytes = AlignedBytes([0u8; 16]);
109          let bytes = &mut bytes.0;
110  
111          // If the next alignment is just past the input, return the empty slice.
112          assert_eq!(
113              aligned_subslice(&mut bytes[9..], 8).unwrap().as_ptr_range(),
114              bytes[16..].as_ptr_range()
115          );
116      }
117  
118      #[test]
aligned_subslice_buffer_overflow()119      fn aligned_subslice_buffer_overflow() {
120          let mut bytes = AlignedBytes([0u8; 7]); // 7 bytes; can't reach the next 8-alignment.
121          let bytes = &mut bytes.0;
122  
123          assert_eq!(aligned_subslice(&mut bytes[1..], 8), Err(Error::BufferTooSmall(Some(7))));
124          assert_eq!(aligned_subslice(&mut bytes[6..], 8), Err(Error::BufferTooSmall(Some(2))));
125      }
126  
127      #[test]
aligned_subslice_alignment_overflow()128      fn aligned_subslice_alignment_overflow() {
129          let mut bytes = AlignedBytes([0u8; 16]);
130          let bytes = &mut bytes.0;
131  
132          assert!(matches!(aligned_subslice(bytes, SafeNum::MAX), Err(Error::ArithmeticOverflow(_))));
133      }
134  }
135