Commit 9b7c3dd5 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch kvm-arm64/idregs-6.12 into kvmarm/fixes



* kvm-arm64/idregs-6.12:
  : .
  : Make some fields of ID_AA64DFR0_EL1 and ID_AA64PFR1_EL1
  : writable from userspace, so that a VMM can influence the
  : set of guest-visible features.
  :
  : - for ID_AA64DFR0_EL1: DoubleLock, WRPs, PMUVer and DebugVer
  :   are writable (courtesy of Shameer Kolothum)
  :
  : - for ID_AA64PFR1_EL1: BT, SSBS, CVS2_frac are writable
  :   (courtesy of Shaoqin Huang)
  : .
  KVM: selftests: aarch64: Add writable test for ID_AA64PFR1_EL1
  KVM: arm64: Allow userspace to change ID_AA64PFR1_EL1
  KVM: arm64: Use kvm_has_feat() to check if FEAT_SSBS is advertised to the guest
  KVM: arm64: Disable fields that KVM doesn't know how to handle in ID_AA64PFR1_EL1
  KVM: arm64: Make the exposed feature bits in AA64DFR0_EL1 writable from userspace

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents a1d402ab dc9b5d7e
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -317,7 +317,7 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
				 * to the guest, and hide SSBS so that the
				 * guest stays protected.
				 */
				if (cpus_have_final_cap(ARM64_SSBS))
				if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, SSBS, IMP))
					break;
				fallthrough;
			case SPECTRE_UNAFFECTED:
@@ -428,7 +428,7 @@ int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
 * Convert the workaround level into an easy-to-compare number, where higher
 * values mean better protection.
 */
static int get_kernel_wa_level(u64 regid)
static int get_kernel_wa_level(struct kvm_vcpu *vcpu, u64 regid)
{
	switch (regid) {
	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
@@ -449,7 +449,7 @@ static int get_kernel_wa_level(u64 regid)
			 * don't have any FW mitigation if SSBS is there at
			 * all times.
			 */
			if (cpus_have_final_cap(ARM64_SSBS))
			if (kvm_has_feat(vcpu->kvm, ID_AA64PFR1_EL1, SSBS, IMP))
				return KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL;
			fallthrough;
		case SPECTRE_UNAFFECTED:
@@ -486,7 +486,7 @@ int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1:
	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2:
	case KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3:
		val = get_kernel_wa_level(reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
		val = get_kernel_wa_level(vcpu, reg->id) & KVM_REG_FEATURE_LEVEL_MASK;
		break;
	case KVM_REG_ARM_STD_BMAP:
		val = READ_ONCE(smccc_feat->std_bmap);
@@ -588,7 +588,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
		if (val & ~KVM_REG_FEATURE_LEVEL_MASK)
			return -EINVAL;

		if (get_kernel_wa_level(reg->id) < val)
		if (get_kernel_wa_level(vcpu, reg->id) < val)
			return -EINVAL;

		return 0;
@@ -624,7 +624,7 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
		 * We can deal with NOT_AVAIL on NOT_REQUIRED, but not the
		 * other way around.
		 */
		if (get_kernel_wa_level(reg->id) < wa_level)
		if (get_kernel_wa_level(vcpu, reg->id) < wa_level)
			return -EINVAL;

		return 0;
+36 −2
Original line number Diff line number Diff line
@@ -1527,6 +1527,14 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
			val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE);

		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_SME);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_RNDR_trap);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_NMI);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTE_frac);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_GCS);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_THE);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTEX);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_DF2);
		val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_PFAR);
		break;
	case SYS_ID_AA64PFR2_EL1:
		/* We only expose FPMR */
@@ -2376,7 +2384,19 @@ static const struct sys_reg_desc sys_reg_descs[] = {
		   ID_AA64PFR0_EL1_RAS |
		   ID_AA64PFR0_EL1_AdvSIMD |
		   ID_AA64PFR0_EL1_FP), },
	ID_SANITISED(ID_AA64PFR1_EL1),
	ID_WRITABLE(ID_AA64PFR1_EL1, ~(ID_AA64PFR1_EL1_PFAR |
				       ID_AA64PFR1_EL1_DF2 |
				       ID_AA64PFR1_EL1_MTEX |
				       ID_AA64PFR1_EL1_THE |
				       ID_AA64PFR1_EL1_GCS |
				       ID_AA64PFR1_EL1_MTE_frac |
				       ID_AA64PFR1_EL1_NMI |
				       ID_AA64PFR1_EL1_RNDR_trap |
				       ID_AA64PFR1_EL1_SME |
				       ID_AA64PFR1_EL1_RES0 |
				       ID_AA64PFR1_EL1_MPAM_frac |
				       ID_AA64PFR1_EL1_RAS_frac |
				       ID_AA64PFR1_EL1_MTE)),
	ID_WRITABLE(ID_AA64PFR2_EL1, ID_AA64PFR2_EL1_FPMR),
	ID_UNALLOCATED(4,3),
	ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0),
