Skip to content

Commit

Permalink
pvh: Augment PVH information returned by KernelLoaderResult
Browse files Browse the repository at this point in the history
Refactor the PVH code to return a field in KernelLoaderResult
that specifies whether the PVH capability is present or not,
or if it is being purposedly ignored.

The only current reason for ignoring the PVH capability is when
a kernel_offset is requested. Since PVH boot protocol expects
the kernel to be loaded at the default load address, it cannot
be used when a specific load offset has been requested by the
VMM using the kernel_offset parameter.

Related to rust-vmm#12

Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
  • Loading branch information
aljimenezb committed Jun 17, 2020
1 parent 86f1ccb commit 69ab5d1
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 14 deletions.
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 84.8,
"coverage_score": 83.4,
"exclude_path": "",
"crate_features": "bzimage,elf",
"exclude_path": "loader_gen"
Expand Down
7 changes: 4 additions & 3 deletions src/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,11 @@ pub struct KernelLoaderResult {
/// See https://www.kernel.org/doc/Documentation/x86/boot.txt.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub setup_header: Option<bootparam::setup_header>,
/// This field optionally holds the address of a PVH entry point, indicating that
/// the kernel supports the PVH boot protocol as described in:
/// Availability of a PVH entry point. Only used for ELF boot, indicates whether the kernel
/// supports the PVH boot protocol as described in:
/// https://xenbits.xen.org/docs/unstable/misc/pvh.html
pub pvh_entry_addr: Option<GuestAddress>,
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
pub pvh_boot_cap: elf::PvhBootCapability,
}

/// Trait that specifies kernel image loading support.
Expand Down
85 changes: 75 additions & 10 deletions src/loader/x86_64/elf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 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.
//
Expand Down Expand Up @@ -85,6 +86,39 @@ impl error::Error for Error {
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
/// Availability of PVH entry point in the kernel, which allows the VMM
/// to use the PVH boot protocol to start guests.
pub enum PvhBootCapability {
/// PVH entry point is present
PvhEntryPresent(GuestAddress),
/// PVH entry point is not present
PvhEntryNotPresent,
/// PVH entry point is ignored, even if available
PvhEntryIgnored,
}

impl Default for PvhBootCapability {
fn default() -> Self {
PvhBootCapability::PvhEntryIgnored
}
}

impl Display for PvhBootCapability {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> fmt::Result {
use self::PvhBootCapability::*;
match self {
PvhEntryPresent(pvh_entry_addr) => write!(
f,
"PVH entry point present at guest address: {:#x}",
pvh_entry_addr.raw_value()
),
PvhEntryNotPresent => write!(f, "PVH entry point not present"),
PvhEntryIgnored => write!(f, "PVH entry point ignored"),
}
}
}

impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Kernel Loader Error: {}", self.description())
Expand Down Expand Up @@ -121,7 +155,10 @@ impl Elf {
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.
/// By default, the kernel is loaded into guest memory at offset `phdr.p_paddr` specified
/// by the elf image. When used, `kernel_offset` specifies a fixed offset from `phdr.p_paddr`
/// at which to load the kernel. If `kernel_offset` is requested, the `pvh_entry_addr` field
/// of the result will not be populated.
///
/// # Arguments
///
Expand Down Expand Up @@ -204,8 +241,17 @@ impl KernelLoader for Elf {
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)?;
// The PVH boot protocol currently requires that the kernel is loaded at
// the default kernel load address in guest memory (specified at kernel
// build time by the value of CONFIG_PHYSICAL_START). Therefore, only
// attempt to use PVH if an offset from the default load address has not
// been requested using the kernel_offset parameter.
if let Some(_offset) = kernel_offset {
loader_result.pvh_boot_cap = PvhBootCapability::PvhEntryIgnored;
} else {
// If kernel_offset is not requested, check if PVH entry point is present
loader_result.pvh_boot_cap = parse_elf_note(&phdr, kernel_image)?;
}
}
continue;
}
Expand Down Expand Up @@ -248,9 +294,9 @@ const PVH_NOTE_STR_SZ: usize = 4;
/// of type `XEN_ELFNOTE_PHYS32_ENTRY` (0x12). Notes of this type encode a physical 32-bit entry
/// point address into the kernel, which is used when launching guests in 32-bit (protected) mode
/// with paging disabled, as described by the PVH boot protocol.
/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries are
/// found in the note header.
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<Option<GuestAddress>>
/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries
/// are found in the note header.
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<PvhBootCapability>
where
F: Read + Seek,
{
Expand Down Expand Up @@ -299,7 +345,7 @@ where

if read_size >= phdr.p_filesz as usize {
// PVH ELF note not found, nothing else to do.
return Ok(None);
return Ok(PvhBootCapability::PvhEntryNotPresent);
}

// Otherwise the correct note type was found.
Expand All @@ -324,7 +370,7 @@ where
.read_exact(&mut pvh_addr_bytes)
.map_err(|_| Error::ReadNoteHeader)?;

Ok(Some(GuestAddress(
Ok(PvhBootCapability::PvhEntryPresent(GuestAddress(
u32::from_le_bytes(pvh_addr_bytes).into(),
)))
}
Expand Down Expand Up @@ -457,15 +503,34 @@ mod tests {
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);
assert_eq!(
loader_result.pvh_boot_cap,
PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
);

// Verify that PVH is ignored when kernel_start is requested
let loader_result = Elf::load(
&gm,
Some(GuestAddress(0x0020_0000)),
&mut Cursor::new(&pvhnote_image),
None,
)
.unwrap();
assert_eq!(
loader_result.pvh_boot_cap,
PvhBootCapability::PvhEntryIgnored
);
}

#[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());
assert_eq!(
loader_result.pvh_boot_cap,
PvhBootCapability::PvhEntryNotPresent
);
}

#[test]
Expand Down

0 comments on commit 69ab5d1

Please sign in to comment.