From 0fc3936448c0636c14b912fc961aeac5d724caac Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Mon, 6 Apr 2020 15:21:41 -0400 Subject: [PATCH] arch: Support loading initramfs with PVH boot protocol Fill and write to guest memory the necessary boot module structure to allow a guest using the PVH boot protocol to load an initramfs image. Signed-off-by: Alejandro Jimenez --- arch/src/lib.rs | 2 ++ arch/src/x86_64/layout.rs | 4 ++++ arch/src/x86_64/mod.rs | 30 ++++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 1a4ae1d0b..d442bfd97 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -43,6 +43,8 @@ pub enum Error { StartInfoSetup, /// Failed to compute initramfs address. InitramfsAddress, + /// Error writing module entry to guest memory. + ModlistSetup(vm_memory::GuestMemoryError), } pub type Result = result::Result; diff --git a/arch/src/x86_64/layout.rs b/arch/src/x86_64/layout.rs index 7c0f83fc9..d29038989 100644 --- a/arch/src/x86_64/layout.rs +++ b/arch/src/x86_64/layout.rs @@ -31,6 +31,10 @@ pub const BOOT_IDT_START: GuestAddress = GuestAddress(0x520); /// Address for the hvm_start_info struct used in PVH boot pub const PVH_INFO_START: GuestAddress = GuestAddress(0x6000); +/// Starting address of array of modules of hvm_modlist_entry type. +/// Used to enable initrd support using the PVH boot ABI. +pub const MODLIST_START: GuestAddress = GuestAddress(0x6040); + /// Address of memory map table used in PVH boot. Can overlap /// with the zero page address since they are mutually exclusive. pub const MEMMAP_START: GuestAddress = GuestAddress(0x7000); diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 3b3c0878e..579ad423c 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -16,7 +16,9 @@ pub mod regs; use crate::InitramfsConfig; use crate::RegionType; use linux_loader::loader::bootparam::{boot_params, setup_header}; -use linux_loader::loader::elf::start_info::{hvm_memmap_table_entry, hvm_start_info}; +use linux_loader::loader::elf::start_info::{ + hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info, +}; use std::mem; use vm_memory::{ Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, @@ -68,6 +70,11 @@ struct MemmapTableEntryWrapper(hvm_memmap_table_entry); unsafe impl ByteValued for MemmapTableEntryWrapper {} +#[derive(Copy, Clone, Default)] +struct ModlistEntryWrapper(hvm_modlist_entry); + +unsafe impl ByteValued for ModlistEntryWrapper {} + // This is a workaround to the Rust enforcement specifying that any implementation of a foreign // trait (in this case `DataInit`) where: // * the type that is implementing the trait is foreign or @@ -164,7 +171,7 @@ pub fn configure_system( match boot_prot { BootProtocol::PvhBoot => { - configure_pvh(guest_mem, cmdline_addr, rsdp_addr)?; + configure_pvh(guest_mem, cmdline_addr, initramfs, rsdp_addr)?; } BootProtocol::LinuxBoot => { configure_64bit_boot( @@ -184,6 +191,7 @@ pub fn configure_system( fn configure_pvh( guest_mem: &GuestMemoryMmap, cmdline_addr: GuestAddress, + initramfs: &Option, rsdp_addr: Option, ) -> super::Result<()> { const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578; @@ -200,6 +208,24 @@ fn configure_pvh( start_info.0.rsdp_paddr = rsdp_addr.0; } + if let Some(initramfs_config) = initramfs { + // The initramfs has been written to guest memory already, here we just need to + // create the module structure that describes it. + let ramdisk_mod: ModlistEntryWrapper = ModlistEntryWrapper(hvm_modlist_entry { + paddr: initramfs_config.address.raw_value(), + size: initramfs_config.size as u64, + ..Default::default() + }); + + start_info.0.nr_modules += 1; + start_info.0.modlist_paddr = layout::MODLIST_START.raw_value(); + + // Write the modlist struct to guest memory. + guest_mem + .write_obj(ramdisk_mod, layout::MODLIST_START) + .map_err(super::Error::ModlistSetup)?; + } + // Vector to hold the memory maps which needs to be written to guest memory // at MEMMAP_START after all of the mappings are recorded. let mut memmap: Vec = Vec::new();