Commit e0b88346 authored by James Morse's avatar James Morse Committed by Wupeng Ma
Browse files

KVM: arm64: Configure PBHA bits for stage2

maillist inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7ZC0H

Reference: https://lore.kernel.org/all/20211015161416.2196-1-james.morse@arm.com/t/#u



--------------------------------

There are two conflicting use-cases for PBHA at stage2. We could copy
the stage1 PBHA bits to stage2, this would ensure the VMMs memory is
exactly reproduced for the guest, including the PBHA bits. The problem
here is how the VMM's memory is allocated with the PBHA bits set.

The other is allowing the guest to configure PBHA directly. This would
allow guest device drivers to map memory with the appropriate PBHA bits.
This would only be safe if the guest can be trusted to only generate
PBHA values that only affect performance.

The arm-arm doesn't describe how the stage1 and stage2 bits are combined.
Arm's implementations appear to all have the same behaviour, according
to the TRM: stage2 wins.

For these CPUs, we can allow a guest to use a PBHA bit by disabling it
in VTCR_EL2. We just need to know which bits...

The DT describes the values that only affect performance, but if value-5
is safe for use, we can't prevent the guest from using value-1 and value-4.
These 'decomposed' values would also need to be listed as only affecting
performance.

Add a cpufeature for CPUs that have this 'stage2 wins' behaviour.
Decompose each performance-only value (5 -> 5, 4, 1), and check each of
these values is listed as only affecting performance. If so, the bits
of the original value (5) can be used by the guest at stage1. (by clearing
the bits from VTCR_EL2)

Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarMa Wupeng <mawupeng1@huawei.com>
parent da52ab48
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@
#define ARM64_HAS_LDAPR                         70
#define ARM64_HAS_PBHA				71
#define ARM64_HAS_PBHA_STAGE1			72
#define ARM64_HAS_PBHA_STAGE2			73

#define ARM64_NCAPS				80

+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ struct arm64_ftr_reg {
};

extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0;
extern unsigned long arm64_pbha_stage2_safe_bits;

int arm64_cpu_ftr_regs_traverse(int (*op)(u32, u64, void *), void *argp);

+105 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@
#include <asm/cpu.h>
#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/cputype.h>
#include <asm/fpsimd.h>
#include <asm/hwcap.h>
#include <asm/mmu_context.h>
@@ -113,6 +114,7 @@ DEFINE_PER_CPU_READ_MOSTLY(const char *, this_cpu_vector) = vectors;

unsigned long __ro_after_init arm64_pbha_perf_only_values;
EXPORT_SYMBOL(arm64_pbha_perf_only_values);
unsigned long __ro_after_init arm64_pbha_stage2_safe_bits;

/*
 * Flag to indicate if we have computed the system wide
@@ -1578,13 +1580,50 @@ static bool has_hw_dbm(const struct arm64_cpu_capabilities *cap,

#endif


#ifdef CONFIG_ARM64_PBHA
static u8 pbha_stage1_enable_bits;
static DEFINE_SPINLOCK(pbha_dt_lock);

/* For the value 5, return a bitmap with bits 5, 4, and 1 set. */
static unsigned long decompose_pbha_values(u8 val)
{
	int i;
	unsigned long mask = 0;

	for (i = 1; i <= 15; i++) {
		if ((i & val) == i)
			set_bit(i, &mask);
	}

	return mask;
}

/*
 * The bits of a value are safe if all values that can be built from those
 * enabled bits are listed as only affecting performance.
 * e.g. 5 would also need 1 and 4 to be listed.
 *
 * When there is a conflict with the bits already enabled, the new value is
 * skipped.
 * e.g. if 5 already caused bit-0 and bit-2 to be enabled, adding 3 to the list
 * would need to test 7 as bit-2 is already enabled. If 7 is not listed, 3 is
 * skipped and bit-1 is not enabled.
 */
static void stage2_test_pbha_value(u8 val)
{
	unsigned long mask;

	mask = decompose_pbha_values(val | arm64_pbha_stage2_safe_bits);
	if ((arm64_pbha_perf_only_values & mask) == mask)
		arm64_pbha_stage2_safe_bits |= val;
}

static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap,
				     int scope)
{
	u8 val;
	static bool dt_check_done;
	struct device_node *cpus;
	const u8 *perf_only_vals;
	int num_perf_only_vals, i;
@@ -1600,6 +1639,10 @@ static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap,
	if (scope == SCOPE_LOCAL_CPU)
		return true;

	spin_lock(&pbha_dt_lock);
	if (dt_check_done)
		goto out_unlock;

	cpus = of_find_node_by_path("/cpus");
	if (!cpus)
		goto done;
@@ -1619,9 +1662,24 @@ static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap,
		set_bit(val, &arm64_pbha_perf_only_values);
	}

	/*
	 * for stage2 the values are collapsed back to 4 bits that can only
	 * enable values in the arm64_pbha_perf_only_values mask.
	 */
	for (i = 0 ; i < num_perf_only_vals; i++) {
		val = perf_only_vals[i];
		if (val > 0xf)
			continue;

		stage2_test_pbha_value(val);
	}

