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, Immutable, KnownLayout, Ref, SplitByteSlice};
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(Ref<B, boot_img_hdr_v0>),
29 /// Version 1 header
30 V1(Ref<B, boot_img_hdr_v1>),
31 /// Version 2 header
32 V2(Ref<B, boot_img_hdr_v2>),
33 /// Version 3 header
34 V3(Ref<B, boot_img_hdr_v3>),
35 /// Version 4 header
36 V4(Ref<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(Ref<B, vendor_boot_img_hdr_v3>),
44 /// Version 4 header
45 V4(Ref<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: SplitByteSlice + PartialEq, T: Immutable + KnownLayout>( buffer: B, ) -> BootResult<Ref<B, T>>76 fn parse_header<B: SplitByteSlice + PartialEq, T: Immutable + KnownLayout>(
77 buffer: B,
78 ) -> BootResult<Ref<B, T>> {
79 Ok(Ref::<B, T>::new_from_prefix(buffer).ok_or(ImageError::BufferTooSmall)?.0)
80 }
81
82 impl<B: SplitByteSlice + PartialEq> BootImage<B> {
83 /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference
84 /// to the associated boot image header.
85 ///
86 /// # Arguments
87 /// * `buffer` - buffer to parse
88 ///
89 /// # Returns
90 ///
91 /// * `Ok(BootImage)` - if parsing was successful.
92 /// * `Err(ImageError)` - if `buffer` does not contain a valid boot image header.
93 ///
94 /// # Example
95 ///
96 /// ```
97 /// use bootimg::BootImage;
98 ///
99 /// let mut buffer = [0; 4096];
100 /// // Not shown: read first 4096 bytes of boot image into buffer
101 /// let header = BootImage::parse(&buffer[..]).unwrap();
102 /// ```
parse(buffer: B) -> BootResult<Self>103 pub fn parse(buffer: B) -> BootResult<Self> {
104 let magic_size = BOOT_MAGIC_SIZE as usize;
105 // Note: even though the v3 header is not a prefix for the v0, v1, or v2 header,
106 // the version and the magic string exist at the same offset and have the same types.
107 // Make a v3 temporary because it is the smallest.
108 let (hdr, _) = Ref::<&[u8], boot_img_hdr_v3>::new_from_prefix(buffer.get(..).unwrap())
109 .ok_or(ImageError::BufferTooSmall)?;
110
111 if hdr.magic.ne(&BOOT_MAGIC[..magic_size]) {
112 return Err(ImageError::BadMagic);
113 }
114
115 match hdr.header_version {
116 0 => Ok(Self::V0(parse_header::<B, boot_img_hdr_v0>(buffer)?)),
117 1 => Ok(Self::V1(parse_header::<B, boot_img_hdr_v1>(buffer)?)),
118 2 => Ok(Self::V2(parse_header::<B, boot_img_hdr_v2>(buffer)?)),
119 3 => Ok(Self::V3(parse_header::<B, boot_img_hdr_v3>(buffer)?)),
120 4 => Ok(Self::V4(parse_header::<B, boot_img_hdr_v4>(buffer)?)),
121 _ => Err(ImageError::UnexpectedVersion),
122 }
123 }
124 }
125
126 impl<B: SplitByteSlice + PartialEq> VendorImageHeader<B> {
127 /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference
128 /// to the associated vendor boot image header.
129 ///
130 /// # Arguments
131 /// * `buffer` - buffer to parse
132 ///
133 /// # Returns
134 ///
135 /// * `Ok(VendorImageHeader)` - if parsing was successful.
136 /// * `Err(ImageError)` - If `buffer` does not contain a valid boot image header.
137 ///
138 /// # Example
139 ///
140 /// ```
141 /// use bootimg::VendorImageHeader;
142 ///
143 /// let mut buffer = [0; 4096];
144 /// // Not shown: read first 4096 bytes of vendor image into buffer
145 /// let header = VendorImageHeader::parse(&buffer[..]).unwrap();
146 /// ```
parse(buffer: B) -> BootResult<Self>147 pub fn parse(buffer: B) -> BootResult<Self> {
148 let magic_size = VENDOR_BOOT_MAGIC_SIZE as usize;
149 let (hdr, _) =
150 Ref::<&[u8], vendor_boot_img_hdr_v3>::new_from_prefix(buffer.get(..).unwrap())
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::IntoBytes;
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: Immutable + IntoBytes>(buffer: &mut [u8], t: T)173 pub fn add<T: Immutable + IntoBytes>(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 = Ok(BootImage::V0(Ref::<&[u8], boot_img_hdr_v0>::new(&buffer).unwrap()));
236 assert_eq!(BootImage::parse(&buffer[..]), expected);
237 }
238
239 #[test]
parse_v1()240 fn parse_v1() {
241 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v1>()];
242 add::<boot_img_hdr_v1>(
243 &mut buffer,
244 boot_img_hdr_v1 {
245 _base: boot_img_hdr_v0 {
246 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
247 header_version: 1,
248 ..Default::default()
249 },
250 ..Default::default()
251 },
252 );
253 let expected = Ok(BootImage::V1(Ref::<&[u8], boot_img_hdr_v1>::new(&buffer).unwrap()));
254 assert_eq!(BootImage::parse(&buffer[..]), expected);
255 }
256
257 #[test]
parse_v2()258 fn parse_v2() {
259 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v2>()];
260 add::<boot_img_hdr_v2>(
261 &mut buffer,
262 boot_img_hdr_v2 {
263 _base: boot_img_hdr_v1 {
264 _base: boot_img_hdr_v0 {
265 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
266 header_version: 2,
267 ..Default::default()
268 },
269 ..Default::default()
270 },
271 ..Default::default()
272 },
273 );
274 let expected = Ok(BootImage::V2(Ref::<&[u8], boot_img_hdr_v2>::new(&buffer).unwrap()));
275 assert_eq!(BootImage::parse(&buffer[..]), expected);
276 }
277
278 #[test]
parse_v3()279 fn parse_v3() {
280 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v3>()];
281 add::<boot_img_hdr_v3>(
282 &mut buffer,
283 boot_img_hdr_v3 {
284 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
285 header_version: 3,
286 ..Default::default()
287 },
288 );
289 let expected = Ok(BootImage::V3(Ref::<&[u8], boot_img_hdr_v3>::new(&buffer).unwrap()));
290 assert_eq!(BootImage::parse(&buffer[..]), expected);
291 }
292
293 #[test]
parse_v4()294 fn parse_v4() {
295 let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v4>()];
296 add::<boot_img_hdr_v4>(
297 &mut buffer,
298 boot_img_hdr_v4 {
299 _base: boot_img_hdr_v3 {
300 magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(),
301 header_version: 4,
302 ..Default::default()
303 },
304 ..Default::default()
305 },
306 );
307 let expected = Ok(BootImage::V4(Ref::<&[u8], boot_img_hdr_v4>::new(&buffer).unwrap()));
308 assert_eq!(BootImage::parse(&buffer[..]), expected);
309 }
310
311 #[test]
vendor_buffer_too_small_for_version()312 fn vendor_buffer_too_small_for_version() {
313 let buffer = [0; VENDOR_MAGIC_SIZE + 3];
314 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
315 }
316
317 #[test]
vendor_bad_magic()318 fn vendor_bad_magic() {
319 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
320 add::<vendor_boot_img_hdr_v3>(
321 &mut buffer,
322 vendor_boot_img_hdr_v3 { magic: *b"VNDRBOOK", header_version: 3, ..Default::default() },
323 );
324 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BadMagic));
325 }
326
327 #[test]
vendor_bad_version()328 fn vendor_bad_version() {
329 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
330 add::<vendor_boot_img_hdr_v3>(
331 &mut buffer,
332 vendor_boot_img_hdr_v3 {
333 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
334 header_version: 2112,
335 ..Default::default()
336 },
337 );
338 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::UnexpectedVersion));
339 }
340
341 #[test]
vendor_buffer_too_small_valid_version()342 fn vendor_buffer_too_small_valid_version() {
343 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
344 add::<vendor_boot_img_hdr_v3>(
345 &mut buffer,
346 vendor_boot_img_hdr_v3 {
347 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
348 // Note: because the v4 header fully encapsulates the v3 header,
349 // we can trigger a buffer-too-small error by providing
350 // a perfectly valid v3 header and changing the version to 4.
351 header_version: 4,
352 ..Default::default()
353 },
354 );
355 assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall));
356 }
357
358 #[test]
vendor_parse_v3()359 fn vendor_parse_v3() {
360 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()];
361 add::<vendor_boot_img_hdr_v3>(
362 &mut buffer,
363 vendor_boot_img_hdr_v3 {
364 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
365 header_version: 3,
366 ..Default::default()
367 },
368 );
369 let expected =
370 Ok(VendorImageHeader::V3(Ref::<&[u8], vendor_boot_img_hdr_v3>::new(&buffer).unwrap()));
371 assert_eq!(VendorImageHeader::parse(&buffer[..]), expected);
372 }
373
374 #[test]
vendor_parse_v4()375 fn vendor_parse_v4() {
376 let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v4>()];
377 add::<vendor_boot_img_hdr_v4>(
378 &mut buffer,
379 vendor_boot_img_hdr_v4 {
380 _base: vendor_boot_img_hdr_v3 {
381 magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(),
382 header_version: 4,
383 ..Default::default()
384 },
385 ..Default::default()
386 },
387 );
388 let expected =
389 Ok(VendorImageHeader::V4(Ref::<&[u8], vendor_boot_img_hdr_v4>::new(&buffer).unwrap()));
390 assert_eq!(VendorImageHeader::parse(&buffer[..]), expected);
391 }
392 }
393