1 // Copyright 2023, 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 //! The public interface for bootimg structs
16 use zerocopy::{ByteSlice, LayoutVerified};
17
18 use bootimg_bindgen::{
19 boot_img_hdr_v0, boot_img_hdr_v1, boot_img_hdr_v2, boot_img_hdr_v3, boot_img_hdr_v4,
20 vendor_boot_img_hdr_v3, vendor_boot_img_hdr_v4, BOOT_MAGIC, BOOT_MAGIC_SIZE, VENDOR_BOOT_MAGIC,
21 VENDOR_BOOT_MAGIC_SIZE,
22 };
23
24 /// Generalized boot image from a backing store of bytes.
25 #[derive(PartialEq, Debug)]
26 pub enum BootImage<B: ByteSlice + PartialEq> {
27 /// Version 0 header
28 V0(LayoutVerified<B, boot_img_hdr_v0>),
29 /// Version 1 header
30 V1(LayoutVerified<B, boot_img_hdr_v1>),
31 /// Version 2 header
32 V2(LayoutVerified<B, boot_img_hdr_v2>),
33 /// Version 3 header
34 V3(LayoutVerified<B, boot_img_hdr_v3>),
35 /// Version 4 header
36 V4(LayoutVerified<B, boot_img_hdr_v4>),
37 }
38
39 /// Generalized vendor boot header from a backing store of bytes.
40 #[derive(PartialEq, Debug)]
41 pub enum VendorImageHeader<B: ByteSlice + PartialEq> {
42 /// Version 3 header
43 V3(LayoutVerified<B, vendor_boot_img_hdr_v3>),
44 /// Version 4 header
45 V4(LayoutVerified<B, vendor_boot_img_hdr_v4>),
46 }
47
48 /// Boot related errors.
49 #[derive(PartialEq, Debug)]
50 pub enum ImageError {
51 /// The provided buffer was too small to hold a header.
52 BufferTooSmall,
53 /// The magic string was incorrect.
54 BadMagic,
55 /// The header version present is not supported.
56 UnexpectedVersion,
57 /// Catch-all for remaining errors.
58 Unknown,
59 }
60
61 impl core::fmt::Display for ImageError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result62 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63 let str = match self {
64 Self::BufferTooSmall => "The provided buffer is too small",
65 Self::BadMagic => "The magic string is incorrect",
66 Self::UnexpectedVersion => "Header version is not supported",
67 Self::Unknown => "Unknown error",
68 };
69 write!(f, "{str}")
70 }
71 }
72
73 /// Common result type for use with boot headers
74 pub type BootResult<T> = Result<T, ImageError>;
75
parse_header<B: ByteSlice + PartialEq, T>(buffer: B) -> BootResult<LayoutVerified<B, T>>76 fn parse_header<B: ByteSlice + PartialEq, T>(buffer: B) -> BootResult<LayoutVerified<B, T>> {
77 Ok(LayoutVerified::<B, T>::new_from_prefix(buffer).ok_or(ImageError::BufferTooSmall)?.0)
78 }
79
80 impl<B: ByteSlice + PartialEq> BootImage<B> {
81 /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference
82 /// to the associated boot image header.
83 ///
84 /// # Arguments
85 /// * `buffer` - buffer to parse
86 ///
87 /// # Returns
88 ///
89 /// * `Ok(BootImage)` - if parsing was successful.
90 /// * `Err(ImageError)` - if `buffer` does not contain a valid boot image header.
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// use bootimg::BootImage;
96 ///
97 /// let mut buffer = [0; 4096];
98 /// // Not shown: read first 4096 bytes of boot image into buffer
99 /// let header = BootImage::parse(&buffer[..]).unwrap();
100 /// ```
parse(buffer: B) -> BootResult<Self>101 pub fn parse(buffer: B) -> BootResult<Self> {
102 let magic_size = BOOT_MAGIC_SIZE as usize;
103 // Note: even though the v3 header is not a prefix for the v0, v1, or v2 header,
104 // the version and the magic string exist at the same offset and have the same types.
105 // Make a v3 temporary because it is the smallest.
106 let (hdr, _) =
107 LayoutVerified::<&[u8], boot_img_hdr_v3>::new_from_prefix(buffer.get(..).unwrap())
108 .ok_or(ImageError::BufferTooSmall)?;
109
110 if hdr.magic.ne(&BOOT_MAGIC[..magic_size]) {
111 return Err(ImageError::BadMagic);
112 }
113
114 match hdr.header_version {
115 0 => Ok(Self::V0(parse_header::<B, boot_img_hdr_v0>(buffer)?)),
116 1 => Ok(Self::V1(parse_header::<B, boot_img_hdr_v1>(buffer)?)),
117 2 => Ok(Self::V2(parse_header::<B, boot_img_hdr_v2>(buffer)?)),
118 3 => Ok(Self::V3(parse_header::<B, boot_img_hdr_v3>(buffer)?)),
119 4 => Ok(Self::V4(parse_header::<B, boot_img_hdr_v4>(buffer)?)),
120 _ => Err(ImageError::UnexpectedVersion),
121 }
122 }
123 }
124
125 impl<B: ByteSlice + PartialEq> VendorImageHeader<B> {
126 /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference
127 /// to the associated vendor boot image header.
128 ///
129 /// # Arguments
130 /// * `buffer` - buffer to parse
131 ///
132 /// # Returns
133 ///
134 /// * `Ok(VendorImageHeader)` - if parsing was successful.
135 /// * `Err(ImageError)` - If `buffer` does not contain a valid boot image header.
136 ///
137 /// # Example
138 ///
139 /// ```
140 /// use bootimg::VendorImageHeader;
141 ///
142 /// let mut buffer = [0; 4096];
143 /// // Not shown: read first 4096 bytes of vendor image into buffer
144 /// let header = VendorImageHeader::parse(&buffer[..]).unwrap();
145 /// ```
parse(buffer: B) -> BootResult<Self>146 pub fn parse(buffer: B) -> BootResult<Self> {
147 let magic_size = VENDOR_BOOT_MAGIC_SIZE as usize;
148 let (hdr, _) = LayoutVerified::<&[u8], vendor_boot_img_hdr_v3>::new_from_prefix(
149 buffer.get(..).unwrap(),
150 )
151 .ok_or(ImageError::BufferTooSmall)?;
152
153 if hdr.magic.ne(&VENDOR_BOOT_MAGIC[..magic_size]) {
154 return Err(ImageError::BadMagic);
155 }
156
157 match hdr.header_version {
158 3 => Ok(Self::V3(parse_header::<B, vendor_boot_img_hdr_v3>(buffer)?)),
159 4 => Ok(Self::V4(parse_header::<B, vendor_boot_img_hdr_v4>(buffer)?)),
160 _ => Err(ImageError::UnexpectedVersion),
161 }
162 }
163 }
164
165 #[cfg(test)]
166 mod tests {
167 use super::*;
168 use zerocopy::AsBytes;
169
170 const MAGIC_SIZE: usize = BOOT_MAGIC_SIZE as usize;
171 const VENDOR_MAGIC_SIZE: usize = VENDOR_BOOT_MAGIC_SIZE as usize;
172
add<T: AsBytes>(buffer: &mut [u8], t: T)173 pub fn add<T: AsBytes>(buffer: &mut [u8], t: T) {
174 t.write_to_prefix(buffer).unwrap();
175 }
176
177 #[test]
buffer_too_small_for_version()178 fn buffer_too_small_for_version() {
179 let buffer = [0; 40];
180 assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
181 }
182
183 #[test]
buffer_too_small_valid_version()184 fn buffer_too_small_valid_version() {
185 // Note: because the v1 header fully encapsulates the v0 header,
186 // we can trigger a buffer-too-small error by providing
187 // a perfectly valid v0 header and changing the version to 1.
188 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()];
189 add::<boot_img_hdr_v0>(
190 &mut buffer,
191 boot_img_hdr_v0 {
192 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
193 header_version: 1,
194 ..Default::default()
195 },
196 );
197 assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
198 }
199
200 #[test]
bad_magic()201 fn bad_magic() {
202 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()];
203 add::<boot_img_hdr_v0>(
204 &mut buffer,
205 boot_img_hdr_v0 { magic: *b"ANDROGEN", ..Default::default() },
206 );
207 assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BadMagic));
208 }
209
210 #[test]
bad_version()211 fn bad_version() {
212 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()];
213 add::<boot_img_hdr_v0>(
214 &mut buffer,
215 boot_img_hdr_v0 {
216 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
217 header_version: 2112,
218 ..Default::default()
219 },
220 );
221 assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::UnexpectedVersion));
222 }
223
224 #[test]
parse_v0()225 fn parse_v0() {
226 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()];
227 add::<boot_img_hdr_v0>(
228 &mut buffer,
229 boot_img_hdr_v0 {
230 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
231 header_version: 0,
232 ..Default::default()
233 },
234 );
235 let expected =
236 Ok(BootImage::V0(LayoutVerified::<&[u8], boot_img_hdr_v0>::new(&buffer).unwrap()));
237 assert_eq!(BootImage::parse(&buffer[..]), expected);
238 }
239
240 #[test]
parse_v1()241 fn parse_v1() {
242 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v1>()];
243 add::<boot_img_hdr_v1>(
244 &mut buffer,
245 boot_img_hdr_v1 {
246 _base: boot_img_hdr_v0 {
247 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
248 header_version: 1,
249 ..Default::default()
250 },
251 ..Default::default()
252 },
253 );
254 let expected =
255 Ok(BootImage::V1(LayoutVerified::<&[u8], boot_img_hdr_v1>::new(&buffer).unwrap()));
256 assert_eq!(BootImage::parse(&buffer[..]), expected);
257 }
258
259 #[test]
parse_v2()260 fn parse_v2() {
261 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v2>()];
262 add::<boot_img_hdr_v2>(
263 &mut buffer,
264 boot_img_hdr_v2 {
265 _base: boot_img_hdr_v1 {
266 _base: boot_img_hdr_v0 {
267 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
268 header_version: 2,
269 ..Default::default()
270 },
271 ..Default::default()
272 },
273 ..Default::default()
274 },
275 );
276 let expected =
277 Ok(BootImage::V2(LayoutVerified::<&[u8], boot_img_hdr_v2>::new(&buffer).unwrap()));
278 assert_eq!(BootImage::parse(&buffer[..]), expected);
279 }
280
281 #[test]
parse_v3()282 fn parse_v3() {
283 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v3>()];
284 add::<boot_img_hdr_v3>(
285 &mut buffer,
286 boot_img_hdr_v3 {
287 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
288 header_version: 3,
289 ..Default::default()
290 },
291 );
292 let expected =
293 Ok(BootImage::V3(LayoutVerified::<&[u8], boot_img_hdr_v3>::new(&buffer).unwrap()));
294 assert_eq!(BootImage::parse(&buffer[..]), expected);
295 }
296
297 #[test]
parse_v4()298 fn parse_v4() {
299 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v4>()];
300 add::<boot_img_hdr_v4>(
301 &mut buffer,
302 boot_img_hdr_v4 {
303 _base: boot_img_hdr_v3 {
304 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
305 header_version: 4,
306 ..Default::default()
307 },
308 ..Default::default()
309 },
310 );
311 let expected =
312 Ok(BootImage::V4(LayoutVerified::<&[u8], boot_img_hdr_v4>::new(&buffer).unwrap()));
313 assert_eq!(BootImage::parse(&buffer[..]), expected);
314 }
315
316 #[test]
vendor_buffer_too_small_for_version()317 fn vendor_buffer_too_small_for_version() {
318 let buffer = [0; VENDOR_MAGIC_SIZE + 3];
319 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
320 }
321
322 #[test]
vendor_bad_magic()323 fn vendor_bad_magic() {
324 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
325 add::<vendor_boot_img_hdr_v3>(
326 &mut buffer,
327 vendor_boot_img_hdr_v3 { magic: *b"VNDRBOOK", header_version: 3, ..Default::default() },
328 );
329 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BadMagic));
330 }
331
332 #[test]
vendor_bad_version()333 fn vendor_bad_version() {
334 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
335 add::<vendor_boot_img_hdr_v3>(
336 &mut buffer,
337 vendor_boot_img_hdr_v3 {
338 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
339 header_version: 2112,
340 ..Default::default()
341 },
342 );
343 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::UnexpectedVersion));
344 }
345
346 #[test]
vendor_buffer_too_small_valid_version()347 fn vendor_buffer_too_small_valid_version() {
348 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
349 add::<vendor_boot_img_hdr_v3>(
350 &mut buffer,
351 vendor_boot_img_hdr_v3 {
352 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
353 // Note: because the v4 header fully encapsulates the v3 header,
354 // we can trigger a buffer-too-small error by providing
355 // a perfectly valid v3 header and changing the version to 4.
356 header_version: 4,
357 ..Default::default()
358 },
359 );
360 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
361 }
362
363 #[test]
vendor_parse_v3()364 fn vendor_parse_v3() {
365 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
366 add::<vendor_boot_img_hdr_v3>(
367 &mut buffer,
368 vendor_boot_img_hdr_v3 {
369 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
370 header_version: 3,
371 ..Default::default()
372 },
373 );
374 let expected = Ok(VendorImageHeader::V3(
375 LayoutVerified::<&[u8], vendor_boot_img_hdr_v3>::new(&buffer).unwrap(),
376 ));
377 assert_eq!(VendorImageHeader::parse(&buffer[..]), expected);
378 }
379
380 #[test]
vendor_parse_v4()381 fn vendor_parse_v4() {
382 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v4>()];
383 add::<vendor_boot_img_hdr_v4>(
384 &mut buffer,
385 vendor_boot_img_hdr_v4 {
386 _base: vendor_boot_img_hdr_v3 {
387 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
388 header_version: 4,
389 ..Default::default()
390 },
391 ..Default::default()
392 },
393 );
394 let expected = Ok(VendorImageHeader::V4(
395 LayoutVerified::<&[u8], vendor_boot_img_hdr_v4>::new(&buffer).unwrap(),
396 ));
397 assert_eq!(VendorImageHeader::parse(&buffer[..]), expected);
398 }
399 }
400