public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc/fw/x86-diagnostics] WIP CPUID consistency checks
@ 2024-06-07 14:44 Florian Weimer
  0 siblings, 0 replies; only message in thread
From: Florian Weimer @ 2024-06-07 14:44 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=11db7b454a9cb72ce14be5498cc3f137e9026291

commit 11db7b454a9cb72ce14be5498cc3f137e9026291
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue May 28 13:12:16 2024 +0200

    WIP CPUID consistency checks

Diff:
---
 sysdeps/unix/sysv/linux/x86_64/Makefile            |   6 +
 .../linux/x86_64/dl-x86_cpu_feature_diagnostics.c  | 227 +++++++++++++++++
 sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S     | 274 +++++++++++++++++++++
 .../x86_64/manual-x86_cpu_feature-diagnostics.py   |  34 +++
 sysdeps/x86/Makefile                               |   7 +-
 sysdeps/x86/dl-diagnostics-cpu.c                   |  19 ++
 sysdeps/x86/dl-get-cpu-features.c                  |  28 ++-
 sysdeps/x86/dl-x86_cpu_feature_diagnostics.c       |  26 ++
 sysdeps/x86/include/cpu-features.h                 |  23 ++
 9 files changed, 632 insertions(+), 12 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/x86_64/Makefile b/sysdeps/unix/sysv/linux/x86_64/Makefile
index fcbffd81cb..4fb0dd4d1d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/Makefile
+++ b/sysdeps/unix/sysv/linux/x86_64/Makefile
@@ -15,6 +15,12 @@ gen-as-const-headers += sigaltstack-offsets.sym
 endif
 
 ifeq ($(subdir),elf)
+sysdep-dl-routines += dl-x86_probes
+shared-only-routines += dl-x86_probes
+# Used internally by dl-x86_cpu_feature_diagnostics.c.
+CFLAGS-dl-catch.os += $(rtld-early-cflags)
+CFLAGS-rtld-__longjmp.os += $(rtld-early-cflags)
+
 ifeq (yes,$(enable-x86-isa-level))
 tests += \
   tst-glibc-hwcaps-2 \
diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c b/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c
new file mode 100644
index 0000000000..6486ebba5d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c
@@ -0,0 +1,227 @@
+/* CPU diagnostics probing.  Linux/x86-64 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <cpu-features.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/wait.h>
+#include <sysdep.h>
+
+static void
+_dl_x86_probe (struct x86_cpu_feature_diagnostics *diag, bool reported,
+               void (*probe) (void))
+{
+  if (reported)
+    diag->reported |= 1ULL << diag->count;
+
+  /* Use fork/waitid for crash handling.  This is simpler than using
+     signal handling: it does not need global data to communicate with
+     the handler, nor building out-of-line helper functions to the
+     baseline ISA, and it avoids dealing differences in sigset_t size.  */
+  long int ret = INTERNAL_SYSCALL_CALL (fork);
+  if (ret == 0)
+    {
+      /* New process that runs the probe.  This may trigger a crash.  */
+      probe ();
+
+      INTERNAL_SYSCALL_CALL (exit_group, 0);
+    }
+  else if (ret > 0)
+    {
+      siginfo_t si;
+      ret = INTERNAL_SYSCALL_CALL (waitid, P_PID, ret, &si, WEXITED, NULL);
+      if (ret >=0 && si.si_status == 0)
+        /* Probe was successful.   */
+        diag->probed |= 1ULL << diag->count;
+    }
+
+  ++diag->count;
+}
+
+void
+_dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *cpu_features,
+                                     struct x86_cpu_feature_diagnostics *diag)
+{
+  /* x86-64-v2 features.  */
+  extern void _dl_x86_probe_cmpxchg16b (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, CMPXCHG16B),
+                 _dl_x86_probe_cmpxchg16b);
+
+  extern void _dl_x86_probe_sahf (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, LAHF64_SAHF64),
+                 _dl_x86_probe_sahf);
+
+  extern void _dl_x86_probe_popcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, POPCNT),
+                 _dl_x86_probe_popcnt);
+
+  extern void _dl_x86_probe_sse3 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE3),
+                 _dl_x86_probe_sse3);
+
+  extern void _dl_x86_probe_sse4_1 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE4_1),
+                 _dl_x86_probe_sse4_1);
+
+  extern void _dl_x86_probe_sse4_2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE4_2),
+                 _dl_x86_probe_sse4_2);
+
+  extern void _dl_x86_probe_ssse3 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSSE3),
+                 _dl_x86_probe_ssse3);
+
+  /* x86-64-v3 features.  */
+  extern void _dl_x86_probe_avx (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_avx);
+
+  /* AVX probe using xmm registers.  */
+  extern void _dl_x86_probe_avx_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_avx_xmm);
+
+  extern void _dl_x86_probe_avx2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX2),
+                 _dl_x86_probe_avx2);
+
+  extern void _dl_x86_probe_bmi1 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI1),
+                 _dl_x86_probe_bmi1);
+
+  /* Alternative BMI1 probe.  Perhaps harder to mask.  */
+  extern void _dl_x86_probe_bmi1_tzcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI1),
+                 _dl_x86_probe_bmi1_tzcnt);
+
+  extern void _dl_x86_probe_bmi2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI2),
+                 _dl_x86_probe_bmi2);
+
+  extern void _dl_x86_probe_f16c (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, F16C),
+                 _dl_x86_probe_f16c);
+
+  extern void _dl_x86_probe_fma (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, FMA),
+                 _dl_x86_probe_fma);
+
+  /* FMA4 is not part of x86-64-v3, but may produce a useful hint.  */
+  extern void _dl_x86_probe_fma4 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, FMA4),
+                 _dl_x86_probe_fma4);
+
+  extern void _dl_x86_probe_lzcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, LZCNT),
+                 _dl_x86_probe_lzcnt);
+
+  extern void _dl_x86_probe_movbe (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, MOVBE),
+                 _dl_x86_probe_movbe);
+
+  extern void _dl_x86_probe_osxsave (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, OSXSAVE),
+                 _dl_x86_probe_osxsave);
+
+  /* x86-64-v4 features.  */
+  extern void _dl_x86_probe_avx512f (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512F),
+                 _dl_x86_probe_avx512f);
+
+  extern void _dl_x86_probe_avx512bw (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512BW),
+                 _dl_x86_probe_avx512bw);
+
+  extern void _dl_x86_probe_avx512bw_ymm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512BW),
+                 _dl_x86_probe_avx512bw_ymm);
+
+  extern void _dl_x86_probe_avx512cd (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512CD),
+                 _dl_x86_probe_avx512cd);
+
+  extern void _dl_x86_probe_avx512cd_ymm0 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512CD),
+                 _dl_x86_probe_avx512cd_ymm0);
+
+  extern void _dl_x86_probe_avx512vl (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512VL),
+                 _dl_x86_probe_avx512vl);
+
+  /* Other CPU features, not part of microarchitecture levels.  */
+  extern void _dl_x86_probe_adx (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, ADX),
+                 _dl_x86_probe_adx);
+
+  extern void _dl_x86_probe_aes (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AES),
+                 _dl_x86_probe_aes);
+
+  extern void _dl_x86_probe_aes_avx (void) attribute_hidden;
+  _dl_x86_probe (diag,
+                 CPU_FEATURE_USABLE_P (cpu_features, AES)
+                 && CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_aes_avx);
+
+  extern void _dl_x86_probe_vaes (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, VAES),
+                 _dl_x86_probe_vaes);
+
+  extern void _dl_x86_probe_sha (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SHA),
+                 _dl_x86_probe_sha);
+
+  extern void _dl_x86_probe_avx512_vbmi (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI),
+                 _dl_x86_probe_avx512_vbmi);
+
+  extern void _dl_x86_probe_avx512_vbmi_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI),
+                 _dl_x86_probe_avx512_vbmi_xmm);
+
+  extern void _dl_x86_probe_avx512_vbmi2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI2),
+                 _dl_x86_probe_avx512_vbmi2);
+
+  extern void _dl_x86_probe_avx512_vbmi2_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI2),
+                 _dl_x86_probe_avx512_vbmi2_xmm);
+
+  extern void _dl_x86_probe_avx_vnni (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX_VNNI),
+                 _dl_x86_probe_avx_vnni);
+
+  extern void _dl_x86_probe_avx512_vnni (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VNNI),
+                 _dl_x86_probe_avx512_vnni);
+
+  extern void _dl_x86_probe_avx512_ifma (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_IFMA),
+                 _dl_x86_probe_avx512_ifma);
+
+  serialize
+  tpause
+  ptwrite
+  xsusldtrk
+  clmul
+  crc32
+
+  extern void _dl_x86_probe_apx_f (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, APX_F),
+                 _dl_x86_probe_apx_f);
+}
diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S b/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S
new file mode 100644
index 0000000000..e1226763ac
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S
@@ -0,0 +1,274 @@
+/* Diagnostics probes for the x86 CPU family.  Generic version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+ENTRY (_dl_x86_probe_cmpxchg16b)
+	xorl %eax, %eax
+	movq %rax, -8(%rsp)
+	movq %rax, -16(%rsp)
+	xorl %edx, %edx
+	cmpxchg16b -8(%rsp)
+	ret
+END (_dl_x86_probe_cmpxchg16b)
+
+ENTRY (_dl_x86_probe_sahf)
+	xorl %eax, %eax
+	sahf
+	ret
+END (_dl_x86_probe_sahf)
+
+ENTRY (_dl_x86_probe_popcnt)
+	xorl %eax, %eax
+	popcnt %eax, %eax
+	ret
+END (_dl_x86_probe_popcnt)
+
+ENTRY (_dl_x86_probe_sse3)
+	pxor %xmm0, %xmm0
+	addsubpd %xmm0,  %xmm0
+	ret
+END (_dl_x86_probe_sse3)
+
+ENTRY (_dl_x86_probe_sse4_1)
+	pxor %xmm0, %xmm0
+	blendpd $1, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_sse4_1)
+
+ENTRY (_dl_x86_probe_sse4_2)
+	pxor %xmm0, %xmm0
+	pcmpestri $0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_sse4_2)
+
+ENTRY (_dl_x86_probe_ssse3)
+	pxor %xmm0, %xmm0
+	phaddd %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_ssse3)
+
+ENTRY (_dl_x86_probe_avx)
+	vzeroall
+	ret
+END (_dl_x86_probe_avx)
+
+ENTRY (_dl_x86_probe_avx_xmm)
+	pxor %xmm0, %xmm0
+	pxor %xmm1, %xmm1
+	vpxor %xmm0, %xmm1, %xmm2
+	ret
+END (_dl_x86_probe_avx_xmm)
+
+ENTRY (_dl_x86_probe_avx2)
+	vpxor %ymm0, %ymm0, %ymm0
+	vpermd %ymm0, %ymm0, %ymm0
+	ret
+END (_dl_x86_probe_avx2)
+
+ENTRY (_dl_x86_probe_bmi1)
+	xorl  %eax,  %eax
+	andnl %eax, %eax, %eax
+	ret
+END (_dl_x86_probe_bmi1)
+
+ENTRY (_dl_x86_probe_bmi1_tzcnt)
+	xorl %eax, %eax
+	/* Executes as bsfl if unsupported.  */
+	tzcntl %eax, %eax
+	cmp $32, %eax
+	jne 1f
+	ret
+1:
+	ud2
+END (_dl_x86_probe_bmi1_tzcnt)
+
+ENTRY (_dl_x86_probe_bmi2)
+	xorl  %eax,  %eax
+	bzhil %eax, %eax, %eax
+	ret
+END (_dl_x86_probe_bmi2)
+
+ENTRY (_dl_x86_probe_f16c)
+	pxor %xmm0, %xmm0
+	vcvtph2ps %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_f16c)
+
+ENTRY (_dl_x86_probe_fma)
+	pxor %xmm0, %xmm0
+	vfmadd132pd %xmm0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_fma)
+
+ENTRY (_dl_x86_probe_fma4)
+	pxor %xmm0, %xmm0
+	vfmaddpd %xmm0, %xmm0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_fma4)
+
+ENTRY (_dl_x86_probe_lzcnt)
+	xorl %eax, %eax
+	/* Executes as bsrl if unsupported.  */
+	lzcntl %eax, %eax
+	cmp $32, %eax
+	jne 1f
+	ret
+1:
+	ud2
+END (_dl_x86_probe_lzcnt)
+
+ENTRY (_dl_x86_probe_movbe)
+	movbeq (%rsp), %rax
+	ret
+1:
+	ud2
+END (_dl_x86_probe_movbe)
+
+ENTRY (_dl_x86_probe_osxsave)
+	xorl %ecx, %ecx
+	xgetbv
+	ret
+END (_dl_x86_probe_osxsave)
+
+ENTRY (_dl_x86_probe_avx512f)
+	xorl %eax, %eax
+	kmovw %eax, %k0
+	ret
+END (_dl_x86_probe_avx512f)
+
+ENTRY (_dl_x86_probe_avx512bw)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vdbpsadbw $0, %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512bw)
+
+ENTRY (_dl_x86_probe_avx512bw_ymm)
+	vpxorq %ymm0, %ymm0, %ymm0
+	vdbpsadbw $0, %ymm0, %ymm0, %ymm0
+	ret
+END (_dl_x86_probe_avx512bw)
+
+ENTRY (_dl_x86_probe_avx512cd)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vplzcntd %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512cd)
+
+ENTRY (_dl_x86_probe_avx512cd_ymm0)
+	vpxorq %ymm0, %ymm0, %ymm0
+	vplzcntd %ymm0, %ymm0
+	ret
+END (_dl_x86_probe_avx512cd_ymm0)
+
+ENTRY (_dl_x86_probe_avx512dq)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vpmullq %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512dq)
+
+ENTRY (_dl_x86_probe_avx512dq_ymm0)
+	vpxorq %ymm0, %ymm0, %ymm0
+	vpmulld %ymm0, %ymm0, %ymm0
+	ret
+END (_dl_x86_probe_avx512dq_ymm0)
+
+ENTRY (_dl_x86_probe_avx512vl)
+	xorl %eax, %eax
+	vpbroadcastq %rax, %xmm1
+	ret
+END (_dl_x86_probe_avx512vl)
+
+ENTRY (_dl_x86_probe_adx)
+	xorl %eax, %eax
+	adcxl %eax, %eax
+	ret
+END (_dl_x86_probe_adx)
+
+ENTRY (_dl_x86_probe_aes)
+	pxor %xmm0, %xmm0
+	aesenc %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_aes)
+
+ENTRY (_dl_x86_probe_aes_avx)
+	pxor %xmm0, %xmm0
+	vaesenc %xmm0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_aes_avx)
+
+ENTRY (_dl_x86_probe_vaes)
+	vpxor %ymm0, %ymm0, %ymm0
+	vaesenc %ymm0, %ymm0, %ymm0
+	ret
+END (_dl_x86_probe_vaes)
+
+ENTRY (_dl_x86_probe_sha)
+	pxor %xmm0, %xmm0
+	sha1rnds4 $0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_sha)
+
+ENTRY (_dl_x86_probe_avx512_vbmi)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vpermb %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512_vbmi)
+
+ENTRY (_dl_x86_probe_avx512_vbmi_xmm)
+	pxor %xmm0, %xmm0
+	vpermb %xmm0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_avx512_vbmi_xmm)
+
+ENTRY (_dl_x86_probe_avx512_vbmi2)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vpshrdd $1, %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512_vbmi2)
+
+ENTRY (_dl_x86_probe_avx512_vbmi2_xmm)
+	pxor %xmm0, %xmm0
+	vpshrdd $1, %xmm0, %xmm0, %xmm0
+	ret
+END (_dl_x86_probe_avx512_vbmi2_xmm)
+
+ENTRY (_dl_x86_probe_avx_vnni)
+	pxor %xmm0, %xmm0
+	/* Default is to use EVEX encoding.  */
+	/* {vex} vpdpbusd %xmm0, %xmm0, %xmm0 */
+	.byte 0xc4, 0xe2, 0x79, 0x50, 0xc0
+	ret
+END (_dl_x86_probe_avx_vnni)
+
+ENTRY (_dl_x86_probe_avx512_vnni)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vpdpbusd %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512_vnni)
+
+ENTRY (_dl_x86_probe_avx512_ifma)
+	vpxorq %zmm0, %zmm0, %zmm0
+	vpmadd52luq %zmm0, %zmm0, %zmm0
+	ret
+END (_dl_x86_probe_avx512_ifma)
+
+ENTRY (_dl_x86_probe_apx_f)
+	.byte 0x62, 0xf4, 0x7c, 0x18, 0x01, 0xff /* add %edi, %edi, %eax */
+	ret
+END (_dl_x86_probe_apx_f)
diff --git a/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py b/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py
new file mode 100644
index 0000000000..a8730d3371
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py
@@ -0,0 +1,34 @@
+# CPU diagnostics probing.  Generating documentatable for t he manual
+# Copyright (C) 2024 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+import re
+import sys
+
+path, = sys.argv[1:]
+
+RE_PROBE = re.compile('^\s+_dl_x86_probe_([a-z0-9_]+)\);$')
+
+bit = 1
+with open(path) as inp:
+    for line in inp:
+        m = RE_PROBE.match(line)
+        if m:
+            name = m.group(1)
+            print('@item 0x{:08x}:{:08x}'.format(bit >> 32, bit & 0xffffffff))
+            print('@code{' + name + '}')
+            bit *= 2
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 5311b594af..010f15b122 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -4,7 +4,11 @@ endif
 
 ifeq ($(subdir),elf)
 sysdep_routines += get-cpuid-feature-leaf
