Blob Blame History Raw
From: Nikolay Borisov <bp@alien8.de>
Date: Tue, 22 Aug 2023 13:53:41 +0200
Subject: kabi: Allow extra bugsints
Patch-mainline: Never, kabi fix
References: bsc#1213927

Allow adding more bugints by stashing them in an extra array added at the
end of cpuid_x86 thus ont breaking kABI. All extra bugints begin at
cpuid_x86::x86_ext_capability and also at the end ot cpu_caps_set array.


Signed-off-by: Nikolay Borisov  <nik.borisov@suse.com>
---
 arch/x86/include/asm/cpufeature.h  |   89 +++++++++++++++++++++++++++++++------
 arch/x86/include/asm/cpufeatures.h |    3 -
 arch/x86/include/asm/processor.h   |    3 -
 arch/x86/kernel/alternative.c      |    5 +-
 arch/x86/kernel/cpu/common.c       |    7 ++
 arch/x86/kernel/cpu/mkcapflags.sh  |    4 -
 arch/x86/kernel/cpu/proc.c         |    4 -
 7 files changed, 92 insertions(+), 23 deletions(-)

--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -33,10 +33,19 @@ enum cpuid_leafs
 	CPUID_7_EDX,
 	CPUID_8000_001F_EAX,
 	CPUID_8000_0021_EAX,
+	CPUID_MAX = CPUID_8000_0021_EAX,
+	/*
+	 * Everything below should go into the extended caps array to preserve
+	 * kABI
+	 */
 	CPUID_LNX_5,
 	CPUID_LNX_6,
 };
 
+#define CPUID_IDX(x) \
+	__builtin_choose_expr((x) > CPUID_MAX, (x) - CPUID_MAX - 1, (x))
+#define IS_EXT_CPUID_BIT(bit) ((bit>>5) >= NCAPINTS)
+
 #ifdef CONFIG_X86_FEATURE_NAMES
 extern const char * const x86_cap_flags[NCAPINTS*32];
 extern const char * const x86_power_flags[32];
@@ -51,10 +60,20 @@ extern const char * const x86_power_flag
  * In order to save room, we index into this array by doing
  * X86_BUG_<name> - NCAPINTS*32.
  */
-extern const char * const x86_bug_flags[NBUGINTS*32];
+extern const char * const x86_bug_flags[(NBUGINTS+NEXTBUGINTS)*32];
+
+/*
+ * This macro must be called with bit belonging to one of the extended bug bits,
+ * this required because the first bug word (19) aliases with the first extended
+ * cap word (19) as such the check would be ambiguous if this macro is called
+ * with a bit which represents an extended cpuid.
+ */
+#define IS_EXT_BUG_BIT(bit) ((bit>>5) >= (NCAPINTS+NBUGINTS))
 
 #define test_cpu_cap(c, bit)						\
-	 test_bit(bit, (unsigned long *)((c)->x86_capability))
+	 (IS_EXT_CPUID_BIT(bit) ? test_bit((bit) - (NCAPINTS*32), \
+				(unsigned long *)((c)->x86_ext_capability)) : \
+				test_bit(bit, (unsigned long *)((c)->x86_capability)))
 
 /*
  * There are 32 bits/features in each mask word.  The high bits
@@ -145,17 +164,47 @@ extern const char * const x86_bug_flags[
 
 #define boot_cpu_has(bit)	cpu_has(&boot_cpu_data, bit)
 
-#define set_cpu_cap(c, bit)	set_bit(bit, (unsigned long *)((c)->x86_capability))
+#define set_cpu_cap(c, bit) do { \
+	if (IS_EXT_CPUID_BIT(bit)) {					      \
+		set_bit(bit - (NCAPINTS*32),                                  \
+			(unsigned long *)(c)->x86_ext_capability);	      \
+	} else {							      \
+		set_bit(bit, (unsigned long *)((c)->x86_capability));	      \
+	}								      \
+} while (0)
+
 
 extern void setup_clear_cpu_cap(unsigned int bit);
 extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit);
 
 #define setup_force_cpu_cap(bit) do { \
-	set_cpu_cap(&boot_cpu_data, bit);	\
-	set_bit(bit, (unsigned long *)cpu_caps_set);	\
+	if (IS_EXT_CPUID_BIT(bit)) {					      \
+		set_bit(bit - (NCAPINTS*32),						      \
+			(unsigned long *)&cpu_caps_set[NCAPINTS + NBUGINTS]); \
+		set_bit(bit - (NCAPINTS*32),                                 \
+			(unsigned long *)(&boot_cpu_data)->x86_ext_capability); \
+	} else {							      \
+		set_cpu_cap(&boot_cpu_data, bit);			      \
+		set_bit(bit, (unsigned long *)cpu_caps_set);		      \
+	}								      \
 } while (0)
 
-#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
+
+/*
+ * This has to be re-implemented because of the aliasing issues between
+ * extended capability bits and bug bits, see comment above IS_EXT_BUG_BIT.
+ */
+#define setup_force_cpu_bug(bit) do { \
+	if (IS_EXT_BUG_BIT(bit)) {					      \
+		set_bit(bit - ((NCAPINTS+NBUGINTS)*32),		      \
+			(unsigned long *)&cpu_caps_set[NCAPINTS + NBUGINTS + NEXTCAPINTS]); \
+		set_bit(bit - ((NCAPINTS+NBUGINTS)*32),                                 \
+			(unsigned long *)&(&boot_cpu_data)->x86_ext_capability[NEXTCAPINTS]); \
+	} else {							      \
+		set_cpu_cap(&boot_cpu_data, bit);			      \
+		set_bit(bit, (unsigned long *)cpu_caps_set);		      \
+	}								      \
+} while (0)
 
 #if defined(__clang__) && !defined(CONFIG_CC_HAS_ASM_GOTO)
 
