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