diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 99b785447..1410b1dae 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -77,4 +77,5 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::{ arch_memory_regions, configure_system, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, + BootProtocol, EntryPoint, }; diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 3c11e9f5a..40df8664f 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -21,6 +21,32 @@ use vm_memory::{ 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_RESERVED: u32 = 2; diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 04e6bc222..d7fca698b 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. 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}; #[cfg(feature = "acpi")] use arch::layout; +use arch::EntryPoint; use devices::{ioapic, BusDevice}; use kvm_bindings::CpuId; use kvm_ioctls::*; @@ -271,11 +274,11 @@ impl Vcpu { /// # Arguments /// /// * `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. pub fn configure( &mut self, - kernel_start_addr: Option, + kernel_entry_point: Option, vm_memory: &GuestMemoryAtomic, cpuid: CpuId, ) -> Result<()> { @@ -286,11 +289,11 @@ impl Vcpu { .map_err(Error::SetSupportedCpusFailed)?; 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 arch::x86_64::regs::setup_regs( &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::ZERO_PAGE_START.raw_value(), ) @@ -537,11 +540,7 @@ impl CpuManager { Ok(cpu_manager) } - fn activate_vcpus( - &mut self, - desired_vcpus: u8, - entry_addr: Option, - ) -> Result<()> { + fn activate_vcpus(&mut self, desired_vcpus: u8, entry_point: Option) -> Result<()> { if desired_vcpus > self.max_vcpus { return Err(Error::DesiredVCPUCountExceedsMax); } @@ -586,7 +585,7 @@ impl CpuManager { register_signal_handler(SIGRTMIN(), handle_signal) .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"); // Block until all CPUs are ready. @@ -628,10 +627,10 @@ impl CpuManager { .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. 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. @@ -657,8 +656,8 @@ impl CpuManager { } // 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<()> { - self.activate_vcpus(self.boot_vcpus(), Some(entry_addr)) + pub fn start_boot_vcpus(&mut self, entry_point: EntryPoint) -> Result<()> { + self.activate_vcpus(self.boot_vcpus(), Some(entry_point)) } pub fn resize(&mut self, desired_vcpus: u8) -> Result { diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index e5165b6ba..c833f8384 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1,3 +1,5 @@ +// Copyright © 2020, Oracle and/or its affiliates. +// // Copyright 2018 Amazon.com, Inc. or its affiliates. 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::memory_manager::{get_host_cpu_phys_bits, Error as MemoryManagerError, MemoryManager}; use anyhow::anyhow; -use arch::layout; +use arch::{layout, BootProtocol, EntryPoint}; use devices::{ioapic, HotPlugNotificationFlags}; use kvm_bindings::{kvm_enable_cap, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP}; use kvm_ioctls::*; @@ -382,7 +384,7 @@ impl Vm { }) } - fn load_kernel(&mut self) -> Result { + fn load_kernel(&mut self) -> Result { let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE); cmdline .insert_str(self.config.lock().unwrap().cmdline.args.clone()) @@ -453,9 +455,24 @@ impl Vm { .checked_add(KERNEL_64BIT_ENTRY_OFFSET) .ok_or(Error::MemOverflow)?; - Ok(GuestAddress(load_addr)) + Ok(EntryPoint { + entry_addr: GuestAddress(load_addr), + protocol: BootProtocol::LinuxBoot, + }) } 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( &mem, arch::layout::CMDLINE_START, @@ -466,7 +483,10 @@ impl Vm { ) .map_err(Error::ConfigureSystem)?; - Ok(entry_addr.kernel_load) + Ok(EntryPoint { + entry_addr: entry_point_addr, + protocol: boot_prot, + }) } } }