Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85bd0ba1 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

arm/arm64: KVM: Add PSCI version selection API



Although we've implemented PSCI 0.1, 0.2 and 1.0, we expose either 0.1
or 1.0 to a guest, defaulting to the latest version of the PSCI
implementation that is compatible with the requested version. This is
no different from doing a firmware upgrade on KVM.

But in order to give a chance to hypothetical badly implemented guests
that would have a fit by discovering something other than PSCI 0.2,
let's provide a new API that allows userspace to pick one particular
version of the API.

This is implemented as a new class of "firmware" registers, where
we expose the PSCI version. This allows the PSCI version to be
save/restored as part of a guest migration, and also set to
any supported version if the guest requires it.

Cc: stable@vger.kernel.org #4.16
Reviewed-by: default avatarChristoffer Dall <cdall@kernel.org>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent bf9a4137
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -1960,6 +1960,9 @@ ARM 32-bit VFP control registers have the following id bit patterns:
ARM 64-bit FP registers have the following id bit patterns:
  0x4030 0000 0012 0 <regno:12>

ARM firmware pseudo-registers have the following bit pattern:
  0x4030 0000 0014 <regno:16>


arm64 registers are mapped using the lower 32 bits. The upper 16 of
that is the register group type, or coprocessor number:
@@ -1976,6 +1979,9 @@ arm64 CCSIDR registers are demultiplexed by CSSELR value:
arm64 system registers have the following id bit patterns:
  0x6030 0000 0013 <op0:2> <op1:3> <crn:4> <crm:4> <op2:3>

arm64 firmware pseudo-registers have the following bit pattern:
  0x6030 0000 0014 <regno:16>


MIPS registers are mapped using the lower 32 bits.  The upper 16 of that is
the register group type:
@@ -2510,7 +2516,8 @@ Possible features:
	  and execute guest code when KVM_RUN is called.
	- KVM_ARM_VCPU_EL1_32BIT: Starts the CPU in a 32bit mode.
	  Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only).
	- KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 for the CPU.
	- KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 (or a future revision
          backward compatible with v0.2) for the CPU.
	  Depends on KVM_CAP_ARM_PSCI_0_2.
	- KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU.
	  Depends on KVM_CAP_ARM_PMU_V3.
+30 −0
Original line number Diff line number Diff line
KVM implements the PSCI (Power State Coordination Interface)
specification in order to provide services such as CPU on/off, reset
and power-off to the guest.

The PSCI specification is regularly updated to provide new features,
and KVM implements these updates if they make sense from a virtualization
point of view.

This means that a guest booted on two different versions of KVM can
observe two different "firmware" revisions. This could cause issues if
a given guest is tied to a particular PSCI revision (unlikely), or if
a migration causes a different PSCI version to be exposed out of the
blue to an unsuspecting guest.

In order to remedy this situation, KVM exposes a set of "firmware
pseudo-registers" that can be manipulated using the GET/SET_ONE_REG
interface. These registers can be saved/restored by userspace, and set
to a convenient value if required.

The following register is defined:

* KVM_REG_ARM_PSCI_VERSION:

  - Only valid if the vcpu has the KVM_ARM_VCPU_PSCI_0_2 feature set
    (and thus has already been initialized)
  - Returns the current PSCI version on GET_ONE_REG (defaulting to the
    highest PSCI version implemented by KVM and compatible with v0.2)
  - Allows any PSCI version implemented by KVM and compatible with
    v0.2 to be set with SET_ONE_REG
  - Affects the whole VM (even if the register view is per-vcpu)
+3 −0
Original line number Diff line number Diff line
@@ -77,6 +77,9 @@ struct kvm_arch {
	/* Interrupt controller */
	struct vgic_dist	vgic;
	int max_vcpus;

	/* Mandated version of PSCI */
	u32 psci_version;
};

#define KVM_NR_MEM_OBJS     40
+6 −0
Original line number Diff line number Diff line
@@ -195,6 +195,12 @@ struct kvm_arch_memory_slot {
#define KVM_REG_ARM_VFP_FPINST		0x1009
#define KVM_REG_ARM_VFP_FPINST2		0x100A

/* KVM-as-firmware specific pseudo-registers */
#define KVM_REG_ARM_FW			(0x0014 << KVM_REG_ARM_COPROC_SHIFT)
#define KVM_REG_ARM_FW_REG(r)		(KVM_REG_ARM | KVM_REG_SIZE_U64 | \
					 KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION	KVM_REG_ARM_FW_REG(0)

/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR	0
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
+13 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <kvm/arm_psci.h>
#include <asm/cputype.h>
#include <linux/uaccess.h>
#include <asm/kvm.h>
@@ -176,6 +177,7 @@ static unsigned long num_core_regs(void)
unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
{
	return num_core_regs() + kvm_arm_num_coproc_regs(vcpu)
		+ kvm_arm_get_fw_num_regs(vcpu)
		+ NUM_TIMER_REGS;
}

@@ -196,6 +198,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
		uindices++;
	}

	ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices);
	if (ret)
		return ret;
	uindices += kvm_arm_get_fw_num_regs(vcpu);

	ret = copy_timer_indices(vcpu, uindices);
	if (ret)
		return ret;
@@ -214,6 +221,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
		return get_core_reg(vcpu, reg);

	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
		return kvm_arm_get_fw_reg(vcpu, reg);

	if (is_timer_reg(reg->id))
		return get_timer_reg(vcpu, reg);

@@ -230,6 +240,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
		return set_core_reg(vcpu, reg);

	if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
		return kvm_arm_set_fw_reg(vcpu, reg);

	if (is_timer_reg(reg->id))
		return set_timer_reg(vcpu, reg);

Loading