diff --git a/src/loader/bzimage/mod.rs b/src/loader/bzimage/mod.rs new file mode 100644 index 00000000..d6aca6b2 --- /dev/null +++ b/src/loader/bzimage/mod.rs @@ -0,0 +1,246 @@ +// Copyright (c) 2019 Intel Corporation. All rights reserved. +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +//! Traits and structs for loading bzimage kernels into guest memory. + +#![cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] + +use std::error::{self, Error as StdError}; +use std::fmt::{self, Display}; +use std::io::{Read, Seek, SeekFrom}; +use std::mem; + +use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsize}; + +use super::{bootparam, Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result}; + +#[derive(Debug, PartialEq)] +/// Bzimage kernel loader errors. +pub enum Error { + /// Invalid bzImage binary. + InvalidBzImage, + /// Unable to read bzImage header. + ReadBzImageHeader, + /// Unable to read bzImage compressed image. + ReadBzImageCompressedKernel, + /// Unable to seek to bzImage end. + SeekBzImageEnd, + /// Unable to seek to bzImage header. + SeekBzImageHeader, + /// Unable to seek to bzImage compressed kernel. + SeekBzImageCompressedKernel, +} + +impl error::Error for Error { + fn description(&self) -> &str { + match self { + Error::InvalidBzImage => "Invalid bzImage", + Error::ReadBzImageHeader => "Unable to read bzImage header", + Error::ReadBzImageCompressedKernel => "Unable to read bzImage compressed kernel", + Error::SeekBzImageEnd => "Unable to seek bzImage end", + Error::SeekBzImageHeader => "Unable to seek bzImage header", + Error::SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel", + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Kernel Loader Error: {}", self.description()) + } +} + +/// Big zImage (bzImage) kernel image support. +pub struct BzImage; + +impl KernelLoader for BzImage { + /// Loads a kernel from a bzImage to guest memory. + /// + /// The kernel is loaded at `code32_start`, the default load address stored in the bzImage + /// setup header. + /// + /// # Arguments + /// + /// * `guest_mem`: [`GuestMemory`] to load the kernel in. + /// * `kernel_start`: Address in guest memory where the kernel is loaded. + /// * `kernel_image` - Input bzImage image. + /// * `highmem_start_address`: Address where high memory starts. + /// + /// # Examples + /// + /// ```rust + /// # extern crate vm_memory; + /// # use linux_loader::loader::*; + /// # use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + /// # use std::io::Cursor; + /// let mem_size: usize = 0x1000000; + /// let himem_start = GuestAddress(0x0); + /// let kernel_addr = GuestAddress(0x200000); + /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap(); + /// let mut kernel_image = vec![]; + /// kernel_image.extend_from_slice(include_bytes!("bzimage")); + /// assert!(BzImage::load( + /// &gm, + /// Some(kernel_addr), + /// &mut Cursor::new(&kernel_image), + /// Some(himem_start), + /// ).is_ok()); + /// ``` + /// + /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html + fn load( + guest_mem: &M, + kernel_start: Option, + kernel_image: &mut F, + highmem_start_address: Option, + ) -> Result + where + F: Read + Seek, + { + let mut kernel_size = kernel_image + .seek(SeekFrom::End(0)) + .map_err(|_| Error::SeekBzImageEnd)? as usize; + kernel_image + .seek(SeekFrom::Start(0x1F1)) + .map_err(|_| Error::SeekBzImageHeader)?; + + let mut boot_header = bootparam::setup_header::default(); + boot_header + .as_bytes() + .read_from(0, kernel_image, mem::size_of::()) + .map_err(|_| Error::ReadBzImageHeader)?; + + // If the `HdrS` magic number is not found at offset 0x202, the boot protocol version is + // "old", the image type is assumed as zImage, not bzImage. + if boot_header.header != 0x5372_6448 { + Err(Error::InvalidBzImage)?; + } + + // Follow the section related to loading the rest of the kernel in the linux boot protocol. + if (boot_header.version < 0x0200) || ((boot_header.loadflags & 0x1) == 0x0) { + Err(Error::InvalidBzImage)?; + } + + let mut setup_size = boot_header.setup_sects as usize; + if setup_size == 0 { + setup_size = 4; + } + setup_size = (setup_size + 1) * 512; + kernel_size -= setup_size; + + // Check that `code32_start`, the default address of the kernel, is not lower than high + // memory. + if (highmem_start_address.is_some()) + && (u64::from(boot_header.code32_start) < highmem_start_address.unwrap().raw_value()) + { + return Err(KernelLoaderError::InvalidKernelStartAddress); + } + + let mem_offset = match kernel_start { + Some(start) => start, + None => GuestAddress(u64::from(boot_header.code32_start)), + }; + + boot_header.code32_start = mem_offset.raw_value() as u32; + + let mut loader_result: KernelLoaderResult = Default::default(); + loader_result.setup_header = Some(boot_header); + loader_result.kernel_load = mem_offset; + + // Seek the compressed `vmlinux.bin` and read it to memory. + kernel_image + .seek(SeekFrom::Start(setup_size as u64)) + .map_err(|_| Error::SeekBzImageCompressedKernel)?; + guest_mem + .read_exact_from(mem_offset, kernel_image, kernel_size) + .map_err(|_| Error::ReadBzImageCompressedKernel)?; + + loader_result.kernel_end = mem_offset + .raw_value() + .checked_add(kernel_size as GuestUsize) + .ok_or(KernelLoaderError::MemoryOverflow)?; + + Ok(loader_result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::io::Cursor; + use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + + const MEM_SIZE: u64 = 0x1000000; + + fn create_guest_mem() -> GuestMemoryMmap { + GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap() + } + + fn make_bzimage() -> Vec { + let mut v = Vec::new(); + v.extend_from_slice(include_bytes!("bzimage")); + v + } + + #[allow(safe_packed_borrows)] + #[allow(non_snake_case)] + #[test] + fn test_load_bzImage() { + let gm = create_guest_mem(); + let image = make_bzimage(); + let mut kernel_start = GuestAddress(0x200000); + let mut highmem_start_address = GuestAddress(0x0); + + // load bzImage with good kernel_start and himem_start setting + let mut loader_result = BzImage::load( + &gm, + Some(kernel_start), + &mut Cursor::new(&image), + Some(highmem_start_address), + ) + .unwrap(); + + assert_eq!(loader_result.kernel_load.raw_value(), 0x200000); + assert_eq!(loader_result.setup_header.unwrap().header, 0x53726448); + assert_eq!(loader_result.setup_header.unwrap().version, 0x20d); + assert_eq!(loader_result.setup_header.unwrap().loadflags, 1); + assert_eq!(loader_result.kernel_end, 0x60c320); + + // load bzImage without kernel_start + loader_result = BzImage::load( + &gm, + None, + &mut Cursor::new(&image), + Some(highmem_start_address), + ) + .unwrap(); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); + + // load bzImage withouth himem_start + loader_result = BzImage::load(&gm, None, &mut Cursor::new(&image), None).unwrap(); + assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); + + // load bzImage with a bad himem setting + kernel_start = GuestAddress(0x1000); + highmem_start_address = GuestAddress(0x200000); + + assert_eq!( + Some(KernelLoaderError::InvalidKernelStartAddress), + BzImage::load( + &gm, + Some(kernel_start), + &mut Cursor::new(&image), + Some(highmem_start_address), + ) + .err() + ); + } +} diff --git a/src/loader/elf.rs b/src/loader/elf/elf.rs similarity index 100% rename from src/loader/elf.rs rename to src/loader/elf/elf.rs diff --git a/src/loader/elf/mod.rs b/src/loader/elf/mod.rs new file mode 100644 index 00000000..62aa7b23 --- /dev/null +++ b/src/loader/elf/mod.rs @@ -0,0 +1,470 @@ +// Copyright (c) 2019 Intel Corporation. All rights reserved. +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +//! Traits and structs for loading elf image kernels into guest memory. + +#![cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] + +use std::error::{self, Error as StdError}; +use std::fmt::{self, Display}; +use std::io::{Read, Seek, SeekFrom}; +use std::mem; + +use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsize}; + +use super::{Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result}; + +#[allow(dead_code)] +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(non_upper_case_globals)] +#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] +mod elf; + +#[allow(missing_docs)] +#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] +pub mod start_info; + +unsafe impl ByteValued for elf::Elf64_Ehdr {} +unsafe impl ByteValued for elf::Elf64_Nhdr {} +unsafe impl ByteValued for elf::Elf64_Phdr {} + +#[derive(Debug, PartialEq)] +/// Elf kernel loader errors. +pub enum Error { + /// Loaded big endian binary on a little endian platform. + BigEndianElfOnLittle, + /// Invalid ELF magic number + InvalidElfMagicNumber, + /// Invalid program header size. + InvalidProgramHeaderSize, + /// Invalid program header offset. + InvalidProgramHeaderOffset, + /// Invalid program header address. + InvalidProgramHeaderAddress, + /// Invalid entry address. + InvalidEntryAddress, + /// Unable to read ELF header. + ReadElfHeader, + /// Unable to read kernel image. + ReadKernelImage, + /// Unable to read program header. + ReadProgramHeader, + /// Unable to seek to kernel start. + SeekKernelStart, + /// Unable to seek to ELF start. + SeekElfStart, + /// Unable to seek to program header. + SeekProgramHeader, + /// Unable to seek to note header. + SeekNoteHeader, + /// Unable to read note header. + ReadNoteHeader, + /// Invalid PVH note. + InvalidPvhNote, +} + +impl error::Error for Error { + fn description(&self) -> &str { + match self { + Error::BigEndianElfOnLittle => { + "Trying to load big-endian binary on little-endian machine" + } + Error::InvalidElfMagicNumber => "Invalid Elf magic number", + Error::InvalidProgramHeaderSize => "Invalid program header size", + Error::InvalidProgramHeaderOffset => "Invalid program header offset", + Error::InvalidProgramHeaderAddress => "Invalid Program Header Address", + Error::InvalidEntryAddress => "Invalid entry address", + Error::ReadElfHeader => "Unable to read elf header", + Error::ReadKernelImage => "Unable to read kernel image", + Error::ReadProgramHeader => "Unable to read program header", + Error::SeekKernelStart => "Unable to seek to kernel start", + Error::SeekElfStart => "Unable to seek to elf start", + Error::SeekProgramHeader => "Unable to seek to program header", + Error::SeekNoteHeader => "Unable to seek to note header", + Error::ReadNoteHeader => "Unable to read note header", + Error::InvalidPvhNote => "Invalid PVH note header", + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Kernel Loader Error: {}", self.description()) + } +} + +/// Raw ELF (a.k.a. vmlinux) kernel image support. +pub struct Elf; + +impl Elf { + fn validate_header(ehdr: &elf::Elf64_Ehdr) -> std::result::Result<(), Error> { + // Sanity checks + if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8 + || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1 + || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2 + || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3 + { + return Err(Error::InvalidElfMagicNumber); + } + if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 { + return Err(Error::BigEndianElfOnLittle); + } + if ehdr.e_phentsize as usize != mem::size_of::() { + return Err(Error::InvalidProgramHeaderSize); + } + if (ehdr.e_phoff as usize) < mem::size_of::() { + return Err(Error::InvalidProgramHeaderOffset); + } + Ok(()) + } +} + +impl KernelLoader for Elf { + /// Loads a kernel from a vmlinux elf image into guest memory. + /// + /// The kernel is loaded into guest memory at offset `phdr.p_paddr` specified by the elf image. + /// + /// # Arguments + /// + /// * `guest_mem`: [`GuestMemory`] to load the kernel in. + /// * `kernel_start`: Address in guest memory where the kernel is loaded. + /// * `kernel_image` - Input vmlinux image. + /// * `highmem_start_address`: Address where high memory starts. + /// + /// # Examples + /// + /// ```rust + /// # extern crate vm_memory; + /// # use linux_loader::loader::*; + /// # use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + /// # use std::io::Cursor; + /// let mem_size: usize = 0x1000000; + /// let himem_start = GuestAddress(0x0); + /// let kernel_addr = GuestAddress(0x200000); + /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap(); + /// let mut kernel_image = vec![]; + /// kernel_image.extend_from_slice(include_bytes!("test_elf.bin")); + /// assert!(Elf::load( + /// &gm, + /// Some(kernel_addr), + /// &mut Cursor::new(&kernel_image), + /// Some(himem_start), + /// ).is_ok()); + /// ``` + /// + /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html + fn load( + guest_mem: &M, + kernel_start: Option, + kernel_image: &mut F, + highmem_start_address: Option, + ) -> Result + where + F: Read + Seek, + { + kernel_image + .seek(SeekFrom::Start(0)) + .map_err(|_| Error::SeekElfStart)?; + + let mut ehdr = elf::Elf64_Ehdr::default(); + ehdr.as_bytes() + .read_from(0, kernel_image, mem::size_of::()) + .map_err(|_| Error::ReadElfHeader)?; + + // Sanity checks. + Self::validate_header(&ehdr)?; + if let Some(addr) = highmem_start_address { + if (ehdr.e_entry as u64) < addr.raw_value() { + Err(Error::InvalidEntryAddress)?; + } + } + + let mut loader_result: KernelLoaderResult = Default::default(); + + // Address where the kernel will be loaded. + loader_result.kernel_load = match kernel_start { + Some(start) => GuestAddress(start.raw_value() + (ehdr.e_entry as u64)), + None => GuestAddress(ehdr.e_entry as u64), + }; + + kernel_image + .seek(SeekFrom::Start(ehdr.e_phoff)) + .map_err(|_| Error::SeekProgramHeader)?; + + let phdr_sz = mem::size_of::(); + let mut phdrs: Vec = vec![]; + for _ in 0usize..ehdr.e_phnum as usize { + let mut phdr = elf::Elf64_Phdr::default(); + phdr.as_bytes() + .read_from(0, kernel_image, phdr_sz) + .map_err(|_| Error::ReadProgramHeader)?; + phdrs.push(phdr); + } + + // Read in each section pointed to by the program headers. + for phdr in phdrs { + if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 { + if phdr.p_type == elf::PT_NOTE { + // This segment describes a Note, check if PVH entry point is encoded. + loader_result.pvh_entry_addr = parse_elf_note(&phdr, kernel_image)?; + } + continue; + } + + kernel_image + .seek(SeekFrom::Start(phdr.p_offset)) + .map_err(|_| Error::SeekKernelStart)?; + + // if the vmm does not specify where the kernel should be loaded, just + // load it to the physical address p_paddr for each segment. + let mem_offset = match kernel_start { + Some(start) => start + .checked_add(phdr.p_paddr as u64) + .ok_or(Error::InvalidProgramHeaderAddress)?, + None => GuestAddress(phdr.p_paddr as u64), + }; + + guest_mem + .read_exact_from(mem_offset, kernel_image, phdr.p_filesz as usize) + .map_err(|_| Error::ReadKernelImage)?; + + loader_result.kernel_end = mem_offset + .raw_value() + .checked_add(phdr.p_memsz as GuestUsize) + .ok_or(KernelLoaderError::MemoryOverflow)?; + } + + // elf image has no setup_header which is defined for bzImage + loader_result.setup_header = None; + + Ok(loader_result) + } +} + +fn parse_elf_note(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result> +where + F: Read + Seek, +{ + // Type of note header that encodes a 32-bit entry point address + // to boot a guest kernel using the PVH boot protocol. + const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18; + + let n_align = phdr.p_align; + + // Seek to the beginning of the note segment + kernel_image + .seek(SeekFrom::Start(phdr.p_offset)) + .map_err(|_| Error::SeekNoteHeader)?; + + // Now that the segment has been found, we must locate an ELF note with the + // correct type that encodes the PVH entry point if there is one. + let mut nhdr: elf::Elf64_Nhdr = Default::default(); + let mut read_size: usize = 0; + let nhdr_sz = mem::size_of::(); + + while read_size < phdr.p_filesz as usize { + nhdr.as_bytes() + .read_from(0, kernel_image, nhdr_sz) + .map_err(|_| Error::ReadNoteHeader)?; + + // If the note header found is not the desired one, keep reading until + // the end of the segment + if nhdr.n_type == XEN_ELFNOTE_PHYS32_ENTRY { + break; + } + // Skip the note header plus the size of its fields (with alignment) + read_size += nhdr_sz + + align_up(u64::from(nhdr.n_namesz), n_align) + + align_up(u64::from(nhdr.n_descsz), n_align); + + kernel_image + .seek(SeekFrom::Start(phdr.p_offset + read_size as u64)) + .map_err(|_| Error::SeekNoteHeader)?; + } + + if read_size >= phdr.p_filesz as usize { + return Ok(None); // PVH ELF note not found, nothing else to do. + } + // Otherwise the correct note type was found. + // The note header struct has already been read, so we can seek from the + // current position and just skip the name field contents. + kernel_image + .seek(SeekFrom::Current( + align_up(u64::from(nhdr.n_namesz), n_align) as i64, + )) + .map_err(|_| Error::SeekNoteHeader)?; + + // The PVH entry point is a 32-bit address, so the descriptor field + // must be capable of storing all such addresses. + if (nhdr.n_descsz as usize) < mem::size_of::() { + Err(Error::InvalidPvhNote)?; + } + + let mut pvh_addr_bytes = [0; mem::size_of::()]; + + // Read 32-bit address stored in the PVH note descriptor field. + kernel_image + .read_exact(&mut pvh_addr_bytes) + .map_err(|_| Error::ReadNoteHeader)?; + + Ok(Some(GuestAddress( + u32::from_le_bytes(pvh_addr_bytes).into(), + ))) +} + +/// Align address upwards. Taken from x86_64 crate: +/// https://docs.rs/x86_64/latest/x86_64/fn.align_up.html +/// +/// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be +/// a power of 2. +fn align_up(addr: u64, align: u64) -> usize { + assert!(align.is_power_of_two(), "`align` must be a power of two"); + let align_mask = align - 1; + if addr & align_mask == 0 { + addr as usize // already aligned + } else { + ((addr | align_mask) + 1) as usize + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; + + const MEM_SIZE: u64 = 0x1000000; + + fn create_guest_mem() -> GuestMemoryMmap { + GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap() + } + + fn make_elf_bin() -> Vec { + let mut v = Vec::new(); + v.extend_from_slice(include_bytes!("test_elf.bin")); + v + } + + fn make_elfnote() -> Vec { + include_bytes!("test_elfnote.bin").to_vec() + } + + fn make_dummy_elfnote() -> Vec { + include_bytes!("test_dummynote.bin").to_vec() + } + + fn make_bad_elfnote() -> Vec { + include_bytes!("test_badnote.bin").to_vec() + } + + #[test] + fn test_load_elf() { + let gm = create_guest_mem(); + let image = make_elf_bin(); + let kernel_addr = GuestAddress(0x200000); + let mut highmem_start_address = GuestAddress(0x0); + let mut loader_result = Elf::load( + &gm, + Some(kernel_addr), + &mut Cursor::new(&image), + Some(highmem_start_address), + ) + .unwrap(); + assert_eq!(loader_result.kernel_load.raw_value(), 0x300000); + + loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap(); + assert_eq!(loader_result.kernel_load.raw_value(), 0x300000); + + loader_result = Elf::load( + &gm, + None, + &mut Cursor::new(&image), + Some(highmem_start_address), + ) + .unwrap(); + assert_eq!(loader_result.kernel_load.raw_value(), 0x100000); + + highmem_start_address = GuestAddress(0xa00000); + assert_eq!( + Some(KernelLoaderError::Elf(Error::InvalidEntryAddress)), + Elf::load( + &gm, + None, + &mut Cursor::new(&image), + Some(highmem_start_address) + ) + .err() + ); + } + + #[test] + fn test_bad_magic_number() { + let gm = create_guest_mem(); + let kernel_addr = GuestAddress(0x0); + let mut bad_image = make_elf_bin(); + bad_image[0x1] = 0x33; + assert_eq!( + Some(KernelLoaderError::Elf(Error::InvalidElfMagicNumber)), + Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err() + ); + } + + #[test] + fn test_bad_endian() { + // Only little endian is supported. + let gm = create_guest_mem(); + let kernel_addr = GuestAddress(0x0); + let mut bad_image = make_elf_bin(); + bad_image[0x5] = 2; + assert_eq!( + Some(KernelLoaderError::Elf(Error::BigEndianElfOnLittle)), + Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err() + ); + } + + #[test] + fn test_bad_phoff() { + // Program header has to be past the end of the elf header. + let gm = create_guest_mem(); + let kernel_addr = GuestAddress(0x0); + let mut bad_image = make_elf_bin(); + bad_image[0x20] = 0x10; + assert_eq!( + Some(KernelLoaderError::Elf(Error::InvalidProgramHeaderOffset)), + Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None).err() + ); + } + + #[test] + fn test_load_pvh() { + let gm = create_guest_mem(); + let pvhnote_image = make_elfnote(); + let loader_result = Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap(); + assert_eq!(loader_result.pvh_entry_addr.unwrap().raw_value(), 0x1e1fe1f); + } + + #[test] + fn test_dummy_elfnote() { + let gm = create_guest_mem(); + let dummynote_image = make_dummy_elfnote(); + let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap(); + assert!(loader_result.pvh_entry_addr.is_none()); + } + + #[test] + fn test_bad_elfnote() { + let gm = create_guest_mem(); + let badnote_image = make_bad_elfnote(); + assert_eq!( + Some(KernelLoaderError::Elf(Error::InvalidPvhNote)), + Elf::load(&gm, None, &mut Cursor::new(&badnote_image), None).err() + ); + } +} diff --git a/src/loader/start_info.rs b/src/loader/elf/start_info.rs similarity index 100% rename from src/loader/start_info.rs rename to src/loader/elf/start_info.rs diff --git a/src/loader/test_badnote.bin b/src/loader/elf/test_badnote.bin similarity index 100% rename from src/loader/test_badnote.bin rename to src/loader/elf/test_badnote.bin diff --git a/src/loader/test_dummynote.bin b/src/loader/elf/test_dummynote.bin similarity index 100% rename from src/loader/test_dummynote.bin rename to src/loader/elf/test_dummynote.bin diff --git a/src/loader/test_elf.bin b/src/loader/elf/test_elf.bin similarity index 100% rename from src/loader/test_elf.bin rename to src/loader/elf/test_elf.bin diff --git a/src/loader/test_elfnote.bin b/src/loader/elf/test_elfnote.bin similarity index 100% rename from src/loader/test_elfnote.bin rename to src/loader/elf/test_elfnote.bin diff --git a/src/loader/mod.rs b/src/loader/mod.rs index 8c299e7a..4e8fe579 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -18,16 +18,10 @@ extern crate vm_memory; -use std::error::{self, Error as KernelLoaderError}; +use std::error::Error as StdError; use std::ffi::CStr; use std::fmt::{self, Display}; -#[cfg(any(feature = "elf", feature = "bzimage"))] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use std::io::SeekFrom; use std::io::{Read, Seek}; -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use std::mem; use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsize}; @@ -39,107 +33,58 @@ use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestUsiz #[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] pub mod bootparam; -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -#[allow(missing_docs)] -#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] -pub mod start_info; +#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] +pub mod elf; -#[allow(dead_code)] -#[allow(non_camel_case_types)] -#[allow(non_snake_case)] -#[allow(non_upper_case_globals)] -#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))] -mod elf; +#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] +pub mod bzimage; + +#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] +pub use elf::Elf; +#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] +pub use elf::Error as ElfError; + +#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] +pub use bzimage::BzImage; +#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] +pub use bzimage::Error as BzImageError; #[derive(Debug, PartialEq)] /// Kernel loader errors. pub enum Error { - /// Loaded big endian binary on a little endian platform. - BigEndianElfOnLittle, + /// Failed to load bzimage. + #[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] + Bzimage(BzImageError), + + /// Failed to load elf image. + #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] + Elf(ElfError), + /// Failed writing command line to guest memory. CommandLineCopy, /// Command line overflowed guest memory. CommandLineOverflow, - /// Invalid ELF magic number - InvalidElfMagicNumber, - /// Invalid program header size. - InvalidProgramHeaderSize, - /// Invalid program header offset. - InvalidProgramHeaderOffset, - /// Invalid program header address. - InvalidProgramHeaderAddress, - /// Invalid entry address. - InvalidEntryAddress, - /// Invalid bzImage binary. - InvalidBzImage, /// Invalid kernel start address. InvalidKernelStartAddress, /// Memory to load kernel image is too small. MemoryOverflow, - /// Unable to read ELF header. - ReadElfHeader, - /// Unable to read kernel image. - ReadKernelImage, - /// Unable to read program header. - ReadProgramHeader, - /// Unable to read bzImage header. - ReadBzImageHeader, - /// Unable to read bzImage compressed image. - ReadBzImageCompressedKernel, - /// Unable to seek to kernel start. - SeekKernelStart, - /// Unable to seek to ELF start. - SeekElfStart, - /// Unable to seek to program header. - SeekProgramHeader, - /// Unable to seek to bzImage end. - SeekBzImageEnd, - /// Unable to seek to bzImage header. - SeekBzImageHeader, - /// Unable to seek to bzImage compressed kernel. - SeekBzImageCompressedKernel, - /// Unable to seek to note header. - SeekNoteHeader, - /// Unable to read note header. - ReadNoteHeader, - /// Invalid PVH note. - InvalidPvhNote, } /// A specialized `Result` type for the kernel loader. pub type Result = std::result::Result; -impl error::Error for Error { +impl StdError for Error { fn description(&self) -> &str { match self { - Error::BigEndianElfOnLittle => { - "Trying to load big-endian binary on little-endian machine" - } + #[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] + Error::Bzimage(ref e) => e.description(), + #[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] + Error::Elf(ref e) => e.description(), + Error::CommandLineCopy => "Failed writing command line to guest memory", Error::CommandLineOverflow => "Command line overflowed guest memory", - Error::InvalidElfMagicNumber => "Invalid Elf magic number", - Error::InvalidProgramHeaderSize => "Invalid program header size", - Error::InvalidProgramHeaderOffset => "Invalid program header offset", - Error::InvalidProgramHeaderAddress => "Invalid Program Header Address", - Error::InvalidEntryAddress => "Invalid entry address", - Error::InvalidBzImage => "Invalid bzImage", Error::InvalidKernelStartAddress => "Invalid kernel start address", Error::MemoryOverflow => "Memory to load kernel image is not enough", - Error::ReadElfHeader => "Unable to read elf header", - Error::ReadKernelImage => "Unable to read kernel image", - Error::ReadProgramHeader => "Unable to read program header", - Error::ReadBzImageHeader => "Unable to read bzImage header", - Error::ReadBzImageCompressedKernel => "Unable to read bzImage compressed kernel", - Error::SeekKernelStart => "Unable to seek to kernel start", - Error::SeekElfStart => "Unable to seek to elf start", - Error::SeekProgramHeader => "Unable to seek to program header", - Error::SeekBzImageEnd => "Unable to seek bzImage end", - Error::SeekBzImageHeader => "Unable to seek bzImage header", - Error::SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel", - Error::SeekNoteHeader => "Unable to seek to note header", - Error::ReadNoteHeader => "Unable to read note header", - Error::InvalidPvhNote => "Invalid PVH note header", } } } @@ -150,12 +95,26 @@ impl Display for Error { } } -#[derive(Debug, Default, Copy, Clone, PartialEq)] -/// Result of the KernelLoader load() call. +#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))] +impl From for Error { + fn from(err: ElfError) -> Self { + Error::Elf(err) + } +} + +#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))] +impl From for Error { + fn from(err: BzImageError) -> Self { + Error::Bzimage(err) + } +} + +/// Result of [`KernelLoader.load()`](trait.KernelLoader.html#tymethod.load). /// /// This specifies where the kernel is loading and passes additional /// information for the rest of the boot process to be completed by /// the VMM. +#[derive(Default)] pub struct KernelLoaderResult { /// Address in the guest memory where the kernel image starts to be loaded pub kernel_load: GuestAddress, @@ -185,313 +144,9 @@ pub trait KernelLoader { F: Read + Seek; } -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -/// Raw ELF (a.k.a. vmlinux) kernel image support. -pub struct Elf; - -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -unsafe impl ByteValued for elf::Elf64_Ehdr {} -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -unsafe impl ByteValued for elf::Elf64_Nhdr {} -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -unsafe impl ByteValued for elf::Elf64_Phdr {} - unsafe impl ByteValued for bootparam::setup_header {} -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl KernelLoader for Elf { - /// Loads a kernel from a vmlinux elf image to a slice - /// - /// kernel is loaded into guest memory at offset phdr.p_paddr specified by elf image. - /// - /// # Arguments - /// - /// * `guest_mem` - The guest memory region the kernel is written to. - /// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel. - /// * `kernel_image` - Input vmlinux image. - /// * `highmem_start_address` - This is the start of the high memory, kernel should above it. - /// - /// # Returns - /// * KernelLoaderResult - fn load( - guest_mem: &M, - kernel_start: Option, - kernel_image: &mut F, - highmem_start_address: Option, - ) -> Result - where - F: Read + Seek, - { - kernel_image - .seek(SeekFrom::Start(0)) - .map_err(|_| Error::SeekElfStart)?; - - let mut ehdr = elf::Elf64_Ehdr::default(); - ehdr.as_bytes() - .read_from(0, kernel_image, mem::size_of::()) - .map_err(|_| Error::ReadElfHeader)?; - - // Sanity checks - if ehdr.e_ident[elf::EI_MAG0 as usize] != elf::ELFMAG0 as u8 - || ehdr.e_ident[elf::EI_MAG1 as usize] != elf::ELFMAG1 - || ehdr.e_ident[elf::EI_MAG2 as usize] != elf::ELFMAG2 - || ehdr.e_ident[elf::EI_MAG3 as usize] != elf::ELFMAG3 - { - return Err(Error::InvalidElfMagicNumber); - } - if ehdr.e_ident[elf::EI_DATA as usize] != elf::ELFDATA2LSB as u8 { - return Err(Error::BigEndianElfOnLittle); - } - if ehdr.e_phentsize as usize != mem::size_of::() { - return Err(Error::InvalidProgramHeaderSize); - } - if (ehdr.e_phoff as usize) < mem::size_of::() { - return Err(Error::InvalidProgramHeaderOffset); - } - if (highmem_start_address.is_some()) - && ((ehdr.e_entry as u64) < highmem_start_address.unwrap().raw_value()) - { - return Err(Error::InvalidEntryAddress); - } - - let mut loader_result: KernelLoaderResult = Default::default(); - // where the kernel will be start loaded. - loader_result.kernel_load = match kernel_start { - Some(start) => GuestAddress(start.raw_value() + (ehdr.e_entry as u64)), - None => GuestAddress(ehdr.e_entry as u64), - }; - - kernel_image - .seek(SeekFrom::Start(ehdr.e_phoff)) - .map_err(|_| Error::SeekProgramHeader)?; - - let phdr_sz = mem::size_of::(); - let mut phdrs: Vec = vec![]; - for _ in 0usize..ehdr.e_phnum as usize { - let mut phdr = elf::Elf64_Phdr::default(); - phdr.as_bytes() - .read_from(0, kernel_image, phdr_sz) - .map_err(|_| Error::ReadProgramHeader)?; - phdrs.push(phdr); - } - - // Read in each section pointed to by the program headers. - for phdr in phdrs { - if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 { - if phdr.p_type == elf::PT_NOTE { - // This segment describes a Note, check if PVH entry point is encoded. - loader_result.pvh_entry_addr = parse_elf_note(&phdr, kernel_image)?; - } - continue; - } - - kernel_image - .seek(SeekFrom::Start(phdr.p_offset)) - .map_err(|_| Error::SeekKernelStart)?; - - // if the vmm does not specify where the kernel should be loaded, just - // load it to the physical address p_paddr for each segment. - let mem_offset = match kernel_start { - Some(start) => start - .checked_add(phdr.p_paddr as u64) - .ok_or(Error::InvalidProgramHeaderAddress)?, - None => GuestAddress(phdr.p_paddr as u64), - }; - - guest_mem - .read_exact_from(mem_offset, kernel_image, phdr.p_filesz as usize) - .map_err(|_| Error::ReadKernelImage)?; - - loader_result.kernel_end = mem_offset - .raw_value() - .checked_add(phdr.p_memsz as GuestUsize) - .ok_or(Error::MemoryOverflow)?; - } - - // elf image has no setup_header which is defined for bzImage - loader_result.setup_header = None; - - Ok(loader_result) - } -} - -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn parse_elf_note(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result> -where - F: Read + Seek, -{ - // Type of note header that encodes a 32-bit entry point address - // to boot a guest kernel using the PVH boot protocol. - const XEN_ELFNOTE_PHYS32_ENTRY: u32 = 18; - - let n_align = phdr.p_align; - - // Seek to the beginning of the note segment - kernel_image - .seek(SeekFrom::Start(phdr.p_offset)) - .map_err(|_| Error::SeekNoteHeader)?; - - // Now that the segment has been found, we must locate an ELF note with the - // correct type that encodes the PVH entry point if there is one. - let mut nhdr: elf::Elf64_Nhdr = Default::default(); - let mut read_size: usize = 0; - let nhdr_sz = mem::size_of::(); - - while read_size < phdr.p_filesz as usize { - nhdr.as_bytes() - .read_from(0, kernel_image, nhdr_sz) - .map_err(|_| Error::ReadNoteHeader)?; - - // If the note header found is not the desired one, keep reading until - // the end of the segment - if nhdr.n_type == XEN_ELFNOTE_PHYS32_ENTRY { - break; - } - // Skip the note header plus the size of its fields (with alignment) - read_size += nhdr_sz - + align_up(u64::from(nhdr.n_namesz), n_align) - + align_up(u64::from(nhdr.n_descsz), n_align); - - kernel_image - .seek(SeekFrom::Start(phdr.p_offset + read_size as u64)) - .map_err(|_| Error::SeekNoteHeader)?; - } - - if read_size >= phdr.p_filesz as usize { - return Ok(None); // PVH ELF note not found, nothing else to do. - } - // Otherwise the correct note type was found. - // The note header struct has already been read, so we can seek from the - // current position and just skip the name field contents. - kernel_image - .seek(SeekFrom::Current( - align_up(u64::from(nhdr.n_namesz), n_align) as i64, - )) - .map_err(|_| Error::SeekNoteHeader)?; - - // The PVH entry point is a 32-bit address, so the descriptor field - // must be capable of storing all such addresses. - if (nhdr.n_descsz as usize) < mem::size_of::() { - return Err(Error::InvalidPvhNote); - } - - let mut pvh_addr_bytes = [0; mem::size_of::()]; - - // Read 32-bit address stored in the PVH note descriptor field. - kernel_image - .read_exact(&mut pvh_addr_bytes) - .map_err(|_| Error::ReadNoteHeader)?; - - Ok(Some(GuestAddress( - u32::from_le_bytes(pvh_addr_bytes).into(), - ))) -} - -#[cfg(feature = "bzimage")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -/// Big zImage (bzImage) kernel image support. -pub struct BzImage; - -#[cfg(feature = "bzimage")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl KernelLoader for BzImage { - /// Loads a bzImage - /// - /// kernel is loaded into guest memory at code32_start the default load address - /// stored in bzImage setup header. - /// - /// # Arguments - /// - /// * `guest_mem` - The guest memory where the kernel image is loaded. - /// * `kernel_start` - The offset into 'guest_mem' at which to load the kernel. - /// * `kernel_image` - Input bzImage image. - /// * `highmem_start_address` - This is the start of the high memory, kernel should above it. - /// - /// # Returns - /// * KernelLoaderResult - fn load( - guest_mem: &M, - kernel_start: Option, - kernel_image: &mut F, - highmem_start_address: Option, - ) -> Result - where - F: Read + Seek, - { - let mut kernel_size = kernel_image - .seek(SeekFrom::End(0)) - .map_err(|_| Error::SeekBzImageEnd)? as usize; - kernel_image - .seek(SeekFrom::Start(0x1F1)) - .map_err(|_| Error::SeekBzImageHeader)?; - - let mut boot_header = bootparam::setup_header::default(); - boot_header - .as_bytes() - .read_from(0, kernel_image, mem::size_of::()) - .map_err(|_| Error::ReadBzImageHeader)?; - - // if the HdrS magic number is not found at offset 0x202, the boot protocol version is "old", - // the image type is assumed as zImage, not bzImage. - if boot_header.header != 0x5372_6448 { - return Err(Error::InvalidBzImage); - } - - // follow section of loading the rest of the kernel in linux boot protocol - if (boot_header.version < 0x0200) || ((boot_header.loadflags & 0x1) == 0x0) { - return Err(Error::InvalidBzImage); - } - - let mut setup_size = boot_header.setup_sects as usize; - if setup_size == 0 { - setup_size = 4; - } - setup_size = (setup_size + 1) * 512; - kernel_size -= setup_size; - - // verify bzImage validation by checking if code32_start, the defaults to the address of - // the kernel is not lower than high memory. - if (highmem_start_address.is_some()) - && (u64::from(boot_header.code32_start) < highmem_start_address.unwrap().raw_value()) - { - return Err(Error::InvalidKernelStartAddress); - } - - let mem_offset = match kernel_start { - Some(start) => start, - None => GuestAddress(u64::from(boot_header.code32_start)), - }; - - boot_header.code32_start = mem_offset.raw_value() as u32; - - let mut loader_result: KernelLoaderResult = Default::default(); - loader_result.setup_header = Some(boot_header); - loader_result.kernel_load = mem_offset; - - //seek the compressed vmlinux.bin and read to memory - kernel_image - .seek(SeekFrom::Start(setup_size as u64)) - .map_err(|_| Error::SeekBzImageCompressedKernel)?; - guest_mem - .read_exact_from(mem_offset, kernel_image, kernel_size) - .map_err(|_| Error::ReadBzImageCompressedKernel)?; - - loader_result.kernel_end = mem_offset - .raw_value() - .checked_add(kernel_size as GuestUsize) - .ok_or(Error::MemoryOverflow)?; - - Ok(loader_result) - } -} - -/// Writes the command line string to the given memory slice. +/// Writes the command line string to the given guest memory slice. /// /// # Arguments /// @@ -522,29 +177,9 @@ pub fn load_cmdline( Ok(()) } -/// Align address upwards. Taken from x86_64 crate: -/// https://docs.rs/x86_64/latest/x86_64/fn.align_up.html -/// -/// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be -/// a power of 2. -#[cfg(feature = "elf")] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn align_up(addr: u64, align: u64) -> usize { - assert!(align.is_power_of_two(), "`align` must be a power of two"); - let align_mask = align - 1; - if addr & align_mask == 0 { - addr as usize // already aligned - } else { - ((addr | align_mask) + 1) as usize - } -} - #[cfg(test)] -mod test { +mod tests { use super::*; - #[cfg(any(feature = "elf", feature = "bzimage"))] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use std::io::Cursor; use vm_memory::{Address, GuestAddress, GuestMemoryMmap}; const MEM_SIZE: u64 = 0x1000000; @@ -553,199 +188,6 @@ mod test { GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap() } - #[cfg(feature = "bzimage")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn make_bzimage() -> Vec { - let mut v = Vec::new(); - v.extend_from_slice(include_bytes!("bzimage")); - v - } - - // Elf64 image that prints hello world on x86_64. - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn make_elf_bin() -> Vec { - let mut v = Vec::new(); - v.extend_from_slice(include_bytes!("test_elf.bin")); - v - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn make_elfnote() -> Vec { - include_bytes!("test_elfnote.bin").to_vec() - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn make_dummy_elfnote() -> Vec { - include_bytes!("test_dummynote.bin").to_vec() - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn make_bad_elfnote() -> Vec { - include_bytes!("test_badnote.bin").to_vec() - } - - #[allow(safe_packed_borrows)] - #[allow(non_snake_case)] - #[test] - #[cfg(feature = "bzimage")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn load_bzImage() { - let gm = create_guest_mem(); - let image = make_bzimage(); - let mut kernel_start = GuestAddress(0x200000); - let mut highmem_start_address = GuestAddress(0x0); - - // load bzImage with good kernel_start and himem_start setting - let mut loader_result = BzImage::load( - &gm, - Some(kernel_start), - &mut Cursor::new(&image), - Some(highmem_start_address), - ) - .unwrap(); - assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); - println!( - "bzImage version is {:2x} \n", - loader_result.setup_header.unwrap().version - ); - println!( - "bzImage loadflags is {:x} \n", - loader_result.setup_header.unwrap().loadflags - ); - println!( - "bzImage kernel size is {:4x} \n", - (loader_result.kernel_end as u32) - ); - - // load bzImage without kernel_start - loader_result = BzImage::load( - &gm, - None, - &mut Cursor::new(&image), - Some(highmem_start_address), - ) - .unwrap(); - assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); - - // load bzImage withouth himem_start - loader_result = BzImage::load(&gm, None, &mut Cursor::new(&image), None).unwrap(); - assert_eq!(0x53726448, loader_result.setup_header.unwrap().header); - println!( - "bzImage is loaded at {:8x} \n", - loader_result.kernel_load.raw_value() - ); - - // load bzImage with a bad himem setting - kernel_start = GuestAddress(0x1000); - highmem_start_address = GuestAddress(0x200000); - let x = BzImage::load( - &gm, - Some(kernel_start), - &mut Cursor::new(&image), - Some(highmem_start_address), - ); - assert_eq!(x.is_ok(), false); - println!("load bzImage with bad himem setting \n"); - } - - #[test] - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn load_elf() { - let gm = create_guest_mem(); - let image = make_elf_bin(); - let kernel_addr = GuestAddress(0x200000); - let mut highmem_start_address = GuestAddress(0x0); - let mut loader_result = Elf::load( - &gm, - Some(kernel_addr), - &mut Cursor::new(&image), - Some(highmem_start_address), - ) - .unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); - - loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); - - loader_result = Elf::load( - &gm, - None, - &mut Cursor::new(&image), - Some(highmem_start_address), - ) - .unwrap(); - println!( - "load elf at address {:8x} \n", - loader_result.kernel_load.raw_value() - ); - - highmem_start_address = GuestAddress(0xa00000); - assert_eq!( - Err(Error::InvalidEntryAddress), - Elf::load( - &gm, - None, - &mut Cursor::new(&image), - Some(highmem_start_address) - ) - ); - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn load_pvh() { - let gm = create_guest_mem(); - let pvhnote_image = make_elfnote(); - let loader_result = Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap(); - println!( - "PVH entry point at address {:8x} \n", - loader_result.pvh_entry_addr.unwrap().raw_value() - ); - assert_eq!(loader_result.pvh_entry_addr.unwrap().raw_value(), 0x1e1fe1f); - } - - #[test] - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn dumy_elfnote() { - let gm = create_guest_mem(); - let dummynote_image = make_dummy_elfnote(); - let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap(); - assert!(loader_result.pvh_entry_addr.is_none()); - } - - #[test] - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn bad_elfnote() { - let gm = create_guest_mem(); - let badnote_image = make_bad_elfnote(); - assert_eq!( - Err(Error::InvalidPvhNote), - Elf::load(&gm, None, &mut Cursor::new(&badnote_image), None) - ); - } - #[test] fn cmdline_overflow() { let gm = create_guest_mem(); @@ -787,48 +229,4 @@ mod test { let val: u8 = gm.read_obj(cmdline_address).unwrap(); assert_eq!(val, '\0' as u8); } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn bad_magic() { - let gm = create_guest_mem(); - let kernel_addr = GuestAddress(0x0); - let mut bad_image = make_elf_bin(); - bad_image[0x1] = 0x33; - assert_eq!( - Err(Error::InvalidElfMagicNumber), - Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None) - ); - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn bad_endian() { - // Only little endian is supported - let gm = create_guest_mem(); - let kernel_addr = GuestAddress(0x0); - let mut bad_image = make_elf_bin(); - bad_image[0x5] = 2; - assert_eq!( - Err(Error::BigEndianElfOnLittle), - Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None) - ); - } - - #[cfg(feature = "elf")] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn bad_phoff() { - // program header has to be past the end of the elf header - let gm = create_guest_mem(); - let kernel_addr = GuestAddress(0x0); - let mut bad_image = make_elf_bin(); - bad_image[0x20] = 0x10; - assert_eq!( - Err(Error::InvalidProgramHeaderOffset), - Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&bad_image), None) - ); - } }