From: Ma Wupeng mawupeng1@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7ZC0H CVE: NA
--------------------------------
Now PBHA can be enabled via FDT table at early startup.
Here is an example for item in FDT table:
cpus { arm,pbha-performance-only = <0x01000000 0x00000000 0x00000000 0x00000000>; };
Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- arch/arm64/include/asm/cpufeature.h | 14 +++++ arch/arm64/kernel/cpufeature.c | 85 ++++++++++++++++++++++++----- arch/arm64/kernel/head.S | 3 + 3 files changed, 89 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index d6227f23d2ef..711f64fa1aa0 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -768,6 +768,20 @@ static inline bool system_supports_tlb_range(void) cpus_have_const_cap(ARM64_HAS_TLB_RANGE); }
+#ifdef CONFIG_ARM64_PBHA +extern bool pbha_enabled; + +static inline bool system_supports_pbha(void) +{ + return pbha_enabled; +} +#else +static inline bool system_supports_pbha(void) +{ + return false; +} +#endif + extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);
static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 4a0a46981db9..b7d7da84df98 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -72,6 +72,8 @@ #include <linux/mm.h> #include <linux/of.h> #include <linux/cpu.h> +#include <linux/init.h> +#include <linux/libfdt.h>
#include <asm/cpu.h> #include <asm/cpufeature.h> @@ -86,6 +88,7 @@ #include <asm/traps.h> #include <asm/vectors.h> #include <asm/virt.h> +#include <asm/setup.h>
/* Kernel representation of AT_HWCAP and AT_HWCAP2 */ static unsigned long elf_hwcap __read_mostly; @@ -1585,6 +1588,8 @@ static bool has_hw_dbm(const struct arm64_cpu_capabilities *cap, static u8 pbha_stage1_enable_bits; static DEFINE_SPINLOCK(pbha_dt_lock);
+bool pbha_enabled; + /* For the value 5, return a bitmap with bits 5, 4, and 1 set. */ static unsigned long decompose_pbha_values(u8 val) { @@ -1619,11 +1624,26 @@ static void stage2_test_pbha_value(u8 val) arm64_pbha_stage2_safe_bits |= val; }
+void update_pbha_perf_only_bit(const u8 *bits, int cnt) +{ + u8 val; + int i; + + /* any listed value is usable at stage 1 */ + for (i = 0 ; i < cnt; i++) { + val = bits[i]; + if (val > 0xf) + continue; + + pbha_stage1_enable_bits |= val; + set_bit(val, &arm64_pbha_perf_only_values); + } +} + 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; @@ -1640,7 +1660,7 @@ static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap, return true;
spin_lock(&pbha_dt_lock); - if (dt_check_done) + if (pbha_enabled) goto out_unlock;
cpus = of_find_node_by_path("/cpus"); @@ -1652,15 +1672,7 @@ static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap, if (!perf_only_vals) goto done;
- /* any listed value is usable at stage 1 */ - for (i = 0 ; i < num_perf_only_vals; i++) { - val = perf_only_vals[i]; - if (val > 0xf) - continue; - - pbha_stage1_enable_bits |= val; - set_bit(val, &arm64_pbha_perf_only_values); - } + update_pbha_perf_only_bit(perf_only_vals, num_perf_only_vals);
/* * for stage2 the values are collapsed back to 4 bits that can only @@ -1676,14 +1688,14 @@ static bool plat_can_use_pbha_stage1(const struct arm64_cpu_capabilities *cap,
done: of_node_put(cpus); - dt_check_done = true; + pbha_enabled = true;
out_unlock: spin_unlock(&pbha_dt_lock); return !!pbha_stage1_enable_bits; }
-static void cpu_enable_pbha(struct arm64_cpu_capabilities const *cap) +static void enable_pbha_inner(void) { u64 tcr;
@@ -1700,6 +1712,53 @@ static void cpu_enable_pbha(struct arm64_cpu_capabilities const *cap) local_flush_tlb_all(); }
+static void cpu_enable_pbha(struct arm64_cpu_capabilities const *cap) +{ + enable_pbha_inner(); +} + +static inline bool cpu_has_pbha(void) +{ + u64 mmfr1 = read_cpuid(ID_AA64MMFR1_EL1); + int val = cpuid_feature_extract_unsigned_field(mmfr1, + ID_AA64MMFR1_HPD_SHIFT); + + return val == 2; +} + +void __init early_pbha_init(void) +{ + void *fdt; + int node; + const u8 *prop; + int size; + + spin_lock(&pbha_dt_lock); + + fdt = get_early_fdt_ptr(); + if (!fdt) + goto unlock; + + node = fdt_path_offset(fdt, "/cpus"); + if (node < 0) + goto unlock; + + prop = fdt_getprop(fdt, node, "arm,pbha-performance-only", &size); + if (!prop) + goto unlock; + + if (!cpu_has_pbha()) + goto unlock; + + update_pbha_perf_only_bit(prop, size); + enable_pbha_inner(); + + pbha_enabled = true; + +unlock: + spin_unlock(&pbha_dt_lock); +} + /* * PBHA's behaviour is implementation defined, as is the way it combines * stage1 and stage2 attributes. If the kernel has KVM supported, and booted diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index b65f95f25ec8..5bc343fc2a91 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -454,6 +454,9 @@ SYM_FUNC_START_LOCAL(__primary_switched) #endif mov x0, x21 // pass FDT address in x0 bl early_fdt_map // Try mapping the FDT early +#ifdef CONFIG_ARM64_PBHA + bl early_pbha_init // Init PBHA early via FDT +#endif #ifdef CONFIG_RANDOMIZE_BASE tst x23, ~(MIN_KIMG_ALIGN - 1) // already running randomized? b.ne 0f