• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 use std::io::Read;
9 use std::io::Seek;
10 use std::io::SeekFrom;
11 
12 use base::AsRawDescriptor;
13 use data_model::zerocopy_from_reader;
14 use data_model::Le32;
15 use data_model::Le64;
16 use resources::AddressRange;
17 use vm_memory::GuestAddress;
18 use vm_memory::GuestMemory;
19 use zerocopy::FromBytes;
20 
21 use crate::Error;
22 use crate::LoadedKernel;
23 use crate::Result;
24 
25 #[derive(Copy, Clone, FromBytes)]
26 #[allow(unused)]
27 #[repr(C)]
28 struct Arm64ImageHeader {
29     code0: Le32,
30     code1: Le32,
31     text_offset: Le64,
32     image_size: Le64,
33     flags: Le64,
34     res2: Le64,
35     res3: Le64,
36     res4: Le64,
37     magic: Le32,
38     res5: Le32,
39 }
40 
41 const ARM64_IMAGE_MAGIC: u32 = 0x644d5241; // "ARM\x64"
42 
43 const ARM64_IMAGE_FLAG_BE_MASK: u64 = 0x1;
44 
45 const ARM64_TEXT_OFFSET_DEFAULT: u64 = 0x80000;
46 
load_arm64_kernel<F>( guest_mem: &GuestMemory, kernel_start: GuestAddress, mut kernel_image: &mut F, ) -> Result<LoadedKernel> where F: Read + Seek + AsRawDescriptor,47 pub fn load_arm64_kernel<F>(
48     guest_mem: &GuestMemory,
49     kernel_start: GuestAddress,
50     mut kernel_image: &mut F,
51 ) -> Result<LoadedKernel>
52 where
53     F: Read + Seek + AsRawDescriptor,
54 {
55     kernel_image
56         .seek(SeekFrom::Start(0))
57         .map_err(|_| Error::SeekKernelStart)?;
58 
59     let header: Arm64ImageHeader =
60         zerocopy_from_reader(&mut kernel_image).map_err(|_| Error::ReadHeader)?;
61 
62     let magic: u32 = header.magic.into();
63     if magic != ARM64_IMAGE_MAGIC {
64         return Err(Error::InvalidMagicNumber);
65     }
66 
67     let flags: u64 = header.flags.into();
68     if flags & ARM64_IMAGE_FLAG_BE_MASK != 0 {
69         return Err(Error::BigEndianOnLittle);
70     }
71 
72     let file_size = kernel_image
73         .seek(SeekFrom::End(0))
74         .map_err(|_| Error::SeekKernelEnd)?;
75     kernel_image
76         .seek(SeekFrom::Start(0))
77         .map_err(|_| Error::SeekKernelStart)?;
78 
79     let mut text_offset: u64 = header.text_offset.into();
80     let image_size: u64 = header.image_size.into();
81 
82     if image_size == 0 {
83         // arm64/booting.txt: "Where image_size is zero, text_offset can be assumed to be 0x80000."
84         text_offset = ARM64_TEXT_OFFSET_DEFAULT;
85     }
86 
87     // Load the image into guest memory at `text_offset` bytes past `kernel_start`.
88     let load_addr = kernel_start
89         .checked_add(text_offset)
90         .ok_or(Error::InvalidKernelOffset)?;
91     let load_size = usize::try_from(file_size).map_err(|_| Error::InvalidKernelSize)?;
92     guest_mem
93         .read_to_memory(load_addr, kernel_image, load_size)
94         .map_err(|_| Error::ReadKernelImage)?;
95 
96     Ok(LoadedKernel {
97         size: file_size,
98         address_range: AddressRange::from_start_and_size(load_addr.offset(), file_size)
99             .ok_or(Error::InvalidKernelSize)?,
100         entry: load_addr,
101     })
102 }
103 
104 #[cfg(test)]
105 mod test {
106     use std::fs::File;
107     use std::io::Seek;
108     use std::io::SeekFrom;
109     use std::io::Write;
110 
111     use tempfile::tempfile;
112     use vm_memory::GuestAddress;
113     use vm_memory::GuestMemory;
114 
115     use crate::load_arm64_kernel;
116     use crate::Error;
117 
118     const MEM_SIZE: u64 = 0x200_0000;
119 
create_guest_mem() -> GuestMemory120     fn create_guest_mem() -> GuestMemory {
121         GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
122     }
123 
124     #[allow(clippy::unusual_byte_groupings)]
write_valid_kernel() -> File125     fn write_valid_kernel() -> File {
126         let mut f = tempfile().expect("failed to create tempfile");
127 
128         f.write_all(&[0x00, 0xC0, 0x2E, 0x14]).unwrap(); // code0
129         f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap(); // code1
130         f.write_all(&0x00000000_00E70000u64.to_le_bytes()).unwrap(); // text_offset
131         f.write_all(&0x00000000_0000000Au64.to_le_bytes()).unwrap(); // image_size
132         f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // flags
133         f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res2
134         f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res3
135         f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res4
136         f.write_all(&0x644D5241u32.to_le_bytes()).unwrap(); // magic
137         f.write_all(&0x00000000u32.to_le_bytes()).unwrap(); // res5
138 
139         f.set_len(0xDC3808).unwrap();
140         f
141     }
142 
mutate_file(mut f: &File, offset: u64, val: &[u8])143     fn mutate_file(mut f: &File, offset: u64, val: &[u8]) {
144         f.seek(SeekFrom::Start(offset))
145             .expect("failed to seek file");
146         f.write_all(val)
147             .expect("failed to write mutated value to file");
148     }
149 
150     #[test]
load_arm64_valid()151     fn load_arm64_valid() {
152         let gm = create_guest_mem();
153         let kernel_addr = GuestAddress(2 * 1024 * 1024);
154         let mut f = write_valid_kernel();
155         let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
156         assert_eq!(kernel.address_range.start, 0x107_0000);
157         assert_eq!(kernel.address_range.end, 0x1E3_3807);
158         assert_eq!(kernel.size, 0xDC_3808);
159         assert_eq!(kernel.entry, GuestAddress(0x107_0000));
160     }
161 
162     #[test]
load_arm64_image_size_zero()163     fn load_arm64_image_size_zero() {
164         let gm = create_guest_mem();
165         let kernel_addr = GuestAddress(2 * 1024 * 1024);
166         let mut f = write_valid_kernel();
167 
168         // Set image_size = 0 and validate the default text_offset is applied.
169         mutate_file(&f, 16, &0u64.to_le_bytes());
170 
171         let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
172         assert_eq!(kernel.address_range.start, 0x28_0000);
173         assert_eq!(kernel.address_range.end, 0x104_3807);
174         assert_eq!(kernel.size, 0xDC_3808);
175         assert_eq!(kernel.entry, GuestAddress(0x28_0000));
176     }
177 
178     #[test]
load_arm64_bad_magic()179     fn load_arm64_bad_magic() {
180         let gm = create_guest_mem();
181         let kernel_addr = GuestAddress(2 * 1024 * 1024);
182         let mut f = write_valid_kernel();
183 
184         // Mutate magic number so it doesn't match
185         mutate_file(&f, 56, &[0xCC, 0xCC, 0xCC, 0xCC]);
186 
187         assert_eq!(
188             load_arm64_kernel(&gm, kernel_addr, &mut f),
189             Err(Error::InvalidMagicNumber)
190         );
191     }
192 }
193