done:
	of_node_put(cpus);
	dt_check_done = true;

out_unlock:
	spin_unlock(&pbha_dt_lock);
	return !!pbha_stage1_enable_bits;
}

@@ -1641,6 +1699,47 @@ static void cpu_enable_pbha(struct arm64_cpu_capabilities const *cap)
	isb();
	local_flush_tlb_all();
}

/*
 * PBHA's behaviour is implementation defined, as is the way it combines
 * stage1 and stage2 attributes. If the kernel has KVM supported, and booted
 * at EL2, only these CPUs can allow PBHA in a guest, as KVM knows how the PBHA
 * bits are combined. This prevents the host being affected by some
 * implementation defined behaviour from the guest.
 *
 * The TRM for these CPUs describe stage2 as overriding stage1.
 */
static const struct midr_range pbha_stage2_wins[] = {
	MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
	MIDR_ALL_VERSIONS(MIDR_CORTEX_A76),
	MIDR_ALL_VERSIONS(MIDR_CORTEX_A77),
	MIDR_ALL_VERSIONS(MIDR_CORTEX_A78),
	{},
};

static bool plat_can_use_pbha_stage2(const struct arm64_cpu_capabilities *cap,
				     int scope)
{
	/*  Booted at EL2? */
	if (!is_hyp_mode_available() && !is_kernel_in_hyp_mode())
		return false;

	if (!is_midr_in_range_list(read_cpuid_id(), cap->midr_range_list))
		return false;

	/*
	 * Calls with scope == SCOPE_LOCAL_CPU need only testing whether this
	 * cpu has the feature. A later 'system' scope call will check for a
	 * firmware description.
	 */
	if (scope == SCOPE_LOCAL_CPU)
		return true;

	if (!__system_matches_cap(ARM64_HAS_PBHA_STAGE1))
		return false;

	return !!arm64_pbha_stage2_safe_bits;
}
#endif /* CONFIG_ARM64_PBHA */

#ifdef CONFIG_ARM64_AMU_EXTN
@@ -2385,6 +2484,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
		.min_field_value = 2,
		.cpu_enable = cpu_enable_pbha,
	},
	{
		.capability = ARM64_HAS_PBHA_STAGE2,
		.type = ARM64_CPUCAP_SYSTEM_FEATURE,
		.matches = plat_can_use_pbha_stage2,
		.midr_range_list = pbha_stage2_wins,
	},
#endif
	{},
};
+3 −0
Original line number Diff line number Diff line
@@ -95,6 +95,9 @@ KVM_NVHE_ALIAS(gic_nonsecure_priorities);
KVM_NVHE_ALIAS(__start___kvm_ex_table);
KVM_NVHE_ALIAS(__stop___kvm_ex_table);

/* PBHA bits for stage2 */
KVM_NVHE_ALIAS(arm64_pbha_stage2_safe_bits);

#endif /* CONFIG_KVM */

#endif /* __ARM64_KERNEL_IMAGE_VARS_H */
+6 −2
Original line number Diff line number Diff line
@@ -431,7 +431,7 @@ int kvm_arm_setup_stage2(struct kvm *kvm, unsigned long type)
{
	u64 vtcr = VTCR_EL2_FLAGS, mmfr0;
	u32 parange, phys_shift;
	u8 lvls;
	u8 lvls, pbha = 0xf;

	if (type & ~KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
		return -EINVAL;
@@ -479,9 +479,13 @@ int kvm_arm_setup_stage2(struct kvm *kvm, unsigned long type)
	 * value will always be 0, which is defined as the safe default
	 * setting. On Cortex cores, enabling PBHA for stage2 effectively
	 * disables it for stage1.
	 * When the HAS_PBHA_STAGE2 feature is supported, clear the 'safe'
	 * bits to allow the guest's stage1 to use these bits.
	 */
	if (cpus_have_final_cap(ARM64_HAS_PBHA_STAGE2))
		pbha = pbha ^ arm64_pbha_stage2_safe_bits;
	if (cpus_have_final_cap(ARM64_HAS_PBHA))
		vtcr |= FIELD_PREP(VTCR_EL2_PBHA_MASK, 0xf);
		vtcr |= FIELD_PREP(VTCR_EL2_PBHA_MASK, pbha);

	/* Set the vmid bits */
	vtcr |= (kvm_get_vmid_bits() == 16) ?