@@ -2390,7 +2410,21 @@ static const struct sys_reg_desc sys_reg_descs[] = {
	  .get_user = get_id_reg,
	  .set_user = set_id_aa64dfr0_el1,
	  .reset = read_sanitised_id_aa64dfr0_el1,
	  .val = ID_AA64DFR0_EL1_PMUVer_MASK |
	/*
	 * Prior to FEAT_Debugv8.9, the architecture defines context-aware
	 * breakpoints (CTX_CMPs) as the highest numbered breakpoints (BRPs).
	 * KVM does not trap + emulate the breakpoint registers, and as such
	 * cannot support a layout that misaligns with the underlying hardware.
	 * While it may be possible to describe a subset that aligns with
	 * hardware, just prevent changes to BRPs and CTX_CMPs altogether for
	 * simplicity.
	 *
	 * See DDI0487K.a, section D2.8.3 Breakpoint types and linking
	 * of breakpoints for more details.
	 */
	  .val = ID_AA64DFR0_EL1_DoubleLock_MASK |
		 ID_AA64DFR0_EL1_WRPs_MASK |
		 ID_AA64DFR0_EL1_PMUVer_MASK |
		 ID_AA64DFR0_EL1_DebugVer_MASK, },
	ID_SANITISED(ID_AA64DFR1_EL1),
	ID_UNALLOCATED(5,2),
+13 −3
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ struct test_feature_reg {
	}

static const struct reg_ftr_bits ftr_id_aa64dfr0_el1[] = {
	S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, DoubleLock, 0),
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, WRPs, 0),
	S_REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, PMUVer, 0),
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_EL1, DebugVer, ID_AA64DFR0_EL1_DebugVer_IMP),
	REG_FTR_END,
@@ -134,6 +136,13 @@ static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = {
	REG_FTR_END,
};

static const struct reg_ftr_bits ftr_id_aa64pfr1_el1[] = {
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, CSV2_frac, 0),
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, SSBS, ID_AA64PFR1_EL1_SSBS_NI),
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR1_EL1, BT, 0),
	REG_FTR_END,
};

static const struct reg_ftr_bits ftr_id_aa64mmfr0_el1[] = {
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, ECV, 0),
	REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64MMFR0_EL1, EXS, 0),
@@ -200,6 +209,7 @@ static struct test_feature_reg test_regs[] = {
	TEST_REG(SYS_ID_AA64ISAR1_EL1, ftr_id_aa64isar1_el1),
	TEST_REG(SYS_ID_AA64ISAR2_EL1, ftr_id_aa64isar2_el1),
	TEST_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0_el1),
	TEST_REG(SYS_ID_AA64PFR1_EL1, ftr_id_aa64pfr1_el1),
	TEST_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0_el1),
	TEST_REG(SYS_ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1_el1),
	TEST_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2_el1),
@@ -569,9 +579,9 @@ int main(void)
	test_cnt = ARRAY_SIZE(ftr_id_aa64dfr0_el1) + ARRAY_SIZE(ftr_id_dfr0_el1) +
		   ARRAY_SIZE(ftr_id_aa64isar0_el1) + ARRAY_SIZE(ftr_id_aa64isar1_el1) +
		   ARRAY_SIZE(ftr_id_aa64isar2_el1) + ARRAY_SIZE(ftr_id_aa64pfr0_el1) +
		   ARRAY_SIZE(ftr_id_aa64mmfr0_el1) + ARRAY_SIZE(ftr_id_aa64mmfr1_el1) +
		   ARRAY_SIZE(ftr_id_aa64mmfr2_el1) + ARRAY_SIZE(ftr_id_aa64zfr0_el1) -
		   ARRAY_SIZE(test_regs) + 2;
		   ARRAY_SIZE(ftr_id_aa64pfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr0_el1) +
		   ARRAY_SIZE(ftr_id_aa64mmfr1_el1) + ARRAY_SIZE(ftr_id_aa64mmfr2_el1) +
		   ARRAY_SIZE(ftr_id_aa64zfr0_el1) - ARRAY_SIZE(test_regs) + 2;

	ksft_set_plan(test_cnt);