From 6f4f8223b8ac87f8247a859c8f5dd964d21d820a Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Wed, 5 Jan 2022 14:09:09 -0800 Subject: [PATCH] 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 Tested-by: kokoro Reviewed-by: Alexandre Courbot Reviewed-by: Andrew Walbran Commit-Queue: Daniel Verkamp --- aarch64/src/lib.rs | 14 +++++-------- arch/src/lib.rs | 14 ++++++------- hypervisor/src/kvm/aarch64.rs | 13 ++++++++++++ hypervisor/src/kvm/mod.rs | 4 ++++ hypervisor/src/kvm/x86_64.rs | 16 +++++++++++++++ hypervisor/src/lib.rs | 3 +++ src/linux.rs | 6 +++--- x86_64/src/cpuid.rs | 13 ------------ x86_64/src/lib.rs | 36 ++++++++++++++++------------------ x86_64/src/regs.rs | 19 +++++++++++------- x86_64/src/test_integration.rs | 6 +++--- 11 files changed, 82 insertions(+), 62 deletions(-) diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index e5f8f6587c..22f95ee607 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -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(vm: &V) -> SystemAllocator { + Self::get_resource_allocator(vm.get_memory().memory_size()) } fn build_vm( @@ -507,8 +503,8 @@ impl arch::LinuxArch for AArch64 { }) } - fn configure_vcpu( - _guest_mem: &GuestMemory, + fn configure_vcpu( + _vm: &V, _hypervisor: &dyn Hypervisor, _irq_chip: &mut dyn IrqChipAArch64, _vcpu: &mut dyn VcpuAArch64, diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 811c379a98..a4547466ef 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -148,14 +148,12 @@ pub trait LinuxArch { components: &VmComponents, ) -> std::result::Result, 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(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( + vm: &V, hypervisor: &dyn HypervisorArch, irq_chip: &mut dyn IrqChipArch, vcpu: &mut dyn VcpuArch, diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs index 2a090dd4ca..7d552746c9 100644 --- a/hypervisor/src/kvm/aarch64.rs +++ b/hypervisor/src/kvm/aarch64.rs @@ -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 { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index b5b61198ff..9a780c5345 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -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 } diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index fec0f4d9c6..2475b6546b 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -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 { diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 2ca34fbb28..9a4191b0b3 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -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; diff --git a/src/linux.rs b/src/linux.rs index ad03174943..2d8905aa4d 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -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, diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs index 5eabc8f34f..a841377308 100644 --- a/x86_64/src/cpuid.rs +++ b/x86_64/src/cpuid.rs @@ -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::*; diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index f111054596..0564636387 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -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(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( + 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(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( + 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), ), ]), ); diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs index 95282aaaa0..2571092c05 100644 --- a/x86_64/src/regs.rs +++ b/x86_64/src/regs.rs @@ -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) { +fn append_mtrr_entries( + vm: &dyn Vm, + vpu: &dyn VcpuX86_64, + pci_start: u64, + entries: &mut Vec, +) { // 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 Vec { +fn create_msr_entries(vm: &dyn Vm, vcpu: &dyn VcpuX86_64, pci_start: u64) -> Vec { 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 { 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 { /// # 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) } diff --git a/x86_64/src/test_integration.rs b/x86_64/src/test_integration.rs index 758544711a..0b2f55a1f5 100644 --- a/x86_64/src/test_integration.rs +++ b/x86_64/src/test_integration.rs @@ -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,