kernel_loader/
arm64.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Linux arm64 kernel loader.
6//! <https://www.kernel.org/doc/Documentation/arm64/booting.txt>
7
8use std::cmp::max;
9use std::io;
10use std::io::BufRead;
11use std::io::Read;
12use std::io::Seek;
13use std::io::SeekFrom;
14use std::mem::size_of_val;
15
16use base::warn;
17use base::FileGetLen;
18use base::FileReadWriteAtVolatile;
19use base::VolatileSlice;
20use data_model::Le32;
21use data_model::Le64;
22use lz4_flex::frame::FrameDecoder as Lz4FrameDecoder;
23use resources::AddressRange;
24use vm_memory::GuestAddress;
25use vm_memory::GuestMemory;
26use zerocopy::FromBytes;
27use zerocopy::FromZeros;
28use zerocopy::Immutable;
29use zerocopy::IntoBytes;
30use zerocopy::KnownLayout;
31
32use crate::ElfClass;
33use crate::Error;
34use crate::LoadedKernel;
35use crate::Result;
36
37#[derive(Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
38#[allow(unused)]
39#[repr(C)]
40struct Arm64ImageHeader {
41    code0: Le32,
42    code1: Le32,
43    text_offset: Le64,
44    image_size: Le64,
45    flags: Le64,
46    res2: Le64,
47    res3: Le64,
48    res4: Le64,
49    magic: Le32,
50    res5: Le32,
51}
52
53const ARM64_IMAGE_MAGIC: u32 = 0x644d5241; // "ARM\x64"
54
55const ARM64_IMAGE_FLAG_BE_MASK: u64 = 0x1;
56
57const ARM64_TEXT_OFFSET_DEFAULT: u64 = 0x80000;
58
59impl Arm64ImageHeader {
60    fn parse_load_addr(&self, kernel_start: GuestAddress) -> Result<GuestAddress> {
61        let magic: u32 = self.magic.into();
62        if magic != ARM64_IMAGE_MAGIC {
63            return Err(Error::InvalidMagicNumber);
64        }
65
66        let flags: u64 = self.flags.into();
67        if flags & ARM64_IMAGE_FLAG_BE_MASK != 0 {
68            return Err(Error::BigEndianOnLittle);
69        }
70
71        let mut text_offset: u64 = self.text_offset.into();
72        let image_size: u64 = self.image_size.into();
73
74        if image_size == 0 {
75            warn!("arm64 Image header has an effective size of zero");
76            // arm64/booting.txt:
77            // "Where image_size is zero, text_offset can be assumed to be 0x80000."
78            text_offset = ARM64_TEXT_OFFSET_DEFAULT;
79        }
80
81        // Load the image into guest memory at `text_offset` bytes past `kernel_start`.
82        kernel_start
83            .checked_add(text_offset)
84            .ok_or(Error::InvalidKernelOffset)
85    }
86}
87
88pub fn load_arm64_kernel<F>(
89    guest_mem: &GuestMemory,
90    kernel_start: GuestAddress,
91    kernel_image: &mut F,
92) -> Result<LoadedKernel>
93where
94    F: FileReadWriteAtVolatile + FileGetLen,
95{
96    let mut header = Arm64ImageHeader::new_zeroed();
97    kernel_image
98        .read_exact_at_volatile(VolatileSlice::new(header.as_mut_bytes()), 0)
99        .map_err(|_| Error::ReadHeader)?;
100    let load_addr = header.parse_load_addr(kernel_start)?;
101
102    let file_size = kernel_image.get_len().map_err(|_| Error::SeekKernelEnd)?;
103    let load_size = usize::try_from(file_size).map_err(|_| Error::InvalidKernelSize)?;
104    let range_size = max(file_size, u64::from(header.image_size));
105
106    let guest_slice = guest_mem
107        .get_slice_at_addr(load_addr, load_size)
108        .map_err(|_| Error::ReadKernelImage)?;
109    kernel_image
110        .read_exact_at_volatile(guest_slice, 0)
111        .map_err(|_| Error::ReadKernelImage)?;
112
113    Ok(LoadedKernel {
114        size: file_size,
115        address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
116            .ok_or(Error::InvalidKernelSize)?,
117        entry: load_addr,
118        class: ElfClass::ElfClass64,
119    })
120}
121
122fn load_arm64_kernel_from_reader<F: BufRead>(
123    guest_mem: &GuestMemory,
124    kernel_start: GuestAddress,
125    mut kernel_image: F,
126) -> Result<LoadedKernel> {
127    let mut header = Arm64ImageHeader::new_zeroed();
128    let header_size = u64::try_from(size_of_val(&header)).unwrap();
129
130    // Read and parse the kernel header.
131    kernel_image
132        .read_exact(header.as_mut_bytes())
133        .map_err(|_| Error::ReadHeader)?;
134    let load_addr = header.parse_load_addr(kernel_start)?;
135
136    // Write the parsed kernel header to memory. Avoid rewinding the reader back to the start.
137    guest_mem
138        .write_all_at_addr(header.as_bytes(), load_addr)
139        .map_err(|_| Error::ReadKernelImage)?;
140
141    // Continue reading from the source and copy the kernel image into GuestMemory.
142    let mut current_addr = load_addr
143        .checked_add(header_size)
144        .ok_or(Error::InvalidKernelSize)?;
145    loop {
146        let buf = match kernel_image.fill_buf() {
147            Ok([]) => break,
148            Ok(buf) => buf,
149            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
150            Err(_) => return Err(Error::ReadKernelImage),
151        };
152
153        guest_mem
154            .write_all_at_addr(buf, current_addr)
155            .map_err(|_| Error::ReadKernelImage)?;
156
157        let consumed = buf.len();
158        kernel_image.consume(consumed);
159
160        let offset = u64::try_from(consumed).map_err(|_| Error::InvalidKernelSize)?;
161        current_addr = current_addr
162            .checked_add(offset)
163            .ok_or(Error::InvalidKernelSize)?;
164    }
165
166    let file_size = current_addr.offset_from(load_addr);
167    let range_size = max(file_size, u64::from(header.image_size));
168    Ok(LoadedKernel {
169        size: file_size,
170        address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
171            .ok_or(Error::InvalidKernelSize)?,
172        entry: load_addr,
173        class: ElfClass::ElfClass64,
174    })
175}
176
177pub fn load_arm64_kernel_lz4<F: Read + Seek>(
178    guest_mem: &GuestMemory,
179    kernel_start: GuestAddress,
180    mut kernel_image: F,
181) -> Result<LoadedKernel> {
182    kernel_image
183        .seek(SeekFrom::Start(0))
184        .map_err(|_| Error::SeekKernelStart)?;
185    load_arm64_kernel_from_reader(
186        guest_mem,
187        kernel_start,
188        &mut Lz4FrameDecoder::new(kernel_image),
189    )
190}
191
192#[cfg(test)]
193mod test {
194    use std::fs::File;
195    use std::io::Seek;
196    use std::io::SeekFrom;
197    use std::io::Write;
198
199    use tempfile::tempfile;
200    use vm_memory::GuestAddress;
201    use vm_memory::GuestMemory;
202
203    use crate::load_arm64_kernel;
204    use crate::load_arm64_kernel_lz4;
205    use crate::Error;
206
207    const MEM_SIZE: u64 = 0x200_0000;
208
209    fn create_guest_mem() -> GuestMemory {
210        GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
211    }
212
213    #[allow(clippy::unusual_byte_groupings)]
214    fn write_valid_kernel() -> File {
215        let mut f = tempfile().expect("failed to create tempfile");
216
217        f.write_all(&[0x00, 0xC0, 0x2E, 0x14]).unwrap(); // code0
218        f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap(); // code1
219        f.write_all(&0x00000000_00E70000u64.to_le_bytes()).unwrap(); // text_offset
220        f.write_all(&0x00000000_0000000Au64.to_le_bytes()).unwrap(); // image_size
221        f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // flags
222        f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res2
223        f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res3
224        f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res4
225        f.write_all(&0x644D5241u32.to_le_bytes()).unwrap(); // magic
226        f.write_all(&0x00000000u32.to_le_bytes()).unwrap(); // res5
227
228        f.set_len(0xDC3808).unwrap();
229        f
230    }
231
232    fn mutate_file(mut f: &File, offset: u64, val: &[u8]) {
233        f.seek(SeekFrom::Start(offset))
234            .expect("failed to seek file");
235        f.write_all(val)
236            .expect("failed to write mutated value to file");
237    }
238
239    #[test]
240    fn load_arm64_valid() {
241        let gm = create_guest_mem();
242        let kernel_addr = GuestAddress(2 * 1024 * 1024);
243        let mut f = write_valid_kernel();
244        let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
245        assert_eq!(kernel.address_range.start, 0x107_0000);
246        assert_eq!(kernel.address_range.end, 0x1E3_3807);
247        assert_eq!(kernel.size, 0xDC_3808);
248        assert_eq!(kernel.entry, GuestAddress(0x107_0000));
249    }
250
251    #[test]
252    fn load_arm64_image_size_zero() {
253        let gm = create_guest_mem();
254        let kernel_addr = GuestAddress(2 * 1024 * 1024);
255        let mut f = write_valid_kernel();
256
257        // Set image_size = 0 and validate the default text_offset is applied.
258        mutate_file(&f, 16, &0u64.to_le_bytes());
259
260        let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
261        assert_eq!(kernel.address_range.start, 0x28_0000);
262        assert_eq!(kernel.address_range.end, 0x104_3807);
263        assert_eq!(kernel.size, 0xDC_3808);
264        assert_eq!(kernel.entry, GuestAddress(0x28_0000));
265    }
266
267    #[test]
268    fn load_arm64_bad_magic() {
269        let gm = create_guest_mem();
270        let kernel_addr = GuestAddress(2 * 1024 * 1024);
271        let mut f = write_valid_kernel();
272
273        // Mutate magic number so it doesn't match
274        mutate_file(&f, 56, &[0xCC, 0xCC, 0xCC, 0xCC]);
275
276        assert_eq!(
277            load_arm64_kernel(&gm, kernel_addr, &mut f),
278            Err(Error::InvalidMagicNumber)
279        );
280    }
281
282    fn write_valid_kernel_lz4() -> File {
283        let mut f = tempfile().expect("failed to create tempfile");
284
285        f.write_all(&0x184d2204u32.to_le_bytes()).unwrap(); // magic
286        f.write_all(&[0x44, 0x70, 0x1d]).unwrap(); // flg, bd, hc
287
288        // Compressed block #1.
289        f.write_all(&0x00004065u32.to_le_bytes()).unwrap();
290        f.write_all(&[
291            0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
292            0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
293            0x0f, 0x02, 0x00,
294        ])
295        .unwrap();
296        f.write_all(&[0xff; 16447]).unwrap();
297
298        // Compressed block #2.
299        f.write_all(&0x000050c9u32.to_le_bytes()).unwrap();
300        f.write_all(&[
301            0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
302        ])
303        .unwrap();
304        f.write_all(&[0xff; 16448]).unwrap();
305
306        // Compressed block #3.
307        f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
308        f.write_all(&[
309            0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
310        ])
311        .unwrap();
312        f.write_all(&[0xff; 16448]).unwrap();
313
314        // Compressed block #4.
315        f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
316        f.write_all(&[
317            0x00, 0x00, 0x00, 0x5f, 0x1c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
318        ])
319        .unwrap();
320        f.write_all(&[0xff; 7252]).unwrap();
321        f.write_all(&[0x43, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00])
322            .unwrap();
323
324        // EndMark
325        f.write_all(&0x00000000u32.to_le_bytes()).unwrap();
326
327        // Checksum
328        f.write_all(&0x22a9944cu32.to_le_bytes()).unwrap();
329
330        f
331    }
332
333    fn write_valid_kernel_lz4_legacy() -> File {
334        let mut f = tempfile().expect("failed to create tempfile");
335
336        f.write_all(&0x184c2102u32.to_le_bytes()).unwrap(); // magic
337
338        // Compressed block #1.
339        f.write_all(&0x000080a6u32.to_le_bytes()).unwrap();
340        f.write_all(&[
341            0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
342            0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
343            0x0f, 0x02, 0x00,
344        ])
345        .unwrap();
346        f.write_all(&[0xff; 32896]).unwrap();
347
348        // Compressed block #2.
349        f.write_all(&0x0000500au32.to_le_bytes()).unwrap();
350        f.write_all(&[
351            0x00, 0x00, 0x00, 0x9f, 0x5c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
352        ])
353        .unwrap();
354        f.write_all(&[0xff; 23700]).unwrap();
355        f.write_all(&[0x83, 0x50, 0x00]).unwrap();
356
357        // EndMark
358        f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap();
359
360        f
361    }
362
363    #[test]
364    fn load_arm64_lz4_valid() {
365        let gm = create_guest_mem();
366        let kernel_addr = GuestAddress(2 * 1024 * 1024);
367        let mut f = write_valid_kernel_lz4();
368        let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
369        assert_eq!(kernel.address_range.start, 0x107_0000);
370        assert_eq!(kernel.address_range.end, 0x1E3_3807);
371        assert_eq!(kernel.size, 0xDC_3808);
372        assert_eq!(kernel.entry, GuestAddress(0x107_0000));
373    }
374
375    #[test]
376    fn load_arm64_lz4_bad_magic() {
377        let gm = create_guest_mem();
378        let kernel_addr = GuestAddress(2 * 1024 * 1024);
379        let mut f = write_valid_kernel_lz4();
380
381        mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
382
383        assert_eq!(
384            load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
385            Err(Error::ReadHeader)
386        );
387    }
388
389    #[test]
390    fn load_arm64_lz4_bad_block() {
391        let gm = create_guest_mem();
392        let kernel_addr = GuestAddress(2 * 1024 * 1024);
393        let mut f = write_valid_kernel_lz4();
394
395        mutate_file(&f, 7, &[0xCC, 0xCC, 0xCC, 0xCC]);
396
397        assert_eq!(
398            load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
399            Err(Error::ReadHeader)
400        );
401    }
402
403    #[test]
404    fn load_arm64_lz4_legacy_valid() {
405        let gm = create_guest_mem();
406        let kernel_addr = GuestAddress(2 * 1024 * 1024);
407        let mut f = write_valid_kernel_lz4_legacy();
408        let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
409        assert_eq!(kernel.address_range.start, 0x107_0000);
410        assert_eq!(kernel.address_range.end, 0x1E3_3807);
411        assert_eq!(kernel.size, 0xDC_3808);
412        assert_eq!(kernel.entry, GuestAddress(0x107_0000));
413    }
414
415    #[test]
416    fn load_arm64_lz4_legacy_bad_magic() {
417        let gm = create_guest_mem();
418        let kernel_addr = GuestAddress(2 * 1024 * 1024);
419        let mut f = write_valid_kernel_lz4_legacy();
420
421        mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
422
423        assert_eq!(
424            load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
425            Err(Error::ReadHeader)
426        );
427    }
428
429    #[test]
430    fn load_arm64_lz4_legacy_bad_block() {
431        let gm = create_guest_mem();
432        let kernel_addr = GuestAddress(2 * 1024 * 1024);
433        let mut f = write_valid_kernel_lz4_legacy();
434
435        mutate_file(&f, 4, &[0xCC, 0xCC, 0xCC, 0xCC]);
436
437        assert_eq!(
438            load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
439            Err(Error::ReadHeader)
440        );
441    }
442}