From 37e0c6e7ee4f6364540e84914ccb45ac4d2f6cd3 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Mon, 3 Feb 2020 22:56:05 -0500 Subject: [PATCH 1/4] pvh: Add ELF Note definitions Define ELF Note header structures and necessary for parsing the PVH entry point address encoded in the kernel ELF header. Generated the elf.rs file again by running bindgen: bindgen --with-derive-default elf.h > elf.rs From upstream linux include/uapi/linux/elf.h at commit: 3cc6e2c599cdca573a8f347aea5da4c855ff5a78 and then edited to eliminate unnecessary definitions, add comments, and relocate definitions and tests for clarity. Signed-off-by: Alejandro Jimenez --- src/loader/elf.rs | 359 ++++++++++++++++++++++++++-------------------- 1 file changed, 207 insertions(+), 152 deletions(-) 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) ) ); } From ba9f1466e71a4190104263d42ec27bc2ffd1d1a5 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Mon, 3 Feb 2020 23:27:49 -0500 Subject: [PATCH 2/4] pvh: Introduce hvm_start_info structure Introduce the layout and define the start_info, module list and memory map table entry structures used by the PVH boot protocol. The hvm_start_info structure is akin to bootparams in Linux boot protocol, specifying the small set of parameters required by the PVH protocol. Signed-off-by: Alejandro Jimenez --- src/loader/start_info.rs | 394 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 src/loader/start_info.rs 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) + ) + ); + } +} From e4fdfa5d3cfdd2f22088250177e959c216d6121b Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 4 Feb 2020 00:38:20 -0500 Subject: [PATCH 3/4] pvh: Enable PVH boot support Parse the ELF header looking for a PVH Note section and retrieve the encoded PVH entry point address if there is one. The entry point address is returned in KernelLoaderResult alongside the typical ELF entry point used for direct boot. A VMM implementing KernelLoader can now determine whether a PVH entry point is available and choose to configure its guests to boot using either PVH or Linux 64-bit protocol. Signed-off-by: Alejandro Jimenez --- src/loader/mod.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/loader/mod.rs b/src/loader/mod.rs index b5804449..74e172e4 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::*; From 7a3b22bcb5082e45d2d6fe973351240cf283ce05 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Thu, 27 Feb 2020 23:14:59 -0500 Subject: [PATCH 4/4] pvh: Add tests for PVH note parsing Add test cases to verify the functionality that parses the ELF Note header to look for a PVH entry point address if one is encoded. Parse a minimal ELF binary that encodes a predefined address of 0x1e1fe1f, and verify that the same value is read. Also test the case in which a note header is present but no PVH entry point is encoded, as well as a case where the PVH entry address is encoded in the note header using a field of incorrect size. The minimal ELF source code (elfnote.S): #define ELFNOTE_START(name, type, flags) \ .pushsection .note.name, flags, @note ; \ .balign 4 ; \ .long 2f - 1f /* namesz */ ; \ .long 4484f - 3f /* descsz */ ; \ .long type ; \ 1:.asciz #name ; \ 2:.balign 4 ; \ 3: #define ELFNOTE_END \ 4484:.balign 4 ; \ .popsection ; #define ELFNOTE(name, type, desc) \ ELFNOTE_START(name, type, "a") \ desc ; \ ELFNOTE_END #define XEN_ELFNOTE_PHYS32_ENTRY 18 #define NT_VERSION 1 ELFNOTE(dummy, NT_VERSION, .quad 0xcafecafe) ELFNOTE(PVHNote, XEN_ELFNOTE_PHYS32_ENTRY, .quad 0x1e1fe1f) .section ".text","ax" .global _start _start: Built with: $ gcc elfnote.S -s -nostdlib -o test_elfnote.bin The elfnote.S source above is modified to generate the binaries for the rest of the test cases. Signed-off-by: Alejandro Jimenez --- coverage_config_aarch64.json | 2 +- coverage_config_x86_64.json | 2 +- src/loader/mod.rs | 54 ++++++++++++++++++++++++++++++++++ src/loader/test_badnote.bin | Bin 0 -> 640 bytes src/loader/test_dummynote.bin | Bin 0 -> 544 bytes src/loader/test_elfnote.bin | Bin 0 -> 648 bytes 6 files changed, 56 insertions(+), 2 deletions(-) create mode 100755 src/loader/test_badnote.bin create mode 100755 src/loader/test_dummynote.bin create mode 100755 src/loader/test_elfnote.bin 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/mod.rs b/src/loader/mod.rs index 74e172e4..a0637635 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -556,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] @@ -678,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/test_badnote.bin b/src/loader/test_badnote.bin new file mode 100755 index 0000000000000000000000000000000000000000..cbe4cb5971f7b3b1883a62eee27e7766bd63d410 GIT binary patch literal 640 zcmb<-^>JfjWMqH=CI&kO5RZe=0W1U|85kT8N*Ej%EEt%8Br5|8SS<@!C5(XU01I$H z^}%Qc1_iJ3^sGodQx|aV8)w z1jGSh9)9^HsSNUR5PL*`V$4A7?ib3iAl6vdnPMt+a&k1g_wr>Gw2m(6qgj0 zBqlNF3OAkNu`-NDY}^{Kocng+(7N@Kn!v>2*CUg3l|xPFr0*i z157|1$UxSA1{#iMpb8{l3ZXQ*epNL6PoVmrK-H@d&<~0SP`IK4SbSTcNkEeWToeEw C>oXAm literal 0 HcmV?d00001 diff --git a/src/loader/test_dummynote.bin b/src/loader/test_dummynote.bin new file mode 100755 index 0000000000000000000000000000000000000000..990e69ed9259b477b54172c7fcf5e6f1b78c6067 GIT binary patch literal 544 zcmb<-^>JfjWMqH=CI&kO5bpzn16T+`GB7A0lmKNc7?^-03j;G)EelvBjDYEY&>x_} zFdAqQSOla8gg1bBAld=Ug3%Bj15^~E7Nmd;h&g~5Ghffm(P6e{?;A-5z2c1GlA@BtBnG{_{E}2Xh#?SmdS0ns zQfX#Rif(2K$UrIpZlD?LK+FU+3+6wVzh%(uKn+U<25~g~XP_Z+2C7emfPPRofx-hF Oz~Vy-O#&&J!7>2cXDkr_ literal 0 HcmV?d00001 diff --git a/src/loader/test_elfnote.bin b/src/loader/test_elfnote.bin new file mode 100755 index 0000000000000000000000000000000000000000..36efd2d3f1a02ef7f9228e87506a8ef5c88e1246 GIT binary patch literal 648 zcmb<-^>JfjWMqH=CI&kO5RZq^0W1U|85leeN*Ej%EEt%8Br5|8SS<@!C5(XSfY3Zp zeK4AVK>@4~qz8mIfO#O=0nCEYF;H