@@ -200,22 +249,34 @@ t_no:
 }
 
 #define static_cpu_has(bit)					\
-(								\
-	__builtin_constant_p(boot_cpu_has(bit)) ?		\
+({								\
+	BUILD_BUG_ON_MSG((bit >> 5) >= NCAPINTS+NBUGINTS, "extended bits/bugs not supported"); \
+	(__builtin_constant_p(boot_cpu_has(bit)) ?		\
 		boot_cpu_has(bit) :				\
-		_static_cpu_has(bit)				\
-)
+		_static_cpu_has(bit));				\
+})
 #endif
 
-#define cpu_has_bug(c, bit)		cpu_has(c, (bit))
-#define set_cpu_bug(c, bit)		set_cpu_cap(c, (bit))
+#define cpu_has_bug(c, bit) (IS_EXT_BUG_BIT((bit)) ? test_bit((bit) - ((NCAPINTS+NBUGINTS)*32), \
+				(unsigned long *)(&((c)->x86_ext_capability[NEXTCAPINTS]))) : \
+				test_bit(bit, (unsigned long *)((c)->x86_capability)))
+
+#define set_cpu_bug(c, bit) do {						\
+	if (IS_EXT_BUG_BIT(bit)) {						\
+		set_bit(bit - ((NCAPINTS+NBUGINTS)*32),                         \
+			(unsigned long *)(&((c)->x86_ext_capability[NEXTCAPINTS]))); \
+	} else {								\
+		set_bit(bit, (unsigned long *)((c)->x86_capability));		\
+	}									\
+} while (0)
+
 #define clear_cpu_bug(c, bit)		clear_cpu_cap(c, (bit))
 
 #define static_cpu_has_bug(bit)		static_cpu_has((bit))
 #define boot_cpu_has_bug(bit)		cpu_has_bug(&boot_cpu_data, (bit))
-#define boot_cpu_set_bug(bit)		set_cpu_cap(&boot_cpu_data, (bit))
+#define boot_cpu_set_bug(bit)		set_cpu_bug(&boot_cpu_data, (bit))
 
-#define MAX_CPU_FEATURES		(NCAPINTS * 32)
+#define MAX_CPU_FEATURES		((NCAPINTS + NEXTCAPINTS)* 32)
 #define cpu_have_feature		boot_cpu_has
 
 #define CPU_FEATURE_TYPEFMT		"x86,ven%04Xfam%04Xmod%04X"
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -15,7 +15,8 @@
  */
 #define NCAPINTS			22	   /* N 32-bit words worth of info */
 #define NBUGINTS			1	   /* N 32-bit bug flags */
-
+#define NEXTCAPINTS			1          /* N 32-bit words which come after NCAPINTS */
+#define NEXTBUGINTS			1	   /* N 32-bit extended bug flags */
 /*
  * Note: If the comment begins with a quoted string, that string is used
  * in /proc/cpuinfo instead of the macro name.  If the string is "",
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -143,6 +143,7 @@ struct cpuinfo_x86 {
 #ifndef __GENKSYMS__
 	/* protected processor identification number */
 	u64			ppin;
+	__u32		x86_ext_capability[NEXTCAPINTS+NEXTBUGINTS];
 #endif
 } __randomize_layout;
 
@@ -177,7 +178,7 @@ extern struct cpuinfo_x86	boot_cpu_data;
 extern struct cpuinfo_x86	new_cpu_data;
 
 extern __u32			cpu_caps_cleared[NCAPINTS + NBUGINTS];
