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

Commit e31113fd authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "arm64: Add work around for Arm Cortex-A55 Erratum 1024718"

parents 3a71af90 a0ce5af2
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -43,7 +43,8 @@
#define ARM64_UNMAP_KERNEL_AT_EL0		23
#define ARM64_HARDEN_BRANCH_PREDICTOR		24
#define ARM64_HARDEN_BP_POST_GUEST_EXIT		25
#define ARM64_HW_DBM				26

#define ARM64_NCAPS				26
#define ARM64_NCAPS				27

#endif /* __ASM_CPUCAPS_H */
+39 −0
Original line number Diff line number Diff line
@@ -119,6 +119,45 @@

#define read_cpuid(reg)			read_sysreg_s(SYS_ ## reg)

/*
 * Represent a range of MIDR values for a given CPU model and a
 * range of variant/revision values.
 *
 * @model	- CPU model as defined by MIDR_CPU_MODEL
 * @rv_min	- Minimum value for the revision/variant as defined by
 *		  MIDR_CPU_VAR_REV
 * @rv_max	- Maximum value for the variant/revision for the range.
 */
struct midr_range {
	u32 model;
	u32 rv_min;
	u32 rv_max;
};

#define GENERIC_MIDR_RANGE(m, v_min, r_min, v_max, r_max)	\
	{							\
		.model = m,					\
		.rv_min = MIDR_CPU_VAR_REV(v_min, r_min),	\
		.rv_max = MIDR_CPU_VAR_REV(v_max, r_max),	\
	}

#define GENERIC_MIDR_ALL_VERSIONS(m) GENERIC_MIDR_RANGE(m, 0, 0, 0xf, 0xf)

static inline bool is_midr_in_range(u32 midr, struct midr_range const *range)
{
	return MIDR_IS_CPU_MODEL_RANGE(midr, range->model,
				 range->rv_min, range->rv_max);
}

static inline bool
is_midr_in_range_list(u32 midr, struct midr_range const *ranges)
{
	while (ranges->model)
		if (is_midr_in_range(midr, ranges++))
			return true;
	return false;
}

/*
 * The CPU ID never changes at run time, so we might as well tell the
 * compiler that it's constant.  Use this function to read the CPU ID
+95 −0
Original line number Diff line number Diff line
@@ -880,6 +880,82 @@ static int __init parse_kpti(char *str)
__setup("kpti=", parse_kpti);
#endif	/* CONFIG_UNMAP_KERNEL_AT_EL0 */

#ifdef CONFIG_ARM64_HW_AFDBM
static inline void __cpu_enable_hw_dbm(void)
{
	u64 tcr = read_sysreg(tcr_el1) | TCR_HD;

	write_sysreg(tcr, tcr_el1);
	isb();
}

static bool cpu_has_broken_dbm(void)
{
	/* List of CPUs which have broken DBM support. */
	static const struct midr_range cpus[] = {
#ifdef CONFIG_ARM64_ERRATUM_1024718
		// A55 r0p0 -r1p0
		GENERIC_MIDR_RANGE(MIDR_CORTEX_A55, 0, 0, 1, 0),
#endif
		{},
	};

	return is_midr_in_range_list(read_cpuid_id(), cpus);
}

static bool cpu_can_use_dbm(const struct arm64_cpu_capabilities *cap)
{
	bool has_cpu_feature;

	preempt_disable();
	has_cpu_feature = has_cpuid_feature(cap, SCOPE_LOCAL_CPU);
	preempt_enable();

	return has_cpu_feature && !cpu_has_broken_dbm();
}

static int cpu_enable_hw_dbm(void *entry)
{
	const struct arm64_cpu_capabilities *cap =
		(const struct arm64_cpu_capabilities *) entry;

	if (cpu_can_use_dbm(cap))
		__cpu_enable_hw_dbm();

	return 0;
}

static bool has_hw_dbm(const struct arm64_cpu_capabilities *cap,
		       int __unused)
{
	static bool detected = false;
	/*
	 * DBM is a non-conflicting feature. i.e, the kernel can safely
	 * run a mix of CPUs with and without the feature. So, we
	 * unconditionally enable the capability to allow any late CPU
	 * to use the feature. We only enable the control bits on the
	 * CPU, if it actually supports.
	 *
	 * We have to make sure we print the "feature" detection only
	 * when at least one CPU actually uses it. So check if this CPU
	 * can actually use it and print the message exactly once.
	 *
	 * This is safe as all CPUs (including secondary CPUs - due to the
	 * LOCAL_CPU scope - and the hotplugged CPUs - via verification)
	 * goes through the "matches" check exactly once. Also if a CPU
	 * matches the criteria, it is guaranteed that the CPU will turn
	 * the DBM on, as the capability is unconditionally enabled.
	 */
	if (!detected && cpu_can_use_dbm(cap)) {
		detected = true;
		pr_info("detected: Hardware dirty bit management\n");
	}

	return true;
}

#endif

static const struct arm64_cpu_capabilities arm64_features[] = {
	{
		.desc = "GIC system register CPU interface",
@@ -992,6 +1068,25 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
		.field_pos = ID_AA64ISAR1_DPB_SHIFT,
		.min_field_value = 1,
	},
#endif
#ifdef CONFIG_ARM64_HW_AFDBM
	{
		/*
		 * Since we turn this on always, we don't want the user to
		 * think that the feature is available when it may not be.
		 * So hide the description.
		 *
		 * .desc = "Hardware pagetable Dirty Bit Management",
		 *
		 */
		.capability = ARM64_HW_DBM,
		.sys_reg = SYS_ID_AA64MMFR1_EL1,
		.sign = FTR_UNSIGNED,
		.field_pos = ID_AA64MMFR1_HADBS_SHIFT,
		.min_field_value = 2,
		.matches = has_hw_dbm,
		.enable = cpu_enable_hw_dbm,
	},
#endif
	{},
};
+6 −12
Original line number Diff line number Diff line
@@ -477,21 +477,15 @@ ENTRY(__cpu_setup)
	bfi	x10, x9, #32, #3
#ifdef CONFIG_ARM64_HW_AFDBM
	/*
	 * Hardware update of the Access and Dirty bits.
	 * Enable hardware update of the Access Flags bit.
	 * Hardware dirty bit management is enabled later,
	 * via capabilities.
	 */
	mrs	x9, ID_AA64MMFR1_EL1
	and	x9, x9, #0xf
	cbz	x9, 2f
	cmp	x9, #2
	b.lt	1f
#ifdef CONFIG_ARM64_ERRATUM_1024718
	/* Disable hardware DBM on Cortex-A55 r0p0, r0p1 & r1p0 */
	cpu_midr_match MIDR_CORTEX_A55, MIDR_CPU_VAR_REV(0, 0), MIDR_CPU_VAR_REV(1, 0), x1, x2, x3, x4
	cbnz	x1, 1f
#endif
	orr	x10, x10, #TCR_HD		// hardware Dirty flag update
1:	orr	x10, x10, #TCR_HA		// hardware Access flag update
2:
	cbz	x9, 1f
	orr	x10, x10, #TCR_HA		// hardware Access flag update
1:
#endif	/* CONFIG_ARM64_HW_AFDBM */
	msr	tcr_el1, x10
	ret					// return to head.S