-sysdep-dl-routines += dl-get-cpu-features
+sysdep-dl-routines += \
+  dl-get-cpu-features \
+  dl-x86_cpu_feature_diagnostics \
+  # sysdep-dl-routines
+shared-only-routines += dl-x86_cpu_feature_diagnostics
 sysdep_headers += \
   bits/platform/features.h \
   bits/platform/x86.h \
@@ -12,6 +16,7 @@ sysdep_headers += \
 # sysdep_headers
 
 CFLAGS-dl-get-cpu-features.os += $(rtld-early-cflags)
+CFLAGS-dl-x86_cpu_feature_diagnostics.os += $(rtld-early-cflags)
 CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
 
 tests += \
diff --git a/sysdeps/x86/dl-diagnostics-cpu.c b/sysdeps/x86/dl-diagnostics-cpu.c
index ceafde9481..5555c473c1 100644
--- a/sysdeps/x86/dl-diagnostics-cpu.c
+++ b/sysdeps/x86/dl-diagnostics-cpu.c
@@ -51,6 +51,14 @@ print_cpu_feature_preferred (const char *label, unsigned int flag)
   _dl_printf("x86.cpu_features.preferred.%s=0x%x\n", label, flag);
 }
 
+static void
+_dl_x86_cpu_feature_diagnostics_print (const char *label,
+				       unsigned long long int value)
+{
+  _dl_printf("x86.diagnostics.");
+  _dl_diagnostics_print_labeled_value (label, value);
+}
+
 void
 _dl_diagnostics_cpu (void)
 {
@@ -132,6 +140,17 @@ _dl_diagnostics_cpu (void)
 	  == sizeof (*cpu_features),
       "last cpu_features field has been printed");
 