-extern __u32			cpu_caps_set[NCAPINTS + NBUGINTS];
+extern __u32			cpu_caps_set[NCAPINTS + NBUGINTS + NEXTCAPINTS + NEXTBUGINTS];
 
 #ifdef CONFIG_SMP
 DECLARE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info);
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -388,7 +388,7 @@ void __init_or_module noinline apply_alt
 		instr = (u8 *)&a->instr_offset + a->instr_offset;
 		replacement = (u8 *)&a->repl_offset + a->repl_offset;
 		BUG_ON(a->instrlen > sizeof(insn_buff));
-		BUG_ON(feature >= (NCAPINTS + NBUGINTS) * 32);
+		BUG_ON(feature >= (NCAPINTS + NBUGINTS + NEXTCAPINTS + NEXTBUGINTS) * 32);
 
 		/*
 		 * Patch if either:
@@ -396,7 +396,8 @@ void __init_or_module noinline apply_alt
 		 * - feature not present but ALTINSTR_FLAG_INV is set to mean,
 		 *   patch if feature is *NOT* present.
 		 */
-		if (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV)) {
+		if ((a->cpuid < NCAPINTS && (!boot_cpu_has(feature) == !(a->cpuid & ALTINSTR_FLAG_INV)))
+		    || (a->cpuid > NCAPINTS && (!boot_cpu_has_bug(feature) == !(a->cpuid & ALTINSTR_FLAG_INV)))) {
 			optimize_nops_inplace(instr, a->instrlen);
 			continue;
 		}
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -678,7 +678,7 @@ static const char *table_lookup_model(st
 
 /* Aligned to unsigned long to avoid split lock in atomic bitmap ops */
 __u32 cpu_caps_cleared[NCAPINTS + NBUGINTS] __aligned(sizeof(unsigned long));
-__u32 cpu_caps_set[NCAPINTS + NBUGINTS] __aligned(sizeof(unsigned long));
+__u32 cpu_caps_set[NCAPINTS + NBUGINTS + NEXTCAPINTS + NEXTBUGINTS] __aligned(sizeof(unsigned long));
 
 void load_percpu_segment(int cpu)
 {
@@ -940,6 +940,11 @@ static void apply_forced_caps(struct cpu
 		c->x86_capability[i] &= ~cpu_caps_cleared[i];
 		c->x86_capability[i] |= cpu_caps_set[i];
 	}
+
+	for (i = 0; i < NEXTCAPINTS + NEXTBUGINTS; i++) {
+		c->x86_ext_capability[i] |= cpu_caps_set[NCAPINTS+NBUGINTS + i];
+	}
+
 }
 
 static void init_speculation_control(struct cpuinfo_x86 *c)
--- a/arch/x86/kernel/cpu/mkcapflags.sh
+++ b/arch/x86/kernel/cpu/mkcapflags.sh
@@ -57,10 +57,10 @@ trap 'rm "$OUT"' EXIT
 	echo "#endif"
 	echo ""
 
-	dump_array "x86_cap_flags" "NCAPINTS*32" "X86_FEATURE_" "" $2
+	dump_array "x86_cap_flags" "(NCAPINTS+NEXTCAPINTS)*32" "X86_FEATURE_" "" $2
 	echo ""
 
-	dump_array "x86_bug_flags" "NBUGINTS*32" "X86_BUG_" "NCAPINTS*32" $2
+	dump_array "x86_bug_flags" "(NBUGINTS+NEXTBUGINTS)*32" "X86_BUG_" "NCAPINTS*32" $2
 	echo ""
 
 	echo "#ifdef CONFIG_X86_VMX_FEATURE_NAMES"
--- a/arch/x86/kernel/cpu/proc.c
+++ b/arch/x86/kernel/cpu/proc.c
@@ -102,7 +102,7 @@ static int show_cpuinfo(struct seq_file
 	show_cpuinfo_misc(m, c);
 
 	seq_puts(m, "flags\t\t:");
-	for (i = 0; i < 32*NCAPINTS; i++)
+	for (i = 0; i < 32*(NCAPINTS+NEXTCAPINTS); i++)
 		if (cpu_has(c, i) && x86_cap_flags[i] != NULL)
 			seq_printf(m, " %s", x86_cap_flags[i]);
 
@@ -118,7 +118,7 @@ static int show_cpuinfo(struct seq_file
 #endif
 
 	seq_puts(m, "\nbugs\t\t:");
-	for (i = 0; i < 32*NBUGINTS; i++) {
+	for (i = 0; i < 32*(NBUGINTS+NEXTBUGINTS); i++) {
 		unsigned int bug_bit = 32*NCAPINTS + i;
 
 		if (cpu_has_bug(c, bug_bit) && x86_bug_flags[i])