diff mbox series

[1/3] x86/cpuid: Disentangle logic for new feature leaves

Message ID 20220127160940.19469-2-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86/cpuid: Disntangle, and PPIN | expand

Commit Message

Andrew Cooper Jan. 27, 2022, 4:09 p.m. UTC
Adding a new feature leaf is a reasonable amount of boilerplate and for the
patch to build, at least one feature from the new leaf needs defining.  This
typically causes two non-trivial changes to be merged together.

First, have gen-cpuid.py write out some extra placeholder defines:

  #define CPUID_BITFIELD_11 bool :1, :1, lfence_dispatch:1, ...
  #define CPUID_BITFIELD_12 uint32_t :32 /* placeholder */
  #define CPUID_BITFIELD_13 uint32_t :32 /* placeholder */
  #define CPUID_BITFIELD_14 uint32_t :32 /* placeholder */
  #define CPUID_BITFIELD_15 uint32_t :32 /* placeholder */

This allows DECL_BITFIELD() to be added to struct cpuid_policy without
requiring a XEN_CPUFEATURE() declared for the leaf.  The choice of 4 is
arbitrary, and allows us to add more than one leaf at a time if necessary.

Second, rework generic_identify() to not use feature leaf names.

The choice of deriving the index from a feature was to avoid mismatches, but
its correctness depends on bugs like c/s 249e0f1d8f20 ("x86/cpuid: Fix
TSXLDTRK definition") not happening.

Switch to using FEATURESET_* just like the policy/featureset helpers.  This
breaks the cognitive complexity of needing to know which leaf a specifically
named feature should reside in, and is shorter to write.  It is also far
easier to identify as correct at a glance, given the correlation with the
CPUID leaf being read.

In addition, tidy up some other bits of generic_identify()
 * Drop leading zeros from leaf numbers.
 * Don't use a locked update for X86_FEATURE_APERFMPERF.
 * Rework extended_cpuid_level calculation to avoid setting it twice.
 * Use "leaf >= $N" consistently so $N matches with the CPUID input.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Wei Liu <wl@xen.org>
---
 xen/arch/x86/cpu/common.c | 54 +++++++++++++++++++++++------------------------
 xen/tools/gen-cpuid.py    |  2 ++
 2 files changed, 29 insertions(+), 27 deletions(-)

Comments

Andrew Cooper Jan. 27, 2022, 4:20 p.m. UTC | #1
On 27/01/2022 16:09, Andrew Cooper wrote:
> Adding a new feature leaf is a reasonable amount of boilerplate and for the
> patch to build, at least one feature from the new leaf needs defining.  This
> typically causes two non-trivial changes to be merged together.
>
> First, have gen-cpuid.py write out some extra placeholder defines:
>
>   #define CPUID_BITFIELD_11 bool :1, :1, lfence_dispatch:1, ...
>   #define CPUID_BITFIELD_12 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_13 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_14 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_15 uint32_t :32 /* placeholder */
>
> This allows DECL_BITFIELD() to be added to struct cpuid_policy without
> requiring a XEN_CPUFEATURE() declared for the leaf.  The choice of 4 is
> arbitrary, and allows us to add more than one leaf at a time if necessary.
>
> Second, rework generic_identify() to not use feature leaf names.

This should say "not use specific feature names."

Fixed locally.

~Andrew

> The choice of deriving the index from a feature was to avoid mismatches, but
> its correctness depends on bugs like c/s 249e0f1d8f20 ("x86/cpuid: Fix
> TSXLDTRK definition") not happening.
>
> Switch to using FEATURESET_* just like the policy/featureset helpers.  This
> breaks the cognitive complexity of needing to know which leaf a specifically
> named feature should reside in, and is shorter to write.  It is also far
> easier to identify as correct at a glance, given the correlation with the
> CPUID leaf being read.
>
> In addition, tidy up some other bits of generic_identify()
>  * Drop leading zeros from leaf numbers.
>  * Don't use a locked update for X86_FEATURE_APERFMPERF.
>  * Rework extended_cpuid_level calculation to avoid setting it twice.
>  * Use "leaf >= $N" consistently so $N matches with the CPUID input.
>
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Jan Beulich Jan. 27, 2022, 4:39 p.m. UTC | #2
On 27.01.2022 17:09, Andrew Cooper wrote:
> Adding a new feature leaf is a reasonable amount of boilerplate and for the
> patch to build, at least one feature from the new leaf needs defining.  This
> typically causes two non-trivial changes to be merged together.
> 
> First, have gen-cpuid.py write out some extra placeholder defines:
> 
>   #define CPUID_BITFIELD_11 bool :1, :1, lfence_dispatch:1, ...
>   #define CPUID_BITFIELD_12 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_13 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_14 uint32_t :32 /* placeholder */
>   #define CPUID_BITFIELD_15 uint32_t :32 /* placeholder */
> 
> This allows DECL_BITFIELD() to be added to struct cpuid_policy without
> requiring a XEN_CPUFEATURE() declared for the leaf.  The choice of 4 is
> arbitrary, and allows us to add more than one leaf at a time if necessary.
> 
> Second, rework generic_identify() to not use feature leaf names.
> 
> The choice of deriving the index from a feature was to avoid mismatches, but
> its correctness depends on bugs like c/s 249e0f1d8f20 ("x86/cpuid: Fix
> TSXLDTRK definition") not happening.
> 
> Switch to using FEATURESET_* just like the policy/featureset helpers.  This
> breaks the cognitive complexity of needing to know which leaf a specifically
> named feature should reside in, and is shorter to write.  It is also far
> easier to identify as correct at a glance, given the correlation with the
> CPUID leaf being read.
> 
> In addition, tidy up some other bits of generic_identify()
>  * Drop leading zeros from leaf numbers.
>  * Don't use a locked update for X86_FEATURE_APERFMPERF.
>  * Rework extended_cpuid_level calculation to avoid setting it twice.
>  * Use "leaf >= $N" consistently so $N matches with the CPUID input.
> 
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Reviewed-by: Jan Beulich <jbeulich@suse.com>

I will admit that I'm not sure yet whether I will want to break up the
KeyLocker patch just like you did with the PPIN one.

The one thing that worries me some that there's still the unvalidated
connection between FEATURESET_* and the numbers used in the public
cpufeatureset.h. But I have no good idea (yet) for a build-time check.

Jan
Andrew Cooper Jan. 27, 2022, 4:46 p.m. UTC | #3
On 27/01/2022 16:39, Jan Beulich wrote:
> On 27.01.2022 17:09, Andrew Cooper wrote:
>> Adding a new feature leaf is a reasonable amount of boilerplate and for the
>> patch to build, at least one feature from the new leaf needs defining.  This
>> typically causes two non-trivial changes to be merged together.
>>
>> First, have gen-cpuid.py write out some extra placeholder defines:
>>
>>   #define CPUID_BITFIELD_11 bool :1, :1, lfence_dispatch:1, ...
>>   #define CPUID_BITFIELD_12 uint32_t :32 /* placeholder */
>>   #define CPUID_BITFIELD_13 uint32_t :32 /* placeholder */
>>   #define CPUID_BITFIELD_14 uint32_t :32 /* placeholder */
>>   #define CPUID_BITFIELD_15 uint32_t :32 /* placeholder */
>>
>> This allows DECL_BITFIELD() to be added to struct cpuid_policy without
>> requiring a XEN_CPUFEATURE() declared for the leaf.  The choice of 4 is
>> arbitrary, and allows us to add more than one leaf at a time if necessary.
>>
>> Second, rework generic_identify() to not use feature leaf names.
>>
>> The choice of deriving the index from a feature was to avoid mismatches, but
>> its correctness depends on bugs like c/s 249e0f1d8f20 ("x86/cpuid: Fix
>> TSXLDTRK definition") not happening.
>>
>> Switch to using FEATURESET_* just like the policy/featureset helpers.  This
>> breaks the cognitive complexity of needing to know which leaf a specifically
>> named feature should reside in, and is shorter to write.  It is also far
>> easier to identify as correct at a glance, given the correlation with the
>> CPUID leaf being read.
>>
>> In addition, tidy up some other bits of generic_identify()
>>  * Drop leading zeros from leaf numbers.
>>  * Don't use a locked update for X86_FEATURE_APERFMPERF.
>>  * Rework extended_cpuid_level calculation to avoid setting it twice.
>>  * Use "leaf >= $N" consistently so $N matches with the CPUID input.
>>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> Reviewed-by: Jan Beulich <jbeulich@suse.com>

Thanks.

>
> I will admit that I'm not sure yet whether I will want to break up the
> KeyLocker patch just like you did with the PPIN one.
>
> The one thing that worries me some that there's still the unvalidated
> connection between FEATURESET_* and the numbers used in the public
> cpufeatureset.h. But I have no good idea (yet) for a build-time check.

I was also struggling with that.  One thing I considered was having some
semantic structure to

/* Intel-defined CPU features, CPUID level 0x00000007:1.ebx, word 12 */

and have FEATURESET_* written automatically too, but I can't think of a
nice way of organising this right now.

~Andrew
diff mbox series

Patch

diff --git a/xen/arch/x86/cpu/common.c b/xen/arch/x86/cpu/common.c
index 4a163afbfc7e..c6773c85fd3e 100644
--- a/xen/arch/x86/cpu/common.c
+++ b/xen/arch/x86/cpu/common.c
@@ -379,7 +379,7 @@  static void generic_identify(struct cpuinfo_x86 *c)
 	u32 eax, ebx, ecx, edx, tmp;
 
 	/* Get vendor name */
-	cpuid(0x00000000, &c->cpuid_level, &ebx, &ecx, &edx);
+	cpuid(0, &c->cpuid_level, &ebx, &ecx, &edx);
 	*(u32 *)&c->x86_vendor_id[0] = ebx;
 	*(u32 *)&c->x86_vendor_id[8] = ecx;
 	*(u32 *)&c->x86_vendor_id[4] = edx;
@@ -394,7 +394,7 @@  static void generic_identify(struct cpuinfo_x86 *c)
 	/* Note that the vendor-specific code below might override */
 
 	/* Model and family information. */
-	cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
+	cpuid(1, &eax, &ebx, &ecx, &edx);
 	c->x86 = get_cpu_family(eax, &c->x86_model, &c->x86_mask);
 	c->apicid = phys_pkg_id((ebx >> 24) & 0xFF, 0);
 	c->phys_proc_id = c->apicid;
@@ -404,53 +404,53 @@  static void generic_identify(struct cpuinfo_x86 *c)
 
 	/* c_early_init() may have adjusted cpuid levels/features.  Reread. */
 	c->cpuid_level = cpuid_eax(0);
-	cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
-	c->x86_capability[cpufeat_word(X86_FEATURE_FPU)] = edx;
-	c->x86_capability[cpufeat_word(X86_FEATURE_SSE3)] = ecx;
+	cpuid(1, &eax, &ebx,
+	      &c->x86_capability[FEATURESET_1c],
+	      &c->x86_capability[FEATURESET_1d]);
 
 	if ( cpu_has(c, X86_FEATURE_CLFLUSH) )
 		c->x86_clflush_size = ((ebx >> 8) & 0xff) * 8;
 
 	if ( (c->cpuid_level >= CPUID_PM_LEAF) &&
 	     (cpuid_ecx(CPUID_PM_LEAF) & CPUID6_ECX_APERFMPERF_CAPABILITY) )
-		set_bit(X86_FEATURE_APERFMPERF, c->x86_capability);
+		__set_bit(X86_FEATURE_APERFMPERF, c->x86_capability);
+
+	eax = cpuid_eax(0x80000000);
+	if ((eax >> 16) == 0x8000)
+		c->extended_cpuid_level = eax;
 
 	/* AMD-defined flags: level 0x80000001 */
-	c->extended_cpuid_level = cpuid_eax(0x80000000);
-	if ((c->extended_cpuid_level >> 16) != 0x8000)
-		c->extended_cpuid_level = 0;
-	if (c->extended_cpuid_level > 0x80000000)
+	if (c->extended_cpuid_level >= 0x80000001)
 		cpuid(0x80000001, &tmp, &tmp,
-		      &c->x86_capability[cpufeat_word(X86_FEATURE_LAHF_LM)],
-		      &c->x86_capability[cpufeat_word(X86_FEATURE_SYSCALL)]);
+		      &c->x86_capability[FEATURESET_e1c],
+		      &c->x86_capability[FEATURESET_e1d]);
 
 	if (c->extended_cpuid_level >= 0x80000004)
 		get_model_name(c); /* Default name */
 	if (c->extended_cpuid_level >= 0x80000007)
-		c->x86_capability[cpufeat_word(X86_FEATURE_ITSC)]
-			= cpuid_edx(0x80000007);
+		c->x86_capability[FEATURESET_e7d] = cpuid_edx(0x80000007);
 	if (c->extended_cpuid_level >= 0x80000008)
-		c->x86_capability[cpufeat_word(X86_FEATURE_CLZERO)]
-			= cpuid_ebx(0x80000008);
+		c->x86_capability[FEATURESET_e8b] = cpuid_ebx(0x80000008);
 	if (c->extended_cpuid_level >= 0x80000021)
-		c->x86_capability[cpufeat_word(X86_FEATURE_LFENCE_DISPATCH)]
-			= cpuid_eax(0x80000021);
+		c->x86_capability[FEATURESET_e21a] = cpuid_eax(0x80000021);
 
 	/* Intel-defined flags: level 0x00000007 */
-	if ( c->cpuid_level >= 0x00000007 ) {
-		cpuid_count(0x00000007, 0, &eax,
-			    &c->x86_capability[cpufeat_word(X86_FEATURE_FSGSBASE)],
-			    &c->x86_capability[cpufeat_word(X86_FEATURE_PKU)],
-			    &c->x86_capability[cpufeat_word(X86_FEATURE_AVX512_4VNNIW)]);
-		if (eax > 0)
-			cpuid_count(0x00000007, 1,
-				    &c->x86_capability[cpufeat_word(X86_FEATURE_AVX512_BF16)],
+	if (c->cpuid_level >= 7) {
+		uint32_t max_subleaf;
+
+		cpuid_count(7, 0, &max_subleaf,
+			    &c->x86_capability[FEATURESET_7b0],
+			    &c->x86_capability[FEATURESET_7c0],
+			    &c->x86_capability[FEATURESET_7d0]);
+		if (max_subleaf >= 1)
+			cpuid_count(7, 1,
+				    &c->x86_capability[FEATURESET_7a1],
 				    &tmp, &tmp, &tmp);
 	}
 
 	if (c->cpuid_level >= 0xd)
 		cpuid_count(0xd, 1,
-			    &c->x86_capability[cpufeat_word(X86_FEATURE_XSAVEOPT)],
+			    &c->x86_capability[FEATURESET_Da1],
 			    &tmp, &tmp, &tmp);
 }
 
diff --git a/xen/tools/gen-cpuid.py b/xen/tools/gen-cpuid.py
index b953648b6572..470cd76d1c52 100755
--- a/xen/tools/gen-cpuid.py
+++ b/xen/tools/gen-cpuid.py
@@ -423,6 +423,8 @@  def write_results(state):
 
 """)
 
+    state.bitfields += ["uint32_t :32 /* placeholder */"] * 4
+
     for idx, text in enumerate(state.bitfields):
         state.output.write(
             "#define CPUID_BITFIELD_%d \\\n    %s\n\n"