+  {
+    struct x86_cpu_feature_diagnostics diag;
+    _dl_x86_cpu_feature_diagnostics_init (&diag);
+    _dl_x86_cpu_feature_diagnostics_run (cpu_features, &diag);
+    _dl_x86_cpu_feature_diagnostics_print ("count", diag.count);
+    _dl_x86_cpu_feature_diagnostics_print ("reported", diag.reported);
+    _dl_x86_cpu_feature_diagnostics_print ("probed", diag.probed);
+    _dl_x86_cpu_feature_diagnostics_print ("filtered",
+					   diag.probed & ~diag.reported);
+  }
+
   _dl_diagnostics_cpuid ();
 }
 
diff --git a/sysdeps/x86/dl-get-cpu-features.c b/sysdeps/x86/dl-get-cpu-features.c
index f36d42d6af..7961aa3b9d 100644
--- a/sysdeps/x86/dl-get-cpu-features.c
+++ b/sysdeps/x86/dl-get-cpu-features.c
@@ -33,14 +33,20 @@ void (*const __x86_cpu_features_p) (void) attribute_hidden
   = __x86_cpu_features;
 
 _Noreturn static void __attribute__ ((unused))
-_dl_x86_init_cpu_failure (const struct cpu_features *cpu_features, int level)
+_dl_x86_init_cpu_failure (const struct cpu_features *cpu_features,
+			  const char *label)
 {
-  if (level == 5)
-    _dl_fatal_printf ("\
-Fatal glibc error: CPU does not support APX\n");
-  else
-    _dl_fatal_printf ("\
-Fatal glibc error: CPU does not support x86-64-v%d\n", level);
+  struct x86_cpu_feature_diagnostics diag;
+  _dl_x86_cpu_feature_diagnostics_init (&diag);
+  _dl_x86_cpu_feature_diagnostics_run (cpu_features, &diag);
+
+  _dl_fatal_printf ("\
+Fatal glibc error: CPU does not support %s [%u 0x%x:%x 0x%x:%x]\n",
+		    label, diag.count,
+		    (unsigned int) (diag.reported >> 32),
+		    (unsigned int) diag.reported,
+		    (unsigned int) (diag.probed >> 32),
+		    (unsigned int) diag.probed);
 }
 
 void
