Loading arch/arm64/include/asm/cpucaps.h +2 −1 Original line number Diff line number Diff line Loading @@ -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 */ arch/arm64/include/asm/cputype.h +39 −0 Original line number Diff line number Diff line Loading @@ -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 Loading arch/arm64/kernel/cpufeature.c +95 −0 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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 {}, }; Loading arch/arm64/mm/proc.S +6 −12 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
arch/arm64/include/asm/cpucaps.h +2 −1 Original line number Diff line number Diff line Loading @@ -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 */
arch/arm64/include/asm/cputype.h +39 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
arch/arm64/kernel/cpufeature.c +95 −0 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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 {}, }; Loading
arch/arm64/mm/proc.S +6 −12 Original line number Diff line number Diff line Loading @@ -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 Loading