• 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 //! This library provides a wrapper APIs for libdttable_c
16 //! https://source.android.com/docs/core/architecture/dto/partitions
17 
18 #![cfg_attr(not(test), no_std)]
19 
20 use core::mem::size_of;
21 use libdttable_bindgen::{dt_table_entry, dt_table_header, DT_TABLE_MAGIC};
22 use liberror::{Error, Result};
23 use safemath::SafeNum;
24 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref};
25 
26 /// Rust wrapper for the dt table header
27 #[repr(transparent)]
28 #[derive(Debug, Copy, Clone, IntoBytes, FromBytes, Immutable, KnownLayout, PartialEq)]
29 struct DtTableHeader(dt_table_header);
30 
31 impl DtTableHeader {
32     /// Get magic handling the bytes order
magic(self) -> u3233     fn magic(self) -> u32 {
34         u32::from_be(self.0.magic)
35     }
36 
37     /// Get dt_entry_count handling the bytes order
dt_entry_count(self) -> u3238     fn dt_entry_count(self) -> u32 {
39         u32::from_be(self.0.dt_entry_count)
40     }
41 
42     /// Get dt_entry_size handling the bytes order
dt_entry_size(self) -> u3243     fn dt_entry_size(self) -> u32 {
44         u32::from_be(self.0.dt_entry_size)
45     }
46 
47     /// Get dt_entries_offset handling the bytes order
dt_entries_offset(self) -> u3248     fn dt_entries_offset(self) -> u32 {
49         u32::from_be(self.0.dt_entries_offset)
50     }
51 }
52 
53 /// Rust wrapper for the dt table entry
54 #[repr(transparent)]
55 #[derive(Debug, Copy, Clone, Immutable, IntoBytes, KnownLayout, FromBytes, PartialEq)]
56 struct DtTableHeaderEntry(dt_table_entry);
57 
58 impl DtTableHeaderEntry {
59     /// Get id handling the bytes order
id(self) -> u3260     fn id(self) -> u32 {
61         u32::from_be(self.0.id)
62     }
63 
64     /// Get rev handling the bytes order
rev(self) -> u3265     fn rev(self) -> u32 {
66         u32::from_be(self.0.rev)
67     }
68 
69     /// Get dt_size handling the bytes order
dt_size(self) -> u3270     fn dt_size(self) -> u32 {
71         u32::from_be(self.0.dt_size)
72     }
73 
74     /// Get dt_offset handling the bytes order
dt_offset(self) -> u3275     fn dt_offset(self) -> u32 {
76         u32::from_be(self.0.dt_offset)
77     }
78 }
79 
80 /// Metadata provided by entry header
81 #[derive(Copy, Default, Clone, Eq, PartialEq, Debug)]
82 pub struct DtTableMetadata {
83     /// id field from corresponding entry header
84     pub id: u32,
85     /// rev field from corresponding entry header
86     pub rev: u32,
87     /// custom field from corresponding entry header
88     pub custom: [u32; 4],
89 }
90 
91 /// Device tree blob obtained from multidt table image
92 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
93 pub struct DtTableEntry<'a> {
94     /// dtb payload extracted from image
95     pub dtb: &'a [u8],
96     /// Metadata provided by corresponding entry header
97     pub metadata: DtTableMetadata,
98 }
99 
100 /// Represents entier multidt table image
101 pub struct DtTableImage<'a> {
102     buffer: &'a [u8],
103     header: Ref<&'a [u8], DtTableHeader>,
104     entries: Ref<&'a [u8], [DtTableHeaderEntry]>,
105 }
106 
107 /// To iterate over entries.
108 pub struct DtTableImageIterator<'a> {
109     table_image: &'a DtTableImage<'a>,
110     current_index: usize,
111 }
112 
113 impl<'a> Iterator for DtTableImageIterator<'a> {
114     type Item = DtTableEntry<'a>;
115 
next(&mut self) -> Option<Self::Item>116     fn next(&mut self) -> Option<Self::Item> {
117         if self.current_index < self.table_image.entries_count() {
118             let result = self.table_image.nth_entry(self.current_index).unwrap();
119             self.current_index += 1;
120             Some(result)
121         } else {
122             None
123         }
124     }
125 }
126 
127 impl<'a> DtTableImage<'a> {
128     /// Verify and parse passed buffer following multidt table structure
from_bytes(buffer: &'a [u8]) -> Result<DtTableImage<'a>>129     pub fn from_bytes(buffer: &'a [u8]) -> Result<DtTableImage<'a>> {
130         let (header_layout, _) = Ref::new_from_prefix(buffer)
131             .ok_or(Error::BufferTooSmall(Some(size_of::<DtTableHeader>())))?;
132 
133         let header: &DtTableHeader = &header_layout;
134         if header.magic() != DT_TABLE_MAGIC {
135             return Err(Error::BadMagic);
136         }
137 
138         let entries_offset: SafeNum = header.dt_entries_offset().into();
139         let entry_size: SafeNum = header.dt_entry_size().into();
140         let entries_count: SafeNum = header.dt_entry_count().into();
141 
142         let entries_start = entries_offset.try_into()?;
143         let entries_end = (entries_offset + entry_size * entries_count).try_into()?;
144 
145         let entries_buffer = buffer
146             .get(entries_start..entries_end)
147             .ok_or(Error::BufferTooSmall(Some(entries_end)))?;
148         let entries_layout = Ref::new_slice(entries_buffer).ok_or(Error::InvalidInput)?;
149 
150         Ok(DtTableImage { buffer: buffer, header: header_layout, entries: entries_layout })
151     }
152 
153     /// Get amount of presented dt entries in the multidt table image
entries_count(&self) -> usize154     pub fn entries_count(&self) -> usize {
155         self.header.dt_entry_count().try_into().unwrap()
156     }
157 
158     /// Returns an iterator over the entries in the DT table image
entries(&'a self) -> DtTableImageIterator<'a>159     pub fn entries(&'a self) -> DtTableImageIterator<'a> {
160         DtTableImageIterator { table_image: self, current_index: 0 }
161     }
162 
163     /// Get nth dtb buffer with multidt table structure metadata
nth_entry(&self, n: usize) -> Result<DtTableEntry<'a>>164     pub fn nth_entry(&self, n: usize) -> Result<DtTableEntry<'a>> {
165         let entry = self.entries.get(n).ok_or(Error::BadIndex(n))?;
166 
167         let dtb_offset: SafeNum = entry.dt_offset().into();
168         let dtb_size: SafeNum = entry.dt_size().into();
169 
170         let dtb_start: usize = dtb_offset.try_into()?;
171         let dtb_end: usize = (dtb_offset + dtb_size).try_into()?;
172 
173         let dtb_buffer =
174             self.buffer.get(dtb_start..dtb_end).ok_or(Error::BufferTooSmall(Some(dtb_end)))?;
175 
176         Ok(DtTableEntry {
177             dtb: dtb_buffer,
178             metadata: DtTableMetadata { id: entry.id(), rev: entry.rev(), custom: entry.0.custom },
179         })
180     }
181 }
182 
183 #[cfg(test)]
184 mod test {
185     use super::*;
186     use fdt::Fdt;
187 
188     #[test]
test_dt_table_is_parsed()189     fn test_dt_table_is_parsed() {
190         let dttable = include_bytes!("../test/data/dttable.img").to_vec();
191         let table = DtTableImage::from_bytes(&dttable[..]).unwrap();
192 
193         assert_eq!(table.entries_count(), 2, "Test data dttable image must have 2 dtb entries");
194 
195         let first_entry = table.nth_entry(0).unwrap();
196         let second_entry = table.nth_entry(1).unwrap();
197 
198         assert_eq!(
199             first_entry.metadata,
200             DtTableMetadata { id: 1, rev: 0, custom: Default::default() },
201             "First dttable entry is incorrect"
202         );
203         assert_eq!(
204             second_entry.metadata,
205             DtTableMetadata { id: 2, rev: 0, custom: Default::default() },
206             "Second dttable entry is incorrect"
207         );
208 
209         // verify fdt headers are properly parsed
210         let _ = Fdt::new(first_entry.dtb).unwrap();
211         let _ = Fdt::new(second_entry.dtb).unwrap();
212     }
213 
214     #[test]
test_dt_table_is_parsed_iterator()215     fn test_dt_table_is_parsed_iterator() {
216         let dttable = include_bytes!("../test/data/dttable.img").to_vec();
217         let table = DtTableImage::from_bytes(&dttable[..]).unwrap();
218 
219         // Collect entries from the iterator
220         let entries: Vec<_> = table.entries().collect();
221 
222         // Verify that the iterator yields the correct number of entries
223         assert_eq!(entries.len(), 2, "Iterator should yield 2 entries");
224 
225         // Unwrap the entries from Result
226         let first_entry = &entries[0];
227         let second_entry = &entries[1];
228 
229         assert_eq!(
230             first_entry.metadata,
231             DtTableMetadata { id: 1, rev: 0, custom: Default::default() },
232             "First dttable entry metadata is incorrect"
233         );
234         assert_eq!(
235             second_entry.metadata,
236             DtTableMetadata { id: 2, rev: 0, custom: Default::default() },
237             "Second dttable entry metadata is incorrect"
238         );
239 
240         // Verify FDT headers are properly parsed
241         let _ = Fdt::new(first_entry.dtb).unwrap();
242         let _ = Fdt::new(second_entry.dtb).unwrap();
243     }
244 
245     #[test]
test_failed_to_parse_corrupted_dt_table()246     fn test_failed_to_parse_corrupted_dt_table() {
247         let dttable = include_bytes!("../test/data/corrupted_dttable.img").to_vec();
248 
249         assert!(
250             DtTableImage::from_bytes(&dttable[..]).is_err(),
251             "Must fail when trying to parse corrupted dt table image"
252         );
253     }
254 }
255