@@ -58,23 +64,23 @@ _dl_x86_init_cpu_features (void)
   && defined GCCMACRO__SSE3__ && defined GCCMACRO__SSSE3__		\
   && defined GCCMACRO__SSE4_1__ && defined GCCMACRO__SSE4_2__
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V2))
-	_dl_x86_init_cpu_failure (cpu_features, 2);
+	_dl_x86_init_cpu_failure (cpu_features, "x86-64-v2");
 #   if defined GCCMACRO__AVX__ && defined GCCMACRO__AVX2__ \
   && defined GCCMACRO__F16C__ && defined GCCMACRO__FMA__   \
   && defined GCCMACRO__LZCNT__ && defined HAVE_X86_MOVBE
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V3))
-	_dl_x86_init_cpu_failure (cpu_features, 3);
+	_dl_x86_init_cpu_failure (cpu_features, "x86-64-v3");
 #    if defined GCCMACRO__AVX512F__ && defined GCCMACRO__AVX512BW__ \
      && defined GCCMACRO__AVX512CD__ && defined GCCMACRO__AVX512DQ__ \
      && defined GCCMACRO__AVX512VL__
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V4))
-	_dl_x86_init_cpu_failure (cpu_features, 4);
+	_dl_x86_init_cpu_failure (cpu_features, "x86-64-v4");
 #    endif /* ISA level 4 */
 #   endif /* ISA level 3 */
 #  endif /* ISA level 2 */
 # ifdef GCCMACRO__APX_F__
       if (!CPU_FEATURE_USABLE_P (cpu_features, APX_F))
