Skip to content

Commit b1246be

Browse files
committed
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. Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
1 parent 34b0516 commit b1246be

File tree

2 files changed

+75
-11
lines changed

2 files changed

+75
-11
lines changed

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: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,39 @@ impl error::Error for Error {
8585
}
8686
}
8787

88+
#[derive(Clone, Copy, Debug, PartialEq)]
89+
/// Availability of PVH entry point in the kernel, which allows the VMM
90+
/// to use the PVH boot protocol to start guests.
91+
pub enum PvhBootCapability {
92+
/// PVH entry point is present
93+
PvhEntryPresent(GuestAddress),
94+
/// PVH entry point is not present
95+
PvhEntryNotPresent,
96+
/// PVH entry point is ignored, even if available
97+
PvhEntryIgnored,
98+
}
99+
100+
impl Default for PvhBootCapability {
101+
fn default() -> Self {
102+
PvhBootCapability::PvhEntryIgnored
103+
}
104+
}
105+
106+
impl Display for PvhBootCapability {
107+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> fmt::Result {
108+
use self::PvhBootCapability::*;
109+
match self {
110+
PvhEntryPresent(pvh_entry_addr) => write!(
111+
f,
112+
"PVH entry point present at guest address: {:#x}",
113+
pvh_entry_addr.raw_value()
114+
),
115+
PvhEntryNotPresent => write!(f, "PVH entry point not present"),
116+
PvhEntryIgnored => write!(f, "PVH entry point ignored"),
117+
}
118+
}
119+
}
120+
88121
impl Display for Error {
89122
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90123
write!(f, "Kernel Loader Error: {}", self.description())
@@ -121,7 +154,9 @@ impl Elf {
121154
impl KernelLoader for Elf {
122155
/// Loads a kernel from a vmlinux elf image into guest memory.
123156
///
124-
/// The kernel is loaded into guest memory at offset `phdr.p_paddr` specified by the elf image.
157+
/// By default, the kernel is loaded into guest memory at offset `phdr.p_paddr` specified by the elf image.
158+
/// When used, `kernel_offset` specifies a fixed offset from `phdr.p_paddr` at which to load the kernel. If
159+
/// `kernel_offset` is requested, the `pvh_entry_addr` field of the result will not be populated.
125160
///
126161
/// # Arguments
127162
///
@@ -204,8 +239,17 @@ impl KernelLoader for Elf {
204239
for phdr in phdrs {
205240
if phdr.p_type != elf::PT_LOAD || phdr.p_filesz == 0 {
206241
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)?;
242+
// The PVH boot protocol currently requires that the kernel is loaded at
243+
// the default kernel load address in guest memory (specified at kernel
244+
// build time by the value of CONFIG_PHYSICAL_START). Therefore, only
245+
// attempt to use PVH if an offset from the default load address has not
246+
// been requested using the kernel_offset parameter.
247+
if let Some(_offset) = kernel_offset {
248+
loader_result.pvh_boot_cap = PvhBootCapability::PvhEntryIgnored;
249+
} else {
250+
// If kernel_offset is not requested, check if PVH a entry point is present
251+
loader_result.pvh_boot_cap = parse_elf_note(&phdr, kernel_image)?;
252+
}
209253
}
210254
continue;
211255
}
@@ -250,7 +294,7 @@ const PVH_NOTE_STR_SZ: usize = 4;
250294
/// with paging disabled, as described by the PVH boot protocol.
251295
/// Returns the encoded entry point address, or `None` if no `XEN_ELFNOTE_PHYS32_ENTRY` entries are
252296
/// found in the note header.
253-
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<Option<GuestAddress>>
297+
fn parse_elf_note<F>(phdr: &elf::Elf64_Phdr, kernel_image: &mut F) -> Result<PvhBootCapability>
254298
where
255299
F: Read + Seek,
256300
{
@@ -299,7 +343,7 @@ where
299343

300344
if read_size >= phdr.p_filesz as usize {
301345
// PVH ELF note not found, nothing else to do.
302-
return Ok(None);
346+
return Ok(PvhBootCapability::PvhEntryNotPresent);
303347
}
304348

305349
// Otherwise the correct note type was found.
@@ -324,7 +368,7 @@ where
324368
.read_exact(&mut pvh_addr_bytes)
325369
.map_err(|_| Error::ReadNoteHeader)?;
326370

327-
Ok(Some(GuestAddress(
371+
Ok(PvhBootCapability::PvhEntryPresent(GuestAddress(
328372
u32::from_le_bytes(pvh_addr_bytes).into(),
329373
)))
330374
}
@@ -457,15 +501,34 @@ mod tests {
457501
let gm = create_guest_mem();
458502
let pvhnote_image = make_elfnote();
459503
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);
504+
assert_eq!(
505+
loader_result.pvh_boot_cap,
506+
PvhBootCapability::PvhEntryPresent(GuestAddress(0x1e1fe1f))
507+
);
508+
509+
// Verify that PVH is ignored when kernel_start is requested
510+
let loader_result = Elf::load(
511+
&gm,
512+
Some(GuestAddress(0x0020_0000)),
513+
&mut Cursor::new(&pvhnote_image),
514+
None,
515+
)
516+
.unwrap();
517+
assert_eq!(
518+
loader_result.pvh_boot_cap,
519+
PvhBootCapability::PvhEntryIgnored
520+
);
461521
}
462522

463523
#[test]
464524
fn test_dummy_elfnote() {
465525
let gm = create_guest_mem();
466526
let dummynote_image = make_dummy_elfnote();
467527
let loader_result = Elf::load(&gm, None, &mut Cursor::new(&dummynote_image), None).unwrap();
468-
assert!(loader_result.pvh_entry_addr.is_none());
528+
assert_eq!(
529+
loader_result.pvh_boot_cap,
530+
PvhBootCapability::PvhEntryNotPresent
531+
);
469532
}
470533

471534
#[test]

0 commit comments

Comments
 (0)