pvh: Write start_info structure to guest memory

Fill the hvm_start_info and related memory map structures as
specified in the PVH boot protocol. Write the data structures
to guest memory at the GPA that will be stored in %rbx when
the guest starts.

Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
This commit is contained in:
Alejandro Jimenez 2020-02-12 23:14:12 -05:00 committed by Sebastien Boeuf
parent 840a9a97ff
commit a22bc3559f
2 changed files with 182 additions and 7 deletions

View File

@ -154,15 +154,153 @@ pub fn configure_system(
num_cpus: u8,
setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>,
boot_prot: BootProtocol,
) -> super::Result<()> {
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;
match boot_prot {
BootProtocol::PvhBoot => {
configure_pvh(guest_mem, cmdline_addr, rsdp_addr)?;
}
BootProtocol::LinuxBoot => {
configure_64bit_boot(guest_mem, cmdline_addr, cmdline_size, setup_hdr, rsdp_addr)?;
}
}
Ok(())
}
fn configure_pvh(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
rsdp_addr: Option<GuestAddress>,
) -> super::Result<()> {
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336ec578;
let mut start_info: StartInfoWrapper = StartInfoWrapper(hvm_start_info::default());
start_info.0.magic = XEN_HVM_START_MAGIC_VALUE;
start_info.0.version = 1; // pvh has version 1
start_info.0.nr_modules = 0;
start_info.0.cmdline_paddr = cmdline_addr.raw_value() as u64;
start_info.0.memmap_paddr = layout::MEMMAP_START.raw_value();
if let Some(rsdp_addr) = rsdp_addr {
start_info.0.rsdp_paddr = rsdp_addr.0;
}
// 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();
// Create the memory map entries.
add_memmap_entry(&mut memmap, 0, layout::EBDA_START.raw_value(), E820_RAM)?;
let mem_end = guest_mem.last_addr();
if mem_end < layout::MEM_32BIT_RESERVED_START {
add_memmap_entry(
&mut memmap,
layout::HIGH_RAM_START.raw_value(),
mem_end.unchecked_offset_from(layout::HIGH_RAM_START) + 1,
E820_RAM,
)?;
} else {
add_memmap_entry(
&mut memmap,
layout::HIGH_RAM_START.raw_value(),
layout::MEM_32BIT_RESERVED_START.unchecked_offset_from(layout::HIGH_RAM_START),
E820_RAM,
)?;
if mem_end > layout::RAM_64BIT_START {
add_memmap_entry(
&mut memmap,
layout::RAM_64BIT_START.raw_value(),
mem_end.unchecked_offset_from(layout::RAM_64BIT_START) + 1,
E820_RAM,
)?;
}
}
add_memmap_entry(
&mut memmap,
layout::PCI_MMCONFIG_START.0,
layout::PCI_MMCONFIG_SIZE,
E820_RESERVED,
)?;
start_info.0.memmap_entries = memmap.len() as u32;
// Copy the vector with the memmap table to the MEMMAP_START address
// which is already saved in the memmap_paddr field of hvm_start_info struct.
let mut memmap_start_addr = layout::MEMMAP_START;
guest_mem
.checked_offset(
memmap_start_addr,
mem::size_of::<hvm_memmap_table_entry>() * start_info.0.memmap_entries as usize,
)
.ok_or(super::Error::MemmapTablePastRamEnd)?;
// For every entry in the memmap vector, create a MemmapTableEntryWrapper
// and write it to guest memory.
for memmap_entry in memmap {
let map_entry_wrapper: MemmapTableEntryWrapper = MemmapTableEntryWrapper(memmap_entry);
guest_mem
.write_obj(map_entry_wrapper, memmap_start_addr)
.map_err(|_| super::Error::MemmapTableSetup)?;
memmap_start_addr =
memmap_start_addr.unchecked_add(mem::size_of::<hvm_memmap_table_entry>() as u64);
}
// The hvm_start_info struct itself must be stored at PVH_START_INFO
// address, and %rbx will be initialized to contain PVH_INFO_START prior to
// starting the guest, as required by the PVH ABI.
let start_info_addr = layout::PVH_INFO_START;
guest_mem
.checked_offset(start_info_addr, mem::size_of::<hvm_start_info>())
.ok_or(super::Error::StartInfoPastRamEnd)?;
// Write the start_info struct to guest memory.
guest_mem
.write_obj(start_info, start_info_addr)
.map_err(|_| super::Error::StartInfoSetup)?;
Ok(())
}
fn add_memmap_entry(
memmap: &mut Vec<hvm_memmap_table_entry>,
addr: u64,
size: u64,
mem_type: u32,
) -> Result<(), Error> {
// Add the table entry to the vector
memmap.push(hvm_memmap_table_entry {
addr,
size,
type_: mem_type,
reserved: 0,
});
Ok(())
}
fn configure_64bit_boot(
guest_mem: &GuestMemoryMmap,
cmdline_addr: GuestAddress,
cmdline_size: usize,
setup_hdr: Option<setup_header>,
rsdp_addr: Option<GuestAddress>,
) -> super::Result<()> {
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
const KERNEL_HDR_MAGIC: u32 = 0x53726448;
const KERNEL_LOADER_OTHER: u8 = 0xff;
const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000; // Must be non-zero.
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;
let mut params: BootParamsWrapper = BootParamsWrapper(boot_params::default());
if let Some(hdr) = setup_hdr {
@ -272,7 +410,15 @@ mod tests {
fn test_system_configuration() {
let no_vcpus = 4;
let gm = GuestMemoryMmap::from_ranges(&vec![(GuestAddress(0), 0x10000)]).unwrap();
let config_err = configure_system(&gm, GuestAddress(0), 0, 1, None, None);
let config_err = configure_system(
&gm,
GuestAddress(0),
0,
1,
None,
None,
BootProtocol::LinuxBoot,
);
assert!(config_err.is_err());
// Now assigning some memory that falls before the 32bit memory hole.
@ -284,7 +430,16 @@ mod tests {
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(&gm, GuestAddress(0), 0, no_vcpus, None, None).unwrap();
configure_system(
&gm,
GuestAddress(0),
0,
no_vcpus,
None,
None,
BootProtocol::LinuxBoot,
)
.unwrap();
// Now assigning some memory that is equal to the start of the 32bit memory hole.
let mem_size = 3328 << 20;
@ -295,7 +450,16 @@ mod tests {
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(&gm, GuestAddress(0), 0, no_vcpus, None, None).unwrap();
configure_system(
&gm,
GuestAddress(0),
0,
no_vcpus,
None,
None,
BootProtocol::LinuxBoot,
)
.unwrap();
// Now assigning some memory that falls after the 32bit memory hole.
let mem_size = 3330 << 20;
@ -306,7 +470,16 @@ mod tests {
.map(|r| (r.0, r.1))
.collect();
let gm = GuestMemoryMmap::from_ranges(&ram_regions).unwrap();
configure_system(&gm, GuestAddress(0), 0, no_vcpus, None, None).unwrap();
configure_system(
&gm,
GuestAddress(0),
0,
no_vcpus,
None,
None,
BootProtocol::LinuxBoot,
)
.unwrap();
}
#[test]

View File

@ -446,6 +446,7 @@ impl Vm {
boot_vcpus,
Some(hdr),
rsdp_addr,
BootProtocol::LinuxBoot,
)
.map_err(Error::ConfigureSystem)?;
@ -480,6 +481,7 @@ impl Vm {
boot_vcpus,
None,
rsdp_addr,
boot_prot,
)
.map_err(Error::ConfigureSystem)?;