diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 38d81ae2..70ee9da9 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 71, + "coverage_score": 70.9, "exclude_path": "", "crate_features": "" } diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 4ad4557d..648083ff 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 75.9, + "coverage_score": 76.3, "exclude_path": "", "crate_features": "" } diff --git a/src/loader/elf.rs b/src/loader/elf.rs index b7d71d7a..600d848e 100644 --- a/src/loader/elf.rs +++ b/src/loader/elf.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. @@ -7,42 +9,53 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause /* - * automatically generated by rust-bindgen + * automatically generated by rust-bindgen using: + * + * # bindgen --with-derive-default elf.h > elf.rs + * * From upstream linux include/uapi/linux/elf.h at commit: - * 806276b7f07a39a1cc3f38bb1ef5c573d4594a38 + * 3cc6e2c599cdca573a8f347aea5da4c855ff5a78 + * and then edited to eliminate unnecessary definitions, add comments, + * and relocate definitions and tests for clarity. */ -pub const EI_MAG0: ::std::os::raw::c_uint = 0; -pub const EI_MAG1: ::std::os::raw::c_uint = 1; -pub const EI_MAG2: ::std::os::raw::c_uint = 2; -pub const EI_MAG3: ::std::os::raw::c_uint = 3; -pub const EI_DATA: ::std::os::raw::c_uint = 5; -pub const ELFMAG0: ::std::os::raw::c_uint = 127; +pub const PT_LOAD: u32 = 1; +pub const PT_NOTE: u32 = 4; + +pub const EI_MAG0: u32 = 0; +pub const EI_MAG1: u32 = 1; +pub const EI_MAG2: u32 = 2; +pub const EI_MAG3: u32 = 3; +pub const EI_DATA: u32 = 5; -pub const ELFDATA2LSB: ::std::os::raw::c_uint = 1; -pub const PT_LOAD: ::std::os::raw::c_uint = 1; +pub const ELFMAG0: u32 = 127; +// The values for the following definitions have been edited +// to use their equivalent byte literal representations. pub const ELFMAG1: u8 = b'E'; pub const ELFMAG2: u8 = b'L'; pub const ELFMAG3: u8 = b'F'; -type Elf64_Addr = __u64; -type Elf64_Half = __u16; -type Elf64_Off = __u64; -type Elf64_Word = __u32; -type Elf64_Xword = __u64; +pub const ELFDATA2LSB: u32 = 1; + +pub type __s8 = ::std::os::raw::c_schar; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __s16 = ::std::os::raw::c_short; +pub type __u16 = ::std::os::raw::c_ushort; +pub type __s32 = ::std::os::raw::c_int; +pub type __u32 = ::std::os::raw::c_uint; +pub type __s64 = ::std::os::raw::c_longlong; +pub type __u64 = ::std::os::raw::c_ulonglong; -type __s8 = ::std::os::raw::c_schar; -type __u8 = ::std::os::raw::c_uchar; -type __s16 = ::std::os::raw::c_short; -type __u16 = ::std::os::raw::c_ushort; -type __s32 = ::std::os::raw::c_int; -type __u32 = ::std::os::raw::c_uint; -type __s64 = ::std::os::raw::c_longlong; -type __u64 = ::std::os::raw::c_ulonglong; +pub type Elf64_Addr = __u64; +pub type Elf64_Half = __u16; +pub type Elf64_Off = __u64; +pub type Elf64_Sword = __s32; +pub type Elf64_Word = __u32; +pub type Elf64_Xword = __u64; #[repr(C)] -#[derive(Debug, Default, Copy)] +#[derive(Debug, Default, Copy, Clone)] pub struct elf64_hdr { pub e_ident: [::std::os::raw::c_uchar; 16usize], pub e_type: Elf64_Half, @@ -59,15 +72,10 @@ pub struct elf64_hdr { pub e_shnum: Elf64_Half, pub e_shstrndx: Elf64_Half, } -impl Clone for elf64_hdr { - fn clone(&self) -> Self { - *self - } -} pub type Elf64_Ehdr = elf64_hdr; #[repr(C)] -#[derive(Debug, Default, Copy)] +#[derive(Debug, Default, Copy, Clone)] pub struct elf64_phdr { pub p_type: Elf64_Word, pub p_flags: Elf64_Word, @@ -78,262 +86,309 @@ pub struct elf64_phdr { pub p_memsz: Elf64_Xword, pub p_align: Elf64_Xword, } +pub type Elf64_Phdr = elf64_phdr; -impl Clone for elf64_phdr { - fn clone(&self) -> Self { - *self - } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct elf64_note { + pub n_namesz: Elf64_Word, + pub n_descsz: Elf64_Word, + pub n_type: Elf64_Word, } -pub type Elf64_Phdr = elf64_phdr; +pub type Elf64_Nhdr = elf64_note; #[cfg(test)] mod tests { use super::*; #[test] - fn bindgen_test_layout_elf64_phdr() { + fn bindgen_test_layout_elf64_hdr() { assert_eq!( - ::std::mem::size_of::(), - 56usize, - concat!("Size of: ", stringify!(elf64_phdr)) + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(elf64_hdr)) ); assert_eq!( - ::std::mem::align_of::(), + ::std::mem::align_of::(), 8usize, - concat!("Alignment of ", stringify!(elf64_phdr)) + concat!("Alignment of ", stringify!(elf64_hdr)) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_type as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).e_ident as *const _ as usize }, 0usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_type) + stringify!(e_ident) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_flags as *const _ as usize }, - 4usize, + unsafe { &(*(::std::ptr::null::())).e_type as *const _ as usize }, + 16usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_flags) + stringify!(e_type) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_offset as *const _ as usize }, - 8usize, + unsafe { &(*(::std::ptr::null::())).e_machine as *const _ as usize }, + 18usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_offset) + stringify!(e_machine) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_vaddr as *const _ as usize }, - 16usize, + unsafe { &(*(::std::ptr::null::())).e_version as *const _ as usize }, + 20usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_vaddr) + stringify!(e_version) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_paddr as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).e_entry as *const _ as usize }, 24usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_paddr) + stringify!(e_entry) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_filesz as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).e_phoff as *const _ as usize }, 32usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_filesz) + stringify!(e_phoff) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_memsz as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).e_shoff as *const _ as usize }, 40usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_memsz) + stringify!(e_shoff) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_phdr)).p_align as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).e_flags as *const _ as usize }, 48usize, concat!( - "Alignment of field: ", - stringify!(elf64_phdr), + "Offset of field: ", + stringify!(elf64_hdr), "::", - stringify!(p_align) + stringify!(e_flags) ) ); - } - - #[test] - fn bindgen_test_layout_elf64_hdr() { assert_eq!( - ::std::mem::size_of::(), - 64usize, - concat!("Size of: ", stringify!(elf64_hdr)) + unsafe { &(*(::std::ptr::null::())).e_ehsize as *const _ as usize }, + 52usize, + concat!( + "Offset of field: ", + stringify!(elf64_hdr), + "::", + stringify!(e_ehsize) + ) ); assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(elf64_hdr)) + unsafe { &(*(::std::ptr::null::())).e_phentsize as *const _ as usize }, + 54usize, + concat!( + "Offset of field: ", + stringify!(elf64_hdr), + "::", + stringify!(e_phentsize) + ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_ident as *const _ as usize }, - 0usize, + unsafe { &(*(::std::ptr::null::())).e_phnum as *const _ as usize }, + 56usize, concat!( - "Alignment of field: ", + "Offset of field: ", stringify!(elf64_hdr), "::", - stringify!(e_ident) + stringify!(e_phnum) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_type as *const _ as usize }, - 16usize, + unsafe { &(*(::std::ptr::null::())).e_shentsize as *const _ as usize }, + 58usize, concat!( - "Alignment of field: ", + "Offset of field: ", stringify!(elf64_hdr), "::", - stringify!(e_type) + stringify!(e_shentsize) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_machine as *const _ as usize }, - 18usize, + unsafe { &(*(::std::ptr::null::())).e_shnum as *const _ as usize }, + 60usize, concat!( - "Alignment of field: ", + "Offset of field: ", stringify!(elf64_hdr), "::", - stringify!(e_machine) + stringify!(e_shnum) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_version as *const _ as usize }, - 20usize, + unsafe { &(*(::std::ptr::null::())).e_shstrndx as *const _ as usize }, + 62usize, concat!( - "Alignment of field: ", + "Offset of field: ", stringify!(elf64_hdr), "::", - stringify!(e_version) + stringify!(e_shstrndx) ) ); + } + + #[test] + fn bindgen_test_layout_elf64_phdr() { assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_entry as *const _ as usize }, - 24usize, + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(elf64_phdr)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(elf64_phdr)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).p_type as *const _ as usize }, + 0usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_entry) + stringify!(p_type) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_phoff as *const _ as usize }, - 32usize, + unsafe { &(*(::std::ptr::null::())).p_flags as *const _ as usize }, + 4usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_phoff) + stringify!(p_flags) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_shoff as *const _ as usize }, - 40usize, + unsafe { &(*(::std::ptr::null::())).p_offset as *const _ as usize }, + 8usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_shoff) + stringify!(p_offset) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_flags as *const _ as usize }, - 48usize, + unsafe { &(*(::std::ptr::null::())).p_vaddr as *const _ as usize }, + 16usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_flags) + stringify!(p_vaddr) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_ehsize as *const _ as usize }, - 52usize, + unsafe { &(*(::std::ptr::null::())).p_paddr as *const _ as usize }, + 24usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_ehsize) + stringify!(p_paddr) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_phentsize as *const _ as usize }, - 54usize, + unsafe { &(*(::std::ptr::null::())).p_filesz as *const _ as usize }, + 32usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_phentsize) + stringify!(p_filesz) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_phnum as *const _ as usize }, - 56usize, + unsafe { &(*(::std::ptr::null::())).p_memsz as *const _ as usize }, + 40usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_phnum) + stringify!(p_memsz) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_shentsize as *const _ as usize }, - 58usize, + unsafe { &(*(::std::ptr::null::())).p_align as *const _ as usize }, + 48usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_phdr), "::", - stringify!(e_shentsize) + stringify!(p_align) ) ); + } + + #[test] + fn bindgen_test_layout_elf64_note() { assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_shnum as *const _ as usize }, - 60usize, + ::std::mem::size_of::(), + 12usize, + concat!("Size of: ", stringify!(elf64_note)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(elf64_note)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).n_namesz as *const _ as usize }, + 0usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_note), "::", - stringify!(e_shnum) + stringify!(n_namesz) ) ); assert_eq!( - unsafe { &(*(0 as *const elf64_hdr)).e_shstrndx as *const _ as usize }, - 62usize, + unsafe { &(*(::std::ptr::null::())).n_descsz as *const _ as usize }, + 4usize, concat!( - "Alignment of field: ", - stringify!(elf64_hdr), + "Offset of field: ", + stringify!(elf64_note), "::", - stringify!(e_shstrndx) + stringify!(n_descsz) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).n_type as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(elf64_note), + "::", + stringify!(n_type) ) ); } diff --git a/src/loader/mod.rs b/src/loader/mod.rs index b5804449..a0637635 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright (c) 2019 Intel Corporation. All rights reserved. // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // @@ -36,6 +38,13 @@ use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize}; #[allow(missing_docs)] #[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; + #[allow(dead_code)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] @@ -93,6 +102,12 @@ pub enum Error { 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. @@ -125,6 +140,9 @@ impl error::Error for Error { 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,6 +168,10 @@ pub struct KernelLoaderResult { /// This field is only for bzImage following https://www.kernel.org/doc/Documentation/x86/boot.txt /// VMM should make use of it to fill zero page for bzImage direct boot. pub setup_header: Option, + /// This field optionally holds the address of a PVH entry point, indicating that + /// the kernel supports the PVH boot protocol as described in: + /// https://xenbits.xen.org/docs/unstable/misc/pvh.html + pub pvh_entry_addr: Option, } /// A kernel image loading support must implement the KernelLoader trait. @@ -247,6 +269,10 @@ impl KernelLoader for Elf { // 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; } @@ -280,6 +306,79 @@ impl KernelLoader for Elf { } } +#[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; + + while read_size < phdr.p_filesz as usize { + unsafe { + // read_struct is safe when reading a POD struct. + // It can be used and dropped without issue. + struct_util::read_struct(kernel_image, &mut nhdr).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 += mem::size_of::() + + 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. @@ -409,6 +508,23 @@ 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 { use super::*; @@ -440,6 +556,24 @@ mod test { 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] @@ -562,6 +696,42 @@ mod test { ); } + #[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(); diff --git a/src/loader/start_info.rs b/src/loader/start_info.rs new file mode 100644 index 00000000..90384240 --- /dev/null +++ b/src/loader/start_info.rs @@ -0,0 +1,394 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2016, Citrix Systems, Inc. + */ + +/* + * automatically generated by rust-bindgen using: + * + * # bindgen start_info.h -- -include stdint.h > start_info.rs + * + * From the canonical version in upstream Xen repository + * xen/include/public/arch-x86/hvm/start_info.h + * at commit: + * a2e84d8e42c9e878fff17b738d8e5c5d83888f31 + * + * The generated file has been edited to eliminate unnecessary + * definitions, add comments, and relocate definitions and tests for clarity. + * Added Default to the list of traits that are automatically derived. + * + * The definitions in this file are intended to be exported and used by a particular + * VMM implementation in order to boot a Linux guest using the PVH entry point as + * specified in the x86/HVM direct boot ABI. + * These structures contain all the required information (cmdline address, ACPI RSDP, + * memory maps, etc) that must be written to guest memory before starting guest + * execution by jumping to the PVH entry point address. + * A comparable set of definitions to hvm_start_info and hvm_memmap_table_entry in this + * file would be the boot_params and boot_e820_entry definitions used by the Linux + * 64-bit boot protocol. + * + * Start of day structure passed to PVH guests and to HVM guests in %ebx. + * + * NOTE: nothing will be loaded at physical address 0, so a 0 value in any + * of the address fields should be treated as not present. + * + * 0 +----------------+ + * | magic | Contains the magic value XEN_HVM_START_MAGIC_VALUE + * | | ("xEn3" with the 0x80 bit of the "E" set). + * 4 +----------------+ + * | version | Version of this structure. Current version is 1. New + * | | versions are guaranteed to be backwards-compatible. + * 8 +----------------+ + * | flags | SIF_xxx flags. + * 12 +----------------+ + * | nr_modules | Number of modules passed to the kernel. + * 16 +----------------+ + * | modlist_paddr | Physical address of an array of modules + * | | (layout of the structure below). + * 24 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 32 +----------------+ + * | rsdp_paddr | Physical address of the RSDP ACPI data structure. + * 40 +----------------+ + * | memmap_paddr | Physical address of the (optional) memory map. Only + * | | present in version 1 and newer of the structure. + * 48 +----------------+ + * | memmap_entries | Number of entries in the memory map table. Zero + * | | if there is no memory map being provided. Only + * | | present in version 1 and newer of the structure. + * 52 +----------------+ + * | reserved | Version 1 and newer only. + * 56 +----------------+ + * + * The layout of each entry in the module structure is the following: + * + * 0 +----------------+ + * | paddr | Physical address of the module. + * 8 +----------------+ + * | size | Size of the module in bytes. + * 16 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 24 +----------------+ + * | reserved | + * 32 +----------------+ + * + * The layout of each entry in the memory map table is as follows: + * + * 0 +----------------+ + * | addr | Base address + * 8 +----------------+ + * | size | Size of mapping in bytes + * 16 +----------------+ + * | type | Type of mapping as defined between the hypervisor + * | | and guest. See XEN_HVM_MEMMAP_TYPE_* values below. + * 20 +----------------| + * | reserved | + * 24 +----------------+ + * + * The address and sizes are always a 64bit little endian unsigned integer. + * + * NB: Xen on x86 will always try to place all the data below the 4GiB + * boundary. + * + * Version numbers of the hvm_start_info structure have evolved like this: + * + * Version 0: Initial implementation. + * + * Version 1: Added the memmap_paddr/memmap_entries fields (plus 4 bytes of + * padding) to the end of the hvm_start_info struct. These new + * fields can be used to pass a memory map to the guest. The + * memory map is optional and so guests that understand version 1 + * of the structure must check that memmap_entries is non-zero + * before trying to read the memory map. + */ + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct hvm_start_info { + pub magic: u32, + pub version: u32, + pub flags: u32, + pub nr_modules: u32, + pub modlist_paddr: u64, + pub cmdline_paddr: u64, + pub rsdp_paddr: u64, + pub memmap_paddr: u64, + pub memmap_entries: u32, + pub reserved: u32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct hvm_modlist_entry { + pub paddr: u64, + pub size: u64, + pub cmdline_paddr: u64, + pub reserved: u64, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct hvm_memmap_table_entry { + pub addr: u64, + pub size: u64, + pub type_: u32, + pub reserved: u32, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bindgen_test_layout_hvm_start_info() { + assert_eq!( + ::std::mem::size_of::(), + 56usize, + concat!("Size of: ", stringify!(hvm_start_info)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_start_info)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).magic as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(magic) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).version as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(version) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).nr_modules as *const _ as usize }, + 12usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(nr_modules) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).modlist_paddr as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(modlist_paddr) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).cmdline_paddr as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(cmdline_paddr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).rsdp_paddr as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(rsdp_paddr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).memmap_paddr as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(memmap_paddr) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).memmap_entries as *const _ as usize + }, + 48usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(memmap_entries) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 52usize, + concat!( + "Offset of field: ", + stringify!(hvm_start_info), + "::", + stringify!(reserved) + ) + ); + } + + #[test] + fn bindgen_test_layout_hvm_modlist_entry() { + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(hvm_modlist_entry)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_modlist_entry)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).paddr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(paddr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(size) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).cmdline_paddr as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(cmdline_paddr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).reserved as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(hvm_modlist_entry), + "::", + stringify!(reserved) + ) + ); + } + + #[test] + fn bindgen_test_layout_hvm_memmap_table_entry() { + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(hvm_memmap_table_entry)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(hvm_memmap_table_entry)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).addr as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(addr) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(size) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).type_ as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).reserved as *const _ as usize + }, + 20usize, + concat!( + "Offset of field: ", + stringify!(hvm_memmap_table_entry), + "::", + stringify!(reserved) + ) + ); + } +} diff --git a/src/loader/test_badnote.bin b/src/loader/test_badnote.bin new file mode 100755 index 00000000..cbe4cb59 Binary files /dev/null and b/src/loader/test_badnote.bin differ diff --git a/src/loader/test_dummynote.bin b/src/loader/test_dummynote.bin new file mode 100755 index 00000000..990e69ed Binary files /dev/null and b/src/loader/test_dummynote.bin differ diff --git a/src/loader/test_elfnote.bin b/src/loader/test_elfnote.bin new file mode 100755 index 00000000..36efd2d3 Binary files /dev/null and b/src/loader/test_elfnote.bin differ