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