pvh: Introduce EntryPoint struct

In order to properly initialize the kvm regs/sregs structs for
the guest, the load_kernel() return type must specify which
boot protocol to use with the entry point address it returns.

Make load_kernel() return an EntryPoint struct containing the
required information. This structure will later be used
in the vCPU configuration methods to setup the appropriate
initial conditions for the guest.

Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
This commit is contained in:
Alejandro Jimenez 2020-02-11 22:37:33 -05:00 committed by Sebastien Boeuf
parent 98b956886e
commit 24f0e42e6a
4 changed files with 64 additions and 18 deletions

View File

@ -77,4 +77,5 @@ 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, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START,
BootProtocol, EntryPoint,
}; };

View File

@ -21,6 +21,32 @@ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestUsize, Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestUsize,
}; };
#[derive(Debug, Copy, Clone)]
pub enum BootProtocol {
LinuxBoot,
PvhBoot,
}
impl ::std::fmt::Display for BootProtocol {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
BootProtocol::LinuxBoot => write!(f, "Linux 64-bit boot protocol"),
BootProtocol::PvhBoot => write!(f, "PVH boot protocol"),
}
}
}
#[derive(Debug, Copy, Clone)]
/// Specifies the entry point address where the guest must start
/// executing code, as well as which of the supported boot protocols
/// is to be used to configure the guest initial state.
pub struct EntryPoint {
/// Address in guest memory where the guest must start execution
pub entry_addr: GuestAddress,
/// Specifies which boot protocol to use
pub protocol: BootProtocol,
}
const E820_RAM: u32 = 1; const E820_RAM: u32 = 1;
const E820_RESERVED: u32 = 2; const E820_RESERVED: u32 = 2;

View File