-	_dl_x86_init_cpu_failure (cpu_features, 5);
+	_dl_x86_init_cpu_failure (cpu_features, "APX");
 # endif
 # endif /* IS_IN (rtld) */
     }
diff --git a/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c b/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c
new file mode 100644
index 0000000000..bb7ff9c24d
--- /dev/null
+++ b/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c
@@ -0,0 +1,26 @@
+/* CPU diagnostics probing.  Generic x86 version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <cpu-features
+
+/* The generic version does not have any probes.  */
+void
+_dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *cpu_features,
+                                     struct x86_cpu_feature_diagnostics *diag)
+{
+}
diff --git a/sysdeps/x86/include/cpu-features.h b/sysdeps/x86/include/cpu-features.h
index cd7bd27cf3..2eea95d56b 100644
--- a/sysdeps/x86/include/cpu-features.h
+++ b/sysdeps/x86/include/cpu-features.h
@@ -987,6 +987,29 @@ extern const struct cpu_features *_dl_x86_get_cpu_features (void)
 
 #define __get_cpu_features() _dl_x86_get_cpu_features()
 
+/* Used to store diagnostic information for startup failure reporting.
+   See _dl_x86_init_cpu_failure in sysdeps/x86/dl-diagnostics-cpu.c.  */
+struct x86_cpu_feature_diagnostics
+{
+  unsigned int count;		/* Bits recorded.  */
+  unsigned long long int reported; /* From CPUID.  */
+  unsigned long long int probed;  /* From execution probing.  */
+};
+
+/* Initialize *DIAG prior to the diagnostics run below.   */
+static inline void
+_dl_x86_cpu_feature_diagnostics_init (struct x86_cpu_feature_diagnostics *diag)
+{
+  diag->count = 0;
+  diag->reported = 0;
+  diag->probed = 0;
+}
+
+/* Updated the diagnostics with CPUID and execution probing information.  */
+void _dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *,
+					  struct x86_cpu_feature_diagnostics *)
+  attribute_hidden;
+
 #if defined (_LIBC) && !IS_IN (nonlib)
 /* Unused for x86.  */
 # define INIT_ARCH()

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-06-07 14:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-07 14:44 [glibc/fw/x86-diagnostics] WIP CPUID consistency checks Florian Weimer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).