1#![cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
14
15use std::fmt;
16use std::io::{Read, Seek, SeekFrom};
17use std::mem;
18use std::result;
19
20use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory, GuestUsize, ReadVolatile};
21
22use crate::loader::{Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result};
23use crate::loader_gen::elf;
24pub use crate::loader_gen::start_info;
25
26unsafe impl ByteValued for elf::Elf64_Ehdr {}
29
30unsafe impl ByteValued for elf::Elf64_Nhdr {}
33
34unsafe impl ByteValued for elf::Elf64_Phdr {}
37
38#[derive(Debug, PartialEq, Eq)]
39pub enum Error {
41 Align,
43 BigEndianElfOnLittle,
45 InvalidElfMagicNumber,
47 InvalidProgramHeaderSize,
49 InvalidProgramHeaderOffset,
51 InvalidProgramHeaderAddress,
53 InvalidEntryAddress,
55 Overflow,
57 ReadElfHeader,
59 ReadKernelImage,
61 ReadProgramHeader,
63 SeekKernelStart,
65 SeekElfStart,
67 SeekProgramHeader,
69 SeekNoteHeader,
71 ReadNoteHeader,
73 InvalidPvhNote,
75}
76
77impl fmt::Display for Error {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 let desc = match self {
80 Error::Align => "Invalid alignment",
81 Error::BigEndianElfOnLittle => {
82 "Trying to load big-endian binary on little-endian machine"
83 }
84 Error::InvalidElfMagicNumber => "Invalid Elf magic number",
85 Error::InvalidProgramHeaderSize => "Invalid program header size",
86 Error::InvalidProgramHeaderOffset => "Invalid program header offset",
87 Error::InvalidProgramHeaderAddress => "Invalid Program Header Address",
88 Error::InvalidEntryAddress => "Invalid entry address",
89 Error::Overflow => "Overflow occurred during an arithmetic operation",
90 Error::ReadElfHeader => "Unable to read elf header",
91 Error::ReadKernelImage => "Unable to read kernel image",
92 Error::ReadProgramHeader => "Unable to read program header",
93 Error::SeekKernelStart => "Unable to seek to kernel start",
94 Error::SeekElfStart => "Unable to seek to elf start",
95 Error::SeekProgramHeader => "Unable to seek to program header",
96 Error::SeekNoteHeader => "Unable to seek to note header",
97 Error::ReadNoteHeader => "Unable to read note header",
98 Error::InvalidPvhNote => "Invalid PVH note header",
99 };
100
101 write!(f, "Kernel Loader: {}", desc)
102 }
103}
104
105impl std::error::Error for Error {}
106
107#[derive(Clone, Default, Copy, Debug, PartialEq, Eq)]
108pub enum PvhBootCapability {
111 PvhEntryPresent(GuestAddress),
113 PvhEntryNotPresent,
115 #[default]
117 PvhEntryIgnored,
118}
119
120impl fmt::Display for PvhBootCapability {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122 use self::PvhBootCapability::*;
123 match self {
124 PvhEntryPresent(pvh_entry_addr) => write!(
125 f,
126 "PVH entry point present at guest address: {:#x}",
127 pvh_entry_addr.raw_value()
128 ),
129 PvhEntryNotPresent => write!(f, "PVH entry point not present"),
130 PvhEntryIgnored => write!(f, "PVH entry point ignored"),
131 }
132 }
133}
134
135pub struct Elf;
137
138impl Elf {
139 fn validate_header(ehdr: &elf::Elf64_Ehdr) -> std::result::Result<(), Error> {
141 if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8
143 || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1
144 || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2
145 || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3
146 {
147 return Err(Error::InvalidElfMagicNumber);
148 }
149 if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 {
150 return Err(Error::BigEndianElfOnLittle);
151 }
152 if ehdr.e_phentsize as usize != mem::size_of::<elf::Elf64_Phdr>() {
153 return Err(Error::InvalidProgramHeaderSize);
154 }
155 if (ehdr.e_phoff as usize) < mem::size_of::<elf::Elf64_Ehdr>() {
156 return Err(Error::InvalidProgramHeaderOffset);
157 }
158 Ok(())
159 }
160}
161
162impl KernelLoader for Elf {
163 fn load<F, M: GuestMemory>(
202 guest_mem: &M,
203 kernel_offset: Option<GuestAddress>,
204 kernel_image: &mut F,
205 highmem_start_address: Option<GuestAddress>,
206 ) -> Result<KernelLoaderResult>
207 where
208 F: Read + ReadVolatile + Seek,
209 {
210 kernel_image.rewind().map_err(|_| Error::SeekElfStart)?;
211
212 let mut ehdr = elf::Elf64_Ehdr::default();
213 kernel_image
214 .read_exact(ehdr.as_mut_slice())
215 .map_err(|_| Error::ReadElfHeader)?;
216
217 Self::validate_header(&ehdr)?;
219 if let Some(addr) = highmem_start_address {
220 if (ehdr.e_entry) < addr.raw_value() {
221 return Err(Error::InvalidEntryAddress.into());
222 }
223 }
224
225 let mut loader_result = KernelLoaderResult {
226 kernel_load: match kernel_offset {
227 Some(k_offset) => GuestAddress(
228 k_offset
229 .raw_value()
230 .checked_add(ehdr.e_entry)
231 .ok_or(Error::Overflow)?,
232 ),
233 None => GuestAddress(ehdr.e_entry),
234 },
235 ..Default::default()
236 };
237
238 kernel_image
239 .seek(SeekFrom::Start(ehdr.e_phoff))
240 .map_err(|_| Error::SeekProgramHeader)?;
241
242 let mut phdrs: Vec<elf::Elf64_Phdr> = vec![];
243 for _ in 0usize..ehdr.e_phnum as usize {
244 let mut phdr = elf::Elf64_Phdr::default();
245 kernel_image
246 .read_exact(phdr.as_mut_slice())
247 .map_err(|_| Error::ReadProgramHeader)?;
248 phdrs.push(phdr);
249 }
250
251 for phdr in phdrs {
253 if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
254 if phdr.p_type == elf::PT_NOTE {
255 if let Some(_offset) = kernel_offset {
261 loader_result.pvh_boot_cap = PvhBootCapability::PvhEntryIgnored;
262 } else {
263 loader_result.pvh_boot_cap = parse_elf_note(&phdr, kernel_image)?;
265 }
266 }
267 continue;
268 }
269
270 kernel_image
271 .seek(SeekFrom::Start(phdr.p_offset))
272 .map_err(|_| Error::SeekKernelStart)?;
273
274 let mem_offset = match kernel_offset {
277 Some(k_offset) => k_offset
278 .checked_add(phdr.p_paddr)
279 .ok_or(Error::InvalidProgramHeaderAddress)?,
280 None => GuestAddress(phdr.p_paddr),
281 };
282
283 guest_mem
284 .read_exact_volatile_from(mem_offset, kernel_image, phdr.p_filesz as usize)
285 .map_err(|_| Error::ReadKernelImage)?;
286
287 let kernel_end = mem_offset
288 .raw_value()
289 .checked_add(phdr.p_memsz as GuestUsize)
290 .ok_or(KernelLoaderError::MemoryOverflow)?;
291 loader_result.kernel_end = std::cmp::max(loader_result.kernel_end, kernel_end);
292 }
293
294 loader_result.setup_header = None;
296
297 Ok(loader_result)
298 }
299}
300
301const PVH_NOTE_STR_SZ: usize = 4;
303
304fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<PvhBootCapability>
311where
312 F: Read + ReadVolatile + Seek,
313{
314 const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18;
317
318 const ELFNOTE_ALIGN: u64 = 4;
325
326 kernel_image
328 .seek(SeekFrom::Start(phdr.p_offset))
329 .map_err(|_| Error::SeekNoteHeader)?;
330
331 let mut nhdr: elf::Elf64_Nhdr = Default::default();
334 let mut read_size: usize = 0;
335 let nhdr_sz = mem::size_of::<elf::Elf64_Nhdr>();
336
337 while read_size < phdr.p_filesz as usize {
338 kernel_image
339 .read_exact(nhdr.as_mut_slice())
340 .map_err(|_| Error::ReadNoteHeader)?;
341
342 if nhdr.n_type == XEN_ELFNOTE_PHYS32_ENTRY && nhdr.n_namesz as usize == PVH_NOTE_STR_SZ {
344 let mut buf = [0u8; PVH_NOTE_STR_SZ];
345 kernel_image
346 .read_exact(&mut buf)
347 .map_err(|_| Error::ReadNoteHeader)?;
348 if buf == [b'X', b'e', b'n', b'\0'] {
349 break;
350 }
351 }
352
353 let namesz_aligned = align_up(u64::from(nhdr.n_namesz), ELFNOTE_ALIGN)?;
355 let descsz_aligned = align_up(u64::from(nhdr.n_descsz), ELFNOTE_ALIGN)?;
356
357 if namesz_aligned > u32::MAX.into() || descsz_aligned > u32::MAX.into() {
360 return Err(Error::Overflow.into());
361 }
362
363 read_size = read_size
364 .checked_add(nhdr_sz) .and_then(|read_size| read_size.checked_add(namesz_aligned as usize))
368 .and_then(|read_size| read_size.checked_add(descsz_aligned as usize))
369 .ok_or(Error::Overflow)?;
370
371 kernel_image
372 .seek(SeekFrom::Start(phdr.p_offset + read_size as u64))
375 .map_err(|_| Error::SeekNoteHeader)?;
376 }
377
378 if read_size >= phdr.p_filesz as usize {
379 return Ok(PvhBootCapability::PvhEntryNotPresent);
381 }
382
383 kernel_image
387 .seek(SeekFrom::Current(
388 align_up(u64::from(nhdr.n_namesz), ELFNOTE_ALIGN)? as i64 - PVH_NOTE_STR_SZ as i64,
390 ))
391 .map_err(|_| Error::SeekNoteHeader)?;
392
393 if (nhdr.n_descsz as usize) < mem::size_of::<u32>() {
396 return Err(Error::InvalidPvhNote.into());
397 }
398
399 let mut pvh_addr_bytes = [0; mem::size_of::<u32>()];
400
401 kernel_image
403 .read_exact(&mut pvh_addr_bytes)
404 .map_err(|_| Error::ReadNoteHeader)?;
405
406 Ok(PvhBootCapability::PvhEntryPresent(GuestAddress(
407 u32::from_le_bytes(pvh_addr_bytes).into(),
408 )))
409}
410
411fn align_up(addr: u64, align: u64) -> result::Result<u64, Error> {
417 if !align.is_power_of_two() {
418 return Err(Error::Align);
419 }
420 let align_mask = align - 1;
421 if addr & align_mask == 0 {
422 Ok(addr) } else {
424 Ok((addr | align_mask) + 1)
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433 use std::io::Cursor;
434 use vm_memory::{Address, GuestAddress};
435 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
436
437 const MEM_SIZE: u64 = 0x100_0000;
438
439 fn create_guest_mem() -> GuestMemoryMmap {
440 GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
441 }
442
443 fn make_elf_bin() -> Vec<u8> {
444 let mut v = Vec::new();
445 v.extend_from_slice(include_bytes!("test_elf.bin"));
446 v
447 }
448
449 fn make_elfnote() -> Vec<u8> {
450 include_bytes!("test_elfnote.bin").to_vec()
451 }
452
453 fn make_elfnote_8byte_align() -> Vec<u8> {
454 include_bytes!("test_elfnote_8byte_align.bin").to_vec()
455 }
456
457 fn make_dummy_elfnote() -> Vec<u8> {
458 include_bytes!("test_dummy_note.bin").to_vec()
459 }
460
461 fn make_invalid_pvh_note() -> Vec<u8> {
462 include_bytes!("test_invalid_pvh_note.bin").to_vec()
463 }
464
465 fn make_elfnote_bad_align() -> Vec<u8> {
466 include_bytes!("test_bad_align.bin").to_vec()
467 }
468
469 #[test]
470 fn test_load_elf() {
471 let gm = create_guest_mem();
472 let image = make_elf_bin();
473 let kernel_addr = GuestAddress(0x200000);
474 let mut highmem_start_address = GuestAddress(0x0);
475 let mut loader_result = Elf::load(
476 &gm,
477 Some(kernel_addr),
478 &mut Cursor::new(&image),
479 Some(highmem_start_address),
480 )
481 .unwrap();
482 assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
483
484 loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap();
485 assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
486
487 loader_result = Elf::load(
488 &gm,
489 None,
490 &mut Cursor::new(&image),
491 Some(highmem_start_address),
492 )
493 .unwrap();
494 assert_eq!(loader_result.kernel_load.raw_value(), 0x400);
495
496 highmem_start_address = GuestAddress(0xa00000);
497 assert_eq!(
498 Some(KernelLoaderError::Elf(Error::InvalidEntryAddress)),
499 Elf::load(
500 &gm,
501 None,
502 &mut Cursor::new(&image),
503 Some(highmem_start_address)
504 )
505 .err()
506 );
507 }
508
509 #[test]
510 fn test_bad_magic_number() {
511 let gm = create_guest_mem();
512 let kernel_addr = GuestAddress(0x0);
513 let mut bad_image = make_elf_bin();
514 bad_image[0x1] = 0x33;
515 assert_eq!(
516 Some(KernelLoaderError::Elf(Error::InvalidElfMagicNumber)),
517 Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
518 );
519 }
520
521 #[test]
522 fn test_bad_endian() {
523 let gm = create_guest_mem();
525 let kernel_addr = GuestAddress(0x0);
526 let mut bad_image = make_elf_bin();
527 bad_image[0x5] = 2;
528 assert_eq!(
529 Some(KernelLoaderError::Elf(Error::BigEndianElfOnLittle)),
530 Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
531 );
532 }
533
534 #[test]
535 fn test_bad_phoff() {
536 let gm = create_guest_mem();
538 let kernel_addr = GuestAddress(0x0);
539 let mut bad_image = make_elf_bin();
540 bad_image[0x20] = 0x10;
541 assert_eq!(
542 Some(KernelLoaderError::Elf(Error::InvalidProgramHeaderOffset)),
543 Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err()
544 );
545 }
546
547 #[test]
548 fn test_load_pvh() {
549 let gm = create_guest_mem();
550 let pvhnote_image = make_elfnote();
551 let loader_result = Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap();
552 assert_eq!(
553 loader_result.pvh_boot_cap,
554 PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
555 );
556
557 let loader_result = Elf::load(
559 &gm,
560 Some(GuestAddress(0x0020_0000)),
561 &mut Cursor::new(&pvhnote_image),
562 None,
563 )
564 .unwrap();
565 assert_eq!(
566 loader_result.pvh_boot_cap,
567 PvhBootCapability::PvhEntryIgnored
568 );
569 }
570
571 #[test]
572 fn test_dummy_elfnote() {
573 let gm = create_guest_mem();
574 let dummynote_image = make_dummy_elfnote();
575 let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap();
576 assert_eq!(
577 loader_result.pvh_boot_cap,
578 PvhBootCapability::PvhEntryNotPresent
579 );
580 }
581
582 #[test]
583 fn test_bad_elfnote() {
584 let gm = create_guest_mem();
585 let badnote_image = make_invalid_pvh_note();
586 assert_eq!(
587 Some(KernelLoaderError::Elf(Error::InvalidPvhNote)),
588 Elf::load(&gm, None, &mut Cursor::new(&badnote_image), None).err()
589 );
590 }
591
592 #[test]
593 fn test_load_pvh_with_align() {
594 {
597 let gm =
598 GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (0x1000_0000_usize))]).unwrap();
599 let bad_align_image = make_elfnote_bad_align();
600 assert_ne!(
601 Some(KernelLoaderError::Elf(Error::Align)),
602 Elf::load(&gm, None, &mut Cursor::new(&bad_align_image), None).err()
603 );
604 }
605
606 {
609 let gm = create_guest_mem();
610 let pvhnote_image = make_elfnote_8byte_align();
611 let loader_result =
612 Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap();
613 assert_eq!(
614 loader_result.pvh_boot_cap,
615 PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
616 );
617 }
618 }
619
620 #[test]
621 fn test_overflow_loadaddr() {
622 let gm = create_guest_mem();
623 let image = make_elf_bin();
624 assert_eq!(
625 Some(KernelLoaderError::Elf(Error::Overflow)),
626 Elf::load(
627 &gm,
628 Some(GuestAddress(u64::MAX)),
629 &mut Cursor::new(&image),
630 None
631 )
632 .err()
633 );
634 }
635}