#![cfg(any(feature = "elf", feature = "pe", feature = "bzimage"))]
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory};
use std::fmt;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86_64;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use x86_64::*;
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::*;
use std::cmp::max;
use std::mem::size_of;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Linux(linux::Error),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Pvh(pvh::Error),
#[cfg(target_arch = "aarch64")]
Fdt(fdt::Error),
MissingStartAddress,
Overflow,
InvalidAddress,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
let desc = match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Linux(ref _e) => "failed to configure boot parameter by Linux Boot protocol.",
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Pvh(ref _e) => "failed to configure boot parameter by PVH.",
#[cfg(target_arch = "aarch64")]
Fdt(ref _e) => "failed to configure boot parameter by FDT.",
MissingStartAddress => {
"boot parameter was specified without its starting address in guest memory."
}
Overflow => "boot parameter address overflows.",
InvalidAddress => "boot parameter address precedes the starting address.",
};
write!(f, "Boot Configurator: {}", desc)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Linux(ref e) => Some(e),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Pvh(ref e) => Some(e),
#[cfg(target_arch = "aarch64")]
Fdt(ref e) => Some(e),
MissingStartAddress => None,
Overflow => None,
InvalidAddress => None,
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub trait BootConfigurator {
fn write_bootparams<M>(params: &BootParams, guest_memory: &M) -> Result<()>
where
M: GuestMemory;
}
#[derive(Clone)]
pub struct BootParams {
pub header: Vec<u8>,
pub header_start: GuestAddress,
pub sections: Option<Vec<u8>>,
pub sections_start: Option<GuestAddress>,
pub modules: Option<Vec<u8>>,
pub modules_start: Option<GuestAddress>,
}
impl BootParams {
pub fn new<T: ByteValued>(header: &T, header_addr: GuestAddress) -> Self {
BootParams {
header: header.as_slice().to_vec(),
header_start: header_addr,
sections: None,
sections_start: None,
modules: None,
modules_start: None,
}
}
pub fn set_sections<T: ByteValued>(&mut self, sections: &[T], sections_addr: GuestAddress) {
self.sections = Some(
sections
.iter()
.flat_map(|section| section.as_slice().to_vec())
.collect(),
);
self.sections_start = Some(sections_addr);
}
pub fn add_section<T: ByteValued>(
&mut self,
section: &T,
section_addr: Option<GuestAddress>,
) -> Result<GuestAddress> {
Self::add_boot_parameter_to_list(
section,
section_addr,
self.sections.get_or_insert(vec![]),
&mut self.sections_start,
)
}
pub fn set_modules<T: ByteValued>(&mut self, modules: &[T], modules_addr: GuestAddress) {
self.modules = Some(
modules
.iter()
.flat_map(|module| module.as_slice().to_vec())
.collect(),
);
self.modules_start = Some(modules_addr);
}
pub fn add_module<T: ByteValued>(
&mut self,
module: &T,
module_addr: Option<GuestAddress>,
) -> Result<GuestAddress> {
Self::add_boot_parameter_to_list(
module,
module_addr,
self.modules.get_or_insert(vec![]),
&mut self.modules_start,
)
}
fn add_boot_parameter_to_list<T: ByteValued>(
elem: &T,
elem_start_opt: Option<GuestAddress>,
bytes_acc: &mut Vec<u8>,
list_start_opt: &mut Option<GuestAddress>,
) -> Result<GuestAddress> {
if list_start_opt.is_none() {
*list_start_opt = elem_start_opt;
}
let list_start = list_start_opt.ok_or(Error::MissingStartAddress)?;
let elem_start = elem_start_opt.unwrap_or(
list_start
.checked_add(bytes_acc.len() as u64)
.ok_or(Error::Overflow)?,
);
let elem_off = elem_start
.checked_offset_from(list_start)
.ok_or(Error::InvalidAddress)? as usize;
let elem_end = elem_off + size_of::<T>();
bytes_acc.resize(max(elem_end, bytes_acc.len()), 0);
bytes_acc.splice(elem_off..elem_end, elem.as_slice().iter().cloned());
Ok(elem_start)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::undocumented_unsafe_blocks)]
use super::*;
#[derive(Clone, Copy, Default)]
struct Foobar {
_foo: [u8; 5],
}
unsafe impl ByteValued for Foobar {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct DummyHeader {
_dummy: u64,
}
unsafe impl ByteValued for DummyHeader {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct DummySection {
_dummy: u64,
}
unsafe impl ByteValued for DummySection {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct DummyModule {
_dummy: u64,
}
unsafe impl ByteValued for DummyModule {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct OtherDummyModule {
_dummy: u64,
}
unsafe impl ByteValued for OtherDummyModule {}
#[test]
fn test_error_messages() {
#[cfg(target_arch = "x86_64")]
{
assert_eq!(
format!("{}", Error::Linux(linux::Error::ZeroPagePastRamEnd)),
"Boot Configurator: failed to configure boot parameter by Linux Boot protocol."
);
assert_eq!(
format!("{}", Error::Linux(linux::Error::ZeroPageSetup)),
"Boot Configurator: failed to configure boot parameter by Linux Boot protocol."
);
assert_eq!(
format!("{}", Error::Pvh(pvh::Error::MemmapTableMissing)),
"Boot Configurator: failed to configure boot parameter by PVH."
);
assert_eq!(
format!("{}", Error::Pvh(pvh::Error::MemmapTablePastRamEnd)),
"Boot Configurator: failed to configure boot parameter by PVH."
);
assert_eq!(
format!("{}", Error::Pvh(pvh::Error::MemmapTableSetup)),
"Boot Configurator: failed to configure boot parameter by PVH."
);
assert_eq!(
format!("{}", Error::Pvh(pvh::Error::StartInfoPastRamEnd)),
"Boot Configurator: failed to configure boot parameter by PVH."
);
assert_eq!(
format!("{}", Error::Pvh(pvh::Error::StartInfoSetup)),
"Boot Configurator: failed to configure boot parameter by PVH."
);
}
#[cfg(target_arch = "aarch64")]
assert_eq!(
format!("{}", Error::Fdt(fdt::Error::WriteFDTToMemory)),
"Boot Configurator: failed to configure boot parameter by FDT."
);
assert_eq!(
format!("{}", Error::MissingStartAddress),
"Boot Configurator: \
boot parameter was specified without its starting address in guest memory."
);
assert_eq!(
format!("{}", Error::Overflow),
"Boot Configurator: boot parameter address overflows."
);
assert_eq!(
format!("{}", Error::InvalidAddress),
"Boot Configurator: boot parameter address precedes the starting address."
);
}
#[test]
fn test_bootparam_list_addition() {
let mut accumulator: Vec<u8> = vec![];
let start = GuestAddress(0x1000);
let element = Foobar::default();
assert_eq!(
format!(
"{:?}",
BootParams::add_boot_parameter_to_list(&element, None, &mut accumulator, &mut None)
.err()
),
"Some(MissingStartAddress)"
);
assert_eq!(
BootParams::add_boot_parameter_to_list(
&element,
None,
&mut accumulator,
&mut Some(start)
)
.unwrap(),
start
);
assert_eq!(accumulator, element.as_slice().to_vec());
let mut list_start_opt: Option<GuestAddress> = None;
assert_eq!(
BootParams::add_boot_parameter_to_list(
&element,
Some(start),
&mut accumulator,
&mut list_start_opt
)
.unwrap(),
start
);
assert_eq!(list_start_opt, Some(start));
assert_eq!(accumulator, element.as_slice().to_vec());
assert_eq!(
format!(
"{:?}",
BootParams::add_boot_parameter_to_list(
&element,
Some(start.unchecked_sub(0x100)),
&mut accumulator,
&mut list_start_opt
)
.err()
),
"Some(InvalidAddress)"
);
accumulator.clear();
assert!(BootParams::add_boot_parameter_to_list(
&element,
None,
&mut accumulator,
&mut list_start_opt
)
.is_ok());
assert!(BootParams::add_boot_parameter_to_list(
&Foobar {
_foo: [2, 2, 2, 3, 3]
},
None,
&mut accumulator,
&mut list_start_opt
)
.is_ok());
#[rustfmt::skip]
assert_eq!(
accumulator,
&[
0, 0, 0, 0, 0, 2, 2, 2, 3, 3, ]
);
assert!(BootParams::add_boot_parameter_to_list(
&Foobar { _foo: [1u8; 5] },
Some(start.unchecked_add(size_of::<Foobar>() as u64 + 3)),
&mut accumulator,
&mut list_start_opt
)
.is_ok());
#[rustfmt::skip]
assert_eq!(
accumulator,
&[
0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 1, 1, ]
);
assert_eq!(accumulator.len(), 13)
}
#[test]
fn test_bootparams() {
let hdr = DummyHeader::default();
let hdr_addr = GuestAddress(0x1000);
let mut bootparams = BootParams::new(&hdr, hdr_addr);
assert_eq!(bootparams.header, hdr.as_slice());
assert_eq!(bootparams.header_start, hdr_addr);
let sections = vec![DummySection::default(); 2];
let sections_addr = GuestAddress(0x2000);
bootparams.set_sections::<DummySection>(sections.as_slice(), sections_addr);
assert_eq!(
bootparams.sections,
Some(vec![0u8; 2 * size_of::<DummySection>()])
);
assert_eq!(bootparams.sections_start, Some(sections_addr));
let sections = vec![DummySection::default(); 3];
let sections_addr = GuestAddress(0x3000);
bootparams.set_sections::<DummySection>(sections.as_slice(), sections_addr);
assert_eq!(
bootparams.sections,
Some(vec![0u8; 3 * size_of::<DummySection>()])
);
assert_eq!(bootparams.sections_start, Some(sections_addr));
assert_eq!(
bootparams.add_section::<DummySection>(&DummySection::default(), None),
Ok(sections_addr.unchecked_add(3 * size_of::<DummySection>() as u64))
);
assert_eq!(
bootparams.sections,
Some(vec![0u8; 4 * size_of::<DummySection>()])
);
assert_eq!(bootparams.sections_start, Some(sections_addr));
let modules = vec![DummyModule::default(); 2];
let modules_addr = GuestAddress(0x4000);
bootparams.set_modules::<DummyModule>(modules.as_slice(), modules_addr);
assert_eq!(
bootparams.modules,
Some(vec![0u8; 2 * size_of::<DummyModule>()])
);
assert_eq!(bootparams.modules_start, Some(modules_addr));
let modules = vec![DummyModule::default(); 3];
let modules_addr = GuestAddress(0x5000);
bootparams.set_modules::<DummyModule>(modules.as_slice(), modules_addr);
assert_eq!(
bootparams.modules,
Some(vec![0u8; 3 * size_of::<DummyModule>()])
);
assert_eq!(bootparams.modules_start, Some(modules_addr));
assert_eq!(
bootparams.add_module::<DummyModule>(&DummyModule::default(), None),
Ok(modules_addr.unchecked_add(3 * size_of::<DummyModule>() as u64))
);
assert_eq!(
bootparams.add_module::<OtherDummyModule>(&OtherDummyModule::default(), None),
Ok(modules_addr.unchecked_add(
3 * size_of::<DummyModule>() as u64 + size_of::<OtherDummyModule>() as u64
))
);
}
}