Skip to content

Commit ec930d7

Browse files
aljimenezbalxiord
authored andcommitted
pvh: Augment PVH information returned by KernelLoaderResult
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 #12 Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
1 parent d2885ea commit ec930d7

File tree

3 files changed

+80
-14
lines changed

3 files changed

+80
-14
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 84.8,
2+
"coverage_score": 83.4,
33
"exclude_path": "",
44
"crate_features": "bzimage,elf",
55
"exclude_path": "loader_gen"

src/loader/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,11 @@ pub struct KernelLoaderResult {
132132
/// See https://www.kernel.org/doc/Documentation/x86/boot.txt.
133133
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
134134
pub setup_header: Option<bootparam::setup_header>,
135-
/// This field optionally holds the address of a PVH entry point, indicating that
136-
/// the kernel supports the PVH boot protocol as described in:
135+
/// Availability of a PVH entry point. Only used for ELF boot, indicates whether the kernel
136+
/// supports the PVH boot protocol as described in:
137137
/// https://xenbits.xen.org/docs/unstable/misc/pvh.html
138-
pub pvh_entry_addr: Option<GuestAddress>,
138+
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
139+
pub pvh_boot_cap: elf::PvhBootCapability,
139140
}
140141

141142
/// Trait that specifies kernel image loading support.

src/loader/x86_64/elf/mod.rs

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Copyright © 2020, Oracle and/or its affiliates.
12
// Copyright (c) 2019 Intel Corporation. All rights reserved.
23
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
34
//
@@ -85,6 +86,39 @@ impl error::Error for Error {
8586
}
8687
}
8788

89+
#[derive(Clone, Copy, Debug, PartialEq)]
90+
/// Availability of PVH entry point in the kernel, which allows the VMM
91+
/// to use the PVH boot protocol to start guests.
92+
pub enum PvhBootCapability {
93+
/// PVH entry point is present
94+
PvhEntryPresent(GuestAddress),
95+
/// PVH entry point is not present
96+
PvhEntryNotPresent,
97+
/// PVH entry point is ignored, even if available
98+
PvhEntryIgnored,
99+
}
100+
101+
impl Default for PvhBootCapability {
102+
fn default() -> Self {
103+
PvhBootCapability::PvhEntryIgnored
104+
}
105+
}
106+
107+
impl Display for PvhBootCapability {
108+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> fmt::Result {
109+
use self::PvhBootCapability::*;
110+
match self {
111+
PvhEntryPresent(pvh_entry_addr) => write!(
112+
f,
113+
"PVH entry point present at guest address: {:#x}",
114+
pvh_entry_addr.raw_value()
115+
),
116+
PvhEntryNotPresent => write!(f, "PVH entry point not present"),
117+
PvhEntryIgnored => write!(f, "PVH entry point ignored"),
118+
}
119+
}
120+
}
121+
88122
impl Display for Error {
89123
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90124
write!(f, "Kernel Loader Error: {}", self.description())
@@ -121,7 +155,10 @@ impl Elf {
121155
impl KernelLoader for Elf {
122156
/// Loads a kernel from a vmlinux elf image into guest memory.
123157
///
124-
/// The kernel is loaded into guest memory at offset `phdr.p_paddr` specified by the elf image.
158+
/// By default, the kernel is loaded into guest memory at offset `phdr.p_paddr` specified
159+
/// by the elf image. When used, `kernel_offset` specifies a fixed offset from `phdr.p_paddr`
160+
/// at which to load the kernel. If `kernel_offset` is requested, the `pvh_entry_addr` field
161+
/// of the result will not be populated.
125162
///
126163
/// # Arguments
127164
///
@@ -204,8 +241,17 @@ impl KernelLoader for Elf {
204241
for phdr in phdrs {
205242
if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
206243
if phdr.p_type == elf::PT_NOTE {
207-
// This segment describes a Note, check if PVH entry point is encoded.
208-
loader_result.pvh_entry_addr = parse_elf_note(&phdr, kernel_image)?;
244+
// The PVH boot protocol currently requires that the kernel is loaded at
245+
// the default kernel load address in guest memory (specified at kernel
246+
// build time by the value of CONFIG_PHYSICAL_START). Therefore, only
247+
// attempt to use PVH if an offset from the default load address has not
248+
// been requested using the kernel_offset parameter.
249+
if let Some(_offset) = kernel_offset {
250+
loader_result.pvh_boot_cap = PvhBootCapability::PvhEntryIgnored;
251+
} else {
252+
// If kernel_offset is not requested, check if PVH entry point is present
253+
loader_result.pvh_boot_cap = parse_elf_note(&phdr, kernel_image)?;
254+
}
209255
}
210256
continue;
211257
}
@@ -248,9 +294,9 @@ const PVH_NOTE_STR_SZ: usize = 4;
248294
/// of type `XEN_ELFNOTE_PHYS32_ENTRY` (0x12). Notes of this type encode a physical 32-bit entry
249295
/// point address into the kernel, which is used when launching guests in 32-bit (protected) mode
250296
/// with paging disabled, as described by the PVH boot protocol.
251-
/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries are
252-
/// found in the note header.
253-
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<Option<GuestAddress>>
297+
/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries
298+
/// are found in the note header.
299+
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<PvhBootCapability>
254300
where
255301
F: Read + Seek,
256302
{
@@ -299,7 +345,7 @@ where
299345

300346
if read_size >= phdr.p_filesz as usize {
301347
// PVH ELF note not found, nothing else to do.
302-
return Ok(None);
348+
return Ok(PvhBootCapability::PvhEntryNotPresent);
303349
}
304350

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

327-
Ok(Some(GuestAddress(
373+
Ok(PvhBootCapability::PvhEntryPresent(GuestAddress(
328374
u32::from_le_bytes(pvh_addr_bytes).into(),
329375
)))
330376
}
@@ -457,15 +503,34 @@ mod tests {
457503
let gm = create_guest_mem();
458504
let pvhnote_image = make_elfnote();
459505
let loader_result = Elf::load(&gm, None, &mut Cursor::new(&pvhnote_image), None).unwrap();
460-
assert_eq!(loader_result.pvh_entry_addr.unwrap().raw_value(), 0x1e1fe1f);
506+
assert_eq!(
507+
loader_result.pvh_boot_cap,
508+
PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
509+
);
510+
511+
// Verify that PVH is ignored when kernel_start is requested
512+
let loader_result = Elf::load(
513+
&gm,
514+
Some(GuestAddress(0x0020_0000)),
515+
&mut Cursor::new(&pvhnote_image),
516+
None,
517+
)
518+
.unwrap();
519+
assert_eq!(
520+
loader_result.pvh_boot_cap,
521+
PvhBootCapability::PvhEntryIgnored
522+
);
461523
}
462524

463525
#[test]
464526
fn test_dummy_elfnote() {
465527
let gm = create_guest_mem();
466528
let dummynote_image = make_dummy_elfnote();
467529
let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap();
468-
assert!(loader_result.pvh_entry_addr.is_none());
530+
assert_eq!(
531+
loader_result.pvh_boot_cap,
532+
PvhBootCapability::PvhEntryNotPresent
533+
);
469534
}
470535

471536
#[test]

0 commit comments

Comments
 (0)