android_bootimage/
header.rs

1use Section;
2use byteorder::{LittleEndian, ReadBytesExt};
3use quick_error::ResultExt;
4use std::io::{Error as IoError, Read, Seek, SeekFrom};
5
6pub const MAGIC_STR: &'static str = "ANDROID!";
7const MAGIC_SIZE: usize = 8;
8const PRODUCT_NAME_SIZE: usize = 24;
9const BOOT_ARGUMENTS_SIZE: usize = 512;
10const UNIQUE_ID_SIZE: usize = 32;
11const HEADER_SIZE: usize = 616;
12
13/// The different sections in a Samsung boot image, in order.
14const SECTIONS: &'static [Section] = &[
15    Section::Header,
16    Section::Kernel,
17    Section::Ramdisk,
18    Section::Second,
19    Section::DeviceTree,
20];
21
22/// Contains a magic header.
23#[derive(Debug)]
24pub struct MagicHeader {
25    /// Header magic. Used to make sure this is in fact a header.
26    pub magic: [u8; MAGIC_SIZE],
27    /// Ramdisk size, in bytes.
28    pub kernel_size: u32,
29    /// Address the ramdisk should be loaded to.
30    pub kernel_load_address: u32,
31
32    /// Ramdisk size, in bytes.
33    pub ramdisk_size: u32,
34    /// Address the ramdisk should be loaded to.
35    pub ramdisk_load_address: u32,
36
37    /// Size of an optional second file.
38    pub second_size: u32,
39    /// Address the optional second file should be loaded to.
40    pub second_load_address: u32,
41
42    /// The size of the device tree, in bytes.
43    pub device_tree_size: u32,
44    /// Room for future expansion. This should always be set to 0.
45    _reserved: u32,
46
47    /// Physical address of the kernel tags.
48    pub kernel_tags_address: u32,
49    /// The page size.
50    pub page_size: u32,
51    /// Name of the product. This is a null-terminated ASCII string.
52    pub product_name: [u8; PRODUCT_NAME_SIZE],
53    /// Arguments to pass to the kernel during boot. This is a nested array, as
54    /// rust does not allow us to have arrays larger than 32 in size.
55    pub boot_arguments: [[u8; BOOT_ARGUMENTS_SIZE / 16]; 16],
56    /// Used to uniquely identify boot images.
57    pub unique_id: [u8; UNIQUE_ID_SIZE],
58}
59
60impl MagicHeader {
61    /// Reads a magic header from the supplied source.
62    pub fn read_from<R: ReadBytesExt>(
63        source: &mut R,
64        check_magic: bool,
65    ) -> Result<Self, MagicHeaderParseError> {
66        let header = MagicHeader {
67            magic: {
68                let mut buffer = [0; MAGIC_SIZE];
69                source.read_exact(&mut buffer)?;
70                buffer
71            },
72            kernel_size: source.read_u32::<LittleEndian>()?,
73            kernel_load_address: source.read_u32::<LittleEndian>()?,
74            ramdisk_size: source.read_u32::<LittleEndian>()?,
75            ramdisk_load_address: source.read_u32::<LittleEndian>()?,
76            second_size: source.read_u32::<LittleEndian>()?,
77            second_load_address: source.read_u32::<LittleEndian>()?,
78            device_tree_size: source.read_u32::<LittleEndian>()?,
79            _reserved: source.read_u32::<LittleEndian>()?,
80            kernel_tags_address: source.read_u32::<LittleEndian>()?,
81            page_size: source.read_u32::<LittleEndian>()?,
82            product_name: {
83                let mut buffer = [0; PRODUCT_NAME_SIZE];
84                source.read_exact(&mut buffer)?;
85                buffer
86            },
87            boot_arguments: unsafe {
88                use std::mem::transmute;
89                let mut buffer = [0; BOOT_ARGUMENTS_SIZE];
90                source.read_exact(&mut buffer)?;
91                transmute(buffer)
92            },
93            unique_id: {
94                let mut buffer = [0u8; UNIQUE_ID_SIZE];
95                source.read_exact(&mut buffer)?;
96                buffer
97            },
98        };
99
100        if check_magic && header.magic != MAGIC_STR.as_bytes() {
101            Err(MagicHeaderParseError::InvalidMagic(header))
102        } else {
103            Ok(header)
104        }
105    }
106
107    /// Returns the size of a section, in bytes.
108    pub fn section_size(&self, section: Section) -> u64 {
109        match section {
110            Section::Header => HEADER_SIZE as u64,
111            Section::Kernel => self.kernel_size as u64,
112            Section::Ramdisk => self.ramdisk_size as u64,
113            Section::Second => self.second_size as u64,
114            Section::DeviceTree => self.device_tree_size as u64,
115        }
116    }
117
118    /// Returns the start location of a section, in bytes.
119    ///
120    /// Do note that this function can fail because it cannot find a section
121    /// other than the one that was requested. Sections depend on the other
122    /// sections for their locations.
123    pub fn section_start(&self, section: Section) -> Result<u64, LocateSectionError> {
124        if self.page_size == 0 {
125            Err(LocateSectionError::NoPageSize)
126        } else {
127            let offset_in_pages: u64 = SECTIONS
128                .iter()
129                .cloned()
130                // Take every section that comes before the one we want to get the offset for.
131                .take_while(|&i_section| i_section != section)
132                // For every of these sections, calculate the amount of pages it occupies.
133                .map(|section| {
134                    (self.section_size(section) + self.page_size as u64 - 1) / self.page_size as u64
135                })
136                // Calculate how much pages all of these pages together occupy.
137                .sum();
138
139            // Multiply with the size of a page to get the offset in bytes.
140            Ok(offset_in_pages * self.page_size as u64)
141        }
142    }
143
144    /// Returns the start and the end location of a section, in bytes.
145    ///
146    /// Do note that this function can fail because it cannot find a section
147    /// other than the one that was requested. Sections depend on the other
148    /// sections for their locations.
149    pub fn section_location(&self, section: Section) -> Result<(u64, u64), LocateSectionError> {
150        Ok((self.section_start(section)?, self.section_size(section)))
151    }
152
153    /// Reads a section from the given readable resource.
154    ///
155    /// Do note that this function can fail because it cannot find a section
156    /// other than the one that was requested. Sections depend on the other
157    /// sections for their locations.
158    pub fn read_section_from<R: Read + Seek>(
159        &self,
160        source: &mut R,
161        section: Section,
162    ) -> Result<Vec<u8>, ReadSectionError> {
163        let (start, size) = self.section_location(section).context(section)?;
164        try!(source.seek(SeekFrom::Start(start)).context(section));
165        let mut data = vec![0u8; size as usize];
166        try!(source.read_exact(&mut data).context(section));
167        return Ok(data);
168    }
169
170    /// Returns the sections in this boot image, in order. Zero-size sections
171    /// are omitted.
172    pub fn sections(&self) -> Vec<Section> {
173        SECTIONS
174            .iter()
175            .filter(|&&section| self.section_size(section) > 0)
176            .cloned()
177            .collect()
178    }
179}
180
181quick_error! {
182    #[derive(Debug)]
183    pub enum MagicHeaderParseError {
184        Io(error: IoError) {
185            description("I/O error while parsing header")
186            display("I/O error while parsing header.")
187            cause(error)
188            from(error: IoError) -> (error)
189        }
190        InvalidMagic(header: MagicHeader) {
191            description("The header did not have the valid magic prefix")
192            display("The header did not have the valid '{}' magic prefix.", MAGIC_STR)
193        }
194    }
195}
196
197quick_error! {
198    #[derive(Debug)]
199    pub enum LocateSectionError {
200        NoPageSize {
201            description("The header's page size is zero")
202            display("The header's page size is 0.")
203        }
204        NoSection(section: Section) {
205            description("The section does not exist")
206            display("The '{}' section does not exist.", section)
207        }
208    }
209}
210quick_error! {
211    #[derive(Debug)]
212    pub enum ReadSectionError {
213        LocateSection(section: Section, cause: LocateSectionError) {
214            context(section: Section, cause: LocateSectionError) -> (section, cause)
215            description("Could not locate the section")
216            display("Cannot locate the '{}' section.", section)
217            cause(cause)
218        }
219        IoError(section: Section, cause: IoError) {
220            context(section: Section, cause: IoError) -> (section, cause)
221            description("I/O error while reading the section")
222            display("I/O error while reading the '{}' section.", section)
223            cause(cause)
224        }
225    }
226}