From f12c5ea7b415eaad5a970b16dd7aee762c74fed6 Mon Sep 17 00:00:00 2001 From: Keiichi Watanabe Date: Tue, 10 Nov 2020 22:18:27 +0900 Subject: [PATCH] aarch64: Check the current PSCI version when creating device tree on ARM Call KVM_REG_ARM_PSCI_VERSION to see the PSCI version and use the value when creating a device-tree node. Also stop setting PSCI constants which are ignored by kernel. BUG=chromium:1141902 TEST=run crosvm on krane and check /proc/device-tree/psci/compatible Change-Id: I4b8ed7620d7f0e2205b84b5f08cfaa5ae8d94339 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2529289 Tested-by: Keiichi Watanabe Tested-by: kokoro Commit-Queue: Keiichi Watanabe Reviewed-by: Daniel Verkamp --- aarch64/src/fdt.rs | 25 +++++++++++++--------- aarch64/src/lib.rs | 11 +++++++++- hypervisor/src/aarch64.rs | 13 ++++++++++++ hypervisor/src/kvm/aarch64.rs | 39 ++++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/aarch64/src/fdt.rs b/aarch64/src/fdt.rs index cda15fa507..9606982eea 100644 --- a/aarch64/src/fdt.rs +++ b/aarch64/src/fdt.rs @@ -12,6 +12,7 @@ use arch::fdt::{ }; use arch::SERIAL_ADDR; use devices::{PciAddress, PciInterruptPin}; +use hypervisor::PsciVersion; use vm_memory::{GuestAddress, GuestMemory}; // This is the start of DRAM in the physical address space. @@ -182,18 +183,20 @@ fn create_serial_nodes(fdt: &mut Vec) -> Result<()> { Ok(()) } -// TODO(sonnyrao) -- check to see if host kernel supports PSCI 0_2 -fn create_psci_node(fdt: &mut Vec) -> Result<()> { - let compatible = "arm,psci-0.2"; +fn create_psci_node(fdt: &mut Vec, version: &PsciVersion) -> Result<()> { + let compatible = if version.major == 1 { + // Put `psci-0.2` as well because PSCI 1.0 is compatible with PSCI 0.2. + format!( + "\"arm,psci-{}.{}\", \"arm,psci-0.2\"", + version.major, version.minor + ) + } else { + format!("arm,psci-{}.{}", version.major, version.minor) + }; begin_node(fdt, "psci")?; - property_string(fdt, "compatible", compatible)?; + property_string(fdt, "compatible", &compatible)?; // Only support aarch64 guest property_string(fdt, "method", "hvc")?; - // These constants are from PSCI - property_u32(fdt, "cpu_suspend", 0xc4000001)?; - property_u32(fdt, "cpu_off", 0x84000002)?; - property_u32(fdt, "cpu_on", 0xc4000003)?; - property_u32(fdt, "migrate", 0xc4000005)?; end_node(fdt)?; Ok(()) @@ -358,6 +361,7 @@ fn create_rtc_node(fdt: &mut Vec) -> Result<()> { /// * `initrd` - An optional tuple of initrd guest physical address and size /// * `android_fstab` - An optional file holding Android fstab entries /// * `is_gicv3` - True if gicv3, false if v2 +/// * `psci_version` - the current PSCI version pub fn create_fdt( fdt_max_size: usize, guest_mem: &GuestMemory, @@ -371,6 +375,7 @@ pub fn create_fdt( android_fstab: Option, is_gicv3: bool, use_pmu: bool, + psci_version: PsciVersion, ) -> Result<()> { let mut fdt = vec![0; fdt_max_size]; start_fdt(&mut fdt, fdt_max_size)?; @@ -393,7 +398,7 @@ pub fn create_fdt( create_pmu_node(&mut fdt, num_cpus)?; } create_serial_nodes(&mut fdt)?; - create_psci_node(&mut fdt)?; + create_psci_node(&mut fdt, &psci_version)?; create_pci_nodes(&mut fdt, pci_irqs, pci_device_base, pci_device_size)?; create_rtc_node(&mut fdt)?; // End giant node diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index b1ec456952..4232e0ee02 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -18,7 +18,9 @@ use base::Event; use devices::{ Bus, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice, PciInterruptPin, }; -use hypervisor::{DeviceKind, Hypervisor, HypervisorCap, VcpuAArch64, VcpuFeature, VmAArch64}; +use hypervisor::{ + DeviceKind, Hypervisor, HypervisorCap, PsciVersion, VcpuAArch64, VcpuFeature, VmAArch64, +}; use minijail::Minijail; use remain::sorted; use resources::SystemAllocator; @@ -124,6 +126,7 @@ pub enum Error { CreateVcpu(base::Error), CreateVm(Box), DowncastVcpu, + GetPsciVersion(base::Error), GetSerialCmdline(GetSerialCmdlineError), InitrdLoadFailure(arch::LoadImageError), KernelLoadFailure(arch::LoadImageError), @@ -157,6 +160,7 @@ impl Display for Error { CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e), CreateVm(e) => write!(f, "failed to create vm: {}", e), DowncastVcpu => write!(f, "vm created wrong kind of vcpu"), + GetPsciVersion(e) => write!(f, "failed to get PSCI version: {}", e), GetSerialCmdline(e) => write!(f, "failed to get serial cmdline: {}", e), InitrdLoadFailure(e) => write!(f, "initrd cound not be loaded: {}", e), KernelLoadFailure(e) => write!(f, "kernel cound not be loaded: {}", e), @@ -318,6 +322,8 @@ impl arch::LinuxArch for AArch64 { let kernel_size = arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value()) .map_err(Error::KernelLoadFailure)?; let kernel_end = get_kernel_addr().offset() + kernel_size as u64; + let psci_version = vcpus[0].get_psci_version().map_err(Error::GetPsciVersion)?; + Self::setup_system_memory( &mem, components.memory_size, @@ -329,6 +335,7 @@ impl arch::LinuxArch for AArch64 { kernel_end, irq_chip.get_vgic_version() == DeviceKind::ArmVgicV3, use_pmu, + psci_version, )?; Ok(RunnableLinuxVm { @@ -377,6 +384,7 @@ impl AArch64 { kernel_end: u64, is_gicv3: bool, use_pmu: bool, + psci_version: PsciVersion, ) -> Result<()> { let initrd = match initrd_file { Some(initrd_file) => { @@ -406,6 +414,7 @@ impl AArch64 { android_fstab, is_gicv3, use_pmu, + psci_version, ) .map_err(Error::CreateFdt)?; Ok(()) diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs index 584e9836eb..da253dc0f6 100644 --- a/hypervisor/src/aarch64.rs +++ b/hypervisor/src/aarch64.rs @@ -7,6 +7,12 @@ use downcast_rs::impl_downcast; use crate::{Hypervisor, IrqRoute, IrqSource, IrqSourceChip, Vcpu, Vm}; +/// Represents a version of Power State Coordination Interface (PSCI). +pub struct PsciVersion { + pub major: u32, + pub minor: u32, +} + /// A wrapper for using a VM on aarch64 and getting/setting its state. pub trait VmAArch64: Vm { /// Gets the `Hypervisor` that created this VM. @@ -30,6 +36,13 @@ pub trait VcpuAArch64: Vcpu { /// Sets the value of a register on this VCPU. `reg_id` is the register ID, as specified in the /// KVM API documentation for KVM_SET_ONE_REG. fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>; + + /// Gets the value of a register on this VCPU. `reg_id` is the register ID, as specified in the + /// KVM API documentation for KVM_GET_ONE_REG. + fn get_one_reg(&self, reg_id: u64) -> Result; + + /// Gets the current PSCI version. + fn get_psci_version(&self) -> Result; } impl_downcast!(VcpuAArch64); diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs index 8efde70ad8..a88bda1a0a 100644 --- a/hypervisor/src/kvm/aarch64.rs +++ b/hypervisor/src/kvm/aarch64.rs @@ -9,7 +9,8 @@ use kvm_sys::*; use super::{KvmVcpu, KvmVm}; use crate::{ - ClockState, DeviceKind, Hypervisor, IrqSourceChip, VcpuAArch64, VcpuFeature, VmAArch64, VmCap, + ClockState, DeviceKind, Hypervisor, IrqSourceChip, PsciVersion, VcpuAArch64, VcpuFeature, + VmAArch64, VmCap, }; impl KvmVm { @@ -156,6 +157,42 @@ impl VcpuAArch64 for KvmVcpu { errno_result() } } + + fn get_one_reg(&self, reg_id: u64) -> Result { + let val: u64 = 0; + let mut onereg = kvm_one_reg { + id: reg_id, + addr: (&val as *const u64) as u64, + }; + + // Safe because we allocated the struct and we know the kernel will read exactly the size of + // the struct. + let ret = unsafe { ioctl_with_ref(self, KVM_GET_ONE_REG(), &mut onereg) }; + if ret == 0 { + Ok(val) + } else { + return errno_result(); + } + } + + fn get_psci_version(&self) -> Result { + // The definition of KVM_REG_ARM_PSCI_VERSION is in arch/arm64/include/uapi/asm/kvm.h. + const KVM_REG_ARM_PSCI_VERSION: u64 = + KVM_REG_ARM64 | (KVM_REG_SIZE_U64 as u64) | (KVM_REG_ARM_FW as u64); + + match self.get_one_reg(KVM_REG_ARM_PSCI_VERSION) { + Ok(v) => { + let major = (v >> PSCI_VERSION_MAJOR_SHIFT) as u32; + let minor = (v as u32) & PSCI_VERSION_MINOR_MASK; + Ok(PsciVersion { major, minor }) + } + Err(_) => { + // When `KVM_REG_ARM_PSCI_VERSION` is not supported, we can return PSCI 0.2, as vCPU + // has been initialized with `KVM_ARM_VCPU_PSCI_0_2` successfully. + Ok(PsciVersion { major: 0, minor: 2 }) + } + } + } } // This function translates an IrqSrouceChip to the kvm u32 equivalent. It has a different