Some cpufeatures should be handled via the alternatives mechanism
to not incur penalties on unsupporting variants.
So add a mechanism to handle these similar to cpu erratas.
Signed-off-by: Heiko Stuebner <heiko@...>
arch/riscv/include/asm/alternative.h | 3 ++
arch/riscv/include/asm/errata_list.h | 2 ++
arch/riscv/kernel/alternative.c | 2 ++
arch/riscv/kernel/cpufeature.c | 54 +++++++++++++++++++++++++++-
4 files changed, 60 insertions(+), 1 deletion(-)
@@ -39,5 +39,8 @@ struct errata_checkfunc_id {
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
unsigned long archid, unsigned long impid,
unsigned int stage);
+
+void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
+ unsigned int stage);
#endif
#endif
@@ -14,6 +14,8 @@
#define ERRATA_SIFIVE_NUMBER 2
#endif
+#define CPUFEATURE_NUMBER 0
+
#ifdef __ASSEMBLY__
#define ALT_INSN_FAULT(x) \
@@ -63,6 +63,8 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin,
struct alt_entry *end,
unsigned int stage)
{
+ riscv_cpufeature_patch_func(begin, end, stage);
+
if (!vendor_patch_func)
return;
@@ -8,8 +8,12 @@
#include <linux/bitmap.h>
#include <linux/of.h>
-#include <asm/processor.h>
+#include <asm/alternative.h>
+#include <asm/errata_list.h>
#include <asm/hwcap.h>
+#include <asm/patch.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
#include <asm/smp.h>
#include <asm/switch_to.h>
@@ -149,3 +153,51 @@ void __init riscv_fill_hwcap(void)
static_branch_enable(&cpu_hwcap_fpu);
#endif
}
+
+struct cpufeature_info {
+ char name[ERRATA_STRING_LENGTH_MAX];
+ bool (*check_func)(unsigned int stage);
+};
+
+static const struct cpufeature_info cpufeature_list[CPUFEATURE_NUMBER] = {
+};
+
+static u32 __init cpufeature_probe(unsigned int stage)
+{
+ const struct cpufeature_info *info;
+ u32 cpu_req_feature = 0;
+ int idx;
+
+ for (idx = 0; idx < CPUFEATURE_NUMBER; idx++) {
+ info = &cpufeature_list[idx];
+
+ if (info->check_func(stage))
+ cpu_req_feature |= (1U << idx);
+ }
+
+ return cpu_req_feature;
+}
+
+void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
+ unsigned int stage)
+{
+ u32 cpu_req_feature = cpufeature_probe(stage);
+ u32 cpu_apply_feature = 0;
+ struct alt_entry *alt;
+ u32 tmp;
+
+ for (alt = begin; alt < end; alt++) {
+ if (alt->vendor_id != 0)
+ continue;
+ if (alt->errata_id >= CPUFEATURE_NUMBER) {
+ WARN(1, "This feature id:%d is not in kernel cpufeature list", alt->errata_id);
+ continue;
+ }
+
+ tmp = (1U << alt->errata_id);
+ if (cpu_req_feature & tmp) {
+ patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
+ cpu_apply_feature |= tmp;
+ }
+ }
+}