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