From 6cce7b95605132a1295010230d8cf3fdbdba35c5 Mon Sep 17 00:00:00 2001 From: Damjan Georgievski Date: Sun, 15 Mar 2020 18:56:07 +0100 Subject: [PATCH] arch: load initramfs and populate zero page * load the initramfs File into the guest memory, aligned to page size * finally setup the initramfs address and its size into the boot params (in configure_64bit_boot) Signed-off-by: Damjan Georgievski --- arch/src/lib.rs | 14 +++++++++++-- arch/src/x86_64/mod.rs | 46 ++++++++++++++++++++++++++++++++++++++++-- vmm/src/vm.rs | 46 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 5a4056cb3..2dcdc444b 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -41,6 +41,8 @@ pub enum Error { StartInfoPastRamEnd, /// Error writing hvm_start_info to guest memory. StartInfoSetup, + /// Failed to compute initramfs address. + InitramfsAddress, } pub type Result = result::Result; @@ -76,8 +78,8 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::{ - arch_memory_regions, configure_system, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, - BootProtocol, EntryPoint, + arch_memory_regions, configure_system, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, + layout::CMDLINE_START, BootProtocol, EntryPoint, }; /// Safe wrapper for `sysconf(_SC_PAGESIZE)`. @@ -86,3 +88,11 @@ fn pagesize() -> usize { // Trivially safe unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } } + +/// Type for passing information about the initramfs in the guest memory. +pub struct InitramfsConfig { + /// Load address of initramfs in guest memory + pub address: vm_memory::GuestAddress, + /// Size of initramfs in guest memory + pub size: usize, +} diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 845f2ba00..ba6e4dec3 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -13,12 +13,14 @@ pub mod layout; mod mptable; pub mod regs; +use crate::InitramfsConfig; use crate::RegionType; use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::start_info::{hvm_memmap_table_entry, hvm_start_info}; use std::mem; use vm_memory::{ - Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestUsize, + Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, + GuestUsize, }; #[derive(Debug, Copy, Clone)] @@ -151,6 +153,7 @@ pub fn configure_system( guest_mem: &GuestMemoryMmap, cmdline_addr: GuestAddress, cmdline_size: usize, + initramfs: &Option, num_cpus: u8, setup_hdr: Option, rsdp_addr: Option, @@ -164,7 +167,14 @@ pub fn configure_system( configure_pvh(guest_mem, cmdline_addr, rsdp_addr)?; } BootProtocol::LinuxBoot => { - configure_64bit_boot(guest_mem, cmdline_addr, cmdline_size, setup_hdr, rsdp_addr)?; + configure_64bit_boot( + guest_mem, + cmdline_addr, + cmdline_size, + initramfs, + setup_hdr, + rsdp_addr, + )?; } } @@ -293,6 +303,7 @@ fn configure_64bit_boot( guest_mem: &GuestMemoryMmap, cmdline_addr: GuestAddress, cmdline_size: usize, + initramfs: &Option, setup_hdr: Option, rsdp_addr: Option, ) -> super::Result<()> { @@ -319,6 +330,11 @@ fn configure_64bit_boot( params.0.hdr.cmd_line_ptr = cmdline_addr.raw_value() as u32; params.0.hdr.cmdline_size = cmdline_size as u32; + if let Some(initramfs_config) = initramfs { + params.0.hdr.ramdisk_image = initramfs_config.address.raw_value() as u32; + params.0.hdr.ramdisk_size = initramfs_config.size as u32; + } + add_e820_entry(&mut params.0, 0, layout::EBDA_START.raw_value(), E820_RAM)?; let mem_end = guest_mem.last_addr(); @@ -388,6 +404,25 @@ fn add_e820_entry( Ok(()) } +/// Returns the memory address where the initramfs could be loaded. +pub fn initramfs_load_addr( + guest_mem: &GuestMemoryMmap, + initramfs_size: usize, +) -> super::Result { + let first_region = guest_mem + .find_region(GuestAddress::new(0)) + .ok_or(super::Error::InitramfsAddress)?; + // It's safe to cast to usize because the size of a region can't be greater than usize. + let lowmem_size = first_region.len() as usize; + + if lowmem_size < initramfs_size { + return Err(super::Error::InitramfsAddress); + } + + let aligned_addr: u64 = ((lowmem_size - initramfs_size) & !(crate::pagesize() - 1)) as u64; + Ok(aligned_addr) +} + #[cfg(test)] mod tests { use super::*; @@ -417,6 +452,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, 1, None, None, @@ -437,6 +473,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, @@ -448,6 +485,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, @@ -468,6 +506,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, @@ -479,6 +518,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, @@ -499,6 +539,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, @@ -510,6 +551,7 @@ mod tests { &gm, GuestAddress(0), 0, + &None, no_vcpus, None, None, diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 8e9d268c4..dc46cc626 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -38,9 +38,10 @@ use kvm_ioctls::*; use linux_loader::cmdline::Cmdline; use linux_loader::loader::KernelLoader; use signal_hook::{iterator::Signals, SIGINT, SIGTERM, SIGWINCH}; +use std::convert::TryInto; use std::ffi::CString; use std::fs::File; -use std::io; +use std::io::{self, Seek, SeekFrom}; use std::ops::Deref; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; @@ -78,9 +79,15 @@ pub enum Error { /// Cannot open the kernel image KernelFile(io::Error), + /// Cannot open the initramfs image + InitramfsFile(io::Error), + /// Cannot load the kernel in memory KernelLoad(linux_loader::loader::Error), + /// Cannot load the initramfs in memory + InitramfsLoad, + /// Cannot load the command line in memory LoadCmdLine(linux_loader::loader::Error), @@ -219,6 +226,7 @@ impl VmState { pub struct Vm { kernel: File, + initramfs: Option, threads: Vec>, device_manager: Arc>, config: Arc>, @@ -254,6 +262,11 @@ impl Vm { let kernel = File::open(&config.lock().unwrap().kernel.as_ref().unwrap().path) .map_err(Error::KernelFile)?; + let initramfs = match &config.lock().unwrap().initramfs { + Some(initramfs) => Some(File::open(&initramfs.path).map_err(Error::InitramfsFile)?), + None => None, + }; + let fd: VmFd; loop { match kvm.create_vm() { @@ -377,6 +390,7 @@ impl Vm { Ok(Vm { kernel, + initramfs, device_manager, config, on_tty, @@ -388,6 +402,28 @@ impl Vm { }) } + fn load_initramfs(&mut self, guest_mem: &GuestMemoryMmap) -> Result { + let mut initramfs = self.initramfs.as_ref().unwrap(); + let size: usize = initramfs + .seek(SeekFrom::End(0)) + .map_err(|_| Error::InitramfsLoad)? + .try_into() + .unwrap(); + initramfs + .seek(SeekFrom::Start(0)) + .map_err(|_| Error::InitramfsLoad)?; + + let address = + arch::initramfs_load_addr(guest_mem, size).map_err(|_| Error::InitramfsLoad)?; + let address = GuestAddress(address); + + guest_mem + .read_from(address, &mut initramfs, size) + .map_err(|_| Error::InitramfsLoad)?; + + Ok(arch::InitramfsConfig { address, size }) + } + fn load_kernel(&mut self) -> Result { let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE); cmdline @@ -425,6 +461,12 @@ impl Vm { &cmdline_cstring, ) .map_err(Error::LoadCmdLine)?; + + let initramfs_config = match self.initramfs { + Some(_) => Some(self.load_initramfs(mem.deref())?), + None => None, + }; + let boot_vcpus = self.cpu_manager.lock().unwrap().boot_vcpus(); let _max_vcpus = self.cpu_manager.lock().unwrap().max_vcpus(); @@ -447,6 +489,7 @@ impl Vm { &mem, arch::layout::CMDLINE_START, cmdline_cstring.to_bytes().len() + 1, + &initramfs_config, boot_vcpus, Some(hdr), rsdp_addr, @@ -481,6 +524,7 @@ impl Vm { &mem, arch::layout::CMDLINE_START, cmdline_cstring.to_bytes().len() + 1, + &initramfs_config, boot_vcpus, None, rsdp_addr,