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 <alejandro.j.jimenez@oracle.com>
This commit is contained in:
Alejandro Jimenez 2020-04-06 15:21:41 -04:00 committed by Sebastien Boeuf
parent b9f193703a
commit 0fc3936448
3 changed files with 34 additions and 2 deletions

View File

@ -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<T> = result::Result<T, Error>;

View File

@ -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);

View File

@ -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<InitramfsConfig>,
rsdp_addr: Option<GuestAddress>,
) -> 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<hvm_memmap_table_entry> = Vec::new();