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 <keiichiw@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Keiichi Watanabe 2020-11-10 22:18:27 +09:00 committed by Commit Bot
parent bb318f8bf8
commit f12c5ea7b4
4 changed files with 76 additions and 12 deletions

View file

@ -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<u8>) -> Result<()> {
Ok(())
}
// TODO(sonnyrao) -- check to see if host kernel supports PSCI 0_2
fn create_psci_node(fdt: &mut Vec<u8>) -> Result<()> {
let compatible = "arm,psci-0.2";
fn create_psci_node(fdt: &mut Vec<u8>, 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<u8>) -> 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<File>,
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

View file

@ -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<dyn StdError>),
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(())

View file

@ -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<u64>;
/// Gets the current PSCI version.
fn get_psci_version(&self) -> Result<PsciVersion>;
}
impl_downcast!(VcpuAArch64);

View file

@ -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<u64> {
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<PsciVersion> {
// 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