1use std::mem;
8
9use base::FileReadWriteAtVolatile;
10use base::VolatileSlice;
11use remain::sorted;
12use resources::AddressRange;
13use thiserror::Error;
14use vm_memory::GuestAddress;
15use vm_memory::GuestMemory;
16use zerocopy::FromBytes;
17use zerocopy::IntoBytes;
18
19mod multiboot;
20
21#[allow(dead_code)]
22#[allow(non_camel_case_types)]
23#[allow(non_snake_case)]
24#[allow(non_upper_case_globals)]
25#[allow(clippy::all)]
26mod elf;
27
28mod arm64;
29
30pub use arm64::load_arm64_kernel;
31pub use arm64::load_arm64_kernel_lz4;
32pub use multiboot::load_multiboot;
33pub use multiboot::multiboot_header_from_file;
34
35#[sorted]
36#[derive(Error, Debug, PartialEq, Eq)]
37pub enum Error {
38 #[error("trying to load big-endian binary on little-endian machine")]
39 BigEndianOnLittle,
40 #[error("invalid elf class")]
41 InvalidElfClass,
42 #[error("invalid elf version")]
43 InvalidElfVersion,
44 #[error("invalid entry point")]
45 InvalidEntryPoint,
46 #[error("invalid flags")]
47 InvalidFlags,
48 #[error("invalid kernel offset")]
49 InvalidKernelOffset,
50 #[error("invalid kernel size")]
51 InvalidKernelSize,
52 #[error("invalid magic number")]
53 InvalidMagicNumber,
54 #[error("invalid Program Header Address")]
55 InvalidProgramHeaderAddress,
56 #[error("invalid Program Header memory size")]
57 InvalidProgramHeaderMemSize,
58 #[error("invalid program header offset")]
59 InvalidProgramHeaderOffset,
60 #[error("invalid program header size")]
61 InvalidProgramHeaderSize,
62 #[error("no loadable program headers found")]
63 NoLoadableProgramHeaders,
64 #[error("program header address out of allowed address range")]
65 ProgramHeaderAddressOutOfRange,
66 #[error("unable to read header")]
67 ReadHeader,
68 #[error("unable to read kernel image")]
69 ReadKernelImage,
70 #[error("unable to read program header")]
71 ReadProgramHeader,
72 #[error("unable to seek to kernel end")]
73 SeekKernelEnd,
74 #[error("unable to seek to kernel start")]
75 SeekKernelStart,
76 #[error("unable to seek to program header")]
77 SeekProgramHeader,
78}
79pub type Result<T> = std::result::Result<T, Error>;
80
81#[derive(Debug, Copy, Clone, PartialEq, Eq)]
82pub struct LoadedKernel {
84 pub address_range: AddressRange,
88
89 pub size: u64,
91
92 pub entry: GuestAddress,
94
95 pub class: ElfClass,
97}
98
99#[derive(Debug, Copy, Clone, PartialEq, Eq)]
100pub enum ElfClass {
102 ElfClass32,
103 ElfClass64,
104}
105
106pub fn load_elf<F>(
118 guest_mem: &GuestMemory,
119 kernel_start: GuestAddress,
120 kernel_image: &mut F,
121 phys_offset: u64,
122) -> Result<LoadedKernel>
123where
124 F: FileReadWriteAtVolatile,
125{
126 let (elf, class) = read_elf(kernel_image)?;
127 let mut start = None;
128 let mut end = 0;
129
130 for phdr in &elf.program_headers {
132 if phdr.p_type != elf::PT_LOAD {
133 continue;
134 }
135
136 let paddr = phdr
137 .p_paddr
138 .checked_add(phys_offset)
139 .ok_or(Error::ProgramHeaderAddressOutOfRange)?;
140
141 if paddr < kernel_start.offset() {
142 return Err(Error::ProgramHeaderAddressOutOfRange);
143 }
144
145 if start.is_none() {
146 start = Some(paddr);
147 }
148
149 end = paddr
150 .checked_add(phdr.p_memsz)
151 .ok_or(Error::InvalidProgramHeaderMemSize)?;
152
153 if phdr.p_filesz == 0 {
154 continue;
155 }
156
157 let guest_slice = guest_mem
158 .get_slice_at_addr(GuestAddress(paddr), phdr.p_filesz as usize)
159 .map_err(|_| Error::ReadKernelImage)?;
160 kernel_image
161 .read_exact_at_volatile(guest_slice, phdr.p_offset)
162 .map_err(|_| Error::ReadKernelImage)?;
163 }
164
165 let start = start.ok_or(Error::NoLoadableProgramHeaders)?;
167
168 let size = end
169 .checked_sub(start)
170 .ok_or(Error::InvalidProgramHeaderSize)?;
171
172 let address_range =
173 AddressRange::from_start_and_size(start, size).ok_or(Error::InvalidProgramHeaderSize)?;
174
175 let entry = elf
178 .file_header
179 .e_entry
180 .checked_add(phys_offset)
181 .ok_or(Error::InvalidEntryPoint)?;
182 if !address_range.contains(entry) {
183 return Err(Error::InvalidEntryPoint);
184 }
185
186 Ok(LoadedKernel {
187 address_range,
188 size,
189 entry: GuestAddress(entry),
190 class,
191 })
192}
193
194struct Elf64 {
195 file_header: elf::Elf64_Ehdr,
196 program_headers: Vec<elf::Elf64_Phdr>,
197}
198
199fn read_elf<F>(file: &mut F) -> Result<(Elf64, ElfClass)>
203where
204 F: FileReadWriteAtVolatile,
205{
206 let mut ident = [0u8; 16];
208 file.read_exact_at_volatile(VolatileSlice::new(&mut ident), 0)
209 .map_err(|_| Error::ReadHeader)?;
210
211 if ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
213 || ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
214 || ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
215 || ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
216 {
217 return Err(Error::InvalidMagicNumber);
218 }
219 if ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
220 return Err(Error::BigEndianOnLittle);
221 }
222 if ident[elf::EI_VERSION as usize] != elf::EV_CURRENT as u8 {
223 return Err(Error::InvalidElfVersion);
224 }
225
226 let ei_class = ident[elf::EI_CLASS as usize] as u32;
227 match ei_class {
228 elf::ELFCLASS32 => Ok((
229 read_elf_by_type::<_, elf::Elf32_Ehdr, elf::Elf32_Phdr>(file)?,
230 ElfClass::ElfClass32,
231 )),
232 elf::ELFCLASS64 => Ok((
233 read_elf_by_type::<_, elf::Elf64_Ehdr, elf::Elf64_Phdr>(file)?,
234 ElfClass::ElfClass64,
235 )),
236 _ => Err(Error::InvalidElfClass),
237 }
238}
239
240fn read_elf_by_type<F, FileHeader, ProgramHeader>(file: &mut F) -> Result<Elf64>
245where
246 F: FileReadWriteAtVolatile,
247 FileHeader: IntoBytes + FromBytes + Default + Into<elf::Elf64_Ehdr>,
248 ProgramHeader: IntoBytes + FromBytes + Clone + Default + Into<elf::Elf64_Phdr>,
249{
250 let mut ehdr = FileHeader::new_zeroed();
251 file.read_exact_at_volatile(VolatileSlice::new(ehdr.as_mut_bytes()), 0)
252 .map_err(|_| Error::ReadHeader)?;
253 let ehdr: elf::Elf64_Ehdr = ehdr.into();
254
255 if ehdr.e_phentsize as usize != mem::size_of::<ProgramHeader>() {
256 return Err(Error::InvalidProgramHeaderSize);
257 }
258 if (ehdr.e_phoff as usize) < mem::size_of::<FileHeader>() {
259 return Err(Error::InvalidProgramHeaderOffset);
261 }
262
263 let num_phdrs = ehdr.e_phnum as usize;
264 let mut phdrs = vec![ProgramHeader::default(); num_phdrs];
265 file.read_exact_at_volatile(VolatileSlice::new(phdrs.as_mut_bytes()), ehdr.e_phoff)
266 .map_err(|_| Error::ReadProgramHeader)?;
267
268 Ok(Elf64 {
269 file_header: ehdr,
270 program_headers: phdrs.into_iter().map(|ph| ph.into()).collect(),
271 })
272}
273
274impl From<elf::Elf32_Ehdr> for elf::Elf64_Ehdr {
275 fn from(ehdr32: elf::Elf32_Ehdr) -> Self {
276 elf::Elf64_Ehdr {
277 e_ident: ehdr32.e_ident,
278 e_type: ehdr32.e_type as elf::Elf64_Half,
279 e_machine: ehdr32.e_machine as elf::Elf64_Half,
280 e_version: ehdr32.e_version as elf::Elf64_Word,
281 e_entry: ehdr32.e_entry as elf::Elf64_Addr,
282 e_phoff: ehdr32.e_phoff as elf::Elf64_Off,
283 e_shoff: ehdr32.e_shoff as elf::Elf64_Off,
284 e_flags: ehdr32.e_flags as elf::Elf64_Word,
285 e_ehsize: ehdr32.e_ehsize as elf::Elf64_Half,
286 e_phentsize: ehdr32.e_phentsize as elf::Elf64_Half,
287 e_phnum: ehdr32.e_phnum as elf::Elf64_Half,
288 e_shentsize: ehdr32.e_shentsize as elf::Elf64_Half,
289 e_shnum: ehdr32.e_shnum as elf::Elf64_Half,
290 e_shstrndx: ehdr32.e_shstrndx as elf::Elf64_Half,
291 }
292 }
293}
294
295impl From<elf::Elf32_Phdr> for elf::Elf64_Phdr {
296 fn from(phdr32: elf::Elf32_Phdr) -> Self {
297 elf::Elf64_Phdr {
298 p_type: phdr32.p_type as elf::Elf64_Word,
299 p_flags: phdr32.p_flags as elf::Elf64_Word,
300 p_offset: phdr32.p_offset as elf::Elf64_Off,
301 p_vaddr: phdr32.p_vaddr as elf::Elf64_Addr,
302 p_paddr: phdr32.p_paddr as elf::Elf64_Addr,
303 p_filesz: phdr32.p_filesz as elf::Elf64_Xword,
304 p_memsz: phdr32.p_memsz as elf::Elf64_Xword,
305 p_align: phdr32.p_align as elf::Elf64_Xword,
306 }
307 }
308}
309
310#[cfg(test)]
311mod test {
312 use std::fs::File;
313 use std::io::Seek;
314 use std::io::SeekFrom;
315 use std::io::Write;
316
317 use tempfile::tempfile;
318 use vm_memory::GuestAddress;
319 use vm_memory::GuestMemory;
320
321 use super::*;
322
323 const MEM_SIZE: u64 = 0x40_0000;
324
325 fn create_guest_mem() -> GuestMemory {
326 GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
327 }
328
329 fn make_elf32_bin() -> File {
331 let bytes = include_bytes!("test_elf32.bin");
333 make_elf_bin(bytes)
334 }
335
336 fn make_elf64_bin() -> File {
338 let bytes = include_bytes!("test_elf64.bin");
339 make_elf_bin(bytes)
340 }
341
342 fn make_elf_bin(elf_bytes: &[u8]) -> File {
343 let mut file = tempfile().expect("failed to create tempfile");
344 file.write_all(elf_bytes)
345 .expect("failed to write elf to shared memory");
346 file
347 }
348
349 fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {
350 f.seek(SeekFrom::Start(offset))
351 .expect("failed to seek file");
352 f.write_all(&[val])
353 .expect("failed to write mutated value to file");
354 }
355
356 #[test]
357 fn load_elf32() {
358 let gm = create_guest_mem();
359 let kernel_addr = GuestAddress(0x0);
360 let mut image = make_elf32_bin();
361 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).unwrap();
362 assert_eq!(kernel.address_range.start, 0x20_0000);
363 assert_eq!(kernel.address_range.end, 0x20_002e);
364 assert_eq!(kernel.size, 0x2f);
365 assert_eq!(kernel.entry, GuestAddress(0x20_000e));
366 assert_eq!(kernel.class, ElfClass::ElfClass32);
367 }
368
369 #[test]
370 fn load_elf64() {
371 let gm = create_guest_mem();
372 let kernel_addr = GuestAddress(0x0);
373 let mut image = make_elf64_bin();
374 let kernel = load_elf(&gm, kernel_addr, &mut image, 0).expect("failed to load ELF");
375 assert_eq!(kernel.address_range.start, 0x20_0000);
376 assert_eq!(kernel.address_range.end, 0x20_002f);
377 assert_eq!(kernel.size, 0x30);
378 assert_eq!(kernel.entry, GuestAddress(0x20_000e));
379 assert_eq!(kernel.class, ElfClass::ElfClass64);
380 }
381
382 #[test]
383 fn bad_magic() {
384 let gm = create_guest_mem();
385 let kernel_addr = GuestAddress(0x0);
386 let mut bad_image = make_elf64_bin();
387 mutate_elf_bin(&bad_image, 0x1, 0x33);
388 assert_eq!(
389 Err(Error::InvalidMagicNumber),
390 load_elf(&gm, kernel_addr, &mut bad_image, 0)
391 );
392 }
393
394 #[test]
395 fn bad_endian() {
396 let gm = create_guest_mem();
398 let kernel_addr = GuestAddress(0x20_0000);
399 let mut bad_image = make_elf64_bin();
400 mutate_elf_bin(&bad_image, 0x5, 2);
401 assert_eq!(
402 Err(Error::BigEndianOnLittle),
403 load_elf(&gm, kernel_addr, &mut bad_image, 0)
404 );
405 }
406
407 #[test]
408 fn bad_phoff() {
409 let gm = create_guest_mem();
411 let kernel_addr = GuestAddress(0x0);
412 let mut bad_image = make_elf64_bin();
413 mutate_elf_bin(&bad_image, 0x20, 0x10);
414 assert_eq!(
415 Err(Error::InvalidProgramHeaderOffset),
416 load_elf(&gm, kernel_addr, &mut bad_image, 0)
417 );
418 }
419
420 #[test]
421 fn paddr_below_start() {
422 let gm = create_guest_mem();
423 let kernel_addr = GuestAddress(0x30_0000);
425 let mut image = make_elf64_bin();
426 let res = load_elf(&gm, kernel_addr, &mut image, 0);
427 assert_eq!(res, Err(Error::ProgramHeaderAddressOutOfRange));
428 }
429}