extern crate vm_memory;
use std::fmt;
use std::io::{Read, Seek};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use vm_memory::ByteValued;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestUsize};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use crate::loader_gen::bootparam;
pub use crate::cmdline::Cmdline;
#[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::*;
#[derive(Debug, PartialEq)]
pub enum Error {
#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
Bzimage(bzimage::Error),
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
Elf(elf::Error),
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
Pe(pe::Error),
CommandLineCopy,
CommandLineOverflow,
InvalidKernelStartAddress,
MemoryOverflow,
}
pub type Result<T> = std::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self {
#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
Error::Bzimage(ref _e) => "failed to load bzImage kernel image",
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
Error::Elf(ref _e) => "failed to load ELF kernel image",
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
Error::Pe(ref _e) => "failed to load PE kernel image",
Error::CommandLineCopy => "failed writing command line to guest memory",
Error::CommandLineOverflow => "command line overflowed guest memory",
Error::InvalidKernelStartAddress => "invalid kernel start address",
Error::MemoryOverflow => "memory to load kernel image is not enough",
};
write!(f, "Kernel Loader: {}", desc)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
Error::Bzimage(ref e) => Some(e),
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
Error::Elf(ref e) => Some(e),
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
Error::Pe(ref e) => Some(e),
Error::CommandLineCopy => None,
Error::CommandLineOverflow => None,
Error::InvalidKernelStartAddress => None,
Error::MemoryOverflow => None,
}
}
}
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
impl From<elf::Error> for Error {
fn from(err: elf::Error) -> Self {
Error::Elf(err)
}
}
#[cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
impl From<bzimage::Error> for Error {
fn from(err: bzimage::Error) -> Self {
Error::Bzimage(err)
}
}
#[cfg(all(feature = "pe", target_arch = "aarch64"))]
impl From<pe::Error> for Error {
fn from(err: pe::Error) -> Self {
Error::Pe(err)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct KernelLoaderResult {
pub kernel_load: GuestAddress,
pub kernel_end: GuestUsize,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub setup_header: Option<bootparam::setup_header>,
#[cfg(all(feature = "elf", any(target_arch = "x86", target_arch = "x86_64")))]
pub pvh_boot_cap: elf::PvhBootCapability,
}
pub trait KernelLoader {
fn load<F, M: GuestMemory>(
guest_mem: &M,
kernel_offset: Option<GuestAddress>,
kernel_image: &mut F,
highmem_start_address: Option<GuestAddress>,
) -> Result<KernelLoaderResult>
where
F: Read + Seek;
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe impl ByteValued for bootparam::setup_header {}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe impl ByteValued for bootparam::boot_params {}
pub fn load_cmdline<M: GuestMemory>(
guest_mem: &M,
guest_addr: GuestAddress,
cmdline: &Cmdline,
) -> Result<()> {
let len = cmdline.as_str().len();
if len == 0 {
return Ok(());
}
let end = guest_addr
.checked_add(len as u64 + 1)
.ok_or(Error::CommandLineOverflow)?; if end > guest_mem.last_addr() {
return Err(Error::CommandLineOverflow);
}
guest_mem
.write_slice(cmdline.as_str().as_bytes(), guest_addr)
.map_err(|_| Error::CommandLineCopy)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use vm_memory::{Address, GuestAddress};
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
const MEM_SIZE: u64 = 0x100_0000;
fn create_guest_mem() -> GuestMemoryMmap {
GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
}
#[test]
fn test_cmdline_overflow() {
let gm = create_guest_mem();
let cmdline_address = GuestAddress(MEM_SIZE - 5);
let mut cl = Cmdline::new(10);
cl.insert_str("12345").unwrap();
assert_eq!(
Err(Error::CommandLineOverflow),
load_cmdline(&gm, cmdline_address, &cl)
);
}
#[test]
fn test_cmdline_write_end() {
let gm = create_guest_mem();
let mut cmdline_address = GuestAddress(45);
let mut cl = Cmdline::new(10);
cl.insert_str("1234").unwrap();
assert_eq!(Ok(()), load_cmdline(&gm, cmdline_address, &cl));
let val: u8 = gm.read_obj(cmdline_address).unwrap();
assert_eq!(val, b'1');
cmdline_address = cmdline_address.unchecked_add(1);
let val: u8 = gm.read_obj(cmdline_address).unwrap();
assert_eq!(val, b'2');
cmdline_address = cmdline_address.unchecked_add(1);
let val: u8 = gm.read_obj(cmdline_address).unwrap();
assert_eq!(val, b'3');
cmdline_address = cmdline_address.unchecked_add(1);
let val: u8 = gm.read_obj(cmdline_address).unwrap();
assert_eq!(val, b'4');
cmdline_address = cmdline_address.unchecked_add(1);
let val: u8 = gm.read_obj(cmdline_address).unwrap();
assert_eq!(val, b'\0');
}
}