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 //! Image decompression support.
16
17 // gzip [DeflateDecoder] requires heap allocation. LZ4 decompression currently uses the heap but
18 // could potentially be adjusted to use preallocated buffers if necessary.
19 extern crate alloc;
20
21 use crate::{gbl_print, gbl_println, GblOps};
22 use liberror::{Error, Result};
23 use lz4_flex::decompress_into;
24 use zune_inflate::DeflateDecoder;
25
26 /// Decompresses the given kernel if necessary
27 ///
28 /// The possibly-compressed kernel starts in `buffer`. If it's compressed, it will be decompressed
29 /// using heap memory and then copied back into the end of `buffer`.
30 ///
31 /// # Returns
32 /// The offset of the decompressed kernel in `buffer`. If the kernel was not compressed. this
33 /// function is a no-op and will return `kernel_start` unchanged.
decompress_kernel<'a, 'b>( ops: &mut impl GblOps<'a, 'b>, buffer: &mut [u8], kernel_start: usize, ) -> Result<usize>34 pub fn decompress_kernel<'a, 'b>(
35 ops: &mut impl GblOps<'a, 'b>,
36 buffer: &mut [u8],
37 kernel_start: usize,
38 ) -> Result<usize> {
39 if buffer[kernel_start..kernel_start + 2] == [0x1f, 0x8b] {
40 gbl_println!(ops, "kernel is gzip compressed");
41 let mut decoder = DeflateDecoder::new(&buffer[kernel_start..]);
42 let decompressed_data = match decoder.decode_gzip() {
43 Ok(decompressed_data) => decompressed_data,
44 _ => {
45 return Err(Error::InvalidInput.into());
46 }
47 };
48 gbl_println!(ops, "kernel decompressed size {}", decompressed_data.len());
49 let kernel_start = buffer.len() - decompressed_data.len();
50 // Move decompressed data to slice.
51 buffer[kernel_start..].clone_from_slice(&decompressed_data);
52 Ok(kernel_start)
53 } else if buffer[kernel_start..kernel_start + 4] == [0x02, 0x21, 0x4c, 0x18] {
54 gbl_println!(ops, "kernel is lz4 compressed");
55 let kernel_tail_buffer = &buffer[kernel_start..];
56 let mut contents = &kernel_tail_buffer[4..];
57 let mut decompressed_kernel = alloc::vec::Vec::new();
58 loop {
59 if contents.len() < 4 {
60 if contents.len() != 0 {
61 gbl_println!(ops, "Error: some leftover data in the content");
62 }
63 break;
64 }
65 let block_size: usize =
66 u32::from_le_bytes(contents[0..4].try_into().unwrap()).try_into().unwrap();
67 let block;
68 (block, contents) = contents.split_at(block_size + 4);
69 let block = &block[4..];
70 // extend decompressed kernel buffer by 8MB
71 let decompressed_kernel_len = decompressed_kernel.len();
72 decompressed_kernel.resize(decompressed_kernel_len + 8 * 1024 * 1024, 0);
73 // decompress the block
74 let decompressed_data_size =
75 decompress_into(&block, &mut decompressed_kernel[decompressed_kernel_len..])
76 .unwrap();
77 // reduce the size of decompressed kernel buffer
78 decompressed_kernel.resize(decompressed_kernel_len + decompressed_data_size, 0);
79 }
80 gbl_println!(ops, "kernel decompressed size {}", decompressed_kernel.len());
81 let kernel_start = buffer.len() - decompressed_kernel.len();
82 // Move decompressed data to slice
83 buffer[kernel_start..].clone_from_slice(&decompressed_kernel);
84 Ok(kernel_start)
85 } else {
86 Ok(kernel_start)
87 }
88 }
89
90 #[cfg(test)]
91 mod test {
92 use super::*;
93 use crate::ops::test::FakeGblOps;
94
95 #[test]
decompress_kernel_lz4()96 fn decompress_kernel_lz4() {
97 let original_data = "Test TTTTTTTTT 123";
98 let compressed_data = [
99 0x02, 0x21, 0x4c, 0x18, 0x0f, 0x00, 0x00, 0x00, 0x63, 0x54, 0x65, 0x73, 0x74, 0x20,
100 0x54, 0x01, 0x00, 0x50, 0x54, 0x20, 0x31, 0x32, 0x33,
101 ];
102
103 // Create a buffer with the compressed data at the end.
104 let mut buffer = vec![0u8; 8 * 1024];
105 let compressed_offset = buffer.len() - compressed_data.len();
106 buffer[compressed_offset..].clone_from_slice(&compressed_data[..]);
107
108 let offset =
109 decompress_kernel(&mut FakeGblOps::default(), &mut buffer[..], compressed_offset)
110 .unwrap();
111 assert_eq!(&buffer[offset..], original_data.as_bytes());
112 }
113 }
114