@ -1,3 +1,5 @@
// Copyright © 2020, Oracle and/or its affiliates.
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// //
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
@ -13,6 +15,7 @@ use crate::device_manager::DeviceManager;
use acpi_tables::{aml, aml::Aml, sdt::SDT}; use acpi_tables::{aml, aml::Aml, sdt::SDT};
#[cfg(feature = "acpi")] #[cfg(feature = "acpi")]
use arch::layout; use arch::layout;
use arch::EntryPoint;
use devices::{ioapic, BusDevice}; use devices::{ioapic, BusDevice};
use kvm_bindings::CpuId; use kvm_bindings::CpuId;
use kvm_ioctls::*; use kvm_ioctls::*;
@ -271,11 +274,11 @@ impl Vcpu {
/// # Arguments /// # Arguments
/// ///
/// * `machine_config` - Specifies necessary info used for the CPUID configuration. /// * `machine_config` - Specifies necessary info used for the CPUID configuration.
/// * `kernel_start_addr` - Offset from `guest_mem` at which the kernel starts. /// * `kernel_entry_point` - Kernel entry point address in guest memory and boot protocol used.
/// * `vm` - The virtual machine this vcpu will get attached to. /// * `vm` - The virtual machine this vcpu will get attached to.
pub fn configure( pub fn configure(
&mut self, &mut self,
kernel_start_addr: Option<GuestAddress>, kernel_entry_point: Option<EntryPoint>,
vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>, vm_memory: &GuestMemoryAtomic<GuestMemoryMmap>,
cpuid: CpuId, cpuid: CpuId,
) -> Result<()> { ) -> Result<()> {
@ -286,11 +289,11 @@ impl Vcpu {
.map_err(Error::SetSupportedCpusFailed)?; .map_err(Error::SetSupportedCpusFailed)?;
arch::x86_64::regs::setup_msrs(&self.fd).map_err(Error::MSRSConfiguration)?; arch::x86_64::regs::setup_msrs(&self.fd).map_err(Error::MSRSConfiguration)?;
if let Some(kernel_start_addr) = kernel_start_addr { if let Some(kernel_entry_point) = kernel_entry_point {
// Safe to unwrap because this method is called after the VM is configured // Safe to unwrap because this method is called after the VM is configured
arch::x86_64::regs::setup_regs( arch::x86_64::regs::setup_regs(
&self.fd, &self.fd,
kernel_start_addr.raw_value(), kernel_entry_point.entry_addr.raw_value(),
arch::x86_64::layout::BOOT_STACK_POINTER.raw_value(), arch::x86_64::layout::BOOT_STACK_POINTER.raw_value(),
arch::x86_64::layout::ZERO_PAGE_START.raw_value(), arch::x86_64::layout::ZERO_PAGE_START.raw_value(),
) )
@ -537,11 +540,7 @@ impl CpuManager {
Ok(cpu_manager) Ok(cpu_manager)
} }
fn activate_vcpus( fn activate_vcpus(&mut self, desired_vcpus: u8, entry_point: Option<EntryPoint>) -> Result<()> {
&mut self,
desired_vcpus: u8,
entry_addr: Option<GuestAddress>,
) -> Result<()> {
if desired_vcpus > self.max_vcpus { if desired_vcpus > self.max_vcpus {
return Err(Error::DesiredVCPUCountExceedsMax); return Err(Error::DesiredVCPUCountExceedsMax);
} }
@ -586,7 +585,7 @@ impl CpuManager {
register_signal_handler(SIGRTMIN(), handle_signal) register_signal_handler(SIGRTMIN(), handle_signal)
.expect("Failed to register vcpu signal handler"); .expect("Failed to register vcpu signal handler");
vcpu.configure(entry_addr, &vm_memory, cpuid) vcpu.configure(entry_point, &vm_memory, cpuid)
.expect("Failed to configure vCPU"); .expect("Failed to configure vCPU");
// Block until all CPUs are ready. // Block until all CPUs are ready.
@ -628,10 +627,10 @@ impl CpuManager {
.map_err(Error::VcpuSpawn)?, .map_err(Error::VcpuSpawn)?,
); );
// On hot plug calls into this function entry_addr is None. It is for // On hot plug calls into this function entry_point is None. It is for
// those hotplug CPU additions that we need to set the inserting flag. // those hotplug CPU additions that we need to set the inserting flag.
self.vcpu_states[usize::from(cpu_id)].handle = handle; self.vcpu_states[usize::from(cpu_id)].handle = handle;
self.vcpu_states[usize::from(cpu_id)].inserting = entry_addr.is_none(); self.vcpu_states[usize::from(cpu_id)].inserting = entry_point.is_none();
} }
// Unblock all CPU threads. // Unblock all CPU threads.
@ -657,8 +656,8 @@ impl CpuManager {
} }
// Starts all the vCPUs that the VM is booting with. Blocks until all vCPUs are running. // Starts all the vCPUs that the VM is booting with. Blocks until all vCPUs are running.
pub fn start_boot_vcpus(&mut self, entry_addr: GuestAddress) -> Result<()> { pub fn start_boot_vcpus(&mut self, entry_point: EntryPoint) -> Result<()> {
self.activate_vcpus(self.boot_vcpus(), Some(entry_addr)) self.activate_vcpus(self.boot_vcpus(), Some(entry_point))
} }
pub fn resize(&mut self, desired_vcpus: u8) -> Result<bool> { pub fn resize(&mut self, desired_vcpus: u8) -> Result<bool> {

View File

@ -1,3 +1,5 @@
// Copyright © 2020, Oracle and/or its affiliates.
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// //
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
@ -29,7 +31,7 @@ use crate::cpu;
use crate::device_manager::{get_win_size, Console, DeviceManager, DeviceManagerError}; use crate::device_manager::{get_win_size, Console, DeviceManager, DeviceManagerError};
use crate::memory_manager::{get_host_cpu_phys_bits, Error as MemoryManagerError, MemoryManager}; use crate::memory_manager::{get_host_cpu_phys_bits, Error as MemoryManagerError, MemoryManager};
use anyhow::anyhow; use anyhow::anyhow;
use arch::layout; use arch::{layout, BootProtocol, EntryPoint};
use devices::{ioapic, HotPlugNotificationFlags}; use devices::{ioapic, HotPlugNotificationFlags};
use kvm_bindings::{kvm_enable_cap, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP}; use kvm_bindings::{kvm_enable_cap, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP};
use kvm_ioctls::*; use kvm_ioctls::*;
@ -382,7 +384,7 @@ impl Vm {
}) })
} }
fn load_kernel(&mut self) -> Result<GuestAddress> { 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
.insert_str(self.config.lock().unwrap().cmdline.args.clone()) .insert_str(self.config.lock().unwrap().cmdline.args.clone())
@ -453,9 +455,24 @@ impl Vm {
.checked_add(KERNEL_64BIT_ENTRY_OFFSET) .checked_add(KERNEL_64BIT_ENTRY_OFFSET)
.ok_or(Error::MemOverflow)?; .ok_or(Error::MemOverflow)?;
Ok(GuestAddress(load_addr)) Ok(EntryPoint {
entry_addr: GuestAddress(load_addr),
protocol: BootProtocol::LinuxBoot,
})
} }
None => { None => {
// Assume by default Linux boot protocol is used and not PVH
let mut entry_point_addr: GuestAddress = entry_addr.kernel_load;
let boot_prot = if cfg!(feature = "pvh_boot") && entry_addr.pvh_entry_addr.is_some()
{
// entry_addr.pvh_entry_addr field is safe to unwrap here
entry_point_addr = entry_addr.pvh_entry_addr.unwrap();
BootProtocol::PvhBoot
} else {
BootProtocol::LinuxBoot
};
arch::configure_system( arch::configure_system(
&mem, &mem,
arch::layout::CMDLINE_START, arch::layout::CMDLINE_START,
@ -466,7 +483,10 @@ impl Vm {
) )
.map_err(Error::ConfigureSystem)?; .map_err(Error::ConfigureSystem)?;
Ok(entry_addr.kernel_load) Ok(EntryPoint {
entry_addr: entry_point_addr,
protocol: boot_prot,
})
} }
} }
} }