hypervisor: add API to query VM physaddr size

Add a function that returns the size of guest physical addresses. This
wraps KVM_CAP_ARM_VM_IPA_SIZE on aarch64 and CPUID to query host
physical address size on x86_64. This replaces the phy_max_address_bits
function from x86_64, and all callers are migrated to use the new Vm
function instead.

BUG=b:210727578
TEST=check on trogdor64 with debug prints

Change-Id: I3107fe6357fcf166b7ad0e2a7c55919705da3b0c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3364971
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Reviewed-by: Andrew Walbran <qwandor@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Daniel Verkamp 2022-01-05 14:09:09 -08:00 committed by Commit Bot
parent f64db6a932
commit 6f4f8223b8
11 changed files with 82 additions and 62 deletions

View file

@ -14,7 +14,7 @@ use devices::{
Bus, BusDeviceObj, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice,
};
use hypervisor::{
DeviceKind, Hypervisor, HypervisorCap, ProtectionType, VcpuAArch64, VcpuFeature, VmAArch64,
DeviceKind, Hypervisor, HypervisorCap, ProtectionType, VcpuAArch64, VcpuFeature, Vm, VmAArch64,
};
use minijail::Minijail;
use remain::sorted;
@ -245,12 +245,8 @@ impl arch::LinuxArch for AArch64 {
Ok(arch_memory_regions(components.memory_size))
}
fn get_phys_max_addr() -> u64 {
u64::max_value()
}
fn create_system_allocator(guest_mem: &GuestMemory) -> SystemAllocator {
Self::get_resource_allocator(guest_mem.memory_size())
fn create_system_allocator<V: Vm>(vm: &V) -> SystemAllocator {
Self::get_resource_allocator(vm.get_memory().memory_size())
}
fn build_vm<V, Vcpu>(
@ -507,8 +503,8 @@ impl arch::LinuxArch for AArch64 {
})
}
fn configure_vcpu(
_guest_mem: &GuestMemory,
fn configure_vcpu<V: Vm>(
_vm: &V,
_hypervisor: &dyn Hypervisor,
_irq_chip: &mut dyn IrqChipAArch64,
_vcpu: &mut dyn VcpuAArch64,

View file

@ -148,14 +148,12 @@ pub trait LinuxArch {
components: &VmComponents,
) -> std::result::Result<Vec<(GuestAddress, u64)>, Self::Error>;
/// Creates a new `SystemAllocator` that fits the given `GuestMemory`'s layout.
/// Creates a new `SystemAllocator` that fits the given `Vm`'s memory layout.
///
/// # Arguments
///
/// * `guest_mem` - The memory to be used as a template for the `SystemAllocator`.
fn create_system_allocator(guest_mem: &GuestMemory) -> SystemAllocator;
fn get_phys_max_addr() -> u64;
/// * `vm` - The virtual machine to be used as a template for the `SystemAllocator`.
fn create_system_allocator<V: Vm>(vm: &V) -> SystemAllocator;
/// Takes `VmComponents` and generates a `RunnableLinuxVm`.
///
@ -197,15 +195,15 @@ pub trait LinuxArch {
///
/// # Arguments
///
/// * `guest_mem` - The memory to be used by the guest.
/// * `vm` - The virtual machine object.
/// * `hypervisor` - The `Hypervisor` that created the vcpu.
/// * `irq_chip` - The `IrqChip` associated with this vm.
/// * `vcpu` - The VCPU object to configure.
/// * `vcpu_id` - The id of the given `vcpu`.
/// * `num_cpus` - Number of virtual CPUs the guest will have.
/// * `has_bios` - Whether the `VmImage` is a `Bios` image
fn configure_vcpu(
guest_mem: &GuestMemory,
fn configure_vcpu<V: Vm>(
vm: &V,
hypervisor: &dyn HypervisorArch,
irq_chip: &mut dyn IrqChipArch,
vcpu: &mut dyn VcpuArch,

View file

@ -40,6 +40,19 @@ impl Kvm {
// Use the lower 8 bits representing the IPA space as the machine type
Ok((ipa_size & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) | protection_flag)
}
/// Get the size of guest physical addresses (IPA) in bits.
pub fn get_guest_phys_addr_size(&self) -> u8 {
// Safe because we know self is a real kvm fd
let vm_ipa_size = match unsafe {
ioctl_with_val(self, KVM_CHECK_EXTENSION(), KVM_CAP_ARM_VM_IPA_SIZE.into())
} {
// Default physical address size is 40 bits if the extension is not supported.
ret if ret < 0 => 40,
ipa => ipa as u8,
};
vm_ipa_size
}
}
impl KvmVm {

View file

@ -453,6 +453,10 @@ impl Vm for KvmVm {
}
}
fn get_guest_phys_addr_size(&self) -> u8 {
self.kvm.get_guest_phys_addr_size()
}
fn get_memory(&self) -> &GuestMemory {
&self.guest_mem
}

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::arch::x86_64::__cpuid;
use base::IoctlNr;
use libc::E2BIG;
@ -74,6 +76,20 @@ impl Kvm {
Err(Error::new(libc::EINVAL))
}
}
/// Get the size of guest physical addresses in bits.
pub fn get_guest_phys_addr_size(&self) -> u8 {
// Get host cpu max physical address bits.
// Assume the guest physical address size is the same as the host.
let highest_ext_function = unsafe { __cpuid(0x80000000) };
if highest_ext_function.eax >= 0x80000008 {
let addr_size = unsafe { __cpuid(0x80000008) };
// Low 8 bits of 0x80000008 leaf: host physical address size in bits.
addr_size.eax as u8
} else {
36
}
}
}
impl HypervisorX86_64 for Kvm {

View file

@ -52,6 +52,9 @@ pub trait Vm: Send {
/// reflects the usable capabilities.
fn check_capability(&self, c: VmCap) -> bool;
/// Get the guest physical address size in bits.
fn get_guest_phys_addr_size(&self) -> u8;
/// Gets the guest-mapped memory for the Vm.
fn get_memory(&self) -> &GuestMemory;

View file

@ -2071,7 +2071,7 @@ where
}
Arch::configure_vcpu(
vm.get_memory(),
&vm,
vm.get_hypervisor(),
irq_chip,
&mut vcpu,
@ -2778,7 +2778,7 @@ where
let exit_evt = Event::new().context("failed to create event")?;
let reset_evt = Event::new().context("failed to create event")?;
let mut sys_allocator = Arch::create_system_allocator(vm.get_memory());
let mut sys_allocator = Arch::create_system_allocator(&vm);
// Allocate the ramoops region first. AArch64::build_vm() assumes this.
let ramoops_region = match &components.pstore {
@ -2789,7 +2789,7 @@ where
None => None,
};
let phys_max_addr = Arch::get_phys_max_addr();
let phys_max_addr = (1u64 << vm.get_guest_phys_addr_size()) - 1;
let mut devices = create_devices(
&cfg,
&mut vm,

View file

@ -214,19 +214,6 @@ pub fn setup_cpuid(
.map_err(Error::SetSupportedCpusFailed)
}
/// get host cpu max physical address bits
pub fn phy_max_address_bits() -> u32 {
let mut phys_bits: u32 = 36;
let highest_ext_function = unsafe { __cpuid(0x80000000) };
if highest_ext_function.eax >= 0x80000008 {
let addr_size = unsafe { __cpuid(0x80000008) };
phys_bits = addr_size.eax & 0xff;
}
phys_bits
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -53,16 +53,14 @@ use std::sync::Arc;
use crate::bootparam::boot_params;
use acpi_tables::sdt::SDT;
use acpi_tables::{aml, aml::Aml};
use arch::{
get_serial_cmdline, GetSerialCmdlineError, LinuxArch, RunnableLinuxVm, VmComponents, VmImage,
};
use arch::{get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, VmComponents, VmImage};
use base::Event;
use devices::serial_device::{SerialHardware, SerialParameters};
use devices::{
BusDeviceObj, BusResumeDevice, IrqChip, IrqChipX86_64, PciAddress, PciConfigIo, PciConfigMmio,
PciDevice,
};
use hypervisor::{HypervisorX86_64, ProtectionType, VcpuX86_64, VmX86_64};
use hypervisor::{HypervisorX86_64, ProtectionType, VcpuX86_64, Vm, VmX86_64};
use minijail::Minijail;
use remain::sorted;
use resources::SystemAllocator;
@ -370,13 +368,10 @@ impl arch::LinuxArch for X8664arch {
Ok(arch_memory_regions(components.memory_size, bios_size))
}
fn get_phys_max_addr() -> u64 {
(1u64 << cpuid::phy_max_address_bits()) - 1
}
fn create_system_allocator(guest_mem: &GuestMemory) -> SystemAllocator {
fn create_system_allocator<V: Vm>(vm: &V) -> SystemAllocator {
let guest_mem = vm.get_memory();
let high_mmio_start = Self::get_high_mmio_base(guest_mem);
let high_mmio_size = Self::get_high_mmio_size(guest_mem);
let high_mmio_size = Self::get_high_mmio_size(vm);
SystemAllocator::builder()
.add_io_addresses(0xc000, 0x1_0000)
.add_low_mmio_addresses(END_ADDR_BEFORE_32BITS, PCI_MMIO_SIZE)
@ -476,6 +471,7 @@ impl arch::LinuxArch for X8664arch {
let max_bus = ((PCIE_CFG_MMIO_SIZE / 0x100000) - 1) as u8;
let (acpi_dev_resource, bat_control) = Self::setup_acpi_devices(
&vm,
&mem,
&io_bus,
system_allocator,
@ -601,8 +597,8 @@ impl arch::LinuxArch for X8664arch {
})
}
fn configure_vcpu(
guest_mem: &GuestMemory,
fn configure_vcpu<V: Vm>(
vm: &V,
hypervisor: &dyn HypervisorX86_64,
irq_chip: &mut dyn IrqChipX86_64,
vcpu: &mut dyn VcpuX86_64,
@ -627,8 +623,9 @@ impl arch::LinuxArch for X8664arch {
return Ok(());
}
let guest_mem = vm.get_memory();
let kernel_load_addr = GuestAddress(KERNEL_START_OFFSET);
regs::setup_msrs(vcpu, END_ADDR_BEFORE_32BITS).map_err(Error::SetupMsrs)?;
regs::setup_msrs(vm, vcpu, END_ADDR_BEFORE_32BITS).map_err(Error::SetupMsrs)?;
let kernel_end = guest_mem
.checked_offset(kernel_load_addr, KERNEL_64BIT_ENTRY_OFFSET)
.ok_or(Error::KernelOffsetPastEnd)?;
@ -1116,11 +1113,11 @@ impl X8664arch {
///
/// # Arguments
///
/// * mem: The memory to be used by the guest
fn get_high_mmio_size(mem: &GuestMemory) -> u64 {
let phys_mem_end = Self::get_phys_max_addr() + 1;
/// * `vm`: The virtual machine
fn get_high_mmio_size<V: Vm>(vm: &V) -> u64 {
let phys_mem_end = 1u64 << vm.get_guest_phys_addr_size();
let high_mmio_end = std::cmp::min(phys_mem_end, HIGH_MMIO_MAX_END);
high_mmio_end - Self::get_high_mmio_base(mem)
high_mmio_end - Self::get_high_mmio_base(vm.get_memory())
}
/// This returns a minimal kernel command for this architecture
@ -1204,7 +1201,8 @@ impl X8664arch {
/// * - `irq_chip` the IrqChip object for registering irq events
/// * - `battery` indicate whether to create the battery
/// * - `mmio_bus` the MMIO bus to add the devices to
fn setup_acpi_devices(
fn setup_acpi_devices<V: VmX86_64>(
vm: &V,
mem: &GuestMemory,
io_bus: &devices::Bus,
resources: &mut SystemAllocator,
@ -1264,7 +1262,7 @@ impl X8664arch {
aml::AddressSpaceCachable::NotCacheable,
true,
Self::get_high_mmio_base(mem),
Self::get_high_mmio_size(mem),
Self::get_high_mmio_size(vm),
),
]),
);

View file

@ -5,7 +5,7 @@
use std::{mem, result};
use base::{self, warn};
use hypervisor::{Fpu, Register, Regs, Sregs, VcpuX86_64};
use hypervisor::{Fpu, Register, Regs, Sregs, VcpuX86_64, Vm};
use remain::sorted;
use thiserror::Error;
use vm_memory::{GuestAddress, GuestMemory};
@ -93,7 +93,12 @@ fn get_mtrr_pairs(base: u64, len: u64) -> Vec<(u64, u64)> {
vecs
}
fn append_mtrr_entries(vpu: &dyn VcpuX86_64, pci_start: u64, entries: &mut Vec<Register>) {
fn append_mtrr_entries(
vm: &dyn Vm,
vpu: &dyn VcpuX86_64,
pci_start: u64,
entries: &mut Vec<Register>,
) {
// Get VAR MTRR num from MSR_MTRRcap
let mut msrs = vec![Register {
id: crate::msr_index::MSR_MTRRcap,
@ -117,7 +122,7 @@ fn append_mtrr_entries(vpu: &dyn VcpuX86_64, pci_start: u64, entries: &mut Vec<R
return;
}
let phys_mask: u64 = (1 << crate::cpuid::phy_max_address_bits()) - 1;
let phys_mask: u64 = (1 << vm.get_guest_phys_addr_size()) - 1;
for (idx, (base, len)) in vecs.iter().enumerate() {
let reg_idx = idx as u32 * 2;
entries.push(Register {
@ -137,7 +142,7 @@ fn append_mtrr_entries(vpu: &dyn VcpuX86_64, pci_start: u64, entries: &mut Vec<R
});
}
fn create_msr_entries(vcpu: &dyn VcpuX86_64, pci_start: u64) -> Vec<Register> {
fn create_msr_entries(vm: &dyn Vm, vcpu: &dyn VcpuX86_64, pci_start: u64) -> Vec<Register> {
let mut entries = vec![
Register {
id: crate::msr_index::MSR_IA32_SYSENTER_CS,
@ -182,7 +187,7 @@ fn create_msr_entries(vcpu: &dyn VcpuX86_64, pci_start: u64) -> Vec<Register> {
value: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64,
},
];
append_mtrr_entries(vcpu, pci_start, &mut entries);
append_mtrr_entries(vm, vcpu, pci_start, &mut entries);
entries
}
@ -191,8 +196,8 @@ fn create_msr_entries(vcpu: &dyn VcpuX86_64, pci_start: u64) -> Vec<Register> {
/// # Arguments
///
/// * `vcpu` - Structure for the vcpu that holds the vcpu fd.
pub fn setup_msrs(vcpu: &dyn VcpuX86_64, pci_start: u64) -> Result<()> {
let msrs = create_msr_entries(vcpu, pci_start);
pub fn setup_msrs(vm: &dyn Vm, vcpu: &dyn VcpuX86_64, pci_start: u64) -> Result<()> {
let msrs = create_msr_entries(vm, vcpu, pci_start);
vcpu.set_msrs(&msrs).map_err(Error::MsrIoctlFailed)
}

View file

@ -101,9 +101,8 @@ where
let arch_mem_regions = arch_memory_regions(memory_size, None);
let guest_mem = GuestMemory::new(&arch_mem_regions).unwrap();
let mut resources = X8664arch::create_system_allocator(&guest_mem);
let (hyp, mut vm) = create_vm(guest_mem.clone());
let mut resources = X8664arch::create_system_allocator(&vm);
let (irqchip_tube, device_tube) = Tube::pair().expect("failed to create irq tube");
let mut irq_chip = create_irq_chip(vm.try_clone().expect("failed to clone vm"), 1, device_tube);
@ -184,6 +183,7 @@ where
let suspend_evt = Event::new().unwrap();
let mut resume_notify_devices = Vec::new();
let acpi_dev_resource = X8664arch::setup_acpi_devices(
&vm,
&guest_mem,
&io_bus,
&mut resources,
@ -246,7 +246,7 @@ where
.expect("failed to add vcpu to irqchip");
setup_cpuid(&hyp, &irq_chip, &vcpu, 0, 1, false, false).unwrap();
setup_msrs(&vcpu, END_ADDR_BEFORE_32BITS).unwrap();
setup_msrs(&vm, &vcpu, END_ADDR_BEFORE_32BITS).unwrap();
setup_regs(
&vcpu,