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 <gdamjan@gmail.com>
This commit is contained in:
Damjan Georgievski 2020-03-15 18:56:07 +01:00 committed by Sebastien Boeuf
parent 1f9bc68c54
commit 6cce7b9560
3 changed files with 101 additions and 5 deletions

View File

@ -41,6 +41,8 @@ pub enum Error {
StartInfoPastRamEnd, StartInfoPastRamEnd,
/// Error writing hvm_start_info to guest memory. /// Error writing hvm_start_info to guest memory.
StartInfoSetup, StartInfoSetup,
/// Failed to compute initramfs address.
InitramfsAddress,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -76,8 +78,8 @@ pub mod x86_64;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use x86_64::{ pub use x86_64::{
arch_memory_regions, configure_system, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, arch_memory_regions, configure_system, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE,
BootProtocol, EntryPoint, layout::CMDLINE_START, BootProtocol, EntryPoint,
}; };
/// Safe wrapper for `sysconf(_SC_PAGESIZE)`. /// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
@ -86,3 +88,11 @@ fn pagesize() -> usize {
// Trivially safe // Trivially safe
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } 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,
}

View File

@ -13,12 +13,14 @@ pub mod layout;
mod mptable; mod mptable;
pub mod regs; pub mod regs;
use crate::InitramfsConfig;
use crate::RegionType; use crate::RegionType;
use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::bootparam::{boot_params, setup_header};
use linux_loader::loader::start_info::{hvm_memmap_table_entry, hvm_start_info}; use linux_loader::loader::start_info::{hvm_memmap_table_entry, hvm_start_info};
use std::mem; use std::mem;
use vm_memory::{ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestUsize, Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion,
GuestUsize,
}; };
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -151,6 +153,7 @@ pub fn configure_system(
guest_mem: &GuestMemoryMmap, guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress, cmdline_addr: GuestAddress,
cmdline_size: usize, cmdline_size: usize,
initramfs: &Option<InitramfsConfig>,
num_cpus: u8, num_cpus: u8,
setup_hdr: Option<setup_header>, setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>, rsdp_addr: Option<GuestAddress>,
@ -164,7 +167,14 @@ pub fn configure_system(
configure_pvh(guest_mem, cmdline_addr, rsdp_addr)?; configure_pvh(guest_mem, cmdline_addr, rsdp_addr)?;
} }
BootProtocol::LinuxBoot => { 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, guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress, cmdline_addr: GuestAddress,
cmdline_size: usize, cmdline_size: usize,
initramfs: &Option<InitramfsConfig>,
setup_hdr: Option<setup_header>, setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>, rsdp_addr: Option<GuestAddress>,
) -> super::Result<()> { ) -> 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.cmd_line_ptr = cmdline_addr.raw_value() as u32;
params.0.hdr.cmdline_size = cmdline_size 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)?; add_e820_entry(&mut params.0, 0, layout::EBDA_START.raw_value(), E820_RAM)?;
let mem_end = guest_mem.last_addr(); let mem_end = guest_mem.last_addr();
@ -388,6 +404,25 @@ fn add_e820_entry(
Ok(()) Ok(())
} }
/// Returns the memory address where the initramfs could be loaded.
pub fn initramfs_load_addr(
guest_mem: &GuestMemoryMmap,
initramfs_size: usize,
) -> super::Result<u64> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -417,6 +452,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
1, 1,
None, None,
None, None,
@ -437,6 +473,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,
@ -448,6 +485,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,
@ -468,6 +506,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,
@ -479,6 +518,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,
@ -499,6 +539,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,
@ -510,6 +551,7 @@ mod tests {
&gm, &gm,
GuestAddress(0), GuestAddress(0),
0, 0,
&None,
no_vcpus, no_vcpus,
None, None,
None, None,

View File

@ -38,9 +38,10 @@ use kvm_ioctls::*;
use linux_loader::cmdline::Cmdline; use linux_loader::cmdline::Cmdline;
use linux_loader::loader::KernelLoader; use linux_loader::loader::KernelLoader;
use signal_hook::{iterator::Signals, SIGINT, SIGTERM, SIGWINCH}; use signal_hook::{iterator::Signals, SIGINT, SIGTERM, SIGWINCH};
use std::convert::TryInto;
use std::ffi::CString; use std::ffi::CString;
use std::fs::File; use std::fs::File;
use std::io; use std::io::{self, Seek, SeekFrom};
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
@ -78,9 +79,15 @@ pub enum Error {
/// Cannot open the kernel image /// Cannot open the kernel image
KernelFile(io::Error), KernelFile(io::Error),
/// Cannot open the initramfs image
InitramfsFile(io::Error),
/// Cannot load the kernel in memory /// Cannot load the kernel in memory
KernelLoad(linux_loader::loader::Error), KernelLoad(linux_loader::loader::Error),
/// Cannot load the initramfs in memory
InitramfsLoad,
/// Cannot load the command line in memory /// Cannot load the command line in memory
LoadCmdLine(linux_loader::loader::Error), LoadCmdLine(linux_loader::loader::Error),
@ -219,6 +226,7 @@ impl VmState {
pub struct Vm { pub struct Vm {
kernel: File, kernel: File,
initramfs: Option<File>,
threads: Vec<thread::JoinHandle<()>>, threads: Vec<thread::JoinHandle<()>>,
device_manager: Arc<Mutex<DeviceManager>>, device_manager: Arc<Mutex<DeviceManager>>,
config: Arc<Mutex<VmConfig>>, config: Arc<Mutex<VmConfig>>,
@ -254,6 +262,11 @@ impl Vm {
let kernel = File::open(&config.lock().unwrap().kernel.as_ref().unwrap().path) let kernel = File::open(&config.lock().unwrap().kernel.as_ref().unwrap().path)
.map_err(Error::KernelFile)?; .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; let fd: VmFd;
loop { loop {
match kvm.create_vm() { match kvm.create_vm() {
@ -377,6 +390,7 @@ impl Vm {
Ok(Vm { Ok(Vm {
kernel, kernel,
initramfs,
device_manager, device_manager,
config, config,
on_tty, on_tty,
@ -388,6 +402,28 @@ impl Vm {
}) })
} }
fn load_initramfs(&mut self, guest_mem: &GuestMemoryMmap) -> Result<arch::InitramfsConfig> {
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<EntryPoint> { fn load_kernel(&mut self) -> Result<EntryPoint> {
let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE); let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE);
cmdline cmdline
@ -425,6 +461,12 @@ impl Vm {
&cmdline_cstring, &cmdline_cstring,
) )
.map_err(Error::LoadCmdLine)?; .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 boot_vcpus = self.cpu_manager.lock().unwrap().boot_vcpus();
let _max_vcpus = self.cpu_manager.lock().unwrap().max_vcpus(); let _max_vcpus = self.cpu_manager.lock().unwrap().max_vcpus();
@ -447,6 +489,7 @@ impl Vm {
&mem, &mem,
arch::layout::CMDLINE_START, arch::layout::CMDLINE_START,
cmdline_cstring.to_bytes().len() + 1, cmdline_cstring.to_bytes().len() + 1,
&initramfs_config,
boot_vcpus, boot_vcpus,
Some(hdr), Some(hdr),
rsdp_addr, rsdp_addr,
@ -481,6 +524,7 @@ impl Vm {
&mem, &mem,
arch::layout::CMDLINE_START, arch::layout::CMDLINE_START,
cmdline_cstring.to_bytes().len() + 1, cmdline_cstring.to_bytes().len() + 1,
&initramfs_config,
boot_vcpus, boot_vcpus,
None, None,
rsdp_addr, rsdp_addr,