#![cfg(any(feature = "elf", feature = "bzimage"))]
use vm_memory::{ByteValued, Bytes, GuestMemory};
use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};
use crate::loader_gen::start_info::{hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info};
use std::fmt;
pub struct PvhBootConfigurator {}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
MemmapTableAddressMissing,
MemmapTableMissing,
MemmapTablePastRamEnd,
MemmapTableSetup,
StartInfoPastRamEnd,
StartInfoSetup,
ModulesAddressMissing,
ModulesSetup,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
let desc = match self {
MemmapTableAddressMissing => {
"the starting address for the memory map wasn't passed to the boot configurator."
}
MemmapTableMissing => "no memory map was passed to the boot configurator.",
MemmapTablePastRamEnd => "the memory map table extends past the end of guest memory.",
MemmapTableSetup => "error writing memory map table to guest memory.",
StartInfoPastRamEnd => {
"the hvm_start_info structure extends past the end of guest memory."
}
StartInfoSetup => "error writing hvm_start_info to guest memory.",
ModulesAddressMissing => "the starting address for the modules descriptions wasn't passed to the boot configurator.",
ModulesSetup => "error writing module descriptions to guest memory.",
};
write!(f, "PVH Boot Configurator: {}", desc)
}
}
impl std::error::Error for Error {}
impl From<Error> for BootConfiguratorError {
fn from(err: Error) -> Self {
BootConfiguratorError::Pvh(err)
}
}
unsafe impl ByteValued for hvm_start_info {}
unsafe impl ByteValued for hvm_memmap_table_entry {}
unsafe impl ByteValued for hvm_modlist_entry {}
impl BootConfigurator for PvhBootConfigurator {
fn write_bootparams<M>(params: &BootParams, guest_memory: &M) -> Result<()>
where
M: GuestMemory,
{
let memmap = params.sections.as_ref().ok_or(Error::MemmapTableMissing)?;
let memmap_addr = params
.sections_start
.ok_or(Error::MemmapTableAddressMissing)?;
guest_memory
.checked_offset(memmap_addr, memmap.len())
.ok_or(Error::MemmapTablePastRamEnd)?;
guest_memory
.write_slice(memmap.as_slice(), memmap_addr)
.map_err(|_| Error::MemmapTableSetup)?;
guest_memory
.checked_offset(params.header_start, params.header.len())
.ok_or(Error::StartInfoPastRamEnd)?;
guest_memory
.write_slice(params.header.as_slice(), params.header_start)
.map_err(|_| Error::StartInfoSetup)?;
if let Some(modules) = params.modules.as_ref() {
let modules_addr = params.modules_start.ok_or(Error::ModulesAddressMissing)?;
guest_memory
.write_slice(modules.as_slice(), modules_addr)
.map_err(|_| Error::ModulesSetup)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
use vm_memory::{Address, GuestAddress, GuestMemoryMmap};
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
const MEM_SIZE: u64 = 0x100_0000;
const E820_RAM: u32 = 1;
fn create_guest_mem() -> GuestMemoryMmap {
GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
}
fn build_bootparams_common() -> (
hvm_start_info,
Vec<hvm_memmap_table_entry>,
Vec<hvm_modlist_entry>,
) {
let mut start_info = hvm_start_info::default();
let memmap_entry = hvm_memmap_table_entry {
addr: 0x7000,
size: 0,
type_: E820_RAM,
reserved: 0,
};
let modlist_entry = hvm_modlist_entry {
paddr: 0x10000,
size: 0x100,
cmdline_paddr: 0,
reserved: 0,
};
start_info.magic = XEN_HVM_START_MAGIC_VALUE;
start_info.version = 1;
start_info.nr_modules = 0;
start_info.memmap_entries = 0;
(start_info, vec![memmap_entry], vec![modlist_entry])
}
#[test]
fn test_configure_pvh_boot() {
let (mut start_info, memmap_entries, modlist_entries) = build_bootparams_common();
let guest_memory = create_guest_mem();
let start_info_addr = GuestAddress(0x6000);
let memmap_addr = GuestAddress(0x7000);
let modlist_addr = GuestAddress(0x6040);
start_info.memmap_paddr = memmap_addr.raw_value();
let mut boot_params = BootParams::new::<hvm_start_info>(&start_info, start_info_addr);
assert_eq!(
PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
.err(),
Some(Error::MemmapTableMissing.into())
);
let bad_start_info_addr = GuestAddress(
guest_memory.last_addr().raw_value() - mem::size_of::<hvm_start_info>() as u64 + 1,
);
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, memmap_addr);
boot_params.header_start = bad_start_info_addr;
assert_eq!(
PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
.err(),
Some(Error::StartInfoPastRamEnd.into())
);
let himem_start = GuestAddress(0x100000);
boot_params.header_start = himem_start;
let bad_memmap_addr = GuestAddress(
guest_memory.last_addr().raw_value() - mem::size_of::<hvm_memmap_table_entry>() as u64
+ 1,
);
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, bad_memmap_addr);
assert_eq!(
PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(&boot_params, &guest_memory,)
.err(),
Some(Error::MemmapTablePastRamEnd.into())
);
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap_entries, memmap_addr);
boot_params.set_modules::<hvm_modlist_entry>(&modlist_entries, modlist_addr);
assert!(PvhBootConfigurator::write_bootparams::<GuestMemoryMmap>(
&boot_params,
&guest_memory,
)
.is_ok());
}
#[test]
fn test_error_messages() {
assert_eq!(
format!("{}", Error::MemmapTableMissing),
"PVH Boot Configurator: no memory map was passed to the boot configurator."
);
assert_eq!(
format!("{}", Error::MemmapTablePastRamEnd),
"PVH Boot Configurator: the memory map table extends past the end of guest memory."
);
assert_eq!(
format!("{}", Error::MemmapTableSetup),
"PVH Boot Configurator: error writing memory map table to guest memory."
);
assert_eq!(format!("{}", Error::StartInfoPastRamEnd), "PVH Boot Configurator: the hvm_start_info structure extends past the end of guest memory.");
assert_eq!(
format!("{}", Error::StartInfoSetup),
"PVH Boot Configurator: error writing hvm_start_info to guest memory."
);
}
}