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::{AsBytes, FromBytes, FromZeroes, LayoutVerified}; 25 26 /// Rust wrapper for the dt table header 27 #[repr(transparent)] 28 #[derive(Debug, Copy, Clone, AsBytes, FromBytes, FromZeroes, 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, AsBytes, FromBytes, FromZeroes, 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: LayoutVerified<&'a [u8], DtTableHeader>, 104 entries: LayoutVerified<&'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, _) = LayoutVerified::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 = 149 LayoutVerified::new_slice(entries_buffer).ok_or(Error::InvalidInput)?; 150 151 Ok(DtTableImage { buffer: buffer, header: header_layout, entries: entries_layout }) 152 } 153 154 /// Get amount of presented dt entries in the multidt table image entries_count(&self) -> usize155 pub fn entries_count(&self) -> usize { 156 self.header.dt_entry_count().try_into().unwrap() 157 } 158 159 /// Returns an iterator over the entries in the DT table image entries(&'a self) -> DtTableImageIterator<'a>160 pub fn entries(&'a self) -> DtTableImageIterator<'a> { 161 DtTableImageIterator { table_image: self, current_index: 0 } 162 } 163 164 /// Get nth dtb buffer with multidt table structure metadata nth_entry(&self, n: usize) -> Result<DtTableEntry<'a>>165 pub fn nth_entry(&self, n: usize) -> Result<DtTableEntry<'a>> { 166 let entry = self.entries.get(n).ok_or(Error::BadIndex(n))?; 167 168 let dtb_offset: SafeNum = entry.dt_offset().into(); 169 let dtb_size: SafeNum = entry.dt_size().into(); 170 171 let dtb_start: usize = dtb_offset.try_into()?; 172 let dtb_end: usize = (dtb_offset + dtb_size).try_into()?; 173 174 let dtb_buffer = 175 self.buffer.get(dtb_start..dtb_end).ok_or(Error::BufferTooSmall(Some(dtb_end)))?; 176 177 Ok(DtTableEntry { 178 dtb: dtb_buffer, 179 metadata: DtTableMetadata { id: entry.id(), rev: entry.rev(), custom: entry.0.custom }, 180 }) 181 } 182 } 183 184 #[cfg(test)] 185 mod test { 186 use super::*; 187 use fdt::Fdt; 188 189 #[test] test_dt_table_is_parsed()190 fn test_dt_table_is_parsed() { 191 let dttable = include_bytes!("../test/data/dttable.img").to_vec(); 192 let table = DtTableImage::from_bytes(&dttable[..]).unwrap(); 193 194 assert_eq!(table.entries_count(), 2, "Test data dttable image must have 2 dtb entries"); 195 196 let first_entry = table.nth_entry(0).unwrap(); 197 let second_entry = table.nth_entry(1).unwrap(); 198 199 assert_eq!( 200 first_entry.metadata, 201 DtTableMetadata { id: 1, rev: 0, custom: Default::default() }, 202 "First dttable entry is incorrect" 203 ); 204 assert_eq!( 205 second_entry.metadata, 206 DtTableMetadata { id: 2, rev: 0, custom: Default::default() }, 207 "Second dttable entry is incorrect" 208 ); 209 210 // verify fdt headers are properly parsed 211 let _ = Fdt::new(first_entry.dtb).unwrap(); 212 let _ = Fdt::new(second_entry.dtb).unwrap(); 213 } 214 215 #[test] test_dt_table_is_parsed_iterator()216 fn test_dt_table_is_parsed_iterator() { 217 let dttable = include_bytes!("../test/data/dttable.img").to_vec(); 218 let table = DtTableImage::from_bytes(&dttable[..]).unwrap(); 219 220 // Collect entries from the iterator 221 let entries: Vec<_> = table.entries().collect(); 222 223 // Verify that the iterator yields the correct number of entries 224 assert_eq!(entries.len(), 2, "Iterator should yield 2 entries"); 225 226 // Unwrap the entries from Result 227 let first_entry = &entries[0]; 228 let second_entry = &entries[1]; 229 230 assert_eq!( 231 first_entry.metadata, 232 DtTableMetadata { id: 1, rev: 0, custom: Default::default() }, 233 "First dttable entry metadata is incorrect" 234 ); 235 assert_eq!( 236 second_entry.metadata, 237 DtTableMetadata { id: 2, rev: 0, custom: Default::default() }, 238 "Second dttable entry metadata is incorrect" 239 ); 240 241 // Verify FDT headers are properly parsed 242 let _ = Fdt::new(first_entry.dtb).unwrap(); 243 let _ = Fdt::new(second_entry.dtb).unwrap(); 244 } 245 246 #[test] test_failed_to_parse_corrupted_dt_table()247 fn test_failed_to_parse_corrupted_dt_table() { 248 let dttable = include_bytes!("../test/data/corrupted_dttable.img").to_vec(); 249 250 assert!( 251 DtTableImage::from_bytes(&dttable[..]).is_err(), 252 "Must fail when trying to parse corrupted dt table image" 253 ); 254 } 255 } 256