diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c
index 1036b8f933ec..eb635bf80737 100644
--- a/arch/riscv/errata/thead/errata.c
+++ b/arch/riscv/errata/thead/errata.c
@@ -15,6 +15,7 @@
 #include <asm/errata_list.h>
 #include <asm/hwprobe.h>
 #include <asm/patch.h>
+#include <asm/switch_to.h>
 #include <asm/vendorid_list.h>
 
 static bool errata_probe_pbmt(unsigned int stage,
@@ -125,3 +126,45 @@ void __init_or_module thead_feature_probe_func(unsigned int cpu,
 	if ((archid == 0) && (impid == 0))
 		per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_FAST;
 }
+
+
+char *thead_extension_list_func(unsigned long archid,
+				unsigned long impid)
+{
+	if ((archid == 0) && (impid == 0)) {
+		const char *xbase1 = "xtheadba_xtheadbb_xtheadbs_xtheadcmo_xtheadcondmov";
+		const char *xbase2 = "_xtheadint_xtheadmac_xtheadmemidx_xtheadmempair_xtheadsync";
+		const char *xfpu = "_xtheadfmemIdx";
+#ifdef CONFIG_32BIT
+		const char *xfpu32 = "_xtheadfmv";
+#endif
+		int len = strlen(xbase1) + strlen(xbase2);
+		char *str;
+
+		if (has_fpu()) {
+			len += strlen(xfpu);
+#ifdef CONFIG_32BIT
+			len+= strlen(xfpu32);
+#endif
+		}
+
+		str = kzalloc(len, GFP_KERNEL);
+		if (!str)
+			return str;
+
+		strcpy(str, xbase1);
+
+		if (has_fpu()) {
+			strcat(str, xfpu);
+#ifdef CONFIG_32BIT
+			strcat(str, xfpu32);
+#endif
+		}
+
+		strcat(str, xbase2);
+
+		return str;
+	}
+
+	return NULL;
+}
diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h
index a8f5cf6694a1..8c9aec196649 100644
--- a/arch/riscv/include/asm/alternative.h
+++ b/arch/riscv/include/asm/alternative.h
@@ -31,6 +31,7 @@
 #define ALT_ALT_PTR(a)			__ALT_PTR(a, alt_offset)
 
 void __init probe_vendor_features(unsigned int cpu);
+char *list_vendor_extensions(void);
 void __init apply_boot_alternatives(void);
 void __init apply_early_boot_alternatives(void);
 void apply_module_alternatives(void *start, size_t length);
@@ -55,6 +56,8 @@ void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
 
 void thead_feature_probe_func(unsigned int cpu, unsigned long archid,
 			      unsigned long impid);
+char *thead_extension_list_func(unsigned long archid,
+				unsigned long impid);
 
 void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
 				 unsigned int stage);
@@ -62,6 +65,7 @@ void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
 #else /* CONFIG_RISCV_ALTERNATIVE */
 
 static inline void probe_vendor_features(unsigned int cpu) { }
+static inline char *list_vendor_extensions(void) { return NULL; }
 static inline void apply_boot_alternatives(void) { }
 static inline void apply_early_boot_alternatives(void) { }
 static inline void apply_module_alternatives(void *start, size_t length) { }
diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c
index fc65c9293ac5..18913fd1809f 100644
--- a/arch/riscv/kernel/alternative.c
+++ b/arch/riscv/kernel/alternative.c
@@ -29,6 +29,8 @@ struct cpu_manufacturer_info_t {
 				  unsigned int stage);
 	void (*feature_probe_func)(unsigned int cpu, unsigned long archid,
 				   unsigned long impid);
+	char *(*extension_list_func)(unsigned long archid,
+				    unsigned long impid);
 };
 
 static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info)
@@ -54,6 +56,7 @@ static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_inf
 	case THEAD_VENDOR_ID:
 		cpu_mfr_info->patch_func = thead_errata_patch_func;
 		cpu_mfr_info->feature_probe_func = thead_feature_probe_func;
+		cpu_mfr_info->extension_list_func = thead_extension_list_func;
 		break;
 #endif
 	default:
@@ -157,6 +160,24 @@ void __init_or_module probe_vendor_features(unsigned int cpu)
 					cpu_mfr_info.imp_id);
 }
 
+/*
+ * Lists the vendor-specific extensions common to all cores.
+ * Returns a new underscore "_" concatenated string that the
+ * caller is supposed to free after use.
+ */
+char *list_vendor_extensions(void)
+{
+	struct cpu_manufacturer_info_t cpu_mfr_info;
+
+	riscv_fill_cpu_mfr_info(&cpu_mfr_info);
+	if (!cpu_mfr_info.extension_list_func)
+		return NULL;
+
+	return cpu_mfr_info.extension_list_func(cpu_mfr_info.arch_id,
+						cpu_mfr_info.imp_id);
+
+}
+
 /*
  * This is called very early in the boot process (directly after we run
  * a feature detect on the boot CPU). No need to worry about other CPUs
diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
index 71770563199f..6a0a45b2eb20 100644
--- a/arch/riscv/kernel/cpu.c
+++ b/arch/riscv/kernel/cpu.c
@@ -7,6 +7,7 @@
 #include <linux/init.h>
 #include <linux/seq_file.h>
 #include <linux/of.h>
+#include <asm/alternative.h>
 #include <asm/cpufeature.h>
 #include <asm/csr.h>
 #include <asm/hwcap.h>
@@ -260,6 +261,7 @@ static char *riscv_create_isa_string(void)
 {
 	int maxlen = 4;
 	char *isa_str;
+	char *vendor_isa;
 	int i;
 
 	/* calculate the needed string length */
@@ -268,6 +270,10 @@ static char *riscv_create_isa_string(void)
 			maxlen++;
 	maxlen += strlen_isa_ext();
 
+	vendor_isa = list_vendor_extensions();
+	if (vendor_isa)
+		maxlen += strlen(vendor_isa) + 1;
+
 	isa_str = kzalloc(maxlen, GFP_KERNEL);
 	if (!isa_str)
 		return ERR_PTR(-ENOMEM);
@@ -287,6 +293,12 @@ static char *riscv_create_isa_string(void)
 
 	strcat_isa_ext(isa_str);
 
+	if(vendor_isa) {
+		strcat(isa_str, "_");
+		strcat(isa_str, vendor_isa);
+		kfree(vendor_isa);
+	}
+
 	return isa_str